11、音视频编解码:常用编解码器、编解码协商过程、Simulcast与SVC、码率自适应
聊到WebRTC,编解码这块是绕不开的核心。说白了,没有编解码,摄像头采集的原始视频数据根本传不出去——一秒钟几MB甚至几十MB的数据量,网络扛不住。我刚开始接触WebRTC时,总觉得编解码是底层的事,上层调API就行。后来踩了几次坑才发现,不理解编解码的协商逻辑,连个视频通话都调不通。
常用编解码器:VP8、VP9、H.264、AV1
WebRTC标准里强制要求支持的是VP8和H.264。VP9和AV1是可选,但浏览器支持度越来越高。
| 编解码器 | 特点 | 浏览器支持 | 我的经验 |
|---|---|---|---|
| VP8 | 开源、低延迟、兼容性好 | Chrome、Firefox、Edge | 最稳的选择,几乎不出错 |
| H.264 | 硬件编码支持好、带宽利用率高 | Chrome、Safari、Edge | iOS端必选,否则黑屏 |
| VP9 | 压缩率比VP8高30%-50% | Chrome、Firefox | 适合低带宽场景,但编码慢 |
| AV1 | 压缩率最高,但编码开销大 | Chrome(部分版本) | 目前还不太实用,等硬件普及 |
编解码协商过程:SDP Offer/Answer 模型
编解码协商,说白了就是两端互相告诉对方「我能解码什么格式,你发什么格式给我」。这个过程通过SDP(Session Description Protocol)完成。
我记得第一次调试时,发现视频一直黑屏。查了半天,原来是SDP里编解码顺序写错了。浏览器会优先选择SDP里排第一的编解码器。
协商流程大致是这样的:
- 发起方创建Offer,里面列出支持的编解码器列表(按优先级排序)
- 接收方收到Offer后,创建Answer,从Offer的列表里选一个自己支持的
- 双方确认后,开始传输
代码层面,你不需要手动拼SDP。WebRTC API帮你做了大部分工作:
// 创建PeerConnection
const pc = new RTCPeerConnection();
// 创建Offer
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
// 发送offer给远端
// 远端收到后创建Answer
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
Simulcast与SVC:多流与分层编码
这两个概念容易混淆。我简单解释一下:
- Simulcast(同时广播): 发送端同时编码多个不同分辨率的视频流(比如720p、480p、360p),接收端根据网络情况选择订阅哪个流。
- SVC(可伸缩视频编码): 只编码一个流,但这个流包含多个层(基础层+增强层)。网络差时只传基础层,网络好时叠加增强层提高质量。
我个人更倾向于Simulcast,因为实现简单,兼容性好。SVC虽然理论上更优雅,但浏览器支持有限,而且编码开销大。
开启Simulcast的代码示例:
// 在创建Offer之前设置Simulcast
const transceiver = pc.addTransceiver('video', {
direction: 'sendrecv',
sendEncodings: [
{ rid: 'high', maxBitrate: 900000, scaleResolutionDownBy: 1.0 },
{ rid: 'medium', maxBitrate: 300000, scaleResolutionDownBy: 2.0 },
{ rid: 'low', maxBitrate: 100000, scaleResolutionDownBy: 4.0 }
]
});
这里rid是流的标识,maxBitrate是最大码率,scaleResolutionDownBy是分辨率缩放比例。比如scaleResolutionDownBy: 2.0表示分辨率缩小到原来的一半。
码率自适应:让视频流畅不卡顿
码率自适应,说白了就是根据网络状况动态调整视频质量。网络好时用高码率,网络差时降低码率避免卡顿。
WebRTC内置了码率自适应算法,叫GCC(Google Congestion Control)。它会根据丢包率、RTT(往返时延)等指标动态调整发送码率。
但内置算法不一定适合所有场景。我在做直播项目时,发现GCC在弱网环境下反应太慢,导致视频卡顿好几秒才恢复。后来我们手动实现了码率自适应:
// 监听网络状态
pc.oniceconnectionstatechange = () => {
if (pc.iceConnectionState === 'connected') {
// 网络恢复,提高码率
setCodecBitrate(800000);
} else if (pc.iceConnectionState === 'disconnected') {
// 网络断开,降低码率
setCodecBitrate(200000);
}
};
// 手动设置编码器码率
function setCodecBitrate(bitrate) {
const sender = pc.getSenders().find(s => s.track.kind === 'video');
const parameters = sender.getParameters();
parameters.encodings[0].maxBitrate = bitrate;
sender.setParameters(parameters);
}
知识体系总览
下面这张图概括了本章的核心逻辑:
嗯,编解码这块内容确实不少。但说白了,你只要掌握三个核心点:
- 选对编解码器: 根据平台和场景选择VP8或H.264
- 理解协商流程: SDP Offer/Answer是基础,参数匹配是关键
- 做好码率控制: Simulcast或SVC选一个,配合码率自适应算法
我在实际项目中,通常先用VP8做原型,稳定后再根据需求加入H.264和Simulcast。码率自适应这块,内置的GCC够用就别自己折腾,除非你有特殊场景。