17、NADA算法详解:Network-Assisted Dynamic Adaptation、延迟与丢包联合控制、与GCC的异同

NADA,全称是 Network-Assisted Dynamic Adaptation。说实话,我第一次看到这个名字的时候,觉得它有点绕口。但搞懂了它的设计思想之后,你会发现这个名字起得挺贴切的——它确实是在借助网络反馈来做动态调整。

这一章,我们来把 NADA 拆开揉碎了讲。我会结合我自己的踩坑经历,帮你理清楚它的核心逻辑,以及它和 GCC 到底差在哪。

17.1 NADA 的诞生背景

GCC 是 Google 搞出来的,主要服务于 WebRTC 的实时通信场景。但 GCC 有个问题——它主要依赖延迟梯度来做判断,丢包只是辅助信号。这在某些网络环境下,反应不够灵敏。

我记得 2018 年的时候,我在一个跨国会议项目中遇到过这种情况:网络偶尔丢包,但延迟抖动不大。GCC 几乎没反应,视频质量却已经明显下降了。后来换成 NADA 的拥塞控制思路,效果好了不少。

NADA 的设计目标很明确:把延迟和丢包放在同等重要的位置,联合起来做决策。它不再像 GCC 那样,把丢包当成一个“辅助参考”,而是直接参与码率计算。

17.2 核心机制:延迟与丢包联合控制

NADA 的核心公式,说白了就是一句话:根据延迟和丢包的变化,动态调整发送码率。它用了一个非常经典的“加性增、乘性减”框架,但判断条件更丰富。

具体来说,NADA 会维护一个目标码率 R。每次收到接收端的反馈后,它会做两件事:

  • 计算延迟梯度:和 GCC 类似,NADA 也会看延迟的变化趋势。如果延迟在持续增加,说明网络开始拥塞了。
  • 计算丢包率:NADA 会统计一段时间内的丢包比例。如果丢包率超过阈值,它会认为网络已经过载。

然后,NADA 把这两个信号合并成一个“拥塞指数”。这个指数决定了码率是增加还是减少。

关键点:NADA 的拥塞指数不是简单的“延迟大就减、丢包多就减”,而是把两者加权融合。权重可以动态调整,适应不同的网络场景。

17.3 码率调整策略

NADA 的码率调整,我习惯把它分成三种状态:

  1. 正常状态:延迟和丢包都在可接受范围内。这时候 NADA 会尝试增加码率,但增加的速度比较慢,避免一下子把网络撑爆。
  2. 轻度拥塞:延迟开始上升,或者出现少量丢包。NADA 会停止增加码率,甚至小幅下调,让网络缓一缓。
  3. 严重拥塞:延迟持续飙升,或者丢包率超过 5%。这时候 NADA 会大幅降低码率,而且是乘性减,比如直接砍掉 20%。

我曾经在一个 Wi-Fi 信号不稳定的场景下调试过 NADA。当时丢包率在 3% 到 8% 之间波动,NADA 的码率曲线比 GCC 平滑很多。GCC 经常因为延迟抖动而误判,NADA 则能更准确地识别出真正的拥塞。

避坑指南:我曾经在配置 NADA 的丢包阈值时踩过坑。默认的 5% 阈值在某些无线网络下太敏感了,导致码率频繁波动。后来我把它调到了 8%,效果反而更好。所以,阈值不是死的,要根据实际网络环境做微调。

17.4 NADA 与 GCC 的异同

很多同学会问:NADA 和 GCC 到底哪个好?我的答案是:没有绝对的好坏,只有适不适合

下面这张表格,是我根据自己的项目经验总结的:

对比维度 GCC NADA
主要信号 延迟梯度(主)、丢包(辅) 延迟 + 丢包(联合)
码率调整方式 基于延迟梯度的 AIMD 基于拥塞指数的 AIMD
对丢包的敏感度 较低,丢包只触发乘性减 较高,丢包直接参与计算
适用场景 有线网络、低丢包环境 无线网络、高丢包环境
实现复杂度 中等 稍高(需要融合计算)

你想想看,GCC 的设计思路是“延迟优先”,它认为延迟是拥塞的最早信号。而 NADA 则认为,丢包和延迟同样重要,甚至在某些场景下,丢包比延迟更能反映网络状态。

我个人在实际项目中的感受是:如果你的网络环境比较干净,丢包率常年低于 1%,GCC 完全够用。但如果你的用户经常在移动网络、Wi-Fi 信号差的环境下使用,NADA 会更稳

17.5 NADA 的算法流程

为了让你更直观地理解 NADA 的工作流程,我画了一张图:

NADA 算法核心流程 接收端反馈 计算延迟梯度 计算丢包率 融合计算拥塞指数 调整发送码率

嗯,这张图其实已经说得很清楚了。NADA 的流程比 GCC 多了一步“融合计算”,这也是它更灵活的原因。

17.6 实际部署中的注意事项

最后,我想分享几个我在实际部署 NADA 时遇到的坑:

  • 反馈频率不能太低:NADA 对反馈的依赖比 GCC 更强。如果接收端的 RTCP 报告间隔太长,NADA 的拥塞指数会滞后,导致码率调整不及时。我一般建议反馈间隔不超过 100ms。
  • 丢包统计窗口要合理:窗口太小,丢包率波动大;窗口太大,反应慢。我个人习惯用 500ms 到 1s 的窗口。
  • 不要和 GCC 混用:有些同学想“取长补短”,把 NADA 和 GCC 的算法混在一起用。结果往往是两边打架,码率忽高忽低。我的建议是:选一个,用到底。

重要提醒:NADA 虽然对丢包更敏感,但并不意味着它适合所有高丢包场景。如果丢包率超过 15%,任何拥塞控制算法都救不了你。这时候应该先解决网络问题,而不是指望算法。

好了,NADA 的核心内容就这些。它和 GCC 的差异,说白了就是“延迟优先”和“延迟+丢包联合”的区别。没有谁比谁更强,只有谁更适合你的场景。


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