编解码与协商
WebRTC 在编解码上是个协商系统:发送端能编、接收端能解、双方都同意,才能用。本文给出选型矩阵、跨浏览器现状(2026),以及如何用标准 API 控制 codec 选择。
1. 主流编解码器现状(2026-06)
1.1 视频
| Codec | Chrome / Edge | Firefox | Safari (iOS/macOS) | 硬件加速 | 备注 |
|---|---|---|---|---|---|
| VP8 | ✅ 默认 | ✅ 默认 | ✅ | 部分平台 | WebRTC 历史默认,兼容性最好 |
| VP9 | ✅ | ✅ | ✅(Safari 16+) | 部分 GPU | 比 VP8 省 30% 码率,支持 SVC |
| H.264 | ✅ | ✅ | ✅ 必有 | 几乎全平台 | 唯一 Safari 硬解 codec,必带 |
| H.265 / HEVC | ⚠️ 受限 | ❌ | ✅ | Apple 硬件 | 专利/许可复杂,WebRTC 罕见 |
| AV1 | ✅(默认开启) | ✅ | ⚠️(17.4+ 部分) | 新 GPU | 最新一代,省码率明显 |
1.2 音频
| Codec | 支持 | 备注 |
|---|---|---|
| Opus | 全平台 | WebRTC 默认音频 codec,6~510 kbps 自适应 |
| G.722 | 多数 | VoIP 互通 |
| PCMU/PCMA | 多数 | 与传统电话互通用 |
| iSAC/iLBC | 已废弃 | 不要再用 |
| L16 | 受限 | 无损,带宽高 |
结论:
- 音频默认 Opus,没什么可选的。
- 视频默认 VP8 兼容稳,要互通 Safari 必带 H.264,码率敏感场景上 VP9,新业务可以试 AV1。
2. 协商怎么发生
A 端 B 端
─── createOffer ──────────────────────► setRemoteDescription
SDP 列出 A 支持的所有 codec B 比较自己的能力
生成 Answer,**只保留双方都支持的 codec**
setRemoteDescription ◄──── createAnswer
每路 m-line 的 SDP 里 m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 后面的 PT 列表,顺序就是发送端的偏好。Answer 端按自己能力裁剪并保留发送端给出的顺序。
3. 用标准 API 控制 codec(不要 munging SDP)
3.1 查询能力
const sendCaps = RTCRtpSender.getCapabilities('video');
const recvCaps = RTCRtpReceiver.getCapabilities('video');
console.log(sendCaps.codecs);
// [{mimeType: 'video/VP8', clockRate: 90000, ...}, ...]
3.2 设置偏好顺序
const tr = pc.addTransceiver('video', { direction: 'sendrecv' });
const codecs = RTCRtpSender.getCapabilities('video').codecs;
// 把 VP9 排到最前
const preferred = [
...codecs.filter(c => c.mimeType === 'video/VP9'),
...codecs.filter(c => c.mimeType === 'video/VP8'),
...codecs.filter(c => c.mimeType === 'video/H264'),
...codecs.filter(c => !['video/VP9','video/VP8','video/H264'].includes(c.mimeType)),
];
tr.setCodecPreferences(preferred);
下次 createOffer 生成的 SDP 自动按此顺序排列。
3.3 强制单一 codec(用于排障 / 兼容)
tr.setCodecPreferences(codecs.filter(c => c.mimeType === 'video/H264'));
4. H.264 的 profile 坑
H.264 在 SDP 里通过 profile-level-id 参数细分变体:
a=fmtp:97 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
| profile-level-id | 含义 | 适用 |
|---|---|---|
42e01f | Constrained Baseline 3.1 | WebRTC 推荐,全平台兼容 |
42001f | Baseline 3.1 | 老设备 |
4d0032 | Main 5.0 | 高分辨率 |
640032 | High 5.0 | 录播质量 |
Safari 早期版本只支持 42e01f,跨 Safari 互通必须用这个 profile。packetization-mode=1 也是 WebRTC 强制要求。
5. AV1:什么时候用
适用:
- 屏幕共享(AV1 对静态/低运动内容压缩比 VP9 提升 50%+)。
- 端到端都是较新的 Chrome / Edge。
- 业务对码率敏感(直播 CDN 成本)。
不适用:
- 跨 Safari、老移动端互通。
- 低端 CPU(AV1 软解功耗高)。
- 没有硬件加速的设备。
// 只给屏幕共享通道用 AV1,摄像头通道用 VP9
const screenTr = pc.addTransceiver(screenTrack);
screenTr.setCodecPreferences(prefer('AV1'));
const camTr = pc.addTransceiver(cameraTrack);
camTr.setCodecPreferences(prefer('VP9'));
6. RTX、FEC、扩展头
每个视频 codec 通常配套:
a=rtpmap:96 VP8/90000
a=rtpmap:97 rtx/90000 # 重传 codec
a=fmtp:97 apt=96
a=rtpmap:98 red/90000 # 冗余编码
a=rtpmap:99 ulpfec/90000 # 前向纠错
- RTX:NACK 重传走的独立 SSRC,把重传包和原始流分开,方便丢包分析。
- RED + ULPFEC:丢包率高时启用,预先发冗余包让接收端不靠重传也能恢复。Chrome 默认对音频开 RED,对视频通常关。
详见 04-媒体引擎与QoS/抗丢包NACK-RTX-FEC.md。
7. 编码参数控制
const sender = pc.getSenders().find(s => s.track?.kind === 'video');
const params = sender.getParameters();
params.encodings[0].maxBitrate = 1_500_000;
params.encodings[0].maxFramerate = 30;
params.encodings[0].priority = 'high'; // 'very-low' / 'low' / 'medium' / 'high'
params.encodings[0].networkPriority= 'high';
params.encodings[0].scaleResolutionDownBy = 1; // 2 = 降一半分辨率
params.encodings[0].active = true;
params.degradationPreference = 'maintain-framerate'; // 拥塞时优先保帧率
await sender.setParameters(params);
degradationPreference:
| 值 | 拥塞时策略 |
|---|---|
maintain-framerate | 优先保帧率,降分辨率 |
maintain-resolution | 优先保分辨率,降帧率 |
balanced(默认) | 平衡 |
经验:屏幕共享用 maintain-resolution,会议视频用 maintain-framerate。
8. Opus 调参
// 通过 SDP 的 fmtp 设置 Opus(这一类是少数推荐显式 munging 的场景,
// 浏览器目前没有完整 API 暴露所有 Opus 参数)
function setOpusFec(sdp) {
return sdp.replace(/(a=fmtp:111 [^\r\n]+)/, '$1;useinbandfec=1;usedtx=1');
}
| 参数 | 含义 | 推荐 |
|---|---|---|
useinbandfec=1 | 启用前向纠错 | 通话场景必开 |
usedtx=1 | 静音时降码率 | 通话场景开 |
stereo=1 | 立体声 | 音乐场景开,通话场景关 |
maxaveragebitrate=128000 | 最大平均码率 | 高质量场景 |
注意:Opus 参数是少数浏览器没有完整 API 的领域,社区容忍对 SDP 的 fmtp 行做精确替换,但仍要做版本回归。
9. 跨浏览器兼容矩阵速记
| 场景 | 配方 |
|---|---|
| 1v1 通话,浏览器都未知 | VP8(首选)+ H.264(兜底)+ Opus |
| 全 Chrome 内部系统 | VP9 / AV1 + Opus |
| 必须支持 iOS Safari | H.264 (42e01f, packetization-mode=1) + Opus |
| 屏幕共享 | AV1(同 Chrome)/ VP9(混合)+ Opus DTX |
| 与传统电话互通 | G.722 / PCMU + H.264 |
10. 排查 codec 问题
// 看实际协商出的 codec
const stats = await pc.getStats();
for (const r of stats.values()) {
if (r.type === 'codec') {
console.log(r.mimeType, r.payloadType, r.sdpFmtpLine);
}
}
// 看实际收发的 codec
for (const r of stats.values()) {
if (r.type === 'inbound-rtp' && r.kind === 'video') {
const codec = stats.get(r.codecId);
console.log('对端发的 codec:', codec?.mimeType);
}
}
11. 反模式
| 反模式 | 后果 | 修复 |
|---|---|---|
| 用正则改 m-line 里 codec 顺序 | 浏览器升级即坏 | setCodecPreferences |
H.264 不指定 42e01f | Safari 互通失败 | 只发 baseline 兼容 profile |
| 全场景默认开 AV1 | iOS 用户看不到 | 按平台动态选 |
| 强制 stereo Opus 做语音 | 浪费一倍带宽 | 通话场景 mono |
| 关闭 FEC 想省带宽 | 弱网用户体验崩 | FEC 是雪中送炭 |
12. 权威资料
- W3C WebRTC 1.0 §5 RTP Media: https://www.w3.org/TR/webrtc/#rtp-media-api
- IETF RFC 7742 WebRTC Video Processing and Codec Requirements: https://www.rfc-editor.org/rfc/rfc7742
- IETF RFC 7874 WebRTC Audio Codec and Processing Requirements: https://www.rfc-editor.org/rfc/rfc7874
- WebRTC for the Curious codec 章: https://webrtcforthecurious.com/docs/06-media-communication/
- caniuse AV1: https://caniuse.com/av1
- 核对日期:2026-06-22