13、视频处理:分辨率适配、码率控制、视频美颜滤镜
视频处理这块,说实在的,是WebRTC移动端开发里最让人头疼、也最有意思的部分。你想想看,用户拿着手机,网络环境千差万别,设备性能也参差不齐。怎么让视频既清晰又流畅?怎么让画面里的自己看起来更精神?嗯,这就是我们今天要聊的三个核心问题。
13.1 分辨率适配:别让手机白费力气
我刚开始做移动端WebRTC的时候,犯过一个挺傻的错误。我直接把摄像头最高分辨率扔进去,结果低端手机直接卡成PPT。后来我才明白,分辨率适配不是「越高越好」,而是「刚刚好」。
分辨率适配的核心逻辑,说白了就一句话:根据网络带宽和设备能力,动态选择最合适的输出分辨率。
核心原则:
- 带宽充足时,用高分辨率(720p甚至1080p)
- 带宽紧张时,降分辨率保流畅(360p或更低)
- 设备性能差时,主动降低分辨率减少计算压力
在WebRTC里,分辨率适配主要通过 MediaStreamConstraints 的 width 和 height 参数来控制。我个人习惯用 ideal 和 min/max 组合,让浏览器自己选最优解。
// 推荐的分辨率约束写法
const constraints = {
video: {
width: { ideal: 1280, min: 640, max: 1920 },
height: { ideal: 720, min: 480, max: 1080 },
frameRate: { ideal: 30, min: 15, max: 30 }
}
};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
这里有个坑,我踩过。有些Android设备对 ideal 的支持并不好,它会直接返回最高分辨率。所以我在项目里加了一层兜底逻辑:拿到流之后,主动检查实际分辨率,如果超出预期就降级。
我的经验: 在移动端,建议把分辨率上限设为720p。1080p在手机上肉眼提升有限,但CPU和带宽消耗翻倍。除非你做的是直播类应用,否则不划算。
13.2 码率控制:让视频在烂网里也能跑
分辨率搞定了,下一个问题就是码率。你想想看,同样的720p画面,码率给高了浪费带宽,给低了全是马赛克。WebRTC内置了拥塞控制算法,但说实话,在移动端它经常不够聪明。
为什么会这样?因为移动网络波动太剧烈了。地铁里、电梯中、高速上,带宽可能几秒钟内从5Mbps掉到200kbps。WebRTC的默认算法反应不够快。
我个人建议的做法是:主动干预码率,而不是完全依赖WebRTC自动控制。
// 通过 RTCRtpSender 设置编码参数
const sender = peerConnection.getSenders().find(s => s.track.kind === 'video');
const parameters = sender.getParameters();
// 设置目标码率(单位:bps)
parameters.encodings[0].maxBitrate = 1000000; // 1Mbps
parameters.encodings[0].minBitrate = 200000; // 200kbps
// 设置优先级
parameters.encodings[0].priority = 'high';
parameters.encodings[0].networkPriority = 'high';
await sender.setParameters(parameters);
这里要注意,setParameters 不是所有浏览器都支持得一样好。我在iOS的Safari上就遇到过调用后不生效的情况。所以建议加个降级方案:如果 setParameters 失败,就重新协商SDP。
警告: 不要频繁调用 setParameters。每次调用都会触发编码器重新配置,太频繁反而导致卡顿。我一般控制在每2-3秒调整一次。
码率控制的另一个关键点是动态调整策略。我常用的策略是:
- 检测到丢包率 > 5%,立即降低码率20%
- 检测到RTT > 300ms,降低码率10%
- 连续5秒无丢包且RTT < 100ms,尝试提升码率10%
这个策略看起来简单,但实际效果比WebRTC默认的GCC算法要好。我在项目中测试过,在弱网环境下视频流畅度提升了约30%。
13.3 视频美颜滤镜:让用户爱上自己的脸
美颜滤镜,嗯,这个功能在社交类应用里几乎是标配了。但WebRTC里做美颜,跟普通直播不一样。因为WebRTC要求低延迟,你不能在视频处理上花太多时间。
美颜的核心流程是这样的:
- 从摄像头获取原始视频帧
- 对每一帧进行图像处理(磨皮、美白、瘦脸等)
- 将处理后的帧送入WebRTC编码器
在移动端,我推荐用 GPU加速 的方案。CPU做美颜?别想了,720p 30fps的实时处理,CPU根本扛不住。
具体实现上,我一般用 OffscreenCanvas 配合 WebGL 来做。流程是这样的:
// 美颜处理的核心流程(伪代码)
async function processFrame(videoFrame) {
// 1. 将视频帧绘制到 OffscreenCanvas
const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext('2d');
ctx.drawImage(videoFrame, 0, 0);
// 2. 获取像素数据
const imageData = ctx.getImageData(0, 0, width, height);
// 3. 应用美颜滤镜(这里用简单的双边滤波做磨皮)
const processedData = applyBeautyFilter(imageData, {
smoothness: 0.6, // 磨皮强度
brightness: 0.1, // 提亮
skinColor: [0.95, 0.85, 0.8] // 肤色调整
});
// 4. 将处理后的数据放回 canvas
ctx.putImageData(processedData, 0, 0);
// 5. 从 canvas 创建新的视频轨道
const processedStream = canvas.captureStream(30);
return processedStream.getVideoTracks()[0];
}
这里有个关键点:不要每一帧都做全图处理。我做过性能分析,全图处理720p画面,在iPhone 11上大概需要8-12ms。如果再加上人脸检测、瘦脸等操作,很容易超过16ms的帧间隔。
优化技巧: 只处理人脸区域。先用人脸检测框出人脸,只对这个区域做磨皮美白。背景区域不做处理,能省下60%以上的计算量。
我曾经在一个项目中,把美颜处理从CPU迁移到GPU(用WebGL2),处理时间从35ms降到了5ms。效果立竿见影。所以如果你要做美颜,优先考虑GPU方案。
13.4 知识体系总览
下面这张图,是我对本章知识体系的总结。你可以把它当作一个快速索引,遇到问题时回来看看。
这三个方面其实是环环相扣的。分辨率高了,码率就得跟上;码率不够,再高的分辨率也是白搭;美颜处理又会影响帧率,帧率低了又得降分辨率。嗯,说白了就是一个动态平衡的过程。
我在实际项目中,会把这三个模块封装成一个统一的 VideoProcessor 类,内部维护状态机,根据网络和设备状态自动调整参数。这样上层业务代码只需要调用一个接口,省心很多。
最后说一句: 视频处理没有银弹。每个应用场景都不一样,直播、视频通话、短视频录制,对分辨率、码率、美颜的要求都不同。我的建议是:先跑通基础流程,再根据实际数据做调优。别一开始就想做到完美,迭代才是王道。
公众号:蓝海资料掘金营,微信deep3321