
记得第一次接触实时音视频这个领域的时候,我对”流量控制”这个概念其实是懵的。不就是传数据吗?把数据发出去不就行了?后来在实际项目中遇到各种卡顿、花屏、延迟爆炸的问题,才慢慢意识到——在实时音视频这条路上,流量控制才是那个藏在水下的真正主角。
这篇文章我想用最朴素的语言,把几种主流的流量控制算法讲清楚。不会堆砌公式,也不会故作高深,我们就从”为什么需要流量控制”这个最朴素的问题出发,看看业界是怎么一步步解决这个问题的。
想象一下这个场景:你正在和远方的朋友打视频电话。你家的网络带宽是50Mbps,朋友的带宽只有10Mbps。这时候问题来了——你这边每秒能发50兆的数据,但朋友那边每秒只能收10兆。多出来的40兆往哪儿塞?
这就是流量控制要解决的核心问题: sender端发送数据的速度,不能超过receiver端的处理能力和网络的承载能力。如果无视这个限制,数据就会在网络中堆积,形成所谓的”拥塞”。一旦拥塞发生,数据包延迟会急剧上升,甚至大量丢失。对于语音通话来说,延迟超过150毫秒就会明显感觉不对;对于视频通话来说,丢包直接意味着画面卡顿或者马赛克。
在传统的文件下载场景中,这个问题倒是不太要命。下载中断了大不了重传,等一会儿没关系。但实时音视频不一样——数据过了时效就不值钱了。五分钟前的视频画面对现在的通话毫无意义,所以传统的TCP重传机制在实时场景下基本不可行。
很多人可能会问:TCP不是有流量控制机制吗?为什么要另起炉灶?

TCP确实有流量控制,它用的是滑动窗口协议。简单来说,receiver会告诉sender”我这里还能收多少数据”,sender就根据这个来调整发送速度。这套机制在文件传输、网页浏览这些场景下工作得很好,但它有三个致命问题不适合实时音视频。
第一个问题是延迟敏感。 TCP为了保证可靠性,会对丢失的数据包进行重传。重传就意味着等待,等待就意味着延迟。在实时通话中,我们宁愿丢掉一帧数据,也不愿意为了等这一帧而让整个通话卡住。尤其是当网络状况不好的时候,TCP会反复重传那些已经过时的包,这纯粹是在浪费带宽。
第二个问题是公平性。 TCP在检测到丢包时,会把发送窗口减半。这种”断崖式”的减速会导致发送速率剧烈波动。你想象一下,正在打视频电话,突然网络有点拥堵,TCP直接把速率砍一半,画面质量瞬间从高清变成马赛克。这种体验显然不是我们想要的。
第三个问题是基于丢包的判断太滞后。 等到TCP检测到丢包的时候,网络其实已经拥堵好一阵子了。我们希望的是能够更早地预判拥堵,在丢包发生之前就主动调整速率。
正是因为这些原因,实时音视频领域逐渐发展出了一套自己的流量控制算法体系。
既然TCP不行,那很多实时音视频系统就转向了UDP。UDP不保证可靠性,不做重传,延迟确实低了。但新的问题又来了——如果大家都用UDP无视网络状况拼命发数据,整个互联网早就堵死了。
这时候就有人提出了TFRC(TCP-Friendly Rate Control,TCP友好速率控制)的想法:我们不用TCP的重传机制,但我们借鉴TCP的速率调整策略,让使用UDP的应用也能和网络上的TCP应用公平相处。
TFRC的核心思路是这样的:它不像TCP那样每次丢包就把窗口减半,而是把窗口大小维持在一个相对稳定的水平。具体来说,TFRC会计算一个”公平速率”——这个速率是通过对TCP吞吐量公式求解得到的,理论上应该和TCP占用相同的带宽。

