通过上次的​《RTC 系统音视频传输弱网对抗技术概览》​,我们知道 RTC 的三大核心指标为实时性、清晰度、流畅度。在整个通话过程中核心表现达标,才能给用户一个基本的良好体验。关注【融云全球互联网通信云】了解更多

然而音视频数据在网络中传输时,网络是变化多端和不可预知的,比如地铁、公交、 家里、公司公用 WiFi,以及不同时间段流量高峰等造成的网络变化都不一样,这些都会造成网络的抖动、带宽容量动态变化等,最终影响音视频的体验。

因此,我们需要对网络带宽变化进行有效的动态探测和评估,保证音视频链路上发送的音视频数据流不超过链路容量上限。否则,就会带来大量丢包,难以恢复,最终造成通信一端或双方视频卡顿、语音卡顿、丢字等问题。 

本文主要分享音视频传输弱网问题中,针对网络拥塞的对抗技术。


典型动态带宽探测方法


网络拥塞研究不是一个新的话题,前后有近四十年的历史,由于以前内存比较昂贵,传统的动态带宽探测一般是基于丢包拥塞的方法,这种方法不能在丢包之前发现网络已经处于拥塞状态,当探测到丢包时,实际上音视频体验就已经出现了卡顿问题,不太适合音视频应用。 

随着内存价格的降低,现在当网络拥塞时,一般会把发送不过来的数据包先暂存到网络队列内存 buffer 中,待后续发送,只有当 buffer 满了才会丢弃包。

利用网络队列 buffer 较大这个特性,现在比较典型的方法是基于延迟梯度估计和丢包相结合的带宽估计方法


有三种比较经典方案:

GCC 算法[1] 是出自 Google 的一种延时估计和丢包相结合的拥塞控制算法,在 WebRTC 中被默认使用。

NADA 算法[2] 是思科公司提出的一种基于延时估计的方法,这种算法带宽利用率高,跟踪带宽变化方面表现很优秀。 

SCReAM 算法[3] 是爱立信公司提出的一种基于延时估计的算法,在 OpenWebRTC 中被采用。 

这篇论文[4] 比较了以上三种算法的效果:


多路 GCC 能够均衡利用整个带宽,但在动态链接中具有锯齿特征,收敛比 NADA 缓慢;在有损链路方面具有更好的性能,这也使得它特别适用于无线网络。

NADA 可以在动态链路中快速稳定速率,收敛较快,在链路不受随机丢包影响时,带宽利用率高,但若遇随机丢包情况,带宽利用率会变得很低;同时受“后来者效应”影响,表现为后来者会使用更多的带宽,在带宽均衡使用方面不如 GCC。

SCReAM 将链路队列延迟保持在低水平,但带宽利用率低,且对带宽变化比较迟钝。 

总体来说,GCC 更优越一些。


GCC 算法为 Google 提出的基于延时梯度估计和丢包的拥塞控制算法,在 WebRTC 中得到了⼴泛利用,目前为 WebRTC 默认使用的算法。

该算法有两个版本,一个版本是分布在发送端和接收端,接收端通过对延迟进行估计,并通过 REMB RTCP 报文反馈评估出来的带宽;发送端则根据丢包率和接收端反馈的带宽来计算最终的代码码率,进而调整编码器编码码率,这个框架将其称之为 REMB- GCC。

另一个版本是所有估算都放在发送端,即基于延迟带宽估计和丢包率估计都放在发送端,接收端通过定时向发送端反馈 Transport-wide RTCP 包,告知发送端的收包情况,这个方法一般称之为 TFB-GCC

两种算法的框架原理基本一致。TFB-GCC 比 REMB-GCC 更优,也是 WebRTC 后续推荐使用的拥塞控制算法,下面对这两种算法做详细分析。


REMB-GCC 算法


弱网优化,GCC 动态带宽评估算法(内附详细公式)_链路

(REMB-GCC 算法架构图)


基于 REMB 方式的 GCC 算法,分为两大部分,发送端部分和接收端部分。

接收端部分负责基于时间延时梯度的变化来评估接收端的带宽变化,这部分主要分五个子部分, Arrival filter、Adaptive threshold、Overuse Detector、Remote Rate Controller、Remb Processing;

接收端最终评估出来的带宽将通过 REMB RTCP 包反馈到发送端,发送端结合丢包率及反馈的带宽计算最终的带宽,这个最终带宽将用来调整发送端编码器编码带宽,FEC 和 pacing 带宽及重传包的带宽,下面详细介绍各个模块。


