Audio & Video 高级 20m
将广告插入适配 Low-Latency HLS
Adapt ad insertion to Low-Latency HLS
2020年6月26日
一句话判断
如果你在低延迟直播流中插入广告,这场 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