ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
HLSにおけるダイナミックなプレロールとミッドロール
広告とHLSストリーム間のシームレスなトランジションを実現する方法を確認します。HLSタグとAVFoundation APIを組み込んで、メインコンテンツとミッドロールの間を簡単に遷移することのできるメディア体験を構築する方法、Appでこれらのストリームを再生するベストプラクティスを紹介します。
リソース
関連ビデオ
WWDC22
WWDC21
-
ダウンロード
♪ ♪ みなさん こんにちは! AVFoundationのエンジニア パラシャントです WWDC21へようこそ 近年 オーバー・ザ・トップの ストリーミングサービスの 人気は リニアテレビの人気に 近づいています 参入している市場によって ユーザーは 料金を支払う必要のある 広告なしのコンテンツよりも 広告がサポートする 無料のコンテンツを好みます HLSストリームに インタースティシャル広告を 含めたい方は このセッションに ご参加ください やり方の説明の前に HLSストリームに 広告を挿入するための 既存の仕組みを 簡単に説明します DISCONTINUITYタグを使って サーバーサイドで広告挿入を 行うことができます 広告セグメントを コンテンツセグメントと一緒に スティッチする かなり静的なスキームです プレイリストがユーザーに 提供される前に 実行する必要があります ご想像のとおり これではユーザーが 広告マーカーを使って 広告が決定される 遅延バインディング はできません また ユーザーがすでに見た 動画の部分を視聴するとき 同じ広告が もう一度再生される リバインディングを 行うことができません HLSはセグメント単位で 動作するため 広告ポッドとの間の トランジションは セグメントの境界で 行う必要があります コンテンツの途中に 広告マーカーがあった場合 広告を貼り付けるためには そのコンテンツを 分割しなければなりません 品質階層の数は プライマリアセットと インタースティシャル・ アセットで異なります 広告をすべての主要な 品質階層と つなぎ合わせるには コンテンツの エンコーディングに 一致するように広告を 調整する必要があります また 広告が プライマリコンテンツと 同じコーデックを使っている ことを確認してください ライブストリーミングの パッケージャーが バックエンドの管理を 行うことがあります 広告と インタースティシャルは ほとんどがビデオ・オン・ デマンドストリームです ただし パッケージャーは 広告全体にわたって セグメントごとに これらをスプールする 必要があります クライアント側に広告を 挿入することもできます これまで 公式にお勧めする方法は ありませんでした より一般的な アプローチは 一方のプレーヤーが プライマリコンテンツを提示 もう一方が インタースティシャルを 提示する2プレーヤー スキームの使用です 2つのプレーヤーの 再生を調整し プレーヤーのビュー階層を 管理することで トランジションを 実現しています うまく機能するものの ここでの問題は 主に動作側にあります プレーヤー間で バッファリングが適切に 調整されていない場合 広告をプリフェッチ すると プライマリコンテンツ ストリームの アダプティブビットレート の動作に影響します ピクチャ・イン・ピクチャや AirPlayのような機能は こういったカスタム プレーヤーアプローチでは うまく機能しません そこで これらの懸念事項に 対処して 全般的に広告挿入を よりシンプルに しようと試みました HLSインタースティシャルを 導入することで 広告を別のアセットとして 扱い 番組のタイムライン上に スケジュールできます 広告はDISCONTINUITYタグで つなぎ合わされません 代わりにマスター プレイリストを介して 参照できる自己完結型の アセットとして残ります このスキームは動的であり 遅延バインディングや 広告へのリバインディングも 可能です もうセグメントの境界に 制約されません プログラムのタイムラインの どこにでも 広告を任意で配置できます AVKitと組み合わせた HLSインタースティシャルは tvOSのナビゲーション制限の 組み込みサポートを 提供します AirPlayとピクチャ・イン・ピクチャの サポートも 組み込まれています スケジュールした広告と インタースティシャルは AirPlayセッションで 引き継がれます また プライマリプレーヤーと インタースティシャル プレーヤーの間で バッファリングやその他の システムリソースの使用を 調整しシームレスな移行を 実現しています。 次に HLSインタースティシャル を使用した広告とともに ビデオオンデマンドコンテンツ を表示する場合の 一般的な再生フローを 見てみましょう
この写真の青いバーは メインコンテンツを表し オレンジと緑のバーは 再生中にスケジュール したい広告です プライマリは広告 マーカーまで再生され その時点で一時停止し 最初の広告が 再生を開始します 最初の広告が流れると すぐに2回目の 広告が流れます 2回目の広告の後 プライマリの再生は 前回の続きから 再開されます バッファリングシーケンスを 見てみましょう 最初の広告の開始時間 までプライマリを バッファリングすること から始めます 最初の広告を事前に バッファリングして スムーズな移行を 実現できるようにします これが完全に バッファリングされた後 2番目の広告を プリバッファリングします 2番目の広告が完全に バッファリングされた後 シームレスに元に 戻るように プライマリの バッファリングを 再開します ライブシナリオの 再生の流れは 広告の時間分だけ ジャンプした後に プライマリに戻ること 以外は同様です これはライブエッジとの 同期を維持するためです 同様にバッファリング戦略 を採用しているため シームレスな移行が 可能です HLSインタースティシャルを 使った広告の挿入は DATERANGEタグを使って サーバー側でできます このため属性を持つ 日付範囲クラスを 導入しました このプレイリストは PROGRAM-DATE-TIME タグを介してタイミング 情報を伝送します 広告スケジュールが日付 で指定されるので タグ付けが 必須になりました クラスをcom.apple.hls. Interstitialに設定 DATERANGEタグを使用 した広告が並びます ID属性はイベントを 一意に識別します START-DATEは プライマリタイムライン で広告を開始する時間を 設定する箇所です ここでは広告が 再生の5秒後に スケジュールされて います DURATION属性は 広告の持続時間を X-ASSET-URI属性は マスタープレイリストの URIを指定します X-RESUME-OFFSET START-DATEからの オフセットを指定し 再生再開場所を決めます resume offsetが 0の場合 中断したところから 再開することになります resume offset属性が ない場合は 広告期間に等しい オフセットで プライマリに 再結合します ライブシナリオでやりたい ことだと思います
このように インストリーム広告を スキップしたい場合も あります レジュームのオフセットを インストリーム広告の 長さに指定します DATERANGEタグを使って スケジュールできます 1つ目の広告が プライマリの5秒後 2つ目の広告が 10秒後に 予定されています 開始時刻を同じに するだけで 連続した広告の スケジュールを 組むことができます 1つ目の広告が再生開始から 5秒後に始まり 2つ目の広告も同様です 広告はプレイリストに 表示された順に 表示されます X-ASSET-URI属性を 使って広告を参照する これらの方法では プレイリストに DATERANGEタグを 追加する際に Ad Podを定義する 必要があります しかしX-ASSET-LIST 属性を使用して その決定を延期する ことができます X-ASSET-LISTはイベント のスケジュールを含む JSONオブジェクトを 指します。 インタースティシャルの リストを指定する ASSETS配列があり 各エントリは マスタープレイリストのURIと 再生時間を指定します このJSONオブジェクトは バッファリング時にのみ 取得されるため 広告枠への遅延バインドが 可能になります 通常 広告は最後まで 再生されます X-PLAYOUT-LIMIT属性を 使えば 広告の終了時間を 指定することができます この属性を使用して ライブブロードキャストで アーリーリターンシナリオを 実装できます アーリーリターンは ライブフィードに戻るために 広告を中断したい箇所に 使います ニュース速報であったり スポーツの盛り上がる シーンなど 視聴者に見逃してほしくない 場合などです アーリーリターンを どのように実装するのか? 6つのセグメントを含む ライブプレイリストがあります ライブエッジは セグメント6の最後です プレイヘッドは通常 ライブエッジの 3つのtarget duration の後ろにあります 今回は セグメント3の 最後にあります この時点で セグメント6に続く 15秒の広告スポットが 決定します プレイリスト更新に 含まれるのはセグメント6つ 広告スケジュールを 含むDATERANGEタグです 次のアップデートでは 再生ヘッドは セグメント4の最後に 移動し 広告はセグメント6の 後に予定されています 影付きのセグメント7は 広告を表しています 次の更新では 再生ヘッドが セグメント5の 終わりに移動し その次は 広告の 始まりに移動します 何もしなければ 広告が15秒間再生します 例えば12秒後に アーリーリターンを設定 したい場合は 単純に プレイアウトの制限を そのように指定します これで 広告は 12秒間再生され 番組に戻ります 多くの場合 契約上の理由から ユーザーが広告を飛び越えたり スキップしたりしないように する必要があります X-RESTRICTタグを使用して ナビゲーション制限を 指定できます X-RESTRICTに 値としてJUMPを設定すると ユーザーは広告の前後の 時間にシークできません X-RESTRICTに 値としてSKIPを設定すると 希望と異なるレートで 広告を再生できません これらの制限は UIによって強制されます tvOSではAVKitによって 強制されます AVPlayerViewController を使用している場合は これらを利用 できるはずです 他のプラットフォームや AVKitを使用しない場合 制限を適用するかは App次第です DATERANGEタグを使って サーバーサイドの広告を スケジュールする方法を 見てきました クライアントでの進捗状況を 監視する方法についてです 2つの新しいAVFoundation オブジェクトを導入します インタースティシャルが スケジュールまたは 再生されたときにクライアント に通知する AVPlayerInterstitialEvent- Monitorと プレーヤーアイテムの タイムラインに 広告を配置するために 必要なすべての情報を含む AVPlayerInterstitialEvent オブジェクトです AVPlayerInterstitialEvent Monitorは 次のプロパティがあります プライマリアセットを再生する プライマリプレーヤー 広告の再生を監視 するために使用できる インタースティシャル プレーヤーへのハンドル プレーヤーに設定された インタースティシャルを表す オブジェクトを表す AVPlayerInterstitialEvent オブジェクトの配列である イベント配列 AVPlayerInterstitialEvent オブジェクトに ついてのちほど説明します currentEventへのハンドル インタースティシャルが再生 されている場合は有効であり それ以外の場合は nullになります イベントスケジュールが 変更されたときに発生する eventsDidChangeNotification があり 最後に インタースティシャル との間で 遷移するときに発生する currentEvent-DidChange- Notificationがあります インタースティシャルイベント を記述する AVPlayerInterstitialEvent オブジェクトには 前に見た DATERANGE属性の類似物である プロパティがあります primaryItemは インタースティシャルを 予定するタイムライン上の 主要アセットを表します 識別子はID属性に似ており これによりイベントが 一意的に識別されます 時間と日付のフィールドは メディアの時間と日付で インタースティシャルの 開始時間を指定します START-DATE属性に近いです テンプレートアイテムの コピーを使用して 広告ポッドを表す インタースティシャル プレーヤーアイテムを作成 これは先ほどの ASSET-LIST属性と 似ていますね restrictionsプロパティは インタースティシャルの ナビゲーションの制限を 指定します 次に 対応する DATERANGEに類似した resumptionOffsetプロパティと playoutLimit プロパティがあります 最後にuserDefinedAttributes の辞書についてです DATERANGEタグで カスタム属性を指定し userDefinedAttributes プロパティを介して クライアント・ アプリケーションに 表示されることに なります 例えばビーコンの URLや 広告再生のメトリクスを 報告するための カスタム属性を含める ことができます インタースティシャルの再生中 にAPIを使用してUIを更新する 方法を示すサンプルコードを 次に示します プライマリコンテンツを再生 するAVPlayerを作成します このコンテンツは DATERANGEタグを使用して インタースティシャルが スケジュールされています そしてAVPlayerInterstitial- EventMonitorを作成し プレーヤーに設定します 次にcurrentEvent-DidChange- Notificationを サブスクライブして プレーヤーが インタースティシャルに 出入りするときに アプリケーションに 通知します 通知が来たら UIを更新するだけです クライアント側で 広告をスケジュールしたい 場合があります AVPlayerでイベントを プログラムで設定できる
AVPlayerInterstitial- EventMonitorを継承しており 多くの共通プロパティが あります 注意すべき点は eventsプロパティは モニターでは 読み取り専用ですが コントローラでは読み書き 可能なプロパティなので プログラムでイベントを スケジュールできます また、現在再生中の インタースティシャルを キャンセルするための cancel CurrentEvent APIも用意されています AVPlayerで2つの広告を含む 広告ポッドを どのようにスケジュール するかを見ましょう プライマリアセットを再生 するAVPlayerを作成します AVPlayerInterstitial- EventControllerを作成し AVPlayerに設定します 広告ポッドを表す AVPlayerアイテムの 配列を作成します AVPlayerInterstitialEvent オブジェクトを作成し primaryItemをプレーヤーの 現在のアイテムに指定します これは ムービーアセットを 表すもので この例では再生の約10秒後に 開始時間を指定し templateItemsには 先ほど作成した 広告ポッドを指定します オブジェクトが作成されたら それをコントローラの eventsプロパティに 設定するだけです AVPlayerViewControllerで プレーヤーを設定する直前に currentItem.translates- PlayerInterstitialEvents プロパティをtrueに設定して いるのに気づくでしょう AVKitはタイムラインに ナビゲーションマーカーを 配置し tvOSにナビゲーション 制限を適用します それではtvOS上の HLSインタースティシャルとの AVKit統合について説明する デモに移りましょう
再生開始から 約40秒で開始するように スケジュールされた 広告があります ナビゲーション制限は 設定されていません タイムラインに広告 マーカー表示があります
広告が再生されると プレイヘッドの上に カウントダウン タイマーが表示されます この広告には制限が 設定されていないので Apple TVのリモコンの 端に手をかざすと スキップする オプションがあります そうすることで 広告をスキップして プライマリに 戻ることになります 同じ設定ですが 広告にはスキップ制限が 設定されています
広告再生中に 広告をスキップする オプションは 表示されません プライマリに戻る前に 広告が再生し終わるのを 待つ必要があります トム・ ハンクス「グレイハウンド」 PG13 Apple TV+独占配信中 ジョシュ ありがとう 2つ広告が表示されています 1つ目はナビゲーション の制限がなく 2つ目はジャンプの 制限があります 両方の広告を 飛び越えようとすると…
広告の再生中に プレイヘッドが 制限付き広告マーカーに スナップします
我々は 高みから 地獄をもたらす
トム・ハンクス主演 「グレイハウンド」 PG13 Apple TV+で独占配信中 広告が再生されたら シークの位置から 再開します
まとめとして DATERANGEタグを使用して サーバー側の広告を スケジュールできます ビデオ・オン・ デマンドの場合は レジューム・ オフセットを0にして 中断したところから 番組に復帰します ライブストリームでは この属性をスキップして ライブエッジで 再接続できます X-ASSET-LIST属性を 使用して広告アセットの 遅延バインディングを 行い X-PLAYOUT-LIMIT属性を 使用して ライブブロードキャスト でアーリーリターンを スケジュールし X-RESTRICT属性を使って ナビゲーション制限を 指定できます クライアント側で広告の 再生を監視するには AVPlayer-Interstitial- EventMonitorを使います AVPlayerInterstitial- EventControllerを使い プログラムで 広告を設定できます WWDC21をご視聴いただき ありがとうございました
-
-
9:50 - AVPlayerInterstitialEvent
class AVPlayerInterstitialEvent { var primaryItem: AVPlayerItem? { get } var identifier: String { get } var time: CMTime { get } var date: Date? { get } var templateItems: [AVPlayerItem] { get } var restrictions: AVPlayerInterstitialEvent.Restrictions { get } var resumptionOffset: CMTime { get } var playoutLimit: CMTime { get } var userDefinedAttributes: [AnyHashable : Any] { get } }
-
10:58 - Observing server inserted events
// Client observes server-side interstitial playback let player = AVPlayer(url: movieURL) // movieURL has EXT-X-DATERANGE ad tags let observer = AVPlayerInterstitialEventMonitor(primaryPlayer: player) NotificationCenter.default.addObserver( forName: AVPlayerInterstitialEventMonitor.currentEventDidChangeNotification, object: observer, queue: OperationQueue.main) { notification_ in self.updateUI(observer.currentEvent, observer.interstitialPlayer) }
-
11:40 - AVPlayerInterstitialEventController
class AVPlayerInterstitialEventController : AVPlayerInterstitialEventMonitor { var events: [AVPlayerInterstitialEvent]! func cancelCurrentEvent(withResumptionOffset resumptionOffset: CMTime) }
-
12:01 - Client schedules ad pod
// Client inserted events // Client schedules an ad pod at 10s into primary asset let player = AVPlayer(url: movieURL) // no ads in primary asset let controller = AVPlayerInterstitialEventController(primaryPlayer: player) let adPodTemplates = [AVPlayerItem(url: ad1URL), AVPlayerItem(url: ad2URL)] let event = AVPlayerInterstitialEvent( primaryItem: player.currentItem, time: CMTime(seconds: 10, preferredTimescale: 1), templateItems: adPodTemplates, restrictions: [], resumptionOffset: .zero, playoutLimit: .invalid) controller.events = [event] player.currentItem.translatesPlayerInterstitialEvents = true let vc = AVPlayerViewController() vc.player = player player.play()
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。