Adapt ad insertion to Low-Latency HLS
Audio & Video 高级 20m

将广告插入适配 Low-Latency HLS

Adapt ad insertion to Low-Latency HLS

2020年6月26日

在 Apple 官方观看视频

一句话判断

如果你在低延迟直播流中插入广告,这场 Session 解决了一个看似矛盾的问题:如何在保持 3 秒端到端延迟的同时,实现无缝的广告切换。

这场 Session 讲了什么

这场 Session 讨论了在 Low-Latency HLS 中实现广告插入(Ad Insertion)的特殊挑战。传统的 HLS 广告插入依赖 #EXT-X-DATERANGE#EXT-X-CUE-OUT 等标签来标记广告时段,播放器会提前准备广告内容。但在 Low-Latency HLS 中,partial segment 和更高的更新频率让广告插入的时序控制变得更加复杂。

演讲者首先回顾了两种主要的广告插入方式:Client-Side Ad Insertion(CSAI,客户端拼装)和 Server-Side Ad Insertion(SSAI,服务端拼装)。在低延迟场景下,SSAI 更有优势——服务端在推送流时就把广告内容编织进主频道,播放器不需要额外的广告请求,避免了延迟。

Session 的核心部分讲解了如何在 Low-Latency HLS 的 partial segment 级别实现精确的广告切换。关键是用 #EXT-X-GAP 标签标记广告边界,利用 #EXT-X-DATERANGE 的精确时间戳来控制切换时机,以及如何通过 #EXT-X-PRELOAD-HINT 让播放器提前加载广告内容。

最后讨论了广告追踪(Ad Tracking)在低延迟场景下的实现,包括如何在 partial segment 级别触发 impression 和 quartile 事件。

值得深挖的点

  • SSAI vs CSAI 在低延迟下的表现差异:CSAI 需要播放器在检测到广告标记后,额外请求广告 creative,这个过程至少增加 1-2 秒延迟。SSAI 由服务端预混合,播放器无感知,延迟几乎为零。在 Low-Latency HLS 场景下,SSAI 几乎是唯一可行的方案。
  • Partial Segment 级别的精确切换:传统 HLS 在 segment 边界切换广告,精度是 6 秒。Low-Latency HLS 可以在 partial segment 边界切换,精度提升到 1 秒甚至更短。这对用户体验的提升是显著的——观众几乎感知不到广告切入的瞬间。

代码片段

# Low-Latency HLS 中的广告标记
# #EXTM3U
# #EXT-X-TARGETDURATION:6
# #EXT-X-PART-INF:PART-TARGET=1.0
#
# #EXT-X-DATERANGE:ID="ad-123",START-DATE="2020-06-22T15:00:00Z",
#   DURATION=30.0,SCTE35-OUT=0xFC...
#
# #EXT-X-PART:DURATION=1.0,URI="content_part5.mp4"
# #EXT-X-PART:DURATION=1.0,URI="content_part6.mp4"
# --- 广告开始 ---
# #EXT-X-PART:DURATION=1.0,URI="ad_part0.mp4"
# #EXT-X-PART:DURATION=1.0,URI="ad_part1.mp4"
# --- 广告结束 ---
# #EXT-X-PART:DURATION=1.0,URI="content_part7.mp4"
# Server-Side Ad Insertion 的服务端逻辑
insert_ad_into_stream() {
    # 检测到广告触发信号(如 SCTE-35 marker)
    # 在下一个 partial segment 边界切换到广告内容

    # 等待当前 partial segment 完成
    wait_for_partial_segment_complete()

    # 切换到广告流
    ad_creative = fetch_ad_creative(ad_id)
    segment_ad(ad_creative, partial_duration=1.0)

    # 广告结束后切回主内容
    resume_content_stream()
}
// 客户端使用 AVPlayer 处理广告(CSAI 方式的降级方案)
// 监听 timed metadata 来检测广告边界
let asset = AVURLAsset(url: liveStreamURL)

// 观察 metadata 来检测广告事件
let playerItem = AVPlayerItem(asset: asset)
playerItem.addObserver(
    self, forKeyPath: "timedMetadata",
    options: .new, context: nil
)

// 在 KVO 回调中处理广告
override func observeValue(
    forKeyPath keyPath: String?,
    of object: Any?,
    change: [NSKeyValueChangeKey: Any]?,
    context: UnsafeMutableRawPointer?
) {
    if keyPath == "timedMetadata",
       let metadata = playerItem.timedMetadata {
        for item in metadata {
            if item.key as? String == "SCTE35-OUT" {
                // 广告即将开始
                prepareAdPlayback()
            }
        }
    }
}

最佳实践

  • 低延迟直播优先使用 Server-Side Ad Insertion,避免客户端广告请求带来的额外延迟
  • 广告的 partial segment duration 应与主内容一致,避免播放器 buffer 策略混乱
  • 使用 #EXT-X-DATERANGE 而非过时的 #EXT-X-CUE-OUT 标记广告时段
  • 广告创意预先编码为与主流相同的编解码器和分辨率,避免解码器重置
  • 广告追踪事件应与 partial segment 时间对齐,确保数据准确性

还有什么值得关注

  • Apple 的 HTTP Live Streaming (HLS) Authoring Specification for Apple Devices 文档中有详细的广告插入规范
  • 如果你在做个性化广告(每个观众看不同广告),SSAI 需要配合边缘计算来实现
  • 搭配整个 Low-Latency HLS 系列一起看,才能全面理解在低延迟约束下广告插入的完整方案
WWDC 2020