iOS で任意の UIView をピクチャーインピクチャーする
動機
ピクチャーインピクチャー (PiP) は iOS 14 で使用できるようになった機能です。再生中の動画を画面上に小さく配置し、他のアプリに移動してもその画面が隠れずにその動画を見続けられるといった機能で、ながら視聴などに便利で重宝する機能だと思います。自分もよくゲーム配信をみながらブラウジングしたりして重宝しています。
最近、ミラティブのエンジニアさんが実装した、PiP を利用した新しい機能である「配信コメントバー」の紹介記事を見て、任意の View を表示して画面を構成するその仕組みに「これは発明だ!」と感動したので、それを実際に実装するとどうなるのか? を疑問に思い実装をしてみました。
また、自分は SNS クライアントアプリを作成していて、そこでこの PiP を利用して常に最新のコメントが PiP で画面手前に表示されたら面白いなと思い、実際にアプリに組み込めないかを試作してみる意味合いで、サンプルアプリを作成しました。
成果物
成果物では現在時刻を表示する UILabel
を作成し、それを動画ソースとして、PiP に表示しています。このように、PiP で UI が表示され、内容が変化し、また別のアプリに切り替えても動作することが確認できます。
実装
細かい部分はレポジトリを参考にしていただくとして、簡単に説明すると、iOS 15 から新たに AVPictureInPictureController.ContentSource
が追加されました。(公式リンク) これによって、PiP の動画ソースとして AVSampleBufferDisplayLayer
も扱えるようになり、かつ様々なコールバック関数が用意され、非常に PiP で表現できることの幅が広がりました。例として、UIKit
で書かれている場合の基本的な View
のクラスであるUIView
についても、ほぼそのまま PiP で簡単に表示できるようになりました。
成果物においては、UIView
を UIImage
に変換し、更に CMSampleBuffer
に変換した上で、AVSampleBufferDisplayLayer
に enque
することで動画ソースを実現しています。成果物では現在時刻を表示している UILabel
を対象に表示しています。
注意事項
なぜか 実機 でしか PiP に対応していません。iOS シミュレータでは PiP は動作しないので注意してください。
AVKit
, AVFoundation
関連の実装はあまり知識がないので、もしかしたら無駄な実装になっている可能性があります。特に UIImage
から CMSampleBuffer
への変換は一旦 Data (jpeg)
を経由して行っているので、詳しい方がいらっしゃったら、最適化していただければとても嬉しいです。また、PiP のハンドリングが甘い部分があり、条件によって再生が止まってしまう場合があります。
AVSampleBufferDisplayLayer
に enque
したら UIView
の内容が表示されるため、UIView
の内容が変化する度に enque
する必要があり、その描画コストはそれなりに大きいです。アニメーションなどを行う場合には注意してください。本レポジトリでは 0.3 秒毎に描画を行っています。
PiP なので当然ですが、この PiP をしている場合、同時に他の動画の再生はできません。ニコニコみたいなことができればとも思ったんですが、動画に対しては難しそうです。
もし反響があれば、UIView
を継承した PiPView
のような、UIView
としても使えて、API を叩くと、その中身を PiP してくれるようなライブラリを提供しようと考えています。
参考
-
bricklife/ImagePipDemo
- PiP が出た当初に本アイデアを実現されていたレポジトリ。
- iOS15 の API が出る以前から実現自体は可能でした。
-
jazzychad/PiPBugDemo
-
tvOS
macOS
上での PiP の挙動について報告しているレポジトリ。 - このレポジトリは複数の画像を PiP で順に再生しています。
- かなり実装を参考にさせてもらいました。
-
おわりに
この UI を PiP で出す機能については、色々な応用ができそうな機能だと感じています。最新の株価を常に表示したり、ニュースのヘッドラインを常に流してみたり、ウィジェットよりもリアルタイム性の強い情報に活用できると思います。是非このレポジトリを参考にして、面白いアイデアを実現してくれるととても嬉しいです!
Discussion