13、视频处理:视频编解码器、分辨率适配、帧率控制、码率控制

视频处理这块,说实话是多人视频会议里最"吃"性能的环节。你想想看,一个房间里有五六个人同时开摄像头,每个人的画面都要编码、传输、解码、渲染,任何一个环节出问题,画面就卡成PPT了。

我最早做WebRTC项目时,以为只要把摄像头流推出去就行了。结果第一次联调,三个人的画面就把CPU干到了90%,风扇呼呼转。后来才明白——视频处理不是"能不能传"的问题,而是"怎么传得又清晰又流畅"的问题。

这一章,我们就来拆解这四个核心参数:编解码器、分辨率、帧率、码率。它们就像视频的四个旋钮,调好了,体验起飞;调不好,翻车现场。

13.1 视频编解码器:选对工具,事半功倍

编解码器,说白了就是视频的"压缩算法"。原始视频数据量巨大——1080p的一帧画面,未经压缩大概有6MB。按30fps算,一秒就是180MB。这带宽谁也扛不住。

WebRTC里主流的编解码器有三个:

编解码器 特点 我的建议
VP8 开源、兼容性好、CPU消耗低 兼容性首选,老设备也能跑
VP9 压缩率更高,同等画质码率低30%-50% 适合带宽受限场景,但解码耗CPU
H.264 硬件编码支持好,几乎所有设备都支持 我个人最常用,移动端表现稳定

我在项目中遇到过一个问题:用VP9编码,画面确实清晰,但用户的旧手机解码时直接过热降频。后来切回H.264,问题就解决了。所以我的习惯是——优先用H.264,如果设备支持且CPU有余量,再考虑VP9。

小技巧: 可以用 RTCRtpSender.getCapabilities('video') 来检测浏览器支持的编解码器,动态选择最优方案。

13.2 分辨率适配:不是越高越好

很多人有个误区:分辨率越高越好。其实不然。分辨率高意味着数据量大,如果带宽跟不上,画面反而会卡顿或模糊。

我一般这样设计分辨率策略:

  • 大画面(主讲人/共享屏幕):720p 或 1080p,保证清晰度
  • 小画面(其他参会者):360p 或 480p,节省带宽
  • 缩略图(超过6人时):180p,只保留人脸轮廓

WebRTC里可以用 MediaStreamTrack.applyConstraints() 动态调整分辨率:

// 动态调整摄像头分辨率
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const track = stream.getVideoTracks()[0];

// 切换到720p
await track.applyConstraints({
  width: { ideal: 1280 },
  height: { ideal: 720 }
});

// 带宽不足时,降级到480p
await track.applyConstraints({
  width: { ideal: 640 },
  height: { ideal: 480 }
});
注意: 分辨率切换时,建议先停止旧的流,再重新获取。直接applyConstraints在某些浏览器上会有延迟。

13.3 帧率控制:流畅与省电的平衡

帧率决定了画面的流畅度。30fps是视频会议的标准,15fps勉强能看,低于10fps就会有明显的卡顿感。

但帧率不是越高越好。我做过测试:60fps和30fps在视频会议场景下,人眼几乎分辨不出区别,但CPU消耗却翻了一倍。所以我的建议是:

  • 正常通话:25-30fps,流畅且省电
  • 屏幕共享(静态内容):5-10fps,PPT或文档不需要高帧率
  • 屏幕共享(视频播放):30fps,保证播放流畅

帧率控制可以通过 RTCRtpEncodingParameters 来实现:

const sender = pc.addTrack(track, stream);
const params = sender.getParameters();

// 限制最大帧率为20fps
params.encodings[0].maxFramerate = 20;

await sender.setParameters(params);

嗯,这里要注意:maxFramerate 不是所有浏览器都支持。Chrome和Firefox没问题,Safari有时候会忽略这个参数。我一般会在服务端做二次校验,确保帧率被正确限制。

13.4 码率控制:带宽的"精打细算"

码率是视频质量的直接决定因素。码率越高,画面越清晰,但占用的带宽也越大。WebRTC内置了拥塞控制算法,会自动调整码率,但有时候它的判断并不准确。

我习惯手动设置一个码率范围:

分辨率 推荐码率范围 说明
1080p 2-4 Mbps 适合主讲人/共享屏幕
720p 1-2 Mbps 日常通话首选
480p 500-800 Kbps 移动端或带宽受限
360p 300-500 Kbps 多人场景的小画面

设置码率的方法:

const sender = pc.addTrack(track, stream);
const params = sender.getParameters();

// 设置码率范围
params.encodings[0].minBitrate = 500000;   // 500 Kbps
params.encodings[0].maxBitrate = 2000000;  // 2 Mbps

await sender.setParameters(params);
核心思路: 码率控制不是"一刀切",而是动态调整。我一般会结合网络状态监测,带宽好时提高码率,带宽差时降低分辨率+码率,保证通话不中断。

13.5 知识体系总览

下面这张图,是我自己梳理的视频处理核心逻辑。你看一遍,应该就能把四个参数的关系串起来了:

视频处理核心参数关系图 视频流 编解码器 分辨率 帧率 码率 决定压缩算法 决定画面大小 决定流畅度 决定清晰度 核心原则 分辨率 × 帧率 × 色彩深度 ≈ 原始码率 编解码器决定压缩效率,码率控制决定最终质量

说白了,这四个参数是联动的。分辨率高了,帧率就得降一点;码率不够,分辨率再高也是模糊的。我一般会先确定目标分辨率,再根据带宽动态调整帧率和码率。

避坑指南: 我曾经在项目中把码率设得太低(300Kbps跑720p),结果画面全是马赛克。后来学乖了——码率至少是 分辨率宽度×高度×帧率×0.0001,这个公式虽然粗糙,但能保证基本清晰度。

好了,视频处理的四个核心参数就讲到这里。记住一句话:没有最好的参数,只有最适合当前网络和设备的参数。动手调一调,你就能找到那个"甜点"。

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