一、业务需要自定义 M3U8/TS 分片 HTTP 请求头
很多业务场景,需要给 M3U8、TS 分片、密钥接口增加自定义 HTTP 请求头。比如业务需要传递自定义 token 头、自定义 Referer、自定义 Cookie 相关逻辑。
很多新手第一反应,去改浏览器全局 ajax 拦截,重写 XMLHttpRequest。这种方式侵入性很强,会影响页面所有其他网络请求,容易带来副作用。
实际上 hls.js 支持自定义 loader 加载器,可以专门接管 hls.js 内部所有 M3U8、TS 分片、密钥的网络请求,单独修改播放器相关请求的 header,不会干扰页面其他业务接口。
网上大部分入门 demo 只演示基础播放,很少讲自定义 loader,很多开发不知道这个能力。写自定义 loader 有不少坑点:需要处理超时、abort 中止请求、错误回调,写的不完善会出现内存泄漏、请求没有取消等问题。
调试自定义 loader 的效果,我会使用 m3u8live.cn,先确认原始 M3U8 在默认 loader 下可以正常播放,再对比业务页面自定义 loader 之后的网络请求头。
二、什么时候适合使用自定义 loader
- 需要给所有 hls 发起的请求增加自定义请求头;
- 请求之前动态修改 url 参数;
- 对请求做统一签名处理;
- 需要拦截请求,本地替换返回的 M3U8 文本内容。
注意:自定义 loader 只接管 hls.js 内部网络请求,不会干预页面自己的 fetch、axios 业务接口。
三、自定义 loader 高频踩坑
坑 1:忘记处理 abort 中止回调
hls.js 切换源、销毁实例的时候,会调用 loader 的 abort 方法。如果自定义 loader 没有实现 abort,旧的网络请求不会被取消,后台继续请求分片,造成内存泄漏,残留大量无效网络请求。
坑 2:超时逻辑没有实现,请求无限挂起
网络差的时候请求一直 pending,没有超时处理,hls.js 一直等待,不会触发错误回调。
坑 3:回调函数写错,fatal 错误无法正常抛出
loader 的 success、error 回调参数格式要和官方要求保持一致,参数顺序写错,hls.js 拿不到返回数据,直接解析失败黑屏。
坑 4:直接修改全局 XMLHttpRequest
重写全局 XHR 会污染整个页面,页面其他所有 ajax 全部受影响,出现意想不到的副作用,不推荐。
四、开发简单注意点
- 实现自定义 loader,必须完整实现:
load()、abort()两个核心方法; - load 内部发起 fetch 或者 XMLHttpRequest;
- 一定要处理 abort,实例销毁、切换源的时候取消正在进行的网络请求;
- 处理超时,超时之后调用 error 回调;
- success 回调返回对象格式要和官方文档对齐,包含 responseText、status 等字段。
提示:新手不建议一上来完全手写全套 loader,如果只是少量增加请求头,优先使用 hls.js 配置的 xhrSetup 钩子,比完整写 custom loader 简单很多。xhrSetup 适合简单修改头,复杂拦截替换才上自定义 loader。
五、排查简单步骤
第一步,M3U8 先放到网页调试工具,确认默认加载器下流本身播放正常,排除流的问题。 第二步,业务页面 F12 网络面板,查看 M3U8、TS 分片请求头是否已经按照预期修改。 第三步,反复切换视频源、销毁播放器,观察旧请求是否被 abort 取消,有没有残留 pending 请求。
六、总结
hls.js 支持 xhrSetup 钩子以及完整自定义 loader 两种方式修改请求头。简单增加请求头优先 xhrSetup;需要拦截、改写返回内容才完整实现 custom loader。写自定义 loader 务必要实现 abort 中止逻辑,否则会残留网络请求,引发内存泄漏。不要直接重写全局 XMLHttpRequest 污染整个页面。借助网页调试工具确认原始流正常,再调试自定义加载器逻辑,减少无效调试。