为什么 HLS 直播会越播越慢,以及“回到直播”按钮应该跳到哪里

分别测量播放列表边缘、墙上时钟延迟、播放器安全距离、过期清单和低延迟 HLS 行为,准确诊断 HLS 直播延迟。

两位观众观看同一场 HLS 直播时,可能处在不同位置,但两台播放器都显示“直播中”。进度条即使已经到达最右端,也可能落后信号源数秒。如果“回到直播”按钮跳到最后一个已公布的瞬间,而不是播放器支持的直播同步点,它甚至会让播放更不稳定。

第一步是不要再把延迟当成一个数字。需要分别看信号源时间、播放列表可用性、播放位置与播放列表边缘的距离、播放器选取的安全延迟,以及最终显示时间。请只测试自己拥有或获准检查的流,并从共享记录中删除签名 URL、Cookie、观众 ID、IP 地址和私有主机名。

*验证方法 — 2026 年 9 月 23 日:*我们使用 Node.js 24.12.0 对一个构造的媒体播放列表进行了确定性计算。该播放列表包含五个 6 秒分段,EXT-X-PROGRAM-DATE-TIME 从 UTC 16:00:00 开始。已公布窗口在 16:00:30 结束,观察时间为 16:00:32。媒体时间为 6 的播放位置落后播放列表边缘 24 秒,落后测试清单墙上时钟 26 秒。采用特意设定的 12 秒安全延迟后,受控“回到直播”目标为媒体时间 18:落后播放列表边缘 12 秒,落后测试清单时钟 14 秒。这只验证脚本的播放列表计算。它没有测量编码器、网络、CDN、浏览器媒体管线、设备时钟、真实播放或端到端服务目标。脚本位于 scripts/test-hls-live-latency-budget.cjs。

比较之前,先给三个位置准确命名

“直播边缘”常被用来指代不同位置。应在日志和监控面板中为每项观察使用不同名称。

位置或测量值实用定义证据来源
播放列表边缘当前向该客户端公布的最新媒体结束位置媒体播放列表以及分段或部分分段时长
可拖动终点媒体元素当前报告的最晚可拖动时间轴位置video.seekable.end(...)
直播同步点播放器在边缘之后考虑安全策略选出的目标播放器 API 或明确的应用配置
播放位置距离播放列表或可拖动边缘减去当前播放位置同一时间轴上的 currentTime 和对应边缘
墙上时钟延迟观察时间减去播放位置对应的估算节目时间可信时钟和一致的节目日期映射
采集到显示延迟从信号源采集到观众屏幕呈现同步的信号源与显示测量

这些数值不一致并不矛盾。播放器可能只落后其缓存播放列表两秒,但该播放列表本身已经远远落后源站。新鲜播放列表可以很接近信号源,而观众可能在 DVR 窗口内暂停了十分钟。节目日期标签能够标识内容时间,却不能单独证明摄像机何时采集了某一帧。

先检查播放器时间轴

在点击“回到直播”前后,记录全部 seekable 范围、当前播放时间、缓冲范围、时长、播放速率和就绪状态。

function mediaSnapshot(video) {
  const copyRanges = ranges => Array.from(
    { length: ranges.length },
    (_, index) => ({ start: ranges.start(index), end: ranges.end(index) })
  );

  return {
    currentTime: video.currentTime,
    duration: video.duration,
    playbackRate: video.playbackRate,
    seekable: copyRanges(video.seekable),
    buffered: copyRanges(video.buffered),
    readyState: video.readyState,
  };
}

不要用 duration 代替当前直播边缘。MDN 指出,直播媒体可能失去对过期内容的访问,而设置 currentTime 后,实际位置仍可能被调整到媒体支持的位置。如果存在多个可拖动范围,应全部记录,不要假定最后一个范围能够代表所有轨道和呈现版本。

如果请求位置已经离开直播窗口,请使用HLS 拖动和 DVR 诊断指南。只有在当前时间轴已经明确后,才开始诊断延迟。

为什么普通 HLS 会落后最后一个已公布分段

直播客户端需要保留足够媒体,才能应对正常的播放列表重新加载和下载波动。RFC 8216 建议:对于尚未结束的播放列表,开始普通播放时不要选择起点距离末尾不足三个目标时长的分段,因为起得太靠后可能导致卡顿。这是协议时代的安全建议,并不是每台播放器都会精确保持三个分段延迟的承诺。