TFRC的优势在于发送速率比较平滑,不会像TCP那样剧烈波动。这对于视频编码器来说非常重要,因为编码器通常需要相对稳定的码率来保证画面质量。如果码率忽高忽低,编码器要么会浪费带宽,要么会导致画面质量不稳定。
不过TFRC也有明显的缺点。首先是响应速度慢,当网络状况突然变差时,TFRC的调整不够及时。其次是计算复杂度较高,需要维护大量的状态信息。还有一点是,TFRC的设计目标是与TCP公平分享带宽,但在实际网络中,这个”公平”往往很难精确实现。
2012年左右,Google开始在其视频通话产品中应用一套新的拥塞控制算法,这就是后来被称为GCC(Google Congestion Control)的算法。这套算法在当时引起了不少关注,因为它采用了一种和传统思路完全不同的方法。
传统算法主要依赖丢包来检测拥塞——丢包了,说明网络堵了。GCC的创新在于它引入延迟作为拥塞的早期信号。当网络中的队列开始堆积时,数据包的到达间隔会变大,延迟会升高。GCC就是通过监测这种延迟的变化,来提前预判拥塞的发生。
GCC的算法结构挺有意思,它分成两个部分:发送端的速率控制器和接收端的带宽估计器。接收端会监测数据包的到达间隔,计算出网络延迟的变化趋势,然后把估计的带宽反馈给发送端。发送端再根据这个反馈和自身的延迟监测结果,综合决定当前的发送速率。
这套算法有几个显著的优势。由于使用了延迟信号,GCC能够在丢包发生之前就检测到拥塞的苗头,从而更早地进行速率调整。另外,GCC的设计考虑了不同网络场景的差异,对于有线网络和移动网络采用了不同的参数配置。
不过GCC也不是完美的。在某些网络环境下,延迟信号可能会产生误判。比如,如果传输路径本身发生了变化,延迟也会相应变化,但这种变化不一定意味着拥塞。另外,GCC在多流竞争的场景下,表现不如TFRC稳定。
在GCC之后,学术界和工业界继续探索更先进的拥塞控制算法。Sprout是由斯坦福大学研发的一套算法,它采用了更为激进的策略。Sprout的核心思想是:与其保守地避免拥塞,不如主动探测网络的可用带宽。
Sprout使用概率模型来预测网络的丢包概率,并据此计算发送速率。它会在网络状况好的时候尽可能利用带宽,在检测到丢包风险时再快速撤退。这种”激进探测”的策略在很多场景下能够获得更高的吞吐量,但也存在一定的风险——如果网络状况突然恶化,Sprout可能会经历一段不稳定的调整期。
Google后来推出的BBR( Bottleneck Bandwidth and Round-trip propagation time)则是另一个方向上的突破。BBR的思路是直接测量网络的”瓶颈带宽”和”往返时延”,而不是通过丢包或延迟变化来间接推断网络状况。这种方法在长肥网络(高带宽、高延迟的网络)上表现尤为出色。
不过BBR主要是在TCP上应用的,对于实时音视频场景的适配还需要做一些改造。一些实时音视频平台在这方面做了尝试,把BBR的核心理念和自身的需求结合起来。
为了让对比更加直观,我整理了一个表格来展示这几种主要算法的特点:
| 算法名称 | 检测信号 | 速率波动 | 响应速度 | 适用场景 |
| TFRC | 丢包率 | 平滑 | 较慢 | 对稳定性要求高的场景 |
| GCC | 延迟变化+丢包 | 适中 | 较快 | 通用实时音视频场景 |
| Sprout | 丢包概率模型 | 较大 | 快 | 带宽充裕的网络环境 |
| BBR | 带宽+时延测量 | 适中 | 快 | 高延迟高带宽网络 |
从这个表格可以看出,没有哪种算法是”万能”的。在实际应用中,需要根据具体的网络环境、设备性能、用户需求来选择合适的算法,或者进行组合使用。
说到实时音视频,不得不提声网。作为国内领先的实时音视频服务商,声网在流量控制方面积累了大量实践经验。
声网的算法设计团队有一个很务实的观点:网络环境是多变的,没有哪种算法能够适应所有情况。与其追求一个”最优”的算法,不如设计一个能够自适应切换的框架。
在声网的系统中,他们实现了多种流量控制算法的动态切换机制。系统会实时监测当前的网络状况——丢包率是多少、延迟是多少、带宽估计是多少——然后根据这些指标自动选择最合适的控制策略。比如在检测到网络状况良好时,系统会采用较为激进的策略以充分利用带宽;当检测到网络开始拥塞时,则切换到保守策略,优先保证流畅性。
除了自适应切换,声网还针对不同的应用场景进行了定制优化。视频会议场景和直播场景对延迟和稳定性的要求侧重点不同,互动直播和一对一通话的需求也有差异。声网在这些场景中都做了相应的参数调优,力求在各种条件下都能给用户提供尽可能好的体验。
聊了这么多算法和原理,最后我想说的是,流量控制这个领域其实没有什么”银弹”。每种算法都是在延迟、稳定性、带宽利用率之间做权衡。TCP稳定但延迟高,UDP延迟低但需要额外的控制机制,基于延迟的检测快但可能有误判,基于丢包的检测准但滞后。
真正的挑战在于如何把这些碎片化的技术整合起来,形成一个能够在复杂多变的网络环境中稳定工作的系统。这需要大量的实验数据、长期的工程积累,以及对用户需求的深刻理解。
实时音视频这条路,技术在进步,网络环境在变化,用户的期望也在不断提高。流量控制算法也会继续演进,可能会出现我们现在还无法想象的的新思路。但不管怎样,保证通话流畅、让用户获得好的体验,这个目标是不会变的。
