18、WebRTC的视频处理:视频JitterBuffer、NACK与FEC、视频SVC与Simulcast

视频通话卡成幻灯片,画面碎成马赛克——这几乎是每个做实时音视频的人都会遇到的噩梦。我早期做海外会议系统时,用户从新加坡连到法兰克福,延迟400ms,丢包率15%,那画面简直没法看。后来我才真正搞明白,WebRTC里那套视频处理机制,才是保命的底牌。

今天咱们就聊聊视频链路上的四个核心武器:JitterBufferNACKFEC,以及SVC与Simulcast。它们各自解决什么问题?怎么配合?踩过哪些坑?我尽量一次说透。

WebRTC 视频处理核心模块 视频接收处理 JitterBuffer 抗网络抖动 · 排序 · 丢帧 NACK 丢包重传 · RTCP反馈 FEC 前向纠错 · 冗余包 SVC / Simulcast 分层编码 · 多流 · 带宽自适应 抗抖动 丢包恢复 冗余恢复 码率自适应

18.1 视频JitterBuffer:把乱序的包排好队

网络不是一根直管子。包会乱序、会延迟、会一股脑涌过来。JitterBuffer 就是接收端的一个缓冲区,专门干两件事:排序抗抖动

我见过不少新手直接把收到的包往解码器里塞,结果画面一顿一顿的。为什么?因为包到达的时间不均匀。有的早到50ms,有的晚到200ms。解码器需要稳定的帧率输入,JitterBuffer 就是那个“蓄水池”。

核心机制:

  • 每个视频包都有 RTP 时间戳和序列号。JitterBuffer 按序列号重排。
  • 缓冲区会维持一个目标延迟(比如 100ms),动态调整。
  • 如果某个帧的包一直没到齐,超时后直接丢帧——总比卡住好。

我的经验:JitterBuffer 的延迟不能设死。我曾经在 Wi-Fi 环境下把缓冲设成 50ms,结果频繁丢帧。后来改成自适应算法,根据最近 500ms 的抖动统计动态调整,效果好了很多。WebRTC 内部用的是 NetEq 算法,音频视频各有一套,但思路相通。

18.2 NACK:丢了包,我喊你重传

NACK(Negative Acknowledgment)就是“我没收到,再发一次”。接收端发现某个序列号缺失,就通过 RTCP 反馈给发送端,发送端重新发送那个包。

听起来简单,但坑不少。

  • 延迟问题:一个 RTT 的代价。如果 RTT 是 200ms,重传回来至少 200ms,对于实时视频来说已经很久了。
  • 风暴问题:如果丢包率突然升高,接收端会疯狂发 NACK,发送端疯狂重传,网络更堵。WebRTC 里有限制:每个包最多重传 3 次,而且有 NACK 频率控制。
  • 关键帧保护:我习惯对关键帧(IDR)的 NACK 优先级调高。因为丢了关键帧,后面一堆 P 帧都解不出来,画面会一直花屏直到下一个关键帧到来。

注意:NACK 不是万能的。在丢包率超过 20% 的网络里,重传只会让情况更糟。这时候需要 FEC 上场。

18.3 FEC:提前给你冗余包

FEC(Forward Error Correction)是发送端提前算好一些冗余数据,跟原始数据一起发出去。接收端即使丢了一部分包,也能靠冗余包把原始数据恢复出来。

WebRTC 里常用的 FEC 是 ULP FEC(Uneven Level Protection)和 FlexFEC。说白了就是 XOR 运算:把几个包异或一下,生成一个冗余包。丢了一个,用剩下的 XOR 回来。

特性 NACK FEC
延迟 至少 1 RTT 无额外延迟(接收端直接恢复)
带宽开销 丢包时才产生 始终存在(冗余率固定或动态)
适用场景 低丢包、低延迟要求 高丢包、低延迟要求
恢复能力 100%(只要重传成功) 有限(取决于冗余率)

实际项目中,我通常把两者结合使用。丢包率低于 5% 时只用 NACK,高于 10% 时开启 FEC,冗余率设在 20% 左右。WebRTC 的 VideoSendStream 配置里可以设置 fec_params,建议根据网络探测结果动态调整。

避坑指南:我曾经在 FEC 冗余率上吃过亏。设了 50% 冗余,结果带宽不够,反而加剧了丢包。记住:FEC 不是越多越好,它会吃掉带宽。建议用 REMBB 带宽估计来动态调整 FEC 和编码码率的比例。

18.4 视频SVC与Simulcast:让不同带宽的人都能看

一个会议里,有人用 5G,有人用 3G,还有人用 2G 热点。你不能让所有人都收同一路高清流。SVC 和 Simulcast 就是解决这个问题的。

Simulcast:同时发多路流

发送端同时编码出多个分辨率的流,比如 1080p、720p、360p。接收端根据自己的带宽选一路来收。

  • 优点:每路流独立编码,质量好。接收端切换流时延迟低。
  • 缺点:发送端编码压力大,带宽开销大(同时发多路)。

我在做 SFU(选择性转发单元)时,Simulcast 是最常用的方案。SFU 只需要根据订阅者的带宽,转发对应质量的流即可,不需要转码,性能很高。

SVC:一层流,多种质量

SVC(Scalable Video Coding)把视频编码成多层:基础层(低分辨率/低帧率)加上增强层。接收端可以只收基础层,也可以收基础层+增强层来提升质量。

  • 优点:只发一路流,带宽利用率高。接收端可以灵活选择层数。
  • 缺点:编码复杂度高,增强层丢包会影响上层质量。浏览器支持不如 Simulcast 好。

我的选择:如果是浏览器端,我优先用 Simulcast。Chrome 和 Firefox 对 Simulcast 支持很成熟。如果是自研客户端或者硬件编码器,SVC 更省带宽。VP9 的 SVC 实现比 H.264 的 SVC 更实用,我最近的项目就在用 VP9 SVC,配合带宽估计,效果很稳。

18.5 四者如何配合?

一个典型的视频接收流程是这样的:

  1. 网络包到达,先进入 JitterBuffer,排序并等待。
  2. 如果发现丢包,先尝试 FEC 恢复。恢复不了,再发 NACK 请求重传。
  3. 如果重传超时,直接丢帧,解码器跳过这一帧。
  4. 同时,带宽估计模块在后台跑,根据网络状况决定是否切换 Simulcast 流或调整 SVC 层数。

这套机制不是孤立的。我见过有人只开 NACK 不开 FEC,结果高丢包时画面卡死。也有人只开 FEC 不开 NACK,结果带宽浪费严重。记住:JitterBuffer 是基础,NACK 和 FEC 互补,SVC/Simulcast 是上层策略

一句话总结:JitterBuffer 让视频不乱不卡,NACK 和 FEC 让视频不丢不花,SVC 和 Simulcast 让视频适应各种网络。四者缺一不可。


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