分段生成、播放列表发布、从源站传播到 CDN、播放列表重载时机、对象下载、缓冲策略、解码和渲染都会造成延迟。其中一些可以重叠,另一些则会累积。仅仅改变播放器起点,无法消除媒体出现在播放列表之前已经花掉的时间。

层级用来隔离它的证据常见错误结论
采集和编码画面内时间码或独立观察到的信号源时间EXTINF 能说明采集延迟
打包和发布分段/部分分段完成时间及播放列表可用时间CDN 响应时间等于打包时间
CDN 和缓存Age、缓存状态、最终 URL、重复的播放列表正文HTTP 200 代表拿到了最新播放列表
播放器加载播放列表/分段请求时序和所选起点进度条右端就是信号源边缘
缓冲和恢复缓冲范围、卡顿、播放速率变化每一秒额外延迟都是有意的安全余量
解码和渲染媒体事件加设备级测量仅凭 currentTime 就能得到采集到显示延迟

先测量第一个尚未被证实的层级。如果连续多次请求的播放列表正文,在预期更新周期之外仍完全相同,应先诊断陈旧交付,再调整播放器。我们的陈旧播放列表与缺失分段指南给出了判断路径。

计算两种延迟,而不是一种

无需墙上时钟元数据,就能算出播放位置与播放列表边缘的距离:

播放位置距离 = 播放列表时间轴终点 - 当前播放位置

这有助于控制播放器,但无法说明播放列表边缘本身有多旧。如果有一致的 EXT-X-PROGRAM-DATE-TIME 映射和可信时钟,再估算第二部分:

播放列表边缘年龄 = 观察时间 - 播放列表边缘对应的节目时间
估算墙上时钟延迟 = 播放列表边缘年龄 + 播放位置距离

我们的测试清单把 2 秒的播放列表边缘年龄与 24 秒的播放位置距离分开,受控时间轴上的总和为 26 秒。这些数字是测试输入和输出,不是对 M3U8Online 或生产广播服务的测量。

RFC 8216 将 EXT-X-PROGRAM-DATE-TIME 定义为分段第一个采样与绝对日期时间之间的映射,同时提醒播放列表日期可以表示内容生成时间,也可以表示与播放无关的其他时间。发布延迟指标前,请验证时间戳的实际含义、时区处理、时钟同步,以及变体和不连续点之间的映射一致性。

“回到直播”应跳到同步点,而不是数学终点

可用时,可靠的“回到直播”操作应使用播放器文档明确说明的直播同步点。在 hls.js 中,liveSyncPosition 是直播边缘减去所配置的安全延迟;maxLatency 则描述播放器可以向前跳到该同步点的延迟阈值。因此,播放器 API 比从其他服务照搬的固定减法更可靠。

如果播放路径没有公开直播同步目标,请在当前可拖动范围内定义符合产品要求的目标,并在受支持设备上验证。不要简单执行 currentTime = seekable.end(last),然后假定最后一个已公布瞬间已经可以下载和解码。

按钮流程应当:

  1. 获取当前可拖动范围和播放器延迟值。
  2. 选择文档明确的播放器同步点,或经过验证、位于最新可拖动终点之前的目标。
  3. 将目标限制在当前可拖动范围内。
  4. 只请求一次拖动,不要与播放器自带的延迟控制器对抗。
  5. 记录 seeking、seeked、结果 currentTime 以及操作后的首批请求。
  6. 确认结果后再更新按钮状态。

在确定性测试清单中,所选目标仍落后播放列表边缘 12 秒。跳到该位置后,模型墙上时钟延迟从 26 秒降到 14 秒,并没有变为零。这正是保留 12 秒安全延迟和 2 秒边缘年龄的预期结果。

明确“直播中”标记代表什么

“直播中”是需要书面规定容差的产品决策。它可以表示观众接近播放器目标、接近播放列表边缘,或者接近可信节目时钟。这三种承诺并不相同。

