播放列表加载正常,为什么画面是黑的?
你把一个 M3U8 地址粘贴到播放器里,播放列表解析成功,清晰度选项也出来了,然后——什么都没有。视频区域一片漆黑。播放列表语法没问题,地址也没拼错。流就是不播放。
十有八九,这是 CORS 的问题。
CORS 全称是 Cross-Origin Resource Sharing,跨域资源共享。它是浏览器的安全机制,控制一个域名上的网页能不能向另一个域名请求资源。当你从 cdn.example.com 加载播放列表,而分片文件在 media.cdn.example.com 上,浏览器会检查这些服务器有没有明确允许你的页面访问它们的文件。如果没有,浏览器会从 JavaScript 层面静默拦截请求,播放器看到的就是网络失败。
让人抓狂的是,播放列表本身可能加载完全正常,但每一个 .ts 或 .m4s 分片都失败了。这是因为播放列表接口和媒体接口的 CORS 头可以不一样。服务器可能给 m3u8 文件返回了 Access-Control-Allow-Origin: *,但忘了给分片文件也加上。
CORS 头长什么样
当浏览器发起跨域请求时,服务器需要在响应里带上正确的头信息。最常见的是:
Access-Control-Allow-Origin: *
或者指定某个来源:
Access-Control-Allow-Origin: https://tools.anoiona.net
对于某些请求,浏览器会先发一个预检 OPTIONS 请求。服务器也必须对这个预检请求返回正确的 CORS 头。如果预检失败,实际请求根本不会发出去。
你可以在 DevTools 里看到这个过程。打开 Network 标签,过滤分片请求,点开一个。看 Response Headers 部分。如果 Access-Control-Allow-Origin 缺失,这个分片在播放器里就会失败,即使你直接在浏览器地址栏打开同一个 URL 完全没问题。
“在地址栏能打开”和”在播放器里能播放”是两回事
这一点坑了很多人。你可以在浏览器地址栏打开 M3U8 地址,看到它加载成功。你甚至可以点开分片 URL 下载文件。那为什么播放器不行?
当你在浏览器地址栏直接打开一个 URL 时,没有跨域上下文。浏览器把它当作顶层导航来获取文件,CORS 不适用。
当网页里的 JavaScript 用 fetch() 或 XMLHttpRequest 请求同一个 URL 时,如果页面和资源在不同域名下,浏览器会把它当作跨域请求。这时候 CORS 头才起作用。
HLS.js 用 JavaScript 来获取播放列表和分片。某些浏览器上的原生 HLS 通过 Media Source Extensions 也走这条路。这意味着每个分片请求都要过 CORS 检查。一个在 Safari 地址栏播放正常的流,放到播放器页面里可能就失败了,因为 CORS 头不对。
实际调试步骤
从 Network 标签开始。过滤 m3u8、ts 或 m4s。刷新页面触发播放。找红色的条目或者状态码不是 200 的。
点开一个失败的分片请求。状态码能告诉你很多:
- 403 Forbidden:服务器识别了你的请求但拒绝了访问。常见于基于令牌的认证、链接过期或 Referer 限制。
- 404 Not Found:分片 URL 不对或者文件已被删除。检查播放列表的相对路径是否正确解析。
- 200 但没有 CORS 头:文件加载了,但 JavaScript 读不到响应内容。播放器看到的是空响应或失败响应。
对于”200 但没有 CORS 头”的情况,专门看响应头里的 Access-Control-Allow-Origin。如果缺失,需要在服务器端配置。客户端没有任何办法修复这个问题。
有些服务器还会用 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers 来限制允许的请求类型。HLS 播放通常需要允许 GET,有时还需要 HEAD。
Referer 和 Origin 头也会阻止播放
部分 CDN 和流媒体服务器会检查请求中的 Referer 或 Origin 头。如果请求来自一个不在它们白名单上的域名,它们会返回 403 或者重定向。
你也可以在 DevTools 里检查这个。点开一个分片请求,看 Request Headers 部分。Referer 头会显示触发请求的页面 URL。如果服务器拒绝来自 tools.anoiona.net 的请求但允许来自 example.com 的,那就解释了失败的原因。
麻烦的是 Referer 策略有差异。有些页面设置了 Referrer-Policy: no-referrer 来避免泄露 URL。有些服务器又需要 Referer 存在。这两个要求互相冲突,结果取决于哪一方控制了策略。
我们的 M3U8 播放器怎么处理 CORS 错误
我们的播放器不会绕过 CORS。那将是安全违规,而且需要一个我们刻意不运行的服务端代理。
我们做的是在诊断面板里展示 CORS 错误。当分片请求因网络错误失败时,我们会记录下来,让你能看到到底是哪个请求失败了以及为什么。播放器也会在主状态区域显示失败状态,你不需要翻遍诊断信息才能理解出了什么问题。
如果播放列表加载了但分片失败了,播放器会报告无法播放。它不会静默无限重试,也不会假装正在加载。这种诚实是刻意的:CORS 问题是服务器配置问题,隐藏它只会让调试更困难。
给流媒体运营者的 CORS 快速检查清单
如果你在提供 HLS 流媒体服务,希望它们能在浏览器播放器里正常工作:
- 给播放列表和分片响应都加上
Access-Control-Allow-Origin: *。 - 如果使用基于令牌的 URL,确保令牌不会在播放列表结束前过期。
- 在播放器页面里测试流,不要只在浏览器地址栏里测试。CORS 行为是不同的。
- 检查你的 CDN 是否会透传 CORS 头。有些 CDN 配置会把它们去掉。
- 如果使用 Referer 限制,把播放器域名加入白名单,或者换一种认证方式。
CORS 不是播放器的 bug。它是保护用户免受恶意跨域请求的安全特性。修复始终在服务器端:告诉服务器哪些来源可以访问这些资源。