核心判断:浏览器在决定播放哪个媒体源时,逻辑非常直接——它会从上到下扫描列表,选中第一个自己能够解码的格式,然后立即停止。它不会比较哪个格式更优或码率更合适,而是遵循“第一个能用就行”的原则。

这意味着,如果你把的顺序排错、漏写type属性,或者第一个src指向的文件实际编码与声明的codecs不匹配,后果就是“静默失败”——播放器一直转圈,Network面板只看到一个请求,控制台却毫无错误提示。这种调试体验极为糟糕。
为什么第一个 必须是 H.264 MP4
这里有一个特别容易踩坑的环节:iOS Safari(以及所有基于WebKit的WebView)的规则非常“霸道”。它要求第一个可播放的必须是一个采用H.264 Baseline Profile解码、配合AAC-LC音频编码的MP4文件。而且,type属性不能只写video/mp4,必须精确到编码参数——写成type="video/mp4; codecs="a vc1.42E01E, mp4a.40.2""。一旦不满足,它可能直接静音、禁止自动播放,或者抛出NotAllowedError。更糟的是,哪怕后面紧跟一个完全合法的VP9 WebM,Safari根本不会去请求它。
以下为几个实操要点:
- 使用
ffprobe video.mp4确认实际编码。如果输出显示codec_name=a v1,那么type就不能配a vc1.xxx,否则就是无效声明。 - 导出视频时,在FFmpeg参数中加入
-profile:v baseline -level 3.0 -c:a aac -b:a 128k,确保编码与声明一致。 - 本地测试一定要用HTTP(S)服务。
file://协议下,type属性会被浏览器完全忽略,测试结果毫无参考价值。
type 属性写错等于没写
浏览器判断媒体源能否播放,不看文件后缀名,只看两样东西:上的type属性和服务器响应头里的Content-Type。两者必须严丝合缝。差一个字符,比如把audio/mpeg写成audio/mp3,Safari很可能直接跳过这个源,没有任何提示。
几个常见的精确配对:
- MP3格式:
type="audio/mpeg",不是audio/mp3。 - VP9+Opus的WebM:推荐先试
type="video/webm"(不带codecs),实测比硬写codecs="vp9, opus"更可靠,兼容性更好。 - A V1编码的MP4:必须写成
type="video/mp4; codecs="a v01.0.05M.08""。但注意,Safari目前仍不支持A V1,把它放在最后做“锦上添花”即可。 - 用
curl -I https://xxx.mp4检查服务器返回的Content-Type是否真的是video/mp4,很多时候是服务器配置问题导致失败。
结构错误会导致整个 空白
不是孤立的标签。它必须是或的直接子元素。一旦脱离父容器,浏览器就会抛出DOMException: The element has no supported sources,播放器区域直接变成空白。更让人头疼的是,控制台也不会提示具体是哪一行出了问题。
还有几个结构上的硬性约束:
标签本身不能同时设置src属性和子元素。两者互斥,只能选其一。- 在所有
之后、之前,必须有一段降级文字,比如Your browser does not support the video tag.。这不仅是优雅降级的手段,也是合规性的基本要求。 - 路径错误(404、大小写不匹配、跨域限制)浏览器不会报错,只会静默跳过该源。调试时,逐个在新标签页打开
src链接,确认资源可访问,这是最可靠的方法。 - 不建议用JS动态插入
。这种做法可能绕过浏览器原生的格式探测逻辑,导致播放决策异常。
总结一下,最容易忽视但后果最严重的点:第一个的type声明必须和实际文件的编码格式完全对应。差一个字母、一个空格、一个引用符号,iOS Safari就会拒绝解码,并且不给你任何解释。前端开发中这种“无声的失败”往往最难排查,但根源通常只是毫厘之差。
