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的工作流程

  1. 创建MediaSource对象,关联到<video>的src
  2. 添加SourceBuffer,指定MIME类型(如video/mp4; codecs="hev1.1.6.L93.90")
  3. 将H265的NAL单元封装成fMP4片段
  4. 通过SourceBuffer.appendBuffer()喂数据
  5. 浏览器自动解码并播放

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支持的现状和三种方案的定位梳理清楚了:

浏览器H265支持方案全景图 浏览器H265支持方案全景图 Chrome Edge Firefox Safari ❌ 不支持 ⚠ 部分支持 ❌ 不支持 ✅ 原生支持 WebCodecs API 帧级编解码控制 MSE方案 fMP4片段喂数据 WebRTC原生 仅Safari支持 选择建议 Safari用户多 → WebRTC原生H265 跨平台实时通信 → WebCodecs + DataChannel 点播/直播 → MSE + fMP4封装

4.6 小结

这一章我们聊了浏览器H265支持的现状。说白了,Safari是亲儿子,Chrome/Edge/Firefox是后妈养的。但有了WebCodecs和MSE这两个工具,我们完全可以在非Safari浏览器上实现H265的解码和播放。