WWDC Quick Look 💓 By SwiftGGTeam
Enhance ad experiences with HLS interstitials

Enhance ad experiences with HLS interstitials

元の動画を見る

ハイライト

HLS Interstitials は、メインコンテンツのタイムライン上に独立した広告セグメントを挿入する Apple の方式。広告をメインコンテンツストリームに直接スプライスするより、広告決定の遅延バインディングと正片と一緒に処理不要な広告コンテンツをサポートし、柔軟性が高い。


主要内容

動画プレイヤーに広告を挿入する最も素朴な方法は、広告セグメントをメインコンテンツストリームに直接スプライスすること。問題は明らか——広告決定はコンテンツ準備段階で確定必須。再生時の動的差し替え不可。広告を変えるたびにストリーム全体を再スプライス。HLS Interstitials は異なるアプローチ——広告は独立リソース。メインコンテンツタイムラインの指定位置にマウント。再生がその時点に達して初めてロード・再生。広告は遅延バインディング可能。正片ストリームは一切変更不要。

今年の焦点は Integrated Timeline API。以前開発者が直面した難題: タイムラインに正片と広告セグメントが混在——プログレスバーをどう描く?広告未ロード位置へシークしたら?新 API は AVPlayerItemIntegratedTimelineAVPlayerItemSegment で解決。各時間帯(正片または広告)が Segment。開始・終了時間、タイプ、occupancy などの属性付き。Snapshot メカニズムでタイムライン状態読み取り時に一貫データを保証。タイムライン動的変化でも読み取り不整合を回避。SharePlay も Interstitials 同期再生をサポート開始——全参加者間で広告コンテンツが同一とマークすれば、広告中も同期維持可能。


詳細

3 つのタイムライン表示モード

Interstitials にはタイムライン上で 3 つの表示方式(03:40):

  • Point: プログレスバー上に点をマーク。再生ヘッド到達時に前進停止、広告後に再開。VOD コンテンツの広告マーカー向け。
  • Fill: プログレスバー上に範囲を占有。広告時間が総時間に計上。ライブストリームで焼き込みコンテンツを置換するシーン向け。
  • Supplements Primary Content: Fill と類似だが UI 上正片と区別不可。リキャップ、トランジション、レーティング表示など正片と密接な補助コンテンツ向け。

AVPlayerInterstitialEventtimelineOccupancysupplementsPrimaryContent で制御(03:54):

// Creating a single point interstitial event
let pointEvent = AVPlayerInterstitialEvent(primaryItem: playerItem, time: ten)
pointEvent.timelineOccupancy = .singlePoint

キーポイント:

  • timelineOccupancy.singlePoint に設定。タイムライン上に点マーカーのみ
  • Point タイプは再生時に総時間を増やさない。広告中は再生ヘッド停止
// Creating a fill interstitial event
let fillEvent = AVPlayerInterstitialEvent(primaryItem: playerItem, time: ten)
fillEvent.timelineOccupancy = .fill
fillEvent.plannedDuration = CMTime(value: 15, timescale: 1)

キーポイント:

  • timelineOccupancy.fill に設定。タイムライン上に範囲占有
  • plannedDuration は Fill タイプの時間フォールバック。広告実時間は遅れて判明する場合あり
  • plannedDuration 設定は Fill イベントのベストプラクティス(04:19
// 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)

キーポイント:

  • supplementsPrimaryContent = true は UI 上正片と区別不可を意味
  • レーティング表示、リキャップ、エンドカードなど(04:30

Integrated Timeline API

AVPlayerItem から AVPlayerItemIntegratedTimeline を取得(07:14)。currentSnapshot で UI 描画に必要な全一貫状態を含む不変スナップショットを返す。

// 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)
}

キーポイント:

  • item.integratedTimeline で AVPlayerItem からタイムラインオブジェクト取得
  • integratedTimeline.currentSnapshot は不変スナップショット。再生状態変化で変異しない
  • snapshot.duration は Fill 広告の総時間含む。snapshot.currentTime は Point 広告再生中に前進停止
  • supplementsPrimaryContent == true のセグメントをフィルタ。UI 上区別マークしない
  • 各 Segment に segmentType.primary または .interstitial)と timeMapping 属性

タイムライン変化の監視

タイムラインは動的——広告は動的解決、ライブストリームは継続更新。snapshotsOutOfSyncNotification で監視(08:26):

// 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)
    }
}

キーポイント:

  • segmentsChanged: セグメント構造変化(広告解決または削除)。プログレスバー再描画
  • currentSegmentChanged: 再生ヘッドが正片↔広告に移動。プレイヤーコントロール更新
  • 通知後 currentSnapshot を再取得すれば最新一貫状態

HLS サーバー側新属性

HLS DateRange タグに 2 つの新属性でタイムライン動作制御(09:50):

  • X-TIMELINE-OCCUPIES: 値 POINT または RANGE.singlePoint.fill に対応
  • X-TIMELINE-STYLE: 値 HIGHLIGHT(UI 上マーク)または PRIMARY(正片と区別不可)

SharePlay サポート

Interstitials が SharePlay 同期再生に参加可能に(10:57)。前提: 全参加者間で広告コンテンツが同一。contentMayVary = false でマーク:

// 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 = true

キーポイント:

  • contentMayVary = false は全参加者間で広告コンテンツ同一。同期再生可能
  • HLS DateRange では X-CONTENT-MAY-VARY=NO に対応(11:59
  • パーソナライズ広告(推薦ベースなど)は同期を有効にしない
  • 同期中、一時停止・シークなどの操作は参加者間で自動同期

重要ポイント

  • 何をするか: Integrated Timeline で広告を含むプレイヤープログレスバーを描画。なぜ価値があるか: 統一タイムラインモデルがなければ、プログレスバーは広告占有を正確反映できず、広告位置へのシークも失敗。始め方: AVPlayerItem.integratedTimeline からタイムライン取得。currentSnapshot で segment リスト読み取り UI 描画。

  • 何をするか: supplementsPrimaryContent で動画ストリームの discontinuity セグメントを置換。なぜ価値があるか: リキャップ、トランジションなどは通常 discontinuity として正片に焼き込み——置換コスト高。Interstitials なら補助コンテンツを独立管理し UI 上正片とシームレス。始め方: supplementsPrimaryContent = true の Fill イベント作成。plannedDuration 設定。HLS で X-TIMELINE-STYLE=PRIMARY マーク。

  • 何をするか: SharePlay シーンの共通広告で同期再生を有効化。なぜ価値があるか: 複数人でライブ視聴時、広告中に各自異なるコンテンツだと同期破壊。contentMayVary = false で広告再生・一時停止・シークが自動同期。始め方: 全参加者間で広告コンテンツ同一を確認。イベントに contentMayVary = false 設定。または HLS DateRange に X-CONTENT-MAY-VARY=NO 追加。


関連セッション

コメント

GitHub Issues · utterances