你在笔记本上测试页面,播放器启动了,工单也随之关闭。另一端的观众打开同一个页面,却只得到一个永不结束的转圈。常见的解释是 VPN 架在服务器上,所以播放应该没问题——但发出播放列表请求的组件是浏览器,而浏览器有它自己的网络路径。嵌入在具备私有访问权限的服务器上的页面,并不会把这份访问权限借给加载它的人。

这里说的是一张现有的 ASP.NET 页面,它播放类似 http://10.5.5.9:8080/live/stream.m3u8 的 HLS 源,这是一个示例性的私有地址。有两种部署方式可行;其中一种大概不是你搭的那种。

误区:页面能加载就证明了媒体链路

那张页面的 HTML 来自你的 Web 服务器,走的是你的 Web 服务器拥有的任何路由。播放列表和分片则来自媒体源,走的是发起请求的组件所拥有的那条路由。当播放器运行在观众的浏览器里时,这是两个不同的客户端、两个不同的源地址,而且往往是两个不同的故障域。看到第一个成功,对第二个说明不了什么。

你的测试真正证明的事情,比看上去要窄得多:你的机器能同时访问公开页面和私有媒体主机。观众的机器从未参与这个实验。

播放列表接下来要什么

RFC 8216 把列出分片的媒体播放列表和列出其他播放列表的主播放列表区分开来,并且按照包含它们的那个播放列表的 URI 来解析相对 URI。人们跳过的恰恰是这条规则。成功抓到一个 .m3u8 文件不是播放测试,因为你抓到的文件是索引,而不是媒体本身。

像播放器那样走完整条链:打开选中的播放列表,然后请求它点名的资源,再跟随任何重定向。即使播放列表是通过可访问的 Web 服务器提供的,它仍然可能把观众指向一个无法访问的私有地址。同样,把相对播放列表搬到另一个 URL,会改变其条目解析所依据的基准,这就是为什么只重写第一个可见的 URL 然后就此打住,通常会产生一个看起来合法却无法播放的列表。

直接观看:观众需要这条路径

如果观众要直接访问媒体主机,他们的设备就需要已批准的 VPN 或其他私有路径、能解析你实际所用名称的 DNS、访问这些目的地的权限,以及媒体服务要求的任何身份验证。每一样都要在观众的设备上检查,而不是在服务器上。手机上装了 VPN 配置却没有连接,是一种常见且完全可以复现的故障。

这种部署方式让源保持私有,把访问要求推给观众。它既是技术决定,也是策略决定,所以在围绕它做设计之前,先确认每一位目标观众都有权拥有这条路径。

服务端分发:一个受控的入口

另一种做法是一个经授权的媒体前端,它通过自己的私有路径抓取源,并且只重新发布面向观众的资源。让播放列表、分片和任何密钥资源都与这唯一的入口保持一致,否则播放器会通过前端取到被重写的播放列表,而分片却直接从观众到不了的地址取。

在这个前端里限制源和允许的路径。用户提供的任意 URL 会让一个有用的代理变成你内网的无限抓取器,这比一个播不出来的流严重得多。把源和路径前缀放进允许列表,而不是事后校验字符串。

对于现有的 HLS 源,先确认受控转发是否够用;中间有 VPN 并不意味着必须转码。然后验证真实的浏览器与编解码器组合,因为能拿到响应和支持的格式是两个独立条件。原源的 TLS、身份验证和浏览器安全要求都要保持不变——嵌入式播放器不是它们的例外。

测链路,而不是测 URL

从真正会发出每一次请求的组件去执行检查,并把每一步当作它自己的结果:

curl -s http://10.5.5.9:8080/live/stream.m3u8
curl -s -o /dev/null -w '%{http_code}\n' http://10.5.5.9:8080/live/segment-000123.ts

然后在观众自己的浏览器里,用预期的流重复一遍,而不是用你桌上的测试图案。页面加载、播放列表加载、分片获取和授权失败是四种不同的结果,你希望在宣布修好之前把四种都观察到。不要因为某个旧示例用老式浏览器插件播放过一次流,就去安装那个插件;缺的那块通常是观众的路径,或者未签名的分片 URL。

一旦部署方式是有意选择的,就用一句话写下来:谁去取媒体、走哪条路径、观众被允许请求什么。这句话能了结未来大部分关于这条流究竟是给谁看的争论。

相关文章