浏览器按顺序选择首个可解码的而非最优画质,type必须带精确codecs参数且content-type响应头需严格匹配,ios safari强制首源为h.264 mp4,须嵌入内且不可与src共存。

浏览器只认第一个能解码的 <source></source>,不是“选最优”而是“选首个可用”
写多个 <source></source> 不是为了让浏览器挑画质最好的,而是让它按顺序试播——遇到第一个 type 被识别、HTTP 返回 200、且能成功解码的就立刻用,后面全部跳过。这意味着顺序错了,整个降级链就断了。
常见误判:DevTools Network 面板只看到一个请求,就以为“没 fallback”;其实恰恰说明第一个 <source></source> 被接受了,问题出在它本身不可播(比如编码 profile 不对、缺少关键帧),而非后续没加载。
- iOS Safari 强制要求第一个可播放的
<source></source>必须是 H.264 编码的 MP4,否则可能静音、禁止自动播放,甚至抛NotAllowedError - 哪怕你把 WebM 放第一位、MP4 放第二位,Safari 会跳过 WebM(不支持),但因首源失效,可能直接拒绝播放整个
<video></video> -
media属性不能绕过这个限制——它只控制是否参与候选,不改变顺序逻辑
<source></source> 的 type 必须带精确 codecs 参数,不能只写 MIME 类型
type 不是装饰,是浏览器快速跳过不支持格式的唯一依据。写成 type="video/mp4" 看似省事,实则危险:浏览器无法预判编码是否支持,会发一次请求再解码失败,浪费带宽、延长首帧时间,还可能卡在 loading 状态。
正确写法必须声明具体编码:
- MP4(H.264 Baseline + AAC-LC):
type="video/mp4; codecs="avc1.42E01E, mp4a.40.2"" - WebM(VP9 + Opus):
type="video/webm; codecs="vp9, opus""(不带 codecs 更稳妥,VP9 字符串大小写/点号稍错 Safari 就返回空字符串) - AV1 MP4:
type="video/mp4; codecs="av01.0.05M.08""(仅 Chrome/Firefox 支持,Safari 16+ 才识别,必须放最后) - 绝对避免:
type="mp4"、type="video/mpeg"、type="video/mp4; codecs="h264""(标准写法是avc1)
服务端 Content-Type 响应头必须和 type 值严格匹配
浏览器靠 type 和响应头 Content-Type 双重校验。哪怕 <source src="vid.mp4" type="video/mp4; codecs=" avc1.42e01e> 写得再准,如果服务器返回 <code>Content-Type: application/octet-stream 或 text/plain,Safari 就会静默跳过该源。
验证方式:
- 用
curl -I https://example.com/vid.mp4检查响应头是否为Content-Type: video/mp4 - 用
ffprobe vid.mp4确认真实编码:Stream #0:0: Video: h264 (Baseline)才合规 - 别信文件后缀:一个叫
vid.mp4的文件可能是 AV1 编码,type="video/mp4"会失效
<source></source> 必须嵌在 <video></video> 内部,且不能与 src 属性共存
<source></source> 是多源备选机制,<video src="..."></video> 是单源直连机制,二者互斥。混用会导致行为不可控,浏览器可能忽略所有 <source></source>,或触发未定义行为。
结构必须规范:
- 所有
<source></source>必须作为<video></video>的直接子元素,且在<video></video>开始标签之后、结束标签之前 - 每个
<source></source>的src必须指向真实可访问的文件(相对或绝对 URL) - 在所有
<source></source>之后、之前,加一段降级提示文字,如“您的浏览器不支持视频播放”,供老设备或屏幕阅读器使用 - 不要用 JavaScript 动态插入
<source></source>——这可能绕过浏览器原生的格式探测逻辑