接收端模块


Arrival filter

以 RTP 传输的包,每帧可能被分成多个 RTP 包发送,每个发送的 RTP 包携带了一个 RTP 扩展,称其为 abs-send-time 扩展,这个扩展记录了发送端发送该包时的时间信息;接收端在接收每个 RTP 包时,也会记录该包的到达时间。

该算法将通过如下(公式 1)计算相邻两帧的接收时间差和发送时间差之间的间隔,这个间隔即为该帧在网络传输的时间上的⼀个变化,这个变化包含如下三个部分内容:

① 该帧数据在网络传输时间相对于上⼀帧在网络传输时间的变化,这是包 size 变化方面的度量

② 包在网络队列排队的时间变化

③ 网络噪声干扰

这三个方面的变化,在测量上来看,都体现在以下公式 1 及示意图中。

公式 1:dmi = ti − ti -1 − (Ti − Ti-1 ) 

弱网优化,GCC 动态带宽评估算法(内附详细公式)_链路_02

(公式 1 示意图)


需要说明的是:

① ti 表示第 i 帧的最后⼀个 RTP 包收到的时间

② ti-1 表示第 i-1 帧的最后⼀个 RTP 包收到的时间

③ Ti 表示第 i 帧的第⼀个 RTP 包发送的时间

④ Ti-1 表示第 i-1 帧的第⼀个 RTP 包发送的时间

⑤ RTP 包的发送时间都是携带在 RTP 包的 abs-send-time 扩展中


延时梯度估计的另一个表示方法为:

公式 2dm= dLi/Ci + mi + ni

C为接收第 i 帧时刻的链路容量估计,dLi/C为接收第 i 帧时刻包在网络中传输的时间变化,主要受包 size 大小的变化和链路容量变化影响,这个也可以作为评估抖动的⼀个关键指标。

n为接收第 i 帧时刻网络 jitter 引入的噪声,m为接收第 i 帧时刻网络队列延时深度变化估计,dL表示第 i 帧和 i − 1 帧两相邻包的大小差,按如下公式 3 计算:

公式 3dL L−Li-1

获取到 dmi 和 dL两个样本数据后,将它们作为 kalman 滤波器的输入,利用 kalman 滤波器来估测延时梯度的变化 mi


补充说明:

在延时带宽估计中, 使⽤ kalman 滤波器对输入样本进行估计,但只用到了网络队列延时变化这⼀个指标。

其实,还可以估计网络抖动,在 video jitter buffer 中就使用同样的方法进行了网络抖动的估计,只是延时带宽估计这块只用了“包在网络队列排队延时的变化这⼀个指标而已”,没有用抖动;而 jitter buffer 中只⽤ kalman 滤波器估算抖动。

所以在抗抖动问题上,可以尝试使用这些指标进行相关优化⼯作,融云也将在这方面进行持续优化。


因此,Arrival filter 的功能就是利用 kalman 滤波器,通过测量得到的 dmi 和 dLi 估算出 mi,它反应了网络链路包进入 buffer 队列变化情况;同时 mi 也将作为 Adaptive threshold 和 Overuse Detector 兄弟模块的输入。


卡尔曼滤波(Kalman filter)是一种⾼效率的递归滤波器(自回归滤波器),它能够从一系列的不完全及包含噪声的测量中,估计动态系统的状态。

卡尔曼滤波会根据各测量量在不同时间下的值,考虑各时间下的联合分布,再产生对未知变数的估计,因此会比只以单一测量量为基础的估计方式要准。


Adaptive threshold

该模块根据 Arrival filter 估测的 mi 来定时更新阈值 γi,mi 和 γi 将作为 Overuse Detector 模块的输入,评估当前网络是否处于过载状态。

阈值 γi 是⼀个动态调整的过程,计算如下:

公式 4γ= γ+ ∆T ⋅ kγi(∣mi∣−γi)

公式 5∆T = ti − ti -1

公式 6kd 建议值为 0.039,ku 建议值为 0.0087

弱网优化,GCC 动态带宽评估算法(内附详细公式)_接收端_03

Overuse Detector

该子模块根据前两个模块输出的阈值和队列延时变化估计来评估当前网络是否处于过载状态。

