ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
tvOSにおけるピクチャ・イン・ピクチャをマスターする
Apple TVにピクチャ・イン・ピクチャが導入されます。動画の同時再生と全画面コンテンツとピクチャ・イン・ピクチャを切り替える機能によって、tvOS App内でのマルチタスクの柔軟性がかつてないほどに向上します。AVPictureInPictureControllerをプロジェクトに追加して、使い慣れたAPIでカスタムプレイバックインタフェースを作成し、Appで最高の再生体験を実現する方法について説明します。また、AVPlayerViewControllerはtvOS 14で「スワイプアップ」ジェスチャーを使用するため、customOverlayViewControllerをアクティベートし「スワイプアップ」ジェスチャーを使用しない方法についても説明します。 本セッションの前に、AVKitの基本を理解しておくことをお勧めします。詳細については、”Delivering Intuitive Media Playback with AVKit"を参照してください。 AVPlayerViewControllerを使って、tvOSのユニークなピクチャ・イン・ピクチャ機能をご活用ください。
リソース
- Adopting Picture in Picture in a Custom Player
- Adopting Picture in Picture in a Standard Player
- Adopting Picture in Picture Playback in tvOS
- Human Interface Guidelines: Playing video
関連ビデオ
WWDC21
WWDC19
-
ダウンロード
こんにちは ようこそWWDCへ “tvOSにおける ピクチャ・イン・ピクチャの習得” 私はジャド・オセラン tvOSのソフトウェアエンジニアです まずは tvOSピクチャ・イン・ピクチャの 専門的な概要から説明し Appにピクチャ・イン・ピクチャを 実装するための要件を取り上げます その後ダンに引き継ぎ 既存のAppとピクチャ・イン・ピクチャの 動作方法をお話しします 最後に 本セッション内容をカバーする デモをダンがお見せします
ではまず tvOSのピクチャ・イン・ピクチャの 概要を見ていきます PiPとも呼びます
2つのビデオをブラウザビューに表示した アプリケーションがあります これらをピクチャ・イン・ピクチャに入れ 入れ替える方法を見てみましょう 最初にビデオをフルスクリーンにします 再生UIを表示させると “PiP開始”ボタンが選択でき このビデオを ピクチャ・イン・ピクチャに移せます tvOSに特有な機能で 他のビデオを開始でき 両方のビデオを並べて 同時に再生できます いつでも再生UIを表示させることができ フルスクリーンのビデオを PiPのコンテンツと入れ替えることができます ブラウザビューに戻ると
PiPのビデオは 再生した 2つ目のビデオであることが分かります 両方のビデオを効果的にPiPに入れ 互いに入れ替えました tvOSのPiPは iPadOSと似たような体験をもたらし さらに 同時にビデオを 再生できることが分かりました では どのようにこの機能を 活用できるでしょう?
tvOSにピクチャ・イン・ピクチャを 実装する方法を見てみましょう 最初に Xcodeプロジェクトをセットアップします コードを書く前に Xcode 12上でAppに ピクチャ・イン・ピクチャの機能を追加します そのために Add Capabilitiesウィンドウで バックグラウンドモードを選択します その後 プロジェクトの Signing and Capabilitiesのタブで ピクチャ・イン・ピクチャのボックスに チェックを入れるだけです 次に オーディオセッションの設定のため アプリケーションのAVAudio Categoryを “playback”にします コードはアプリケーション上でこのように 表示されます プロジェクトの設定は以上です PiPをiPadOSに実装したことがあれば このステップにはなじみがあるでしょう 他のプラットフォームでも同じです tvOSのPiPを実装するために 次は 再生UIを適応させます 標準の再生UIで必要なものを 見てみましょう
我々の標準再生UIである AVPlayerViewControllerは PiPにとって正しいUIアフォーダンスを 表示します 今のセットアップステップに従って プロジェクトを設定すれば自動的に表示されます
PiPのライフサイクルに応答するため 現在 tvOSにあるアプリケーションは― AVPlayerViewControllerの Delegateメソッドを活用できます もしこのAPIがあなたにとって新しく 後から復習したい場合は 本セッションで再度お伝えしますが Delegateメソッドがいくつかあります これらのメソッドがカバーするのは Starting PiPと Stopping PiP そしてRestoring PiPです まず Restoreについて説明します このメソッドでは アプリケーションの UIを復元することができるため ピクチャ・イン・ピクチャから出て来る プレーヤーを受け入れることができます このメソッドを実装する時は 早く復元することが理想的です そして アニメーションを避けることで ユーザーのために素早く復元できます 時間がかかり過ぎる場合は 復元プレーヤーは終了してしまい ユーザー体験は乏しくなってしまいます 標準の再生UIを使って始める方法は簡単で 既存のiPadOSへの実装にもなじみがあります 本セッションの後半では AVPlayerViewControllerを使い PiPを最大限 活用する方法を より詳しく説明します しかし カスタム再生UIを必要とする Appがあることも認識しています これに当てはまるアプリケーションの場合は tvOSのPiPを実装するためのステップは 違うものになります それを探っていきましょう AVPictureInPictureControllerは 現在tvOSで入手でき― iPadOSやiOSと同様にアプリケーションが PiPを制御するために使われます
現在 AVPictureInPictureControllerには カスタムUIを管理するための 新たなtvOSのAPIがあります このAPIを見てみましょう
AppはcanStopPictureInPictureを使い ふさわしいアフォーダンスを表示させ カスタム再生UIの中に 正しい反応をもたらします もう少し詳しく見ましょう
canStopPictureInPictureが 全画面表示再生でfalseの場合 カスタム再生UIはPiP開始のアフォーダンスを 表示すべきだと指示されます ユーザがPiP開始のアフォーダンスを 選択したら AVPictureInPictureControllerの管理下で startPictureInPictureが呼び出され アプリケーションはプログラムに従い PiPを開始します
逆に canStopPictureInPictureがtrueの場合は カスタム再生UIの中で PiPの入れ替えと停止用の UIアフォーダンスが表示されます 入れ替えを行うための 特別なコールは必要ありません 入れ替えの際には アプリケーションは startPictureInPictureを呼び出し それから システムが残りを処理します 全画面表示再生の視点から これを考えてみましょう 単なる再生開始と入れ替えの両方のケースで PiPを開始するので startPictureInPictureは両方のシナリオで 適切なメソッドになります 全画面プレーヤーから既存のPiPを停止するには それを管理している― AVPictureInPictureControllerで stopPictureInPictureを呼び出します
ユーザが停止アフォーダンスを 選択した時にのみ AppはstopPictureInPictureを 呼び出すことが重要です
canStopPictureInPictureは いつでも変わることがあります 例えば ユーザはPiPウィンドウ自体から PiPを止めるかもしれません そのため UIは常に最新でなければなりません 最新にするには アプリケーションは canStopPictureInPictureの変化を観察し UIを更新する必要があります ここにあるのは そのコードの例です 最新のヒューマンインタフェースガイドラインを ぜひ読んでください これらのアフォーダンスを最適に表示する方法が 詳細に説明されています 続いて tvOS 14が複数のNowPlayingInfoCenterに 公開される方法を説明します 同時に2つのビデオが再生される時 各々のNow Playing情報も更新されるべきですが そのためには MPNowPlayingSessionに AVPlayersを結びつけます Appは複数のMPNowPlayingSessionを 持つことができます PiPの場合には 全画面表示のコンテンツと同様に コンテンツに対して1つの Now Playingセッションを持ちます つまり tvOSでPiPを使うためには AVPictureInPictureControllerで 使われたのと同じプレーヤーが MPNowPlayingSessionと 結びつかなければなりません どういうことか コードの中で見てみましょう
カスタム再生UIコントローラーは このように見えます AVPictureInPictureControllerを 作るために使用されるプレーヤーもまた MPNowPlayingSessionで 使用されていることが分かります 後ほど 適切な時点で セッションのNowPlayingInfoCenterを設定し アクティブにすることができます これにより MediaPlayerは 情報を公開し始めます MPNowPlayingSessionを 使う際の注意点があります まず MPNowPlayingSessionに更新すると システムはデフォルトの MPNowPlayingInfoCenterの更新を無視します そのため PiPを実装する際には アプリケーション全体にわたって MPNowPlayingSessionに切り替えてください 複数のNowPlayingInfoCenterがあるため システムは Now PlayingのシステムUIに どの情報を表示するか決めます しかし Now Playing情報を 公開し続けることは重要です セッションがシステムUIに現れていなくてもです システムが常にセッションを表示させるために 必要なことです 再生UIをPiPと組み合わせる2つの方法と セットアップを紹介しました tvOSのPiPの実装で 最後にご紹介するのは 入れ替えの際にアプリケーションが 要求するライフサイクルです
最初のシナリオはアプリケーションの 中での画面入れ替えです これらのビデオがあなたのアプリケーションから 出てきたものと仮定して スポーツのPiPコンテンツと 全画面表示の アニメ動画のライフサイクルを見てみましょう
ここに示すのは PlayerDelegateのオブジェクトの一部です まずアプリケーションには PiPに入るプレイヤーの― WillStartデリゲートコールバックが 表示されます 次に 停止しているプレーヤーが アプリケーションにUIの復元を要求し PiPから出てくることに 対応します ここでは早急な復元が必要です UIを復元したら 停止しているプレイヤーの WillStopコールバックを取得します 最後に 入れ替えが完了すると プレーヤーはそれぞれのDidStartとDidStopの デリケートコールバックを得ます 2つ目のシナリオは アプリケーション間での画面入れ替えです ここでは ピクチャ・イン・ピクチャを始め もう1つのAppのPiPと画面を入れ替えるか Appのピクチャ・イン・ピクチャの画面を 全画面表示にすることができます 最初に AppがPiPを始めるケースから 見てみましょう
Appが全画面表示のアニメ動画を 提供しているとします このシナリオでは もう一つのAppが PiPのスポーツコンテンツを提供しています 入れ替えると コンテンツがPiPの中に入ります 先ほどのプレイヤーデリゲートを例に デリゲートのコールバックに 焦点を当てましょう
アニメーションが完了すると 期待通り― WillStartコールバックと 一致するDidStartを取得します
そして アプリケーションは バックグラウンドに移ります これは入れ替えの一部として― 全画面表示のコンテンツを再生するために もう1つのAppが前面に出て来るからです iPadOSでピクチャ・イン・ピクチャを 実装したことがあれば なじみがあるかもしれません ユーザがホーム画面に戻るときに 自動的にAppがPiPに入れられるのと 同じシーケンスです
最後に AppがPiPをフルスクリーンで表示する cross-appを説明します この例では アプリケーションが PiPの中にコンテンツを保持していて もう1つのアプリケーションが 全画面で表示されているとしましょう 入れ替えの間 PiPのコンテンツは全画面表示を引き継ぎます
それでは 再び 再生Delegateの例で見てみましょう
最初に アプリケーションは 前面に表示されます
次に 素早くUIを復元する機会を得ます
その後 ピクチャ・イン・ピクチャの停止が 伝えられます
最後に アニメが終わると プレーヤー用のピクチャ・イン・ピクチャが 止まったことが伝えられます ピクチャ・イン・ピクチャを 一から実装する方法を説明しました プロジェクトを設定した後 再生UIをPiPに統合するための 2つのアプローチを探ってみました AppがAVPlayerViewControllerを使えば 大変な作業は ほぼ終わっています しかし カスタム再生UIが必要な場合は AVPictureInPictureControllerを使えます 最後の実装ステップは 入れ替えの間に アプリケーションが要求すべきものでした これらの手順に従い 簡単にtvOSのピクチャ・イン・ピクチャを 取得し実行することができます
しかし 既存のアプリケーションは どうでしょう? この後はダンに引き継ぎ AVPlayerViewControllerを最大に 活用する方法をご説明します
ありがとう ジャド AVPlayerViewControllerは Apple TVで 標準的なビデオ再生体験を提供します 多種多様なリモート操作 スキッピング スキャニング スクラビング Siriコマンドやインタースティシャル対応などを サポートします 全てのtvOS Appでの動画再生において 採用することを推奨します AVPlayerViewControllerを使ったAppを 提供している方々のために ピクチャ・イン・ピクチャを使ってAppを 動作させる方法を説明します まず インタラクティブオーバーレイについて お話ししましょう
インタラクティブオーバーレイはビデオ上に カスタムAppコントロールを表示します チャンネルガイドやお勧めコンテンツなどです ほとんどのアプリケーションでは リモコンで スワイプアップするとオーバレイが表示されます tvOS13以来 スワイプアップの動きは AVPlayerViewControllerで予約され ピクチャ・イン・ピクチャ制御のために 必要な動作となります インタラクティブなスライドアップ制御には AVPlayerViewControllerの― customOverlayViewControllerプロパティを 使用します このプロパティは昨年 tvOS 13で導入されました customOverlayViewControllerを 採用していないのであれば 最新のSDKに移る時には 採用する必要があります カスタムスワイプアップ動作の認識機能が 動作を止めてしまうからです ユーザはリモコンで素早くスワイプアップし カスタムオーバーレイにアクセスするか トランスファーバーが見えている時は オンスクリーンボタンをクリックします カスタムオーバーレイについて より詳しく知りたい方は “AVKitによる直感的なメディア再生の配信” という昨年のプレゼンをご覧ください カスタムオーバーレイ制御のボタンは トランスポートバーが見えている時は 左側に現れます 続いて Now Playing情報について 説明します Now Playing情報はビデオに関する 静的と動的なメタデータで OSや他のアプリケーション 離れたデバイスに送られます AVKitはアプリケーションに代わり Now Playing情報を公開します このメタデータを増やすなら MediaPlayerフレームワークに 直接データを転送するのではなく AVPlayerItemの外部メタデータプロパティを 使ってください
tvOS 14では 古い共有のNow Playing Info Centerは もう使われていません そのため アプリケーションを 最新のSDKにアップデートする時は 古い情報を公開しようとしていないか 確認してください このコードは ヘルパー機能を使って 外部のメタデータプロパティに入れる メタデータの作成を示しています ほとんどの方は 情報パネルに表示される タイトルや説明 メディアコンテンツの評価 その他の情報を提供するため 似たようなコードを既に使っています ご覧の通り サービス識別子のような 表示されないメタデータにも使えます
次は Appのアーキテクチャに 与える影響についてです AVPlayerViewControllerのDelegateは AVKitからのメッセージを受けて処理します ピクチャ・イン・ピクチャを含む ユーザの アクションやその他のイベントに関するものです 過去には 上位や提示ビューコントローラを Delegateとして実行させていたかもしれません しかし ピクチャ・イン・ピクチャの間 Delegateは存在し メッセージに返答し続けなければなりません しかし同時に ユーザはあなたのAppを 隅から隅まで見て回ることができます Delegateはビューの階層の一部であっては ならないのです ビデオがPiP化されている間は持続する 別のオブジェクトにすべきでしょう ユーザが2つのビデオを入れ替える際 何が起こるのでしょう 視覚的には 2つのビデオは スムーズな動作で入れ替わります しかし これには いくつかの作業が必要です もし単に ビューコントローラを プレーヤーに与えるなら 一方のビューコントローラを破棄して ピクチャ・イン・ピクチャに行き もう一方のビューコントローラを提示することで スワップを実装することができます AVPlayerViewControllerを 別のビューコントローラに埋め込むと さらなる課題が発生します PiPビデオが全画面表示に戻る時 上位のビューコントローラを再形成する必要は あるでしょうか? これが起きた時 ナビゲーションの階層に 何が起こるのでしょう ユーザーがビデオを見終わった後 Appのどの部分が表示されるべきでしょう? 最後のビデオの前に ユーザが見ていたものでしょうか? それとも 全画面表示に切り替えたビデオの ランディングページでしょうか?
もしくは アプリケーションの メインスクリーンでしょう どうしたいかにより 画面切り替えの際 追加の作業が発生します 詳細を見落とさずに 時間に余裕を持ってください アプリケーションと顧客にとって 適切なソリューションを提供するためです カスタム再生UIをお持ちの場合 Appが非アクティブ化されたときに 再生を一時停止しているかもしれません しかし ビデオがピクチャ・イン・ピクチャに ある時は そうすべきではありません AVPlayerViewControllerを使えば そうする必要もありませんし 推奨もしていません AVKitを使ったピクチャ・イン・ピクチャの 実装を説明しました どのようになるか見てみましょう この再生中のビデオを ピクチャ・イン・ピクチャで動かしましょう PiPボタンに焦点を移すため スワイプアップします ピクチャ・イン・ピクチャに進むよう クリックします アプリケーションの中で もう1つのビデオを開始しす
もう1回スワイプアップし PiPコントロールを表示させます 画面を入れ替え PiPを途中で止めます このようにビデオを入れ替えられます
では もう1度やってみましょう 再びスワイプアップし クリックするとこうなります
カスタムオーバーレイ用のボタンは左にあります クリックすると カスタムオーバーレイが表示されます PiPウィンドウは 焦点の当たらないところに動くので ユーザがコントロールを見る際 PiPウィンドウを 画面の端に動かす必要はありません ピクチャ・イン・ピクチャを止めると こうなります つまり AVPlayerViewControllerを使用すれば App上での作業はほとんど必要なく ピクチャ・イン・ピクチャを含む 完全なtvOSの再生体験を得ることができます カスタム再生UIを必要とする場合は ヒューマンインタフェースガイドラインに 従ってください そうすれば ユーザはApple TVと同様に PiPを快適に使うことができます AVPlayerViewControllerの使用中は 自身のオーバーレイUIのために― カスタムスワイプアップ動作の認識機能を インストールしないでください また ピクチャ・イン・ピクチャを 可能な限りサポートするため アプリケーションのアーキテクチャを 調節する時間を確保してください ありがとうございました 残りのカンファレンスもお楽しみください
-
-
2:13 - Setting up your app's audio session
let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setCategory(.playback) } catch { print("Setting category to AVAudioSessionCategoryPlayback failed.") }
-
5:57 - Observering canStopPictureInPicture
_ = pipController.observe(\.canStopPictureInPicture) { controller, change in // Update your UI if controller.canStopPictureInPicture { pipActions = [.swap, .stop] } else { pipActions = [.start] } }
-
7:06 - Tying AVPlayer with MPNowPlayingSession
final class CustomPlayerViewController: UIViewController { init(player: AVPlayer) { let playerLayer = AVPlayerLayer(player: player) pictureInPictureController = AVPictureInPictureController(playerLayer: playerLayer) nowPlayingSession = MPNowPlayingSession(players: [player]) } private func publishNowPlayingMetadata() { nowPlayingSession.nowPlayingInfoCenter.nowPlayingInfo = // Your Now Playing info nowPlayingSession.becomeActiveIfPossible() } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。