13、视频处理:分辨率适配、码率控制、视频美颜滤镜

视频处理这块,说实在的,是WebRTC移动端开发里最让人头疼、也最有意思的部分。你想想看,用户拿着手机,网络环境千差万别,设备性能也参差不齐。怎么让视频既清晰又流畅?怎么让画面里的自己看起来更精神?嗯,这就是我们今天要聊的三个核心问题。

13.1 分辨率适配:别让手机白费力气

我刚开始做移动端WebRTC的时候,犯过一个挺傻的错误。我直接把摄像头最高分辨率扔进去,结果低端手机直接卡成PPT。后来我才明白,分辨率适配不是「越高越好」,而是「刚刚好」。

分辨率适配的核心逻辑,说白了就一句话:根据网络带宽和设备能力,动态选择最合适的输出分辨率

核心原则:

  • 带宽充足时,用高分辨率(720p甚至1080p)
  • 带宽紧张时,降分辨率保流畅(360p或更低)
  • 设备性能差时,主动降低分辨率减少计算压力

在WebRTC里,分辨率适配主要通过 MediaStreamConstraintswidthheight 参数来控制。我个人习惯用 idealmin/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要求低延迟,你不能在视频处理上花太多时间。

美颜的核心流程是这样的:

  1. 从摄像头获取原始视频帧
  2. 对每一帧进行图像处理(磨皮、美白、瘦脸等)
  3. 将处理后的帧送入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 知识体系总览

下面这张图,是我对本章知识体系的总结。你可以把它当作一个快速索引,遇到问题时回来看看。

视频处理知识体系 分辨率适配 • 动态选择策略 • ideal/min/max约束 • 设备能力检测 • 720p上限建议 • 降级兜底逻辑 带宽 + 性能 = 最优解 码率控制 • setParameters干预 • 动态调整策略 • 丢包率/RTT监控 • 2-3秒调整间隔 • 降级SDP重协商 主动干预 > 自动控制 美颜滤镜 • OffscreenCanvas • WebGL GPU加速 • 人脸区域优化 • 磨皮/美白算法 • 性能 < 16ms/帧 GPU加速是唯一出路 三者协同:分辨率决定画幅 → 码率决定画质 → 美颜决定观感

这三个方面其实是环环相扣的。分辨率高了,码率就得跟上;码率不够,再高的分辨率也是白搭;美颜处理又会影响帧率,帧率低了又得降分辨率。嗯,说白了就是一个动态平衡的过程。

我在实际项目中,会把这三个模块封装成一个统一的 VideoProcessor 类,内部维护状态机,根据网络和设备状态自动调整参数。这样上层业务代码只需要调用一个接口,省心很多。

最后说一句: 视频处理没有银弹。每个应用场景都不一样,直播、视频通话、短视频录制,对分辨率、码率、美颜的要求都不同。我的建议是:先跑通基础流程,再根据实际数据做调优。别一开始就想做到完美,迭代才是王道。


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