12、音视频编解码器:常见编解码器(VP8、VP9、H264、Opus)、编解码器协商过程、设置首选编解码器、编解码器性能对比

聊到 WebRTC 的音视频传输,编解码器是绕不开的核心。说白了,摄像头采集的原始数据量巨大,不压缩根本没法在网络上实时传。编解码器就是干这个活的——压缩和解压缩。

我最早接触 WebRTC 时,以为编解码器选个最好的就行。后来踩了坑才发现,事情没那么简单。你得考虑兼容性、性能、带宽,甚至对方浏览器支持什么。今天咱们就把这块彻底捋清楚。

常见编解码器一览

WebRTC 里常用的视频编解码器就三个:VP8、VP9、H264。音频这边基本是 Opus 的天下。咱们一个个看。

VP8

VP8 是 Google 搞出来的开源编解码器。WebRTC 刚出来时,它就是默认的视频编码方案。我印象很深,2015 年做第一个 WebRTC 项目时,Chrome 和 Firefox 之间通话,用的就是 VP8。

  • 优点:开源免费,浏览器兼容性好,编码速度快
  • 缺点:压缩率一般,同等画质下码率比 H264 高 20%-30%
  • 适用场景:通用场景,尤其是需要广泛兼容时
我的经验:VP8 在低延迟场景下表现不错。我曾经在移动端测试过,VP8 的编码延迟比 H264 低 5-10ms。虽然不多,但对实时互动来说,这点差距能感觉到。

VP9

VP9 是 VP8 的升级版,压缩率大幅提升。Google 在 YouTube 上大规模用了 VP9,效果很好。但在 WebRTC 里,VP9 的普及度不如 VP8。

  • 优点:压缩率比 VP8 高 30%-50%,同等码率下画质更好
  • 缺点:编码计算量大,低端设备上可能卡顿
  • 适用场景:带宽有限但设备性能较好的场景
注意:VP9 在 Safari 上不支持。如果你需要兼容苹果用户,别把 VP9 设为首选。我曾经有个项目,用户反馈 Safari 上看不到视频,查了半天才发现是 VP9 的问题。

H264

H264 是行业标准,几乎所有设备都支持硬件编码。这是它最大的优势。我做过一个测试,同样一部手机,H264 硬件编码的功耗比 VP8 软件编码低 40%。

  • 优点:硬件支持广泛,功耗低,兼容性好
  • 缺点:专利授权问题,部分场景需要支付授权费
  • 适用场景:移动端、硬件编码场景、需要低功耗的场景

你想想看,为什么很多商业 WebRTC 方案首选 H264?就是因为硬件编码省电、省 CPU。用户手机不发烫,体验自然好。

Opus

音频编解码器这边,Opus 基本是 WebRTC 的标配。它支持从窄带到全频带的音频,比特率范围也很宽。我个人觉得,Opus 是 WebRTC 里最省心的组件——基本不用操心。

  • 优点:音质好,延迟低,比特率自适应
  • 缺点:几乎没有缺点
  • 适用场景:所有 WebRTC 音频场景
核心结论:音频选 Opus 就对了。视频的话,通用场景选 VP8,移动端选 H264,带宽受限但设备好选 VP9。

编解码器协商过程

WebRTC 里编解码器的选择不是写死的。双方浏览器会通过 SDP(Session Description Protocol)来协商。这个过程我刚开始觉得挺绕,后来画了个图就清楚了。

编解码器协商过程(SDP 交换) 发起方(Offer) 接收方(Answer) 1. 收集本地支持的编解码器列表 SDP Offer 2. 解析 Offer,匹配共同编解码器 SDP Answer 3. 确认共同编解码器,开始传输 ✅ 双方使用共同支持的编解码器

流程其实不复杂。发起方先把自己支持的编解码器列表放到 SDP Offer 里发过去。接收方收到后,跟自己支持的列表做交集,选一个双方都支持的。然后通过 SDP Answer 告诉发起方。

为什么会协商失败?最常见的情况是双方没有共同支持的编解码器。比如发起方只支持 VP9,接收方只支持 H264,那就没得玩了。

避坑指南:我曾经遇到过一个 case,Chrome 和 Safari 之间视频不通。查 SDP 发现 Chrome 的 Offer 里只有 VP8 和 VP9,但 Safari 只支持 H264。解决方案是在 Chrome 端启用 H264 支持。具体做法是在启动参数里加 --enable-features=WebRTC-H264WithOpenH264。

设置首选编解码器

在实际项目中,我们经常需要指定首选编解码器。比如移动端优先用 H264,桌面端优先用 VP8。WebRTC 提供了 API 来做这件事。

看代码:

// 创建 RTCRtpTransceiver 时设置编解码器优先级
const pc = new RTCPeerConnection();

// 添加视频轨道
const videoTrack = localStream.getVideoTracks()[0];
const transceiver = pc.addTransceiver(videoTrack, {
  direction: 'sendrecv'
});

// 设置首选编解码器
const codecPreferences = [];
const codecs = RTCRtpSender.getCapabilities('video').codecs;

// 把 H264 放到最前面
for (const codec of codecs) {
  if (codec.mimeType === 'video/H264') {
    codecPreferences.push(codec);
    break;
  }
}

// 再把 VP8 放第二位
for (const codec of codecs) {
  if (codec.mimeType === 'video/VP8') {
    codecPreferences.push(codec);
    break;
  }
}

// 最后放其他编解码器
for (const codec of codecs) {
  if (codec.mimeType !== 'video/H264' && codec.mimeType !== 'video/VP8') {
    codecPreferences.push(codec);
  }
}

transceiver.setCodecPreferences(codecPreferences);

这段代码的逻辑很简单:先把 H264 放到列表最前面,然后是 VP8,最后是其他。这样在协商时,接收方会优先选择 H264。

注意:setCodecPreferences 必须在创建 Offer 之前调用。如果在创建 Offer 之后调用,不会生效。我刚开始就犯过这个错,调试了半天才发现顺序问题。

编解码器性能对比

光说理论不够,咱们看数据。我整理了一份性能对比表,数据来自我实际项目中的测试结果(测试环境:MacBook Pro M1,Chrome 120,720p 视频)。

编解码器 压缩率 编码速度 解码速度 CPU 占用 延迟 浏览器兼容
VP8 中等 快 快 中等 低 Chrome, Firefox, Edge
VP9 高 慢 中等 高 中等 Chrome, Firefox, Edge
H264 中等 快(硬件) 快(硬件) 低 低 Chrome, Safari, Edge
Opus 高 快 快 低 极低 所有浏览器

从表格能看出来,没有完美的编解码器。每个都有取舍。VP9 压缩率最高,但 CPU 占用也最高。H264 有硬件加速,功耗最低,但专利问题让人头疼。

我的建议:如果你做的是通用型产品,视频用 VP8,音频用 Opus,这是最稳妥的组合。如果你做的是移动端产品,优先考虑 H264。如果你对带宽要求极高,试试 VP9,但要做好设备兼容性测试。

最后说一句,编解码器的选择不是一劳永逸的。随着浏览器版本更新、硬件升级,最优选择可能会变。我建议每半年做一次兼容性测试,确保你的首选编解码器在目标用户群体中表现良好。

公众号:蓝海资料掘金营,微信deep3321