转自;http://blog.csdn.net/ljh081231/article/details/79152578算法
本文在文章[1]的基础上,从源代码实现角度对WebRTC的GCC算法进行分析。主要内容包括: RTCP RR的数据源、报文构造和接收,接收端基于数据包到达延迟的码率估计,发送端码率的计算以及生效于目标模块。网络
拥塞控制是实时流媒体应用的重要服务质量保证。经过本文和文章[1][2],从数学基础、算法步骤到实现细节,对WebRTC的拥塞控制GCC算法有一个全面深刻的理解,为进一步学习WebRTC奠基良好基础。框架
本节内容基本上是文章[1]第1节的复习,目的是再次复习GCC算法的主要框架,梳理其算法流程中的数据流和控制流,以此做为后续章节的行文提纲。GCC算法的数据流和控制流如图1所示。异步
对发送端来说,GCC算法主要负责两件事:1)接收来自接收端的数据包信息反馈,包括来自RTCP RR报文的丢包率和来自RTCP REMB报文的接收端估计码率,综合本地的码率配置信息,计算获得目标码率A。2)把目标码率A生效于目标模块,包括PacedSender模块,RTPSender模块和ViEEncoder模块等。tcp
对于接收端来说,GCC算法主要负责两件事:1)统计RTP数据包的接收信息,包括丢包数、接收RTP数据包的最高序列号等,构造RTCP RR报文,发送回发送端。2)针对每个到达的RTP数据包,执行基于到达时间延迟的码率估计算法,获得接收端估计码率,构造RTCP REMB报文,发送回发送端。ide
因而可知,GCC算法由发送端和接收端配合共同实现,接收端负责码率反馈数据的生成,发送端负责根据码率反馈数据计算目标码率,并生效于目标模块。本文接下来基于本节所述的GCC算法的四项子任务,分别详细分析之。函数
关于WebRTC上的RTP/RTCP协议的具体实现细节,可参考文章[3]。本节主要从RR报文的数据流角度,对其数据源、报文构造和收发进行分析。其数据源和报文构造如图2所示,报文接收和做用于码率控制模块如图3所示。学习
在数据接收端,RTP数据包从Network线程到达Worker线程,通过Call对象,VideoReceiveStream对象到达RtpStreamReceiver对象。在该对象中,主要执行三项任务:1)接收端码率估计;2) 转发RTP数据包到VCM模块;3)接收端数据统计。其中1)是下一节的重点,2)是RTP数据包进一步组帧和解码的地方;3)是统计RTP数据包接收信息,做为RTCP RR报文和其余数据统计模块的数据来源,是咱们本节重点分析的部分。ui
在RtpStreamReceiver对象中,RTP数据包通过解析获得头部信息,做为输入参数调用ReceiveStatistianImpl::IncomingPacket()。该函数中分别调用UpdateCounters()和NotifyRtpCallback(),前者用来更新对象内部的统计信息,如接收数据包计数等,后者用来更新RTP回调对象的统计信息,该信息用来做为getStats调用的数据源。编码
RTCP发送模块在ModuleProcess线程中工做,RTCP报文周期性发送。当线程判断须要发送RTCP报文时,调用SendRTCP()进行发送。接下来调用PrepareReport()准备各种型RTCP报文的数据。对于咱们关心的RR报文,会调用AddReportBlock()获取数据源并构造ReportBlock对象:该函数首先经过ReceiveStatistianImpl::GetStatistics()拿到类型为RtcpStatistics的数据源,而后以此填充ReportBlock对象。GetStatistics()会调用CalculateRtcpStatistics()计算ReportBlock的每一项数据,包括丢包数、接收数据包最高序列号等。ReportBlock对象会在接下来的报文构造环节经过BuildRR()进行序列化。RTCP报文进行序列化以后,交给Network线程进行网络层发送。
在发送端(即RTCP报文接收端),RTCP报文通过Network线程到达Worker线程,最后到达ModuleRtpRtcpImpl模块调用IncomingRtcpPacket()进行报文解析工做。解析完成之后,调用TriggerCallbacksFromRTCPPackets()反馈到回调模块。在码率估计方面,会反馈到BitrateController模块。ReportBlock消息最终会到达BitrateControllerImpl对象,进行下一步的目标码率肯定。
至此,关于RTCP RR报文在拥塞控制中的执行流程分析完毕。
接收端基于数据包到达延迟的码率估计是整个GCC算法最复杂的部分,本节在分析WebRTC代码的基础上,阐述该部分的实现细节。
接收端基于延迟码率估计的基本思想是:RTP数据包的到达时间延迟m(i)反映网络拥塞情况。当延迟很小时,说明网络拥塞不严重,能够适当增大目标码率;当延迟变大时,说明网络拥塞变严重,须要减少目标码率;当延迟维持在一个低水平时,目标码率维持不变。其主要由三个模块组成:到达时间滤波器,过载检查器和速率控制器。
在实现上,WebRTC定义该模块为远端码率估计模块RemoteBitrateEstimator,整个模块的工做流程如图4所示。须要注意的是,该模块须要RTP报文扩展头部abs-send-time的支持,用以记录RTP数据包在发送端的绝对发送时间,详细请参考文献[4]。
接收端收到RTP数据包后,通过一系列调用到RtpStreamReceiver对象,由该对象调用远端码率估计模块的总控对象RemoteBitrateEstimatorAbsSendTime,由该对象的总控函数IncomingPacketInfo()负责整个码率估计流程,如图4所示,算法从左到右依次调用子对象的功能函数。
总控函数首先调用InterArrival::ComputeDeltas()函数,用以计算相邻数据包组的到达时间相对延迟,该部分对应文章[1]的3.1节内容。在计算到达时间相对延迟时,用到了RTP报文头部扩展abs-send-time。另外,实现细节上要注意数据包组的划分,以及对乱序和突发时间的处理。
接下来算法调用OveruseEstimator::Update()函数,用以估计数据包的网络延迟,该部分对应文章[1]的3.2节内容。对网络延迟的估计用到了Kalman滤波,算法的具体细节请参考文章[2]。Kalman滤波的结果为网络延迟m(i),做为下一阶段网络状态检测的输入参数。
算法接着调用OveruseDetector::Detect(),用来检测当前网络的拥塞情况,该部分对应文章[1]的3.2节内容。网络状态检测用当前网络延迟m(i)和阈值gamma_1进行比较,判断出overuse,underuse和normal三种网络状态之一。在算法细节上,要注意overuse的断定相对复杂一些:当m(i) > gamma_1时,计算处于当前状态的持续时间t(ou),若是t(ou) > gamma_2,而且m(i) > m(i-1),则发出网络过载信号Overuse。若是m(i)小于m(i-1),即便高于阀值gamma_1也不须要发出过载信号。在断定网络拥塞状态以后,还要调用UpdateThreshold()更新阈值gamma_1。
算法接着调用AimdRateControl::Update()和UpdateBandwidthEstimate()函数,用以估计当前网络状态下的目标码率Ar,该部分对应文章[1]的3.3节。算法基于当前网络状态和码率变化趋势有限状态机,采用AIMD(Additive Increase Multiplicative Decrease)方法计算目标码率,具体计算公式请参考文章[1]。须要注意的是,当算法处于开始阶段时,会采用Multiplicative Increase方法快速增长码率,以加快码率估计速度。
此时,咱们已经拿到接收端估计的目标码率Ar。接下来以Ar为参数调用VieRemb对象的OnReceiveBitrateChange()函数,发送REMB报文到发送端。REMB报文会推送到RTCP模块,并设置REMB报文发送时间为当即发送。关于REMB报文接下来的发送和接收流程,和第1节描述的RTCP报文通常处理流程是同样的,即通过序列化发送到网络,而后发送端收到之后,反序列化出描述结构,最后经过回调函数到达发送端码率控制模块BitrateControllerImpl。
至此,接收端基于延迟的码率估计过程描述完毕。
在发送端,目标码率计算和生效是异步进行的,即Worker线程从RTCP接收模块经回调函数拿到丢包率和REMB码率以后,计算获得目标码率A;而后ModuleProcess线程异步把目标码率A生效到目标模块如PacedSender和ViEEncoder等。下面分别描述码率计算和生效过程。
码率计算过程如图5所示:Worker线程从RTCPReceiver模块通过回调函数拿到RTCP RR报文和REMB报文的数据,到达BitrateController模块。RR报文中的丢包率会进入Update()函数中计算码率,码率计算公式如文章[1]第2节所述。而后算法流程进入CapBitrateToThreshold()函数,和配置的最大最小码率和远端估计码率进行比较后,肯定最终目标码率。而REMB报文的接收端估计码率Ar则直接进入CapBitrateToThreshold()函数参与目标码率的肯定。目标码率由文章[1]的3.4节所示公式进行肯定。须要注意的是,RR报文和REMB报文通常不在同一个RTCP报文里。
发送端码率生效过程如图6所示:ModuleProcess线程调用拥塞控制总控对象CongestionController周期性从码率控制模块BitrateControllerImpl中获取当前最新目标码率A,而后判断目标码率是否有变化。如果,则把最新目标码率设置到相关模块中,主要包括PacedSender模块,RTPSender模块和ViEEncoder模块。
对于PacedSender模块,设置码率主要是为了平滑RTP数据包的发送速率,尽可能避免数据包Burst形成码率波动。对于RTPSender模块,设置码率是为了给NACK模块预留码率,若是预留码率太小,则在某些状况下对于NACK报文请求选择不响应。对于ViEEncoder模块,设置码率有两个用途:1)控制发送端丢帧策略,根据设定码率和漏桶算法决定是否丢弃当前帧。2)控制编码器内部码率控制,设定码率做为参数传输到编码器内部,参与内部码率控制过程。
至此,发送端码率计算和生效过程分析完毕。
本文结合文章[1],深刻WebRTC代码内部,详细分析了WebRTC的GCC算法的实现细节。经过本文,对WebRTC的代码结构和拥塞控制实现细节有了更深层次的理解,为进一步学习WebRTC奠基良好基础。