18、网络优化:带宽估计、拥塞控制、丢包重传
做WebRTC开发,绕不开网络优化这个话题。
我记得刚接手一个移动端项目时,用户反馈视频通话经常卡成PPT。我排查了半天,发现不是编码问题,也不是设备性能问题——纯粹是网络模块没调好。说白了,你推再清晰的画面,网络扛不住,一切都是白搭。
今天我们就来聊聊WebRTC网络优化的三个核心:带宽估计、拥塞控制、丢包重传。这三者环环相扣,缺一不可。
18.1 带宽估计:摸清网络的底牌
带宽估计,说白了就是让WebRTC知道「当前网络到底能跑多少数据」。你想想看,如果不知道路有多宽,你怎么敢踩油门?
WebRTC的带宽估计分两种:
- 基于丢包的估计(Loss-based):通过观察丢包率来判断网络是否过载。丢包率高了,就降码率;丢包率低了,就升码率。
- 基于延迟的估计(Delay-based):通过观察RTT(往返时延)的变化来判断网络是否开始拥塞。延迟突然增大,说明网络开始排队了,得赶紧降速。
我个人习惯把这两种结合起来用。为什么?因为单靠丢包太被动——等你发现丢包,网络已经堵死了。延迟估计能提前预警,就像开车时看到前方刹车灯亮了,提前减速。
核心公式(简化版):
目标码率 = min( 基于丢包估计的码率, 基于延迟估计的码率 )
WebRTC最终取两者的较小值,确保网络不崩溃。
我在项目中遇到过一个问题:某款Android手机在WiFi下视频通话,画面突然变模糊。查了半天,发现是WiFi信号弱时,延迟抖动很大,WebRTC的延迟估计器误判为网络拥塞,疯狂降码率。后来我调整了延迟估计的阈值参数,问题才解决。
18.2 拥塞控制:踩刹车要果断
带宽估计是「摸清底牌」,拥塞控制就是「根据底牌来开车」。WebRTC用的是GCC(Google Congestion Control)算法,目前主流版本是GCC v2。
GCC的核心逻辑其实不复杂:
- 发送端定期发送探测包,接收端反馈延迟和丢包信息。
- 如果延迟增加且丢包率上升,说明网络拥塞,立即降低发送码率。
- 如果网络恢复平稳,逐步增加码率,但增加速度要慢(加性增,乘性减)。
嗯,这里要注意:GCC的「乘性减」非常激进。一旦检测到拥塞,码率直接砍半。我刚开始觉得这太粗暴了,后来在弱网环境下测试才发现——不这么干,网络马上就会雪崩。
避坑指南:我曾经在P2P通话中遇到一个问题,双方都在做拥塞控制,结果互相「谦让」,码率越降越低。后来发现是双方都开启了GCC的发送端控制,导致控制环路冲突。解决方案是:一方做主控,另一方只做反馈。
18.3 丢包重传:丢了就补
网络再优化,丢包也是难免的。WebRTC提供了两种丢包恢复机制:
| 机制 | 原理 | 适用场景 |
|---|---|---|
| NACK(否定确认) | 接收端发现丢包,通知发送端重传 | 非实时要求高的场景,如视频通话 |
| FEC(前向纠错) | 发送端额外发送冗余数据,接收端直接恢复 | 实时性要求极高,如直播、游戏 |
我个人更倾向于NACK+FEC混合使用。纯FEC会浪费带宽(冗余数据占20%-30%),纯NACK又会有延迟(等重传)。混合使用时,FEC只覆盖关键帧,NACK覆盖普通帧,效果最好。
你想想看,如果每帧都用FEC,带宽浪费太严重。但如果完全依赖NACK,一旦丢包率超过10%,重传风暴就会把网络打垮。我在项目中就踩过这个坑——某次弱网测试,丢包率15%,NACK请求像雪片一样飞回来,发送端忙着重传,新数据反而发不出去了。
注意:NACK不是万能的。如果RTT超过500ms,重传的数据到了也来不及解码。这种情况下,建议直接丢弃旧帧,发送新的关键帧(IDR帧请求)。
18.4 三者的协同工作
带宽估计、拥塞控制、丢包重传,不是各自为战,而是紧密配合的。我画了一张图来说明它们的关系:
从图中可以看出,这是一个闭环系统。带宽估计告诉拥塞控制「路有多宽」,拥塞控制决定「开多快」,丢包重传负责「路上掉了东西赶紧捡」。而丢包率和延迟信息又会反馈给带宽估计,让它重新评估网络状况。
我在实际调优时,会重点关注三个参数:
- 初始码率:不要设太高,我一般从500kbps起步,让GCC自己爬升。
- 最小码率:设一个底线,比如150kbps,防止画面完全不可用。
- NACK超时时间:我习惯设300ms,超过这个时间就不等了,直接请求关键帧。
个人经验:移动端网络环境复杂,WiFi和4G切换时,带宽估计会剧烈波动。我建议在切换瞬间,强制发送一个关键帧,同时重置GCC的状态,让算法重新学习网络。这样能避免切换后的卡顿。
好了,网络优化的三个核心就聊到这里。记住一句话:带宽估计是眼睛,拥塞控制是手脚,丢包重传是安全气囊。三者配合好了,你的WebRTC应用才能在真实网络中跑得稳、跑得顺。
公众号:蓝海资料掘金营,微信deep3321