4. 浏览器H265支持现状:Chrome/Edge/Firefox/Safari对H265的解码支持情况、WebCodecs API介绍、MediaSource Extensions (MSE) 方案
聊到H265与WebRTC的融合,第一个绕不开的现实问题就是:浏览器到底认不认H265?
我2018年第一次在WebRTC项目里尝试推H265流时,踩了个大坑。当时以为Safari能解码,Chrome肯定也行,结果在Chrome上黑屏了整整两天。后来才搞清楚——浏览器对H265的支持,远没有我们想象的那么统一。
这一章,我就把四大浏览器的H265解码现状、WebCodecs这个新API怎么用、以及MSE方案的实战套路,一次性给你讲透。
4.1 四大浏览器H265解码支持现状
先看一张我整理的对比表,这是2024年下半年的真实情况:
| 浏览器 | H265解码支持 | 硬件加速 | WebRTC支持 | 备注 |
|---|---|---|---|---|
| Chrome | ❌ 默认不支持 | ❌ | ❌ | 需启用实验Flag或使用MSE+插件 |
| Edge | ✅ 支持(基于Chromium) | ✅(部分版本) | ❌ | Windows下通过Media Foundation解码 |
| Firefox | ❌ 默认不支持 | ❌ | ❌ | 需手动开启media.wmf.hevc.enabled |
| Safari | ✅ 原生支持 | ✅ | ✅ | macOS/iOS全系支持,WebRTC原生H265 |
看到这个表,你可能会问:为什么Safari支持得这么好,Chrome却死活不开放?
说白了,这是专利授权的问题。H265的专利池太复杂,Google不想交这笔钱,所以Chrome一直推自家的VP9和AV1。而Apple在硬件层面深度集成了H265,从A9芯片开始就支持硬解,自然没有理由放弃。
核心结论:如果你要做H265+WebRTC的方案,Safari是天然盟友,Chrome/Edge/Firefox都需要额外处理。
4.2 WebCodecs API:浏览器原生编解码的新出路
WebCodecs是W3C在2022年推出的新API。它的目标很明确:让JavaScript直接控制音视频的编解码过程,不再依赖浏览器内部的黑盒实现。
我个人习惯把WebCodecs看作浏览器版的FFmpeg。它暴露了底层的编码器和解码器接口,你可以自己喂原始帧,自己拿压缩数据。
4.2.1 核心接口
- VideoDecoder:解码H264/H265/VP9等编码的视频帧
- VideoEncoder:编码原始帧为H264/H265等格式
- AudioDecoder / AudioEncoder:音频编解码
- EncodedVideoChunk:封装编码后的视频数据
- VideoFrame:原始视频帧对象
4.2.2 实战:用WebCodecs解码H265
下面这段代码,是我在项目中实际用过的H265解码片段。注意看codec字符串的写法:
// 检查浏览器是否支持H265解码
if (!('VideoDecoder' in window)) {
console.error('当前浏览器不支持WebCodecs API');
return;
}
// 配置解码器
const config = {
codec: 'hev1.1.6.L93.90', // H265的codec字符串
codedWidth: 1920,
codedHeight: 1080,
description: avcCData, // 从SPS/PPS解析出的描述数据
};
const decoder = new VideoDecoder({
output: (frame) => {
// 拿到解码后的VideoFrame,可以绘制到Canvas或传给WebGL
drawFrame(frame);
frame.close();
},
error: (e) => {
console.error('解码错误:', e.message);
}
});
decoder.configure(config);
// 喂数据
const chunk = new EncodedVideoChunk({
type: 'key',
timestamp: 0,
duration: 33000, // 33ms,约30fps
data: hevcData // H265的NAL单元数据
});
decoder.decode(chunk);
避坑指南:codec字符串必须精确匹配。H265的codec格式是hev1.<profile>.<level>.<tier>,写错一个字解码器就罢工。我曾经因为大小写问题排查了三个小时。
4.2.3 WebCodecs的局限性
虽然WebCodecs很强大,但它不是万能的:
- 浏览器兼容性:Chrome 94+、Edge 94+、Safari 16.4+支持,Firefox目前不支持
- H265支持依赖系统解码器:Chrome在Windows上如果系统没有HEVC解码器,WebCodecs也解不了
- 没有网络传输能力:WebCodecs只管编解码,不管怎么传数据
4.3 MediaSource Extensions (MSE) 方案
MSE是另一种让浏览器播放H265的思路。它的原理是:把H265的编码数据封装成fMP4片段,通过MSE喂给<video>元素。
说白了,就是让浏览器以为你在播一个普通的MP4文件,实际上数据是实时拼接的。
4.3.1 MSE的工作流程
- 创建
MediaSource对象,关联到<video>的src - 添加
SourceBuffer,指定MIME类型(如video/mp4; codecs="hev1.1.6.L93.90") - 将H265的NAL单元封装成fMP4片段
- 通过
SourceBuffer.appendBuffer()喂数据 - 浏览器自动解码并播放
4.3.2 代码示例:MSE喂H265数据
const mediaSource = new MediaSource();
const video = document.getElementById('player');
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', () => {
const mimeType = 'video/mp4; codecs="hev1.1.6.L93.90"';
if (!MediaSource.isTypeSupported(mimeType)) {
console.error('当前浏览器不支持H265的MSE播放');
return;
}
const sourceBuffer = mediaSource.addSourceBuffer(mimeType);
// 假设从WebSocket收到H265数据
ws.onmessage = (event) => {
const fmp4Data = convertHevcToFmp4(event.data); // 封装成fMP4
sourceBuffer.appendBuffer(fmp4Data);
};
});
注意:MSE方案的关键在于fMP4的封装。H265的NAL单元需要按照ISO BMFF规范封装成fMP4片段,包括ftyp、moov、moof、mdat等box。这部分工作通常用mux.js或mp4box.js库来完成。
4.4 三种方案的对比与选择
我把WebCodecs、MSE和WebRTC原生方案放在一起对比:
| 方案 | 延迟 | 兼容性 | 实现复杂度 | 适用场景 |
|---|---|---|---|---|
| WebCodecs | 低(帧级控制) | Chrome/Edge/Safari | 中 | 实时通信、低延迟播放 |
| MSE | 中(片段级) | 所有现代浏览器 | 高(需封装fMP4) | 点播、直播(非实时) |
| WebRTC原生 | 极低 | 仅Safari | 低 | Safari生态内的实时通信 |
我个人建议:如果目标用户主要是iOS/macOS,优先用WebRTC原生H265。如果必须跨平台,用WebCodecs做解码,配合WebRTC的DataChannel传数据。MSE方案更适合点播场景,实时性要求不高的时候用。
4.5 本章知识体系
下面这张图,把浏览器H265支持的现状和三种方案的定位梳理清楚了:
4.6 小结
这一章我们聊了浏览器H265支持的现状。说白了,Safari是亲儿子,Chrome/Edge/Firefox是后妈养的。但有了WebCodecs和MSE这两个工具,我们完全可以在非Safari浏览器上实现H265的解码和播放。