-
AVKitの新機能
ピクチャインピクチャの拡張点とmacOS上のフルスクリーンの改良点について確認しましょう。新しいコンテンツソースAPIを検証して、AVPictureInPictureControllerでAVSampleBufferDisplayLayerがどのようにサポートされているのかを学びましょう。また、macOS上のAppまたはMac Catalyst Appで、シームレスなフルスクリーン体験を提供するための推奨手順をお伝えします。
リソース
関連ビデオ
WWDC21
WWDC19
-
ダウンロード
♪ (AVKitの新機能) 「AVKitの新機能にようこそ!」 私の名前はマーティ パイ AVKitチームのエンジニアです 本日お話するのは PiPの改良点ならびに macOSのフルスクリーン 体験についてです。 ではさっそく PiPから始めましょう PiPがあれば デバイスでマルチタスクを 処理しながら 動画コンテンツを お楽しみいただけます たとえば フルスクリーンで 動画を視聴している間に メッセージを受信した場合 引き続き動画を見ながら そのメッセージに返信できます 動画は自動的にPiPに入り 返信し終わったら すぐにフルスクリーン再生 を再開できます これで本当にシームレスな 視聴体験が生まれ ユーザは動画を視聴しながら このような動作を 期待できるのです PiPをAppに統合する 詳しい 手順については AVKitによる直観的な メディア再生に関する この2019セッションの 視聴をお勧めします 今年の新オプションにより 動画をインライン再生 している場合に ユーザがスワイプで ホーム画面に戻ると 自動的にPiPに入れます この動作の有効と無効を 切り替えるには canStartPictureInPicture AutomaticallyFromInline プロパティを使います ネイティブコントロールを 使用するAppであれば AVPlayerViewController カスタムUIを実装した Appであれば AVPictureInPictureController で利用できます 再生中のコンテンツに 集中したい場合にのみ このフラグをtrueに 設定してください AVPlayerViewControllerで 動画コンテンツを 表示すれば PiPが処理されます 何もする必要はありません AVPlayerViewController を使用しない場合も AVPictureInPictureController でネイティブPiP体験を Appに導入できます まずAppのオーディオ セッション カテゴリを再生用に設定し PiPのバックグラウンド モードを有効にする 必要があります あとは pictureInPictureController を作成し playerLayerへの 参照を渡すだけです ユーザがボタンでPiPを 切り替えようとしたら コントローラオブジェクトの start PiPまたは stop PiPを 呼び出せばいいのです これまでのPiPは AVPlayerベースの コンテンツを中心に 構築されていました 今やAVSampleBufferDisplayLayer についても同レベルで サポートされるのです PiPコントローラを プレーヤーレイヤーで 作成するのではなく まずContentSourceを 作成してAVPlayerLayer または今回のように AVSampleBufferDisplayLayer で設定します ユーザにとっては同一の PiP体験になります AVSampleBufferDisplayLayer のPiP対応に伴い 開発者である皆さんには 新しい責任が発生します この再生デリゲート に目を向けてみましょう メディアの再生は AVPlayerで管理されない のでPiPのUIを レンダリングするには 新しいAVPictureInPictureSample BufferPlaybackDelegate を介して提供される 再生状態の 情報に頼らなねばなりません ユーザがPiP UIからメディア を制御しようとすると それらのコマンドは デリゲートに転送され処理されます 5つのコールバックを 1つずつ見ていきましょう ユーザがPiPウインドウで 再生/停止ボタンを押すと setPlaying 関数が 呼び出されます ユーザがスキップ ボタンのいずれかを押すと skipByInterval 関数が 呼び出されます これらのコールバックを使うと メディアを適宜制御できます timeRangeForPlayback 関数を使うと 現在再生可能な時間範囲 を指定できます タイムラインを レンダリングして 現在の再生ヘッドを示す ことができます 継続期間が有限の 時間範囲には 常にサンプルバッファ ディスプレイレイヤ のタイムベースの 現在時刻を含めるべきです ライブコンテンツを示すには 継続期間が無限の時間範囲 と組み合わせます didTransitionToRenderSize 関数は ピンチ&ズームなどで ウインドウサイズが 変わった場合に 呼び出されます メディアバリアントを 選択する際に 不必要なデコード オーバーヘッドを 避けるにはこのレンダリング サイズを考慮に入れてください isPlaybackPaused 関数は定期的に 呼び出されPiP UI に一時停止/再生ステート のどちらを反映するかを 知らせます 概念的には AVPlayerの timeControlStatus に相当します 次に macOSの フルスクリーン体験の 改善点を見てみましょう Big Surでは動画を Mac Catalyst Appで フルスクリーンにすると 動画はウインドウ いっぱいに広がり ますが全画面には 広がりません macOS Montereyでは 動画が全画面を占めます 結果としてmacOS のネイティブと Mac Catalyst Appの両方 で真の全画面体験が可能になります 再生コントロールは どちらも同じに見えます すべてのMac Catalyst Appでこの 新しい動作が自動的にもたらされます macOSネイティブの フルスクリーン体験と 同じように ユーザはApp ウインドウにスワイプで戻れます 元の映像の代わりに プレースホルダーが表示され 全画面で再生されている ことを示します これはPiPで動画を再生 するときと非常に 似ています ユーザが同じコンテンツを 選択した後でプレーヤー ビューコントローラを全画面に 表示するシナリオでは ビューコントローラは依然と してウィンドウで表示されます しかし 新しい macOS Montereyでは ウインドウ上部にある 緑色の全画面ボタンを 押すことによって ユーザは真の 全画面再生体験に デタッチできます 全画面ライフサイクルを 明示的に管理することで アプリケーションの ニーズに応じてより良い ユーザ体験を提供できます 例を見てみましょう すでにお見せしたように ユーザは再生を 全画面にした後も スワイプしてAppに 戻れます たとえプレイヤービュー コントローラが ビュー階層から削除 されても App内を自由に ナビゲートできるべきです どの時点でも スワイプするか Mission Controlで 全画面動画に 戻れるのです どうすれば動作するように なるか見ていきましょう あなたの責任範囲は playerViewControllerの ライフサイクルです 最適な体験を導くには playerViewControllerが たとえAppのビュー階層 になくても アライブになっていること を確認してください そうしないと動画がある ページからユーザが 離れたときに playerViewController が解放され全画面再生が 終了してしまいます 必要なのは willBeginFullScreenPresentation コールバックの受信時に playerViewController の強い参照を維持して いることのみです ユーザが全画面モードを 終了するとAppは willEndFullScreenPresentation を受信します これは 表示されていた 元のビューから ユーザが離れたと想定し 存続させておいた playerViewController を手放すチャンスです ネイティブmacOSも同じです playerViewWillExitFullScreen コールバックを 受信するまで 新しい playerViewDelegateを使用して playerViewを存続させる ことができます ユーザが全画面を終了 するとAppは このrestoreUserInterface コールバックを受信します ユースケースとして 適している場合は これはAppにとって 動画がある元のページに 戻るチャンスです これはユーザがPiPを 停止したときに受け取る 既存のコールバックと 非常によく似ています 全画面からインライン への移行を妨げないよう できるだけ早くこの completionHandler から戻るように してください falseが返った場合は 復元に失敗したか 復元不能を意味し その場合 コンテンツはアニメーション なしで全画面で終了します ということで本日のセッションを 締めくくりたいと思います AVPlayerLayerの代わりに AVSampleBufferDisplayLayer を使用する場合に 新しいコンテンツソース APIでPiPサポートを Appに追加する方法を ご覧いただきました macOSとMacCatalyst については 改善された全画面体験 に目を向けて コードをシームレスに統合する 手順をざっとご説明しました 本日のセッションを お楽しみ頂けた ならば幸いです これらの機能が皆さんの Appに統合されるのを 楽しみにしています 残りのカンファレンスを お楽しみください ♪
-
-
1:16 - New canStartPictureInPictureAutomaticallyFromInline property
// New property on AVPlayerViewController / AVPictureInPictureController. var canStartPictureInPictureAutomaticallyFromInline: Bool { get set }
-
1:40 - Setting up AVPictureInPictureController with an AVPlayerLayer
func setupPictureInPicture() { // Ensure PiP is supported by current device. if AVPictureInPictureController.isPictureInPictureSupported() { // Create a new controller, passing the reference to the AVPlayerLayer. pictureInPictureController = AVPictureInPictureController(playerLayer: playerLayer) pictureInPictureController.delegate = self // Observe AVPictureInPictureController.isPictureInPicturePossible to update the PiP // button’s enabled state. } else { // PiP isn't supported by the current device. Disable the PiP button. pictureInPictureButton.isEnabled = false } }
-
2:11 - Starting and stopping picture in picture
@IBAction func togglePictureInPictureMode(_ sender: UIButton) { if pictureInPictureController.isPictureInPictureActive { pictureInPictureController.stopPictureInPicture() } else { pictureInPictureController.startPictureInPicture() } }
-
2:56 - AVPictureInPictureSampleBufferPlaybackDelegate
public protocol AVPictureInPictureSampleBufferPlaybackDelegate: NSObjectProtocol{ // Delegate is responsible for: // // - Supplying playback state information for PiP UI. // - Responding to user input from PiP UI. }
-
3:17 - Toggle playback of the video and seek back / ahead 15 seconds
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, setPlaying playing: Bool) func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, skipByInterval skipInterval: CMTime, completion completionHandler: @escaping () -› Void)
-
3:31 - Provide elapsed time information
func pictureInPictureControllerTimeRangeForPlayback(_ pictureInPictureController: AVPictureInPictureController) -> CMTimeRange
-
3:51 - Choose appropriate media variant for render size
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, didTransitionToRenderSize newRenderSize: CMVideoDimensions)
-
4:06 - Update playback state
func pictureInPictureControllerIsPlaybackPaused(pictureInPictureController: AVPictureInPictureController) -> Bool
-
6:05 - iOS / MacCatalyst - Persist full screen playback
func playerViewController(_ playerViewController: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) { coordinator.animate(alongsideTransition: nil) { context in // Keep a strong reference to the playerViewController while in full screen. self.detachedPlayerViewController = playerViewController } }
-
6:38 - iOS / MacCatalyst - Release the playerViewController
func playerViewController(_ playerViewController: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator){ coordinator.animate(alongsideTransition: nil) { context in // Stop keeping the playerViewController alive when transition completes, self.detachedPlayerViewController = nil } }
-
6:46 - Persist full screen playback on macOS
func playerViewWillEnterFullScreen(_ playerView: AVPlayerView) { // Start keeping the player view alive while it is not in the view hierarchy. self.detachedPlayerView = playerView } func playerViewWillExitFullScreen(_ playerView: AVPlayerView) { // Stop keeping the player view alive. self.detachedPlayerView = nil }
-
6:55 - Restoring UI when exiting full screen
// Restoring UI when exiting full screen // iOS / MacCatalyst func playerViewControllerRestoreUserInterfaceForFullScreenExit(_ playerViewController: AVPlayerViewController) async -> Bool { // Custom UI restoration logic return true } // macOS func playerViewRestoreUserInterfaceForFullScreenExit(_ playerView: AVPlayerView) async -> Bool { // custom UI restore logic here return true }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。