HLS 直播停止更新:播放列表未刷新,还是新分段丢失?

对比媒体序列、检查最新分段,并区分 CDN 缓存和播放器恢复问题。附可重复运行的 HTTP 测试。

直播画面卡住时,网页和顶层 .m3u8 地址仍可能返回 200。这个状态码说明不了多少:播放器可能反复拿到同一份媒体播放列表,也可能已经看到列表里的新分段,却下载不到该分段。两类故障涉及的环节和修复方式不同。

下面用尽量少的请求记录把它们分开。只检查你拥有或获授权检查的流;分享日志前,删除签名查询参数、Cookie、令牌、观众标识和 IP 地址。

*验证方法——2026 年 9 月 17 日:*我们在 Node.js 24.12.0 中运行了本机回环 HTTP 测试。/live.m3u8 前两次响应完全相同:媒体序列为 100,列出分段 100–102;第三次推进到序列 101,列出 101–103。分段 102 返回 200,刚列出的分段 103 则被刻意设为 404。它复现的是两种HTTP 层的观察结果:列表快照不变,以及列表已更新但新分段不可用。测试返回的是文本标记,不是可解码视频。我们没有测试实际播放、hls.js 重试、直播编码器、CDN 或设备重连。复现脚本保存在项目的 scripts/test-live-hls-playlist-diagnosis.cjs。

先看媒体播放列表,不要只看主播放列表

多变体(主)播放列表描述可选的变体;所选视频、音频或字幕轨道的直播媒体播放列表才列出当前分段。主列表请求成功,不代表子列表是新的。两者区别见主播放列表与媒体播放列表指南。

重新加载播放器前打开开发者工具并保留网络日志。找到正在使用的媒体播放列表请求、跳转后的最终 URL,以及后续分段请求。可筛选 m3u8、ts、m4s 和实际分段路径;具体找法见 M3U8 开发者工具指南。

至少保存两次列表响应内容,不能只记两个状态码。逐次记录 #EXT-X-MEDIA-SEQUENCE、首末分段 URI、#EXT-X-TARGETDURATION、是否有 #EXT-X-ENDLIST、响应时间,以及可用时的 Date、Age、Cache-Control、ETag 和 Last-Modified。有些响应没有这些头;如实记为缺失,不要补造。

辨认两种主要故障迹象

请求记录可以确定什么下一步检查
列表请求返回 4xx/5xx 或网络错误客户端没拿到可用列表源站、CDN、授权、DNS 或连接
多次响应逐字节相同,序列和最新 URI 都没变客户端在这些响应里没有发现新媒体发布时间、缓存时间、源站与边缘节点的响应、播放器是否重载
序列及最新 URI 已推进,新分段却返回 404列表可能早于分段发布,或分段路径错误打包器发布顺序、源站对象、CDN 传播、URL 解析
分段返回 200,画面仍卡住HTTP 可访问还不够响应正文、媒体时间戳、解码、缓冲、播放器错误
列表含 #EXT-X-ENDLIST该呈现声明不会再增加分段核实直播活动是否确已结束

一次列表不变,不足以证明直播故障。正常直播列表在两次发布之间本来就可能相同;要结合目标时长记录足够多的快照。类似地,分段返回 200 也不证明视频可解码:成功状态下仍可能收到 HTML 错误页或不兼容的媒体片段。

按直播列表的时钟判断

RFC 8216 要求客户端定期重载仍在更新的直播媒体列表。按该规范,列表发生变化后,下次尝试至少等待一个目标时长;列表未变化时,至少等待半个目标时长。服务端发布新版本的时限是另一组独立规则。目标时长为八秒时,不能仅因客户端没有每秒重载,就认定它“卡死”。

这些是协议规则,并不保证第三方 CDN 或应用一定正确执行。低延迟 HLS 可能采用阻塞式列表重载和部分分段;应按实际流的机制分析,而不是套用固定轮询间隔。Apple 对阻塞式重载有单独说明。

常规滑动直播窗口按顺序移除旧分段 URI,#EXT-X-MEDIA-SEQUENCE 随之推进。长时间断网后若序列跳变,播放器可能落在可用窗口之后;但仅凭序列号无法判断解码器是否恢复。分段可用性和播放时间需要分别检查。

一组受控请求记录

测试连续返回以下三个列表快照。它没有等待真实的目标时长,所以只隔离了响应内容,不验证时序合规性。

请求HTTP媒体序列列出的分段最新分段状态
第一次200100100、101、102102 → 200
第二次200100100、101、102响应未变化
第三次200101101、102、103103 → 404

第二次只能表明当时尚未通告新分段。第三次证明列表随后推进,但新通告的对象在所测 URL 上不可用。这两点单独都不能证明真实播放器会如何恢复。它们的价值在于让编码器或 CDN 负责人拿到具体的首个失败请求,而不只是“直播卡了”。

分清源站、CDN 与播放器的证据

若边缘节点持续返回旧列表,在授权条件相同的前提下,对比源站与 CDN 的同一列表。记下最终 URL 和缓存响应头。较大的 Age 值可以作为线索,但不能仅凭一个响应头推断整个缓存策略,也不要假设所有 CDN 以同样方式暴露状态。不要把随意添加查询参数当成长期“修复”:签名 URL 与缓存键可能让这种做法失真或产生风险。

若列表已推进但分段缺失,请按媒体播放列表 URL 解析出该分段的确切 URI,再请求它,同时检查状态码和响应正文。列表新鲜也挡不住“先通告、后可取”的分段让播放停顿。RFC 8216 要求列表中列出的分段立即可用。检查打包器发布顺序,以及源站和 CDN 是否及时提供新对象。

若列表和分段请求都成功,再转向播放器。使用 hls.js 时,可以记录 LEVEL_LOADING、LEVEL_LOADED 或 LEVEL_UPDATED、FRAG_LOADING、FRAG_LOADED 与 ERROR 详情,并标明错误是否致命。其 API 区分列表加载和分段加载故障。原生 Safari 播放不一定触发同样的事件,应使用该浏览器的媒体诊断工具。更广泛的卡顿症状可继续查阅 HLS 缓冲诊断。

网络重连后要验证什么

针对每种受支持的浏览器和真实设备,记录列表请求是否恢复、序列及最新 URI 是否推进、下一个分段是否成功,以及视频播放时间是否再次前进。分别测试短暂离线和长到足以让滑动窗口越过旧位置的离线。桌面浏览器的手机宽度视口不能代替手机网络切换或后台媒体策略测试。

不要在每次网络事件后自动调用 video.play(),然后就报告“恢复成功”。用户可能是主动暂停,也可能被浏览器策略阻止;流本身仍可能是旧数据。保留用户可操作的播放入口;若源已加载却无法开始播放,参考 HLS 自动播放故障诊断。

一份有用的事故记录应包含最后一个正常媒体序列、第一次未更新或失败的响应、确切分段状态、缓存响应头、浏览器与播放器版本,以及最终是否恢复播放。这样的证据才能区分“CDN 仍提供旧窗口”和“播放器无法解码新分段”。

参考资料

按顺序追踪媒体播放列表和首个新通告的分段。证据从哪一步停止变化,就从哪一步继续查;不用只看着冻结的画面猜原因。