ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
HLSインタースティシャルによる広告体験の向上
HLSインタースティシャルを使用すると、HLSコンテンツに広告をシームレスに挿入できます。統合されたタイムラインを使用してUI体験を調整し、インタースティシャル対応のSharePlayを構築する方法もご紹介します。
関連する章
- 0:00 - Introduction
- 0:45 - Interstitals recap
- 1:56 - Integrated timeline
- 10:40 - SharePlay with interstitials
リソース
- Forum: Media Technologies
- Providing an integrated view of your timeline when playing HLS interstitials
関連ビデオ
WWDC21
-
ダウンロード
こんにちは AVFoundationエンジニアのJulianです 近年 メインコンテンツに 広告を挿入する簡単な方法として HLSインタースティシャルが導入されました 今年はインタースティシャルを使用して 表示および操作する新しい方法を提供します 広告掲載に関心がある方や HLSインタースティシャルを新しい方法で 組み込む方法を学びたい方にとって 本セッションは最適です まず 現在のHLSインタースティシャルの 仕組みを振り返ります
次に 新しいIntegrated Timeline APIを ご紹介します このAPIは アプリのUI体験を 構築するためのデータモデルを提供します
最後に インタースティシャル対応の SharePlayの新機能を解説します
それでは HLSインタースティシャルの概要 を簡単に復習しましょう
HLSインタースティシャルでは 広告やインタースティシャルは別のアセットで メインコンテンツのタイムラインで スケジュール設定することができます これは典型的なメディアプレイリストです 例えば コンテンツの開始5秒後に インタースティシャルを再生する場合は コンテンツのPROGRAM-DATE-TIMEで START-DATEを5秒に指定します すると メインコンテンツが 5秒まで再生された後 インタースティシャルURLの再生が 開始されます その後 インタースティシャルが終了すると メインコンテンツの再生が再開されます
HLSインタースティシャルには メインコンテンツへの広告の直接挿入では 得られない多くの利点があります インタースティシャルでは 遅延バインディングによる広告決定ができ 広告をメインコンテンツと 組み合わせる必要がありません HLSインタースティシャルは主に 広告の挿入に使われてきましたが その他の補助コンテンツの表示に 使用することをおすすめします 番組のプロモーション スタジオのバナー 番組の要約のセグメントなど 区切りとして挿入されるコンテンツの 代わりに表示します 以上はHLSインタースティシャルの コンテンツへの 挿入に関する説明のほんの一部です 詳細については 「Explore dynamic pre-rolls and mid-rolls in HLS」をご覧ください Integrated Timelineと呼ばれる 新しいAVFoundation APIが 今年提供開始されます このAPIは インタースティシャルを使ったコンテンツの 再生タイミングと シーケンスのデータモデルを導入します このデータモデルを活用すれば 新しいUI体験を構築でき ユーザーはインタースティシャルの表示の 滑らかな切り替えが可能になります
まず インタースティシャルを 統合されたタイムラインに表示する 様々な方法の概要を説明します
その後 APIとその使い方の説明に 進みます 次に Integrated Timelineを使用して UIを構築する サンプルアプリをお見せします 最後に Integrated Timelineでの インタースティシャルの動作を記述する 新しいHLS構文について説明します
まずは インタースティシャルを タイムラインに表示する各種の方法です
VODコンテンツへの広告挿入の 一般的なユースケースは トランスポートバー上のポイントとして インタースティシャルをマークすることです 再生がこのポイントに達すると 再生ヘッドが停止し インタースティシャルが再生されます インタースティシャルの終了後 メインコンテンツが再開され 再生ヘッドが先に進みます ほかのユースケースの例は インタースティシャルを トランスポートバーに範囲として表示し インタースティシャルの再生時間が トランスポートバー全体の再生時間に 含まれるようにすることです
これは ブロードキャスト形式の ライブストリームにおける広告や インタースティシャルで 埋め込みの コンテンツを置き換える場合に使われます
最後の例は 先ほどの例を拡張したものです 先ほどの例では UIのインタースティシャルが 黄色いハイライトで示されていました ここではインタースティシャルは メインコンテンツと 区別がつかないものとして組み込まれます これは HLSインタースティシャルを 評価バンパー 番組の要約などのプリロール 吹き替えなどのポストロールに 使用する場合に役立ちます 特に コンテンツがメインコンテンツと 密接に関連している場合に適しています
統合タイムラインでモデル化される 様々なケースを確認したところで 各ユースケースで再生時点を示す AVPlayerInterstitalEvent APIの 追加機能を見てみましょう
タイムライン上のポイントを示すには インタースティシャルイベントを作成します 次に timelineOccupancyという 新しいプロパティを設定し 値をsinglePointにします
タイムライン上の範囲を示すには イベントのtimelineOccupancyを fillに設定します ただし インタースティシャルイベントは 動的に読み込まれるため 再生ヘッドが近づいた際 イベントの実際の再生時間の 検出が遅れる可能性があります plannedDurationをタイムラインの フォールバックとして指定すれば 実際の再生時間がわかるまでの間 使用できます 範囲を指定するインタースティシャルでは このプロパティを設定しましょう
最後のケースでは インタースティシャルの範囲を UI上で区別できないようにするために supplementsPrimaryContentを trueに設定します
タイムライン上で インタースティシャルの 再生時点を示す方法を確認しました 次は Integrated Timeline APIの解説です
タイムラインの進行を確認するために Integrated Timelineには currentTimeを 取得するためのAPIが用意されています
再生ヘッドがPointEventに到達すると currentTimeが停止して イベントが再生されます
イベントの再生が終わると currentTimeは再び進みます 一方 次のFillのインタースティシャル イベントに到達した際は currentTimeは進み続けたままで イベントが再生されます
このAPIには インタースティシャル内の シーク機能も用意されています 例えば クライアントが10秒進めて シークしたいと仮定します これはFillEventの真ん中まで 進むことになります このAPIを使わない場合 このシークの実行は難しいでしょう 特に インタースティシャルイベントは 現時点で AVPlayerに読み込まれることも キューに入れられることもないためです このAPIを使えば タイムライン上の任意の位置に 簡単にシークできます メインコンテンツか インタースティシャルかは関係ありません Integrated Timelineの一部として AVPlayerItemSegmentという 新しいオブジェクトが用意されています タイムラインの各領域はセグメントであり 各セグメントには コンテンツに関する主要な詳細情報と タイムライン上での占有時間が 設定されています メインコンテンツの範囲も セグメントと見なされます
Integrated Timelineの最大のメリットは 再生UIを簡単に描画できることです ただし タイムラインは常に変化するので その描画は難しい場合があります 一貫した状態を維持するために導入したのが タイムラインから取得できる スナップショットオブジェクトです スナップショットは UIの描画に必要な 一貫性のあるプロパティセット全体を表します
再生が進んでタイムラインが変化しても 下部のスナップショットは固定されており 変化しません
AVPlayerItemIntegratedSnapshotを 使用すると そのスナップショットに含まれるすべての AVPlayerItemSegmentの一覧を取得できます この例では いくつかのセグメントを取り出して その値を確認できるようにしました 例えば スナップショット内の 最初のメインセグメントは メインとしてマークされ タイムライン上の0〜5の範囲を占めます 次のセグメントはインタースティシャルで PointEventです このセグメントの占有範囲は5〜5であり 再生時間はゼロです インタースティシャルイベントへの アクセスも提供し これにはtimelineOccupancyも singlePointとして含まれます
選択された最後のセグメントは FillEventです このセグメントはタイムライン上の 20〜30の範囲を占めており timelineOccupancyはfillです
すべての主要なオブジェクトを確認しました 次は Integrated Timelineを使った シンプルなUIの作成方法です まず メインのAVPlayerItemを作成します 次に AVPlayerItemから integratedTimelineを取得します タイムラインからスナップショットを取得し 現在の状態を描画できるようになりました 開始位置 終了位置 現在の位置を指定するだけで UIスライダを描画するルーチンがあると 仮定しましょう これらの値はすべて スナップショットから取得できます startはzeroで durationは スナップショットの再生時間です currentPositionは スナップショットのcurrentTimeです この時点で シンプルなトランスポートバーができました スライダ全体の一部として fillインタースティシャルが含まれます 次に タイムラインに 1つのポイントを描画してみます ポイントイベントの位置を取得するには スナップショットのセグメントをフィルタし Typeがinterstitialで timelineOccupancyがsinglePointの セグメントのみを取得します
それぞれのセグメントで セグメントのtimeMapping.target.startを タイムライン全体での位置として使って ポイントを描画します
最後に fillインタースティシャルを ハイライトします ここではスナップショットの セグメントをフィルタし occupancyがfillの インタースティシャルを表す セグメントを取得します supplementsPrimaryContentは イベントがUI上で 区別されるべきでないことを示すので これが設定されたインタースティシャルは 無視されます セグメントを取得したら セグメントのtimeMapping.targetを使って UIスライダ上のその領域をハイライトします
タイムラインは極めて動的で 各インタースティシャルの完了とともに 頻繁に変化します ライブストリームの場合も 頻繁に更新されます そのため タイムラインの snapshotsOutOfSyncNotificationをリッスンし その発生時には UIを更新する必要があります 通知クロージャが呼び出されたら 理由をuserInfoで調査できます 例えば 理由がsegmentsChangedであれば 新しいスナップショットを使って トランスポートバーを再描画できます 一方 理由が currentSegmentChangedであれば インタースティシャルとの 切り替えを行っている場合は playerControlsや その他のUI要素を更新します
以上 APIとサンプルコードを確認しました 次に 本セッションに関連する サンプルアプリを見てみます
このサンプルアプリには 様々な例が含まれています Fill Interstitialという例を 見てみましょう コンテンツ開始10秒後に再生されるよう 範囲設定したインタースティシャルを含みます
下部のトランスポートバーを見ると インタースティシャルが 10秒後の 黄色の範囲から開始することがわかります 再生すると 想定したタイミングで インタースティシャルに切り替わります
ここでシークをいくつか試し メインコンテンツに戻ってみましょう 最後に インタースティシャルまで戻ります
いいですね
先ほど アプリでインタースティシャルを 作成する方法を説明しましたが メインプレイリストで 指定することもできます サーバからタイムライン上の表示を 記述する方法を説明します
ポイントの例から見ていきます これはHLSインタースティシャルの DateRangeタグです タイムラインで これをポイントと見なすために 新しい属性である X-TIMELINE-OCCUPIESを導入しました 値がPOINTとして設定されています DateRangeタグには新たに X-TIMELINE-STYLEの属性も含まれており 値はHIGHLIGHTです これは UI表示でこのポイントを マークしなければならないことを示しています
範囲を設定する場合 新しい属性 X-TIMELINE-OCCUPIESの値は RANGEに設定されています X-TIMELINE-STYLEは 先ほどの例と同じです
最後に インタースティシャルをUI上で 区別できないように表示する場合 X-TIMELINE-OCCUPIESはRANGEのままです X-TIMELINE-SYTLEはPRIMARYに設定し アプリのUI内で その領域を特別な スタイルで表示しないことを示します
Integrated Timelineでは インタースティシャルを メインコンテンツの一部として UIに表示できるので インタースティシャルを SharePlayセッション中に 連携させられると便利です そこで今年 インタースティシャルの 連携のサポートを追加します
インタースティシャル対応の SharePlayの仕組みと その利用方法を見てみましょう 2つのプレイヤーで SharePlayセッションを連携させています 考え方としては Player1とPlayer2の両方が Event1の再生中も引き続き再生を同期します
シークについては Player1で Event1からメインコンテンツに 戻ろうとしているとします このシークはPlayer2に提案され 両方のプレイヤーが この新しいポイントから引き続き 再生を同期します インタースティシャルを SharePlayでサポートするための 重要な要件の一つは インタースティシャルイベントが 参加者全員に共通のものであることです つまり この例のEvent1は 両方のプレイヤーで共通している必要があり 同じコンテンツでなければなりません インタースティシャルが共通でない場合 インタースティシャルの再生は プレイヤー間で連携しません この要件に対応する方法を見てみましょう
AVPlayerInterstitialEventに 追加された新機能として 参加者や再生セッションにより コンテンツが変わるかを ブール値で指定できます この例では contentMayVaryをfalseに設定して このインタースティシャルが静的であり 共通することを示すことで イベントが連携可能になります
最後に DateRangeタグで 同じ動作を実現するために 新しい属性 X-CONTENT-MAY-VARYを NOに設定することもできます
では SharePlay対応の サンプルアプリを見てみましょう FaceTime通話で 2台のスマートフォンを使用しています このデモでは コンテンツの開始10秒後に インタースティシャルを再生する Fill(Supplements Primary)の例を 選択します
両方のスマートフォンで 起動が同期されており インタースティシャルに切り替わっても 同期状態が続きます その後 一時停止して 後方にシークし 最後に前方にシークします
これらすべてのコマンドが 両方のデバイスでシームレスに機能します まとめです インタースティシャルを含むUI体験の構築に Integrated Timelineを活用できます インタースティシャルの体験を カスタマイズするために APIまたは新しいDateRange属性で 動作を指定できます また APIでインタースティシャルを 変更不可としてマークすることで インタースティシャルアセットの SharePlayでの連携をサポートできます 関連するセッションである 「Explore dynamic pre-rolls and mid-rolls in HLS」もご覧ください HLSインタースティシャルの 詳細を説明しています SharePlayの詳細については 「Coordinate media experiences with Group Activities」をご覧ください ご視聴ありがとうございました
-
-
3:55 - Create a point interstitial event
// Creating a single point interstitial event let pointEvent = AVPlayerInterstitialEvent(primaryItem: playerItem, time: ten) pointEvent.timelineOccupancy = .singlePoint
-
4:07 - Create a fill interstitial event
// Creating a fill interstitial event let fillEvent = AVPlayerInterstitialEvent(primaryItem: playerItem, time: ten) fillEvent.timelineOccupancy = .fill fillEvent.plannedDuration = CMTime(value: 15, timescale: 1)
-
4:32 - Create fill interstitial supplementing primary
// Creating a fill interstitial event supplementing primary let fillEvent2 = AVPlayerInterstitialEvent(primaryItem: playerItem, time: ten) fillEvent2.supplementsPrimaryContent = true fillEvent2.timelineOccupancy = .fill fillEvent2.plannedDuration = CMTime(value: 15, timescale: 1)
-
7:14 - Draw simple transport bar with integrated timeline
// Create AVPlayerItem and obtain its integrated timeline let item = AVPlayerItem(url: ...) let integratedTimeline = item.integratedTimeline // Any time we need a new representation of the timeline state, we can request for a snapshot let snapshot = integratedTimeline.currentSnapshot // Using our snapshot, we can build a simple transport bar drawUISlider(start: .zero, duration: snapshot.duration, currentPosition: snapshot.currentTime) // Draw single-point interstitials on the transport bar let pointSegments = snapshot.segments.filter { segment in segment.segmentType == .interstitial && segment.interstitialEvent?.timelineOccupancy == .singlePoint } for segment in pointSegments { drawPoint(position: segment.timeMapping.target.start) } // Draw range interstitials on the transport bar let highlightFillSegments = snapshot.segments.filter { segment in if (segment.segmentType == .interstitial) { if let interstitialEvent = segment.interstitialEvent { return interstitialEvent.timelineOccupancy == .fill && !interstitialEvent.supplementsPrimaryContent } } return false } for segment in highlightFillSegments { let range = segment.timeMapping.target highlightRegion(start: range.start, end: range.end) }
-
8:26 - Listen to snapshotsOutOfSyncNotification to update our UI
// Listen to integrated timeline notifications to update our logic for await _ in NotificationCenter.default.notifications(named: AVPlayerItemIntegratedTimeline.snapshotsOutOfSyncNotification, object: integratedTimeline) { let reason = _.userInfo![AVPlayerItemIntegratedTimeline.snapshotsOutOfSyncReasonKey] as! AVPlayerIntegratedTimelineSnapshotsOutOfSyncReason switch(reason) { case .segmentsChanged: redrawTransportBar(snapshot: integratedTimeline.currentSnapshot) case .currentSegmentChanged: updatePlayerControls(snapshot: integratedTimeline.currentSnapshot) } }
-
11:42 - Set ContentMayVary to false for Interstitial SharePlay support
// Set contentMayVary to false for SharePlay support let event = AVPlayerInterstitialEvent(primaryItem: playerItem, time: ten) event.contentMayVary = false event.timelineOccupancy = .fill event.plannedDuration = CMTime(value: 15, timescale: 1) event.supplementsPrimaryContent = truensert code snippet.
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。