12、音视频编解码器:WebRTC 的“翻译官”们

聊到 WebRTC 的编解码器,我脑子里第一个蹦出来的画面,是几年前一个深夜的线上会议。画面卡成了 PPT,声音像机器人说话。我盯着控制台,发现是 H264 编码器在低带宽下直接“罢工”了。嗯,从那以后,我对编解码器的敬畏心就拉满了。

说白了,编解码器就是音视频数据的“翻译官”。摄像头采集的原始视频,一秒钟的数据量能到几百 MB,不压缩根本没法传。编解码器负责把它压缩(编码),到了对端再解压(解码)。WebRTC 支持哪几个翻译官?它们各自什么脾气?怎么让它们好好干活?这就是今天要聊的。

12.1 WebRTC 支持的编解码器全家福

WebRTC 标准里,浏览器必须支持哪些编解码器,其实是有“硬性规定”的。但不同浏览器、不同设备,支持的列表会有差异。我个人习惯把常用的几个列成一张表,方便对照:

编解码器 类型 浏览器支持情况 我的评价
VP8 视频 Chrome、Firefox、Edge 全支持 老黄牛,兼容性最好,性能开销低
VP9 视频 Chrome、Firefox 支持,Safari 部分支持 压缩率比 VP8 高 30%-50%,但编码慢
H264 视频 Chrome、Safari、Edge 全支持 硬件编码器普及,低功耗首选
AV1 视频 Chrome 支持,其他浏览器逐步跟进 压缩率之王,但编码计算量巨大
Opus 音频 所有浏览器全支持 音频界的万金油,低延迟高音质

核心观点: 别盲目追求最新。AV1 压缩率虽高,但手机端编码一帧可能要几十毫秒,实时通话根本扛不住。我建议优先选 VP8 或 H264,兼容性和性能最平衡。

12.2 编解码器协商:RTCRtpCodecCapability

你想想看,两个浏览器要通话,总得先打个招呼:“我支持 VP8、H264,你支持啥?” 这个“打招呼”的过程,就是编解码器协商。WebRTC 里通过 RTCRtpCodecCapability 来描述能力。

我曾经踩过一个坑:在 Chrome 里用 getCapabilities() 查到的编码器列表,跟实际能用的不一样。原因是 Chrome 的 H264 编码器需要特定硬件支持,软件模式下可能不暴露。所以,别完全相信能力查询的结果,最好做一次实际编码测试。

代码示例:查看本地支持的视频编码器

// 获取视频发送端的能力
const capabilities = RTCRtpSender.getCapabilities('video');
console.log('支持的视频编解码器:');
capabilities.codecs.forEach(codec => {
    console.log(`${codec.mimeType},时钟频率:${codec.clockRate}`);
    // 输出示例:video/VP8,时钟频率:90000
    //          video/H264,时钟频率:90000
});

协商的底层逻辑是这样的:

  • 发起方(offer)会把自己支持的编解码器列表,按优先级排序,塞进 SDP 里。
  • 应答方(answer)收到后,从列表里挑一个自己也能支持的,作为最终使用的编码器。
  • 如果双方没有交集,协商失败,连接建立不了。

小技巧: 如果你想强制使用某个编码器,可以在创建 offer 之前,手动修改 SDP 里的编解码器顺序,把目标编码器提到最前面。但注意,这属于“黑科技”,不同浏览器行为可能不一致。

12.3 编解码器参数配置与优化

选定了编码器,只是第一步。真正决定通话质量的,是参数配置。我见过太多人直接用默认参数,结果在弱网下画质糊成一片。

12.3.1 视频编码器核心参数

以 H264 为例,几个关键参数:

  • profile(档次): baseline、main、high。baseline 兼容性最好,但压缩率低。我建议移动端用 baseline,桌面端用 high。
  • level(级别): 决定了分辨率、帧率的上限。比如 level 3.1 支持 720p@30fps。
  • bitrate(码率): 直接决定画质。码率太低会出马赛克,太高会卡顿。
  • keyFrameInterval(关键帧间隔): 默认可能 2 秒一个关键帧。弱网下我建议缩短到 1 秒,减少丢帧后的等待时间。

配置示例:通过 RTCRtpEncodingParameters 设置

const sender = peerConnection.addTrack(videoTrack, stream);
const parameters = sender.getParameters();
// 设置编码参数
parameters.encodings[0].maxBitrate = 500000; // 500kbps
parameters.encodings[0].minBitrate = 100000; // 100kbps
parameters.encodings[0].maxFramerate = 30;
parameters.encodings[0].scaleResolutionDownBy = 1.0; // 不缩放

// 应用参数
await sender.setParameters(parameters);

注意: setParameters() 不是所有浏览器都支持动态修改。我建议在建立连接前就配置好,连接建立后尽量少改,否则可能触发编码器重启,导致短暂黑屏。

12.3.2 音频编码器 Opus 的优化

Opus 是音频界的“瑞士军刀”。它支持从 6 kbps 到 510 kbps 的码率,采样率从 8 kHz 到 48 kHz。我一般这样配置:

  • 语音通话: 码率 32 kbps,采样率 16 kHz,够用且省带宽。
  • 音乐场景: 码率 128 kbps,采样率 48 kHz,保证音质。
  • 弱网环境: 开启 FEC(前向纠错),码率降到 24 kbps,牺牲一点音质换流畅。

配置 Opus 的示例:

const audioSender = pc.addTrack(audioTrack, stream);
const audioParams = audioSender.getParameters();
// 设置 Opus 的 FEC 和码率
audioParams.encodings[0].maxBitrate = 32000;
// 通过 codec 参数设置 FEC
audioParams.codecs[0].parameters['useinbandfec'] = '1';
audioParams.codecs[0].parameters['maxplaybackrate'] = '16000';
await audioSender.setParameters(audioParams);

12.4 知识体系:编解码器选择与协商流程

下面这张图,是我自己画的一个决策流程。每次做新项目,我都会拿出来对照一遍:

WebRTC 编解码器选择与协商流程 1. 查询本地能力 getCapabilities() 2. 选择编码器 VP8 / H264 / VP9 / AV1 3. 配置参数 码率 / 帧率 / Profile 4. SDP 协商 Offer / Answer 交换 5. 连接建立 避坑指南 • 别全信 getCapabilities() • H264 硬件编码器可能不暴露 • 弱网下缩短关键帧间隔 • Opus 开启 FEC 抗丢包

12.5 实战中的几个建议

最后,分享几个我在项目中积累的经验:

  • 优先用 H264: 大部分手机和笔记本都有 H264 硬件编码器,功耗低、发热小。VP8 虽然兼容,但纯软件编码,手机发烫严重。
  • 码率不要设死: 我习惯用 maxBitrate 设上限,让编码器自己根据网络动态调整。设死码率,网络波动时画面直接崩。
  • 音频比视频重要: 视频卡了还能忍,音频卡了通话直接中断。我会给 Opus 开 FEC,哪怕视频降到 15 帧,音频也要保证清晰。
  • 测试要覆盖弱网: 我曾经在办公室测得好好的,上线后用户在农村用 3G 网络,画面全是马赛克。后来加了码率自适应,才解决问题。

一句话总结: 编解码器没有绝对的好坏,只有合不合适的场景。理解每个编码器的脾气,再根据网络和设备动态调整,才能做出流畅的通话体验。


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