Audio & Video 高级 20m
使用 HLS Playlist Delta Updates 优化直播流
Optimize live streams with HLS Playlist Delta Updates
2020年6月25日
一句话判断
如果你在运营大规模直播服务,Playlist Delta Updates 可以将你的 CDN 带宽成本显著降低——每次 playlist 更新只传增量,不传全量。
这场 Session 讲了什么
这场 Session 聚焦于 Low-Latency HLS 的一个关键优化机制:Playlist Delta Updates。在传统 HLS 中,每次客户端请求 playlist 时,服务器都会返回完整的 playlist 文件。对于直播流来说,playlist 每隔几秒就更新一次,大量客户端同时请求完整的 playlist 会给 CDN 带来不小的带宽压力——尤其是 Low-Latency HLS 的 playlist 更新频率更高(每秒级别)。
Delta Updates 的思路很简单:客户端第一次请求时获取完整 playlist,后续请求只需要告诉服务器”我上次看到的 playlist 是什么状态”,服务器就只返回变化的部分。这通过 #EXT-X-DATERANGE 和 _HLS_skip 参数实现——客户端在请求 playlist 时带上 _HLS_skip=YES 参数,服务器会跳过已删除的 segment 条目,只保留新增的 partial segment。
演讲者通过多个 playlist 示例展示了 Delta Updates 的工作流程,并详细讲解了服务端如何生成 Delta Playlist、客户端如何合并 Delta 和本地缓存的 playlist。Session 还讨论了 CDN 缓存策略——Delta Playlist 需要和完整 Playlist 分别缓存。
值得深挖的点
- Delta Updates 的 CDN 缓存策略:这是最容易出错的地方。完整 playlist 和 Delta playlist 是同一个 URL 的不同版本(通过查询参数区分),CDN 必须根据
_HLS_skip参数分别缓存。如果 CDN 把 Delta 响应当作完整 playlist 缓存了,所有后续请求都会拿到错误数据。 - Playlist 滚动窗口的维护:直播 playlist 是一个滑动窗口,旧的 segment 会被移除。在 Delta 模式下,服务器不能直接移除旧条目——需要用
#EXT-X-DATERANGE的END-DATE来标记过期。这要求服务端维护更复杂的状态。
代码片段
# 客户端请求 Delta Playlist
# 第一次请求获取完整 playlist:
# GET /live/playlist.m3u8
# 后续请求带 _HLS_skip 参数:
# GET /live/playlist.m3u8?_HLS_skip=YES
# 服务端返回的 Delta Playlist(只包含增量):
# #EXTM3U
# #EXT-X-TARGETDURATION:6
# #EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=18.0
# #EXT-X-PART-INF:PART-TARGET=1.0
# #EXT-X-SKIP:SKIPPED-SEGMENTS=3
# #EXT-X-PART:DURATION=1.0,URI="part3.mp4"
# #EXT-X-PART:DURATION=1.0,URI="part4.mp4"
# 注意:前 3 个完整 segment 被 SKIPPED-SEGMENTS 替代
# 服务端生成 Delta Playlist 的逻辑
generate_delta_playlist() {
# 读取完整 playlist
full_playlist=$(cat /hls/live/playlist.m3u8)
# 计算可跳过的 segment 数量
skip_until=$((current_time - 18)) # CAN-SKIP-UNTIL = 18 秒
# 只保留最近 18 秒内的完整 segment 和所有 partial segment
delta_playlist=$(filter_old_segments "$full_playlist" "$skip_until")
# 添加 SKIPPED-SEGMENTS 计数
echo "#EXT-X-SKIP:SKIPPED-SEGMENTS=$skipped_count"
echo "$delta_playlist"
}
// AVPlayer 自动支持 Delta Updates
// 只需在 playlist 中包含正确的服务器控制标签:
// #EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=18.0
// AVPlayer 会自动在后续请求中添加 _HLS_skip=YES
// 开发者无需手动处理 Delta 合并逻辑
let asset = AVURLAsset(url: liveStreamURL)
let playerItem = AVPlayerItem(asset: asset)
let player = AVPlayer(playerItem: playerItem)
player.play()
最佳实践
CAN-SKIP-UNTIL建议设为 target duration 的 3 倍(如 6 秒 target duration 则设 18 秒),在带宽节省和客户端恢复能力之间取得平衡- CDN 必须根据请求中的
_HLS_skip参数区分缓存完整 playlist 和 Delta playlist - 监控 Delta Playlist 的平均大小,对比完整 Playlist,量化带宽节省效果
- 客户端长时间暂停后恢复播放时,应请求完整 playlist 而非 Delta playlist,避免合并失败
- 确保服务端的 Delta 生成逻辑是确定性的——同一时刻的相同请求应返回相同的 Delta
还有什么值得关注
- Delta Updates 与 Blocking Playlist Reload 配合使用效果最佳,可以同时降低延迟和带宽
- Apple 推荐使用 mediastreamvalidator 的
--validate-delta-playlists选项来验证 Delta 实现 - 这场 Session 是 Low-Latency HLS 系列的第三篇,搭配前两篇一起看效果最好
WWDC 2020