Reduce latency with HLS Blocking Playlist Reload
Audio & Video 高级 20m

使用 HLS Blocking Playlist Reload 降低延迟

Reduce latency with HLS Blocking Playlist Reload

2020年6月26日

在 Apple 官方观看视频

一句话判断

传统 HLS 的”轮询等待新数据”模型是延迟的主要来源——Blocking Playlist Reload 把轮询变成了长连接推送,这是低延迟直播的关键一环。

这场 Session 讲了什么

这场 Session 深入讲解了 Low-Latency HLS 的 Blocking Playlist Reload 机制。在标准 HLS 中,播放器通过定期重新请求 playlist 来发现新内容。播放器加载 playlist 后,需要等待 target duration 的一半时间才能再次请求。这种轮询模式在直播场景下造成了两方面的浪费:一是延迟(等了太长时间才拿到新数据),二是带宽(请求了太多次拿到的是旧数据)。

Blocking Playlist Reload 的解决方案是:当播放器重新请求 playlist 时,如果服务器判断 playlist 还没有更新,它不会立即返回旧数据,而是保持 HTTP 连接打开,等到 playlist 真正更新后再返回。这样播放器不需要猜测轮询间隔,新数据产生后第一时间就能拿到。

演讲者详细讲解了 #EXT-X-SERVER-CONTROL 标签中的 CAN-BLOCK-RELOAD=YES 声明,以及客户端如何通过 _HLS_msn_HLS_part 查询参数指定自己期望的 Media Sequence Number。服务端收到请求后,如果指定的 MSN 还没准备好,就 blocking 等待。Session 还讨论了超时处理、错误恢复和多码率场景下的 playlist 同步问题。

值得深挖的点

  • MSN 和 Part 编号的精确控制:客户端不是盲目请求 playlist,而是明确告诉服务器”我要的是 Media Sequence Number X 的 Part Y”。这种确定性请求让服务端可以精确判断是否需要 blocking。如果客户端请求的 MSN 已经存在,服务端立即返回;如果还没生成,服务端 blocking 等待。这个设计比简单的”有更新就告诉我”要精确得多。
  • 多码率同步的挑战:在 ABR(自适应码率)场景下,不同码率的 variant playlist 是独立的。如果客户端在一个 variant 上 blocking 等待,另一个 variant 可能已经更新了。Apple 建议客户端在每个活跃的 variant 上都维护独立的 blocking 连接。

代码片段

# Blocking Playlist Reload 的请求方式
# 播放器在 playlist URL 后添加 MSN 和 Part 参数:
# GET /live/playlist.m3u8?_HLS_msn=42&_HLS_part=3

# 服务端行为:
# - 如果 MSN 42 Part 3 已存在:立即返回 playlist
# - 如果尚未生成:保持连接,等待生成后返回
# - 如果超过超时时间:返回当前最新 playlist

# 服务端在 playlist 中声明支持 blocking:
# #EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=3.0
# 服务端实现 blocking 的简化逻辑
handle_playlist_request(msn, part) {
    current_msn = get_current_msn()

    if msn <= current_msn:
        # 请求的 MSN 已存在,立即返回
        return full_playlist()
    else:
        # 等待新的 MSN 生成
        timeout = get_part_hold_back()  # 通常 3 秒
        wait_for_msn(msn, timeout)

        if msn_ready(msn):
            return full_playlist()
        else:
            # 超时,返回当前最新状态
            return current_playlist()
}
// AVPlayer 原生支持 Blocking Playlist Reload
// 只要 playlist 包含 CAN-BLOCK-RELOAD=YES 声明
// AVPlayer 会自动使用 blocking 模式请求 playlist

// 开发者可以通过 AVPlayerItemAccessLog 监控效果
let playerItem = AVPlayerItem(url: liveStreamURL)
playerItem.addObserver(
    self, forKeyPath: "accessLog",
    options: .new, context: nil
)

// 在 KVO 回调中查看请求详情
if let accessLog = playerItem.accessLog() {
    for event in accessLog.events {
        print("请求间隔: \(event.numberOfSegmentsDownloaded)")
        print("指示比特率: \(event.indicatedBitrate)")
    }
}

最佳实践

  • 服务端 blocking 超时建议设为 PART-HOLD-BACK 值,通常 3 秒,避免连接占用时间过长
  • CDN 必须支持 HTTP/2 的长连接,否则 blocking 请求会被中间代理截断
  • 监控服务端的并发 blocking 连接数,这是容量规划的核心指标
  • 客户端在网络切换(Wi-Fi 到蜂窝)后应重置 blocking 状态,重新请求完整 playlist
  • 如果 blocking 请求超时,客户端应回退到传统轮询模式作为降级策略

还有什么值得关注

  • Blocking Playlist Reload 和 Delta Updates 可以同时使用,既降低延迟又节省带宽
  • 这场 Session 是 Low-Latency HLS 系列的深度技术讲解之一,建议按顺序观看整个系列
  • Apple 在 GitHub 上提供了参考的服务端实现,可以帮助理解 blocking 逻辑
WWDC 2020