こんにちは、クライアントエンジニアのちぎら(@_naru_jpn)です。先日ミラティブの iOS アプリバージョン 9.38.0 にて「配信コメントバー」機能をリリースしました。この機能は iOS15 から利用可能になった PiP に関する技術を活用したもので、ミラティブ以外のアプリを開いている時に、端末の画面上にミラティブ固有のコンテンツを表示できる革新的な機能です。今回は、この「配信コメントバー」機能について背景や概要をご紹介したいと思います。
「配信コメントバー」機能とは
従来、iOS ではアプリがバックグラウンド状態に移行した後は、画面上に独自のコンテンツを表示するといったことは基本的にできませんでした。例えば、ミラティブ iOS アプリでは配信中のコメントを届けるために、プッシュ通知を利用しています。
iOS 15 からピクチャ・イン・ピクチャ(以下: PiP)の柔軟性が増したことで、例えば標準の UI ライクな動的コンテンツを PiP 上に描画するといったことが可能になりました。新機能である「配信コメントバー」は、他のアプリを開きながらでも、視聴者のコメントや配信情報を常に画面上に表示することができます。
本格開発のきっかけ
iOS 15 での OS の変更には、ミラティブ iOS アプリにとって悪い事と良い事が両方含まれていました。
悪い事:iOS 15.1 からの画面共有中の通知の制限
iOS 15.1 から画面共有中のプッシュ通知が設定によって制限されるようになりました。1「画面共有」とは Apple 的には iOS 15.1 から利用可能になった SharePlay2 を主に意識しているのだと思いますが、現時点で「ミラティブで配信している状態」も「画面共有」中として扱われおり、設定によってはプッシュ通知が表示されなくなります。加えてこの設定はデフォルトでオフになっているため、配信前にユーザーに設定を変更してもらわないと配信中に視聴者のコメントを受け取れないという事態になります。
良い事:iOS 15.0 で導入されたピクチャ・イン・ピクチャ機能の拡張
iOS 15.0 から新たにクラス AVPictureInPictureController.ContentSource
が追加されました。3 この追加によって、AVSampleBufferDisplayLayer
上で再生している動画も PiP で扱えるようになりました。AVSampleBufferDisplayLayer
は CMSampleBuffer
を与えることによって動画を再生してくれますが、CMSampleBuffer
は UIImage
などを材料にして生成することができます。要するに、PiP の描画方法に自由度が増え、再現できる機能や表示のバリエーションが増えたということです。
iOS 15.0 がリリースされた時点で AVPictureInPictureController.ContentSource
の存在は社内で把握されていて、ごく簡単なサンプルも存在していたのですが、実装コストの観点からなかなか本格的な開発に着手できずにいました。iOS 15.1 で配信中のプッシュ通知の問題が把握され、ユーザーに設定の変更を促すと同時に、そもそも PiP を活用するというアイディアが実用に足るのかどうかということも含めて R&D が本格的に開始されました。
動作の概要
配信コメントバー機能で定義した、もしくは使用しているクラスとその役割を簡単にご紹介します。
PiPController
- PiP の開始/停止などの制御
- CMSampleBuffer の生成
- 描画ループの管理
PiPRenderer
- PiP に表示するコンテンツの描画
AVSampleBufferDisplayLayer
AVFoundation
に含まれているクラス- ここに描画された内容が PiP 上に映る
めちゃくちゃ簡略化すると以下のように動作をしています。
/* PiPController.swift */ // AVPictureInPictureController の生成 let controller = AVPictureInPictureController(contentSource: .init(sampleBufferDisplayLayer: sampleBufferDisplayLayer, playbackDelegate: self)) ... // PiP の開始 controller.startPictureInPicture() // PiP の停止 controller.stopPictureInPicture() ... // 描画ループ func render() { let sampleBuffer = makeCMSampleBuffer() pipRenderer.render(on: sampleBuffer) sampleBufferDisplayLayer.enque(sampleBuffer) }
描画処理はミラティブのアプリケーションが行なっているのですが、通常のアプリが動作している中で PiP 内のコンテンツも描画するとなると、描画のコストが非常に大事になってきます。描画の方法によってはメインスレッドで処理を行う必要があり、描画にコストがかかると、アプリが UI 操作を受け付けなくなったり、スクロールのような動作が滑らかでなくなったりします。
PiP の描画コストを如何に削減したかについては、次回のテックブログに書きたいと思います。お楽しみにしてください!
まとめ
iOS 15 からの PiP を活用した、新しい取り組みについてご紹介しました。実際の動作が気になる方はぜひ配信をしてみてください!技術的に非常におもしろい領域ですが、PiP の挙動は OS の制御下にある部分が多く、使い勝手やユーザーのメリットに繋がるのかといったことは継続的に見ていく必要があると思っています。一方で、新しい領域に挑戦をして、このように高速でリリースまで持ち込めたことには非常に興奮しています。こういった動きができる環境は大事にしていきたいですね。
We are hiring!
ミラティブでは一緒にアプリを作ってくれる iOS エンジニアを募集しています!
少しでも興味を持っていただいた方はお話を聞いていただくだけでも結構ですので、気軽にご連絡ください。
www.mirrativ.co.jp
エンジニア向け会社紹介資料
- 正確には iOS 15.0 の時点で画面共有中の通知の設定の項目は存在していましたが、機能していませんでした。後述する SharePlay 機能のリリースタイミングと関係していると思いますが、不思議ですね。↩
- https://support.apple.com/ja-jp/HT212823↩
- https://developer.apple.com/documentation/avkit/avpictureinpicturecontroller/contentsource↩