界面状态示例判断条件用户操作
直播中播放位置在直播同步点的验证容差内无需操作
落后直播当前位置有效,但超出该容差提供“回到直播”
已暂停播放已经暂停,即使刚才仍在直播位置分别提供继续播放和回到直播
正在重新加入播放器正在加载校正后的目标显示进度,暂不宣称直播中
无可靠时钟已知边缘距离,但不知道墙上时钟延迟不要显示凭空编出的精确延迟

请提供可访问的文字标签,不要只用红点。如果观众主动在 DVR 窗口内回看,不要自动把他们拉回前方,除非产品明确承诺低延迟观看并清楚说明这种行为。

诊断播放过程中持续增长的延迟

如果播放器开始时接近目标,随后却越来越落后,请把偏离开始的时间与以下观察进行比对:

  • 播放列表重载迟到、内容不变,或来自意外陈旧的缓存。
  • 分段下载时间接近或超过媒体时长。
  • 缓冲恢复时从较旧位置继续,而不是直播同步点。
  • 追赶策略改变播放速率后,速率一直低于 1。
  • 移动标签页进入后台或应用暂停后,常规加载停止。
  • 广告时段、时间戳不连续、轨道切换或变体切换改变了时间轴。
  • 应用状态反复把旧的 currentTime 写回播放器。
  • 配置的最大延迟校正从未触发,或与自定义代码冲突。

把播放列表、网络、播放器和媒体元素事件记录在同一条单调时间线上。如果流反复卡顿,请先修复交付问题,再缩小缓冲;HLS 卡顿诊断会分别检查吞吐量、分段可用性、解码和播放器状态。

低延迟 HLS 是一种端到端模式

不能通过给播放列表改名或添加一个查询参数来启用低延迟 HLS。Apple 文档把部分分段、服务器控制、阻塞式播放列表重载、预加载提示、呈现版本报告,以及特定服务器/CDN 行为作为协同工作的组成部分。当前编写指南还对 Part Target Duration 和 PART-HOLD-BACK 提出约束。

应把编码器或打包器、源站、CDN 缓存行为、播放列表响应、播放器支持和回退路径放在一起验证。不支持低延迟特性的客户端可能采用不同的请求模式;即使清单包含低延迟标签,错误缓冲或缓存播放列表请求的 CDN 也会抵消预期收益。

不要仅根据 PART-TARGET 宣传延迟数字。使用同步时钟和受支持的生产路径,测量真实的信号源到显示行为。未测试的浏览器和设备必须标为未测试。

执行受控测试矩阵

对每条受支持播放路径记录:

  • 原生 HLS 与 JavaScript/MSE(适用时两者都测)。
  • 标准直播与真实的低延迟 HLS 配置。
  • 冷启动、正常稳定播放、回到直播、暂停/继续、DVR 回看,以及卡顿后恢复。
  • 新鲜源站响应、CDN 命中和有意构造的陈旧播放列表测试。
  • 低码率和高码率变体,以及备用音频和字幕。
  • 实体移动设备上的前台播放和后台恢复。
  • 在正式支持的桌面、手机、平板和电视产品上播放同一条获准测试的流。

缩小桌面窗口并不等于测试移动端后台策略或解码器。应如实记录缺少的硬件。签名媒体 URL 和观众标识不得出现在截图与错误报告中。

用证据报告延迟预算

可执行的报告可以写成:“UTC 16:00:32 时,最新公布的节目时间为 16:00:30。播放位置映射到 16:00:06,因此播放列表边缘年龄为 2 秒,播放位置距离为 24 秒。点击‘回到直播’后,请求了媒体时间 18 的播放器同步点,并在该位置完成。”这样,打包、CDN 和播放器负责人就能看清每部分延迟属于哪个层级。

报告中应包含时钟来源与同步方式、节目日期语义、播放列表请求和缓存数据、播放列表边缘、可拖动范围、当前位置、播放器直播同步和最大延迟设置、校正后的首批请求、卡顿与播放速率变化,以及设备/播放器版本。如果时间戳映射或时钟未经验证,就不要给出墙上时钟延迟。

主要参考资料

请分别测量播放列表边缘、边缘年龄和播放位置距离,再让“回到直播”跳到经过测试的同步点,而不是最后一个已公布瞬间。这样既能保留安全策略,又能暴露陈旧交付,并防止界面承诺一个从未真正测量的延迟数字。