m大于 0,表示网络中的队列深度正在增加,说明链路中的包发送量在增加,延时变大,当不做任何处理时,将出现网络队列变满而导致大量丢包;

mi 等于 0,表示当前网络发送延时没有变化,网络发送量没有队列;

mi 小于 0, 表示当前网络队列正在减少,网络拥塞在改善。


算法通过比较 mi 和 的 γ值来判断当前网络负载状况,如下公式 7 和图所示:

弱网优化,GCC 动态带宽评估算法(内附详细公式)_链路_04弱网优化,GCC 动态带宽评估算法(内附详细公式)_链路_05

(公式 7 示意图)


Remote Rate Controller

该子模块将根据 Overuse Detector 模块输出的网络过载状态来调整带宽。

GCC 维护了 increase、decrease 、hold 三个状态,三个状态的转换关系如下图所示:

弱网优化,GCC 动态带宽评估算法(内附详细公式)_链路_06

(GCC 维护的三个状态关系)


这三种状态下的带宽调整策略如下公式 8 所示:

弱网优化,GCC 动态带宽评估算法(内附详细公式)_链路_07


Ri 表示接收第 i 帧时刻,统计出来的接收端实际收包带宽

Ar表示接收第 i 帧时, 基于延时估计估算出来的网络带宽


在 increase 状态时,在上⼀次估算带宽基础上提升  8%,但实际值不超过接收码率的 1.5 倍,即不超过 1.5 ∗ Ri

在过载情况下,需要降低码率,并且以实际接收端接收的码率为参考,按其 0.85 倍为当前估算带宽,即 0.85 ∗ Ri

这样可快速降低发送端带宽,恢复网络拥塞状态。


Remb Processing[5]

Remote Rate Controller 子模块计算出最终的接收端的评估带宽后,将通过 REMB RTCP 报文反馈到发送端,用来告知发送端接收端评估的链路带宽。REMB 的报文格式如下所示:

弱网优化,GCC 动态带宽评估算法(内附详细公式)_接收端_08

该消息中包含如下几个信息:

  • 反馈消息类型(FMT)为 15 
  • 有效载荷类型(PT)为 206
  • 该报文发送端的 SSRC
  • 媒体源 SSRC, 一般为 0 
  • 标识为“REMB” 
  • 接收包的 SSRC 条数 
  • 估算带宽值 
  • 估算该带宽链路上接收的媒体流 SSRC,1 个或多个

GCC 算法在反馈 REMB RTCP 报文时,一般是每隔 200ms 发送一次,当探测到带宽过载时,探测的带宽小于上次带宽的 95%, 则立即反馈。目的是快速降低,平稳上升。


发送端模块

发送端主要是根据丢包率修正带宽,目的是:当延迟估计模块带宽没有及时调整发送端带宽,拥塞还存在时,能够基于丢包来调整带宽。

发送端最终的带宽将结合丢包调整的带宽和 REMB 反馈过来的带宽,取两者中较小的值。基于丢包率调整带宽的逻辑如下公式 9 及公式 10:

弱网优化,GCC 动态带宽评估算法(内附详细公式)_接收端_09

式 10:Ai = min(Asi, Ari)

A为第 i 帧时刻最终 GCC 根据发送端和接收端估算出来的带宽,并为发送端基于丢包计算的带宽和接收端基于延时估计的带宽的较小值。

编码器带宽、发送端 pacing 带宽、重传带宽都将参考该带宽进行调整,一般编码器带宽为:max(0.5 ∗ Ai,Ai − FecRi − RtxRi) , pacing 带宽为 2.0 ∗ Ai,重传带宽为 1.5 ∗ Ai。

注 : As为第 i 帧时刻发送端根据丢包率估算的带宽值,fli 为第 i 帧时刻接收到接收端反馈的丢包率。

REMB-GCC 算法总结​

REMB-GCC 算法目前已经被 Google 放弃维护,由于其分布发布端和接收端, 需要发送端和接收端相互配合,而且使用了 kalman 滤波估算延时梯度变化,实际使用中存在一些问题,如 kalman 滤波估算不够准确且复杂,接收端和发送端同时参与带宽估算不如都放在一端进行估算方便、准确和快速。

因此,Google 在 WebRTC 后续版本中使用了 TFB-GCC 替代 REMB-GCC。


TFB-GCC 算法

弱网优化,GCC 动态带宽评估算法(内附详细公式)_链路_10

(TFB-GCC 算法架构图)


从上图可以看到,对带宽的估算大部分工作都放在发送端,接收端仅做两件事,一个是定期反馈  transport-wide-seqnumber rtcp 包和丢包率 fl,fl 这里描述的丢包率是 RR 反馈的,当有多路流时实际计算是有 SR 和 RR ⼀起计算出来的。基于延迟估计和基于丢包估计都放在发送端处理了,基于丢包估计和 REMB-GCC 一样,没有变化;基于延迟估计主要是用 TrendLine filter 替换了 kalman filter。

Transport-wide sequence number[7]

发送端发送的 RTP 包会携带⼀个扩展头 Transport-wide sequence number,扩展头内容如下:

弱网优化,GCC 动态带宽评估算法(内附详细公式)_丢包_11

这里是单个字节表示扩展头(0xBEDE)为标识符,length = 1,表示该扩展占有 4 个字节,L=1,表示 Transport-wide sequence number 占 2 个字节。

当发送端每发出一个包,都会将该包的该扩展字段的 Transport-wide sequence number 累加 1,需要注意的是,当发送端发送多路流时(每路流的 SSRC 不⼀样),所有流的 RTP 包的该扩展字段都是连续计数的,不会分开独立计数。该扩展头的作用是为了标识发送的包和反馈的包对应关系。

接收端反馈 Transport-wide RTCP 包[6]

接收端在 TFB-GCC 框架下,主要是定期发送 Transport-wide 反馈包,用来告知发送端,接收端收包相关信息,包括包的到达情况及包的到达时间等信息,其消息格式及核心字段解析如下:

弱网优化,GCC 动态带宽评估算法(内附详细公式)_链路_12

base sequence number:此反馈中第⼀个数据包的传输范围序列号,该数字不⼀定会随着每个反馈增加,在重新排序的情况下,它可能会减少。

packet status count:此反馈包含多少个 RTP 数据包的数量,从由基本序列号标识的数据包开始;比如记录的第一个 RTP 包的 transport sequence number 为 base sequence number,那么记录的第二个 RTP 包 transport sequence number 为 base sequence number + 1。

reference time:表示参考时间,以 64ms 为单位,RTCP 包记录的 RTP 包到达时间信息以这个 reference time 为基准进行计算。此数据包中的第⼀个 recv 增量是相对于参考时间的。即使某些反馈数据包丢失,参考时间也可以计算反馈之间的增量,因为它始终使用相同的时基。 

feedback packets count:用于记录接收端发送的 Transport-wide 反馈包的个数,每发送⼀个反馈数据包,计数器就加一。这个字段可用于检测反馈包是否丢失。

packet chunk:数据包状态块列表,用来指示数据包到达的状态,指示的 RTP 数据包范围是从基本序列号标识的数据包开始的多个数据包。 

recv delta:对于 packet chunk 中的“packet received”状态的包,也就是收到的 RTP 包,在 recv delta 列表中添加对应的的到达时间间隔信息,用于记录 RTP 包到达时间信息。通过前面的基整时间以及 recv delta,发送端可以计算出该 RTP 包在接收端的到达时间。

Delay-based controller

该模块为基于延时估计带宽模块,等同于 REMB-GCC 的接收端部分。

具体包含了 ATF(等同于 REMB-GCC 中的 Arrival filter/Adaptive threshold)/Overuse Dectector/Remote Rate Controller。

ATF

其主要功能是估算延时梯度变化 mi,它根据输入 dmi,利用 Trendline 滤波器中的最小二乘法对 mi 做最优估计 , Trendline 最小二乘法过程如下:

公式 11:dmi = ti − ti -1 − (Ti − Ti-1 ) 

注:ti 和 ti -1是通过客户端反馈的 Transport-wide RTCP 包的到达时间获取,发送端在发送 RTP 数据包时,会为每个 Transport-wide sequence number RTP 包记录⼀个发送时间 T。

以下公式 12 描述累计的队列时间延时:

弱网优化,GCC 动态带宽评估算法(内附详细公式)_丢包_13

以下公式 13 为平滑累计队列时间延时:

公式 13:

smoothedDelay=smoothingCoef∗smoothedDelayi_1 +(1−smoothingCoef)∗accuDelayi

以下公式 14 按照第 i 帧的收包相对时间和平滑时间延迟来构造了⼆元组:

(xi, yi) ⇒ (ti − t1 , smoothedDelayi)

二元组将按照以下公式 15 来估算 mi

弱网优化,GCC 动态带宽评估算法(内附详细公式)_接收端_14

这里的 TrendlineSlope 即为 mi

趋势线斜率是链路队列状态的反映。当链路队列长度增加时,数据包到达间隔也趋于增加,当小于 0 时,表明链路队列正在缩小;数据包到达间隔也在减少;等于 0 表示数据包到达间隔恒定。

Adaptive threshold 和 REMB-GCC ⼀样,即同公式 4。


Overuse Detector

根据上节计算出来的 mi 和阈值 γi 判断当前网络的状态,是否过载、低负载还是正常,根据网络状态确定接下来对带宽估计的调整,是增加、降低还是保存不变,这个和 REMB-GCC ⼀样。

Remote Rate Controller

这⾥根据 Overuse Detector 输出的结果,来调整估算带宽。与 REMB-GCC 不同的是,这里主要使用 AIMD 方式调整带宽,即当增加估算带宽时,可以柔和⼀些,也可以激进⼀些。

由于 TFB-GCC 会跟踪每次链路过载时的带宽,当当前实际接收带宽离之前链路估算带宽近时,需要增加带宽时,就使用柔和方式增加一些带宽,增加幅度小;

当当前实际接收带宽离链路带宽变差很大时,采用激进方式增加带宽,即增加幅度大;

当识别网络过载时,需要降低带宽,以当前实际带宽的 0.85 倍作为估算带宽。

最终基于延时估计计算出来的带宽为 Ari

基于丢包的网络拥塞带宽估计

这里和 REMB-GCC 的丢包网络拥塞估计一样, 为了防止基于延时估计失效,通过接收端反馈的 RR 来计算丢包率 fli,利用 fli 来估计拥塞状态,这里同样使用公式 9 来确定最终带宽 Asi,最终估算的带宽 Ai 计算如下:

公式 16:Ai = min(Asi, Ari)

TFB-GCC 算法总结

采用此架构的 GCC 算法,由于估算带宽都放在发送端,不需要接收端的同步优化,方便后续版本的部署和优化。准确性和及时性相对更高更好,同时使用 Trendline 滤波器,较 kalman 滤波器简单且准确性更高,灵敏性也更高。

由于考虑了对每次过载时链路带宽的估计,在增加带宽时,表现得更加灵活和安全。 

论文[4] 给出了 TFB-GCC 和 REMB-GCC 的一个带宽受限的比较,可以看出 TFB-GCC 效果较 REMB-GCC 恢复更快,跟随链路带宽更准确,整体效果更好。

弱网优化,GCC 动态带宽评估算法(内附详细公式)_丢包_15

(TFB-GCC 与 REMB-GCC 比较)


GCC 算法优化点


REMB-GCC 优化点​

  • 接收端使用 TrendLine 中的滤波器替换 kalman 滤波器
  • 过载逻辑判断优化,剔除噪点引入误判
  • 实际接收带宽优化
  • 基于丢包拥塞估计分场景优化
  • 基于延迟估计带宽增加或降低计算算法优化
  • 针对过载可能不会降带宽进行优化
  • 针对当延时估计带宽一直处于上升,而实际接收带宽却起伏波动,可能造成加大累计误差问题进行优化
  • ……


TFB-GCC 优化点

  • 根据 Transport-wide RTCP 报文,估算接收带宽优化
  • Trendline 滤波器优化
  • 根据 Transport-wide RTCP 包统计丢包率优化
  • 延时估计中使用 RTT 优化
  • 过载逻辑优化及阈值优化
  • 过载可能被忽略逻辑优化
  • AIMD 带宽计算等相关逻辑优化
  • 链路带宽估计优化
  • 基于丢包拥塞带宽估计优化
  • TFB-GCC 发送端⽀持接收 REMB 优化
  • ……

参考资料:

[1] https://datatracker.ietf.org/doc/html/draft-ietf-rmcat-gcc-02

[2] https://datatracker.ietf.org/doc/html/rfc8698

[3] https://www.rfc-editor.org/rfc/rfc8298.html

[4] Congestion Control for RTP Media: a Comparison on Simulated Environment

[5] https://tools.ietf.org/id/draft-alvestrand-rmcat-remb-03.html

[6]: https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01

[7]: Congestion Control for Real-time Communications: a comparison between NADA and GCC