WebRTC基於GCC的擁塞控制(下) - 實現分析

本文在文章[1]的基礎上,從源代碼實現角度對WebRTC的GCC算法進行分析。主要內容包括: RTCP RR的數據源、報文構造和接收,接收端基於數據包到達延遲的碼率估計,發送端碼率的計算以及生效於目標模塊。</br>

擁塞控制是實時流媒體應用的重要服務質量保證。通過本文和文章[1][2],從數學基礎、算法步驟到實現細節,對WebRTC的擁塞控制GCC算法有一個全面深入的理解,爲進一步學習WebRTC奠定良好基礎。</br>

1 GCC算法框架再學習

</br>
</br>本節內容基本上是文章[1]第1節的複習,目的是再次複習GCC算法的主要框架,梳理其算法流程中的數據流和控制流,以此作爲後續章節的行文提綱。GCC算法的數據流和控制流如圖1所示。</br>

圖1 GCC算法數據流和控制流

對發送端來講,GCC算法主要負責兩件事:1)接收來自接收端的數據包信息反饋,包括來自RTCP RR報文的丟包率和來自RTCP REMB報文的接收端估計碼率,綜合本地的碼率配置信息,計算得到目標碼率A。2)把目標碼率A生效於目標模塊,包括PacedSender模塊,RTPSender模塊和ViEEncoder模塊等。</br>

對於接收端來講,GCC算法主要負責兩件事:1)統計RTP數據包的接收信息,包括丟包數、接收RTP數據包的最高序列號等,構造RTCP RR報文,發送回發送端。2)針對每一個到達的RTP數據包,執行基於到達時間延遲的碼率估計算法,得到接收端估計碼率,構造RTCP REMB報文,發送回發送端。</br>

由此可見,GCC算法由發送端和接收端配合共同實現,接收端負責碼率反饋數據的生成,發送端負責根據碼率反饋數據計算目標碼率,並生效於目標模塊。本文接下來基於本節所述的GCC算法的四項子任務,分別詳細分析之。</br>

2 RTCP RR報文構造及收發

</br>
</br>關於WebRTC上的RTP/RTCP協議的具體實現細節,可參考文章[3]。本節主要從RR報文的數據流角度,對其數據源、報文構造和收發進行分析。其數據源和報文構造如圖2所示,報文接收和作用於碼率控制模塊如圖3所示。</br>

在數據接收端,RTP數據包從Network線程到達Worker線程,經過Call對象,VideoReceiveStream對象到達RtpStreamReceiver對象。在該對象中,主要執行三項任務:1)接收端碼率估計;2) 轉發RTP數據包到VCM模塊;3)接收端數據統計。其中1)是下一節的重點,2)是RTP數據包進一步組幀和解碼的地方;3)是統計RTP數據包接收信息,作爲RTCP RR報文和其他數據統計模塊的數據來源,是我們本節重點分析的部分。</br>

在RtpStreamReceiver對象中,RTP數據包經過解析得到頭部信息,作爲輸入參數調用ReceiveStatistianImpl::IncomingPacket()。該函數中分別調用UpdateCounters()和NotifyRtpCallback(),前者用來更新對象內部的統計信息,如接收數據包計數等,後者用來更新RTP回調對象的統計信息,該信息用來作爲getStats調用的數據源。</br>

圖2 RTCP RR報文數據源及報文構造

RTCP發送模塊在ModuleProcess線程中工作,RTCP報文週期性發送。當線程判斷需要發送RTCP報文時,調用SendRTCP()進行發送。接下來調用PrepareReport()準備各類型RTCP報文的數據。對於我們關心的RR報文,會調用AddReportBlock()獲取數據源並構造ReportBlock對象:該函數首先通過ReceiveStatistianImpl::GetStatistics()拿到類型爲RtcpStatistics的數據源,然後以此填充ReportBlock對象。GetStatistics()會調用CalculateRtcpStatistics()計算ReportBlock的每一項數據,包括丟包數、接收數據包最高序列號等。ReportBlock對象會在接下來的報文構造環節通過BuildRR()進行序列化。RTCP報文進行序列化之後,交給Network線程進行網絡層發送。</br>

圖3 RTCP RR報文接收及反饋

在發送端(即RTCP報文接收端),RTCP報文經過Network線程到達Worker線程,最後到達ModuleRtpRtcpImpl模塊調用IncomingRtcpPacket()進行報文解析工作。解析完成以後,調用TriggerCallbacksFromRTCPPackets()反饋到回調模塊。在碼率估計方面,會反饋到BitrateController模塊。ReportBlock消息最終會到達BitrateControllerImpl對象,進行下一步的目標碼率確定。</br>

至此,關於RTCP RR報文在擁塞控制中的執行流程分析完畢。</br>

3 接收端基於延遲的碼率估計

</br>
</br>接收端基於數據包到達延遲的碼率估計是整個GCC算法最複雜的部分,本節在分析WebRTC代碼的基礎上,闡述該部分的實現細節。</br>

接收端基於延遲碼率估計的基本思想是:RTP數據包的到達時間延遲m(i)反映網絡擁塞狀況。當延遲很小時,說明網絡擁塞不嚴重,可以適當增大目標碼率;當延遲變大時,說明網絡擁塞變嚴重,需要減小目標碼率;當延遲維持在一個低水平時,目標碼率維持不變。其主要由三個模塊組成:到達時間濾波器,過載檢查器和速率控制器。</br>

在實現上,WebRTC定義該模塊爲遠端碼率估計模塊RemoteBitrateEstimator,整個模塊的工作流程如圖4所示。需要注意的是,該模塊需要RTP報文擴展頭部abs-send-time的支持,用以記錄RTP數據包在發送端的絕對發送時間,詳細請參考文獻[4]。</br>

圖4 GCC算法基於延遲的碼率估計

接收端收到RTP數據包後,經過一系列調用到RtpStreamReceiver對象,由該對象調用遠端碼率估計模塊的總控對象RemoteBitrateEstimatorAbsSendTime,由該對象的總控函數IncomingPacketInfo()負責整個碼率估計流程,如圖4所示,算法從左到右依次調用子對象的功能函數。</br>

總控函數首先調用InterArrival::ComputeDeltas()函數,用以計算相鄰數據包組的到達時間相對延遲,該部分對應文章[1]的3.1節內容。在計算到達時間相對延遲時,用到了RTP報文頭部擴展abs-send-time。另外,實現細節上要注意數據包組的劃分,以及對亂序和突發時間的處理。</br>

接下來算法調用OveruseEstimator::Update()函數,用以估計數據包的網絡延遲,該部分對應文章[1]的3.2節內容。對網絡延遲的估計用到了Kalman濾波,算法的具體細節請參考文章[2]。Kalman濾波的結果爲網絡延遲m(i),作爲下一階段網絡狀態檢測的輸入參數。</br>

算法接着調用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。</br>

算法接着調用AimdRateControl::Update()和UpdateBandwidthEstimate()函數,用以估計當前網絡狀態下的目標碼率Ar,該部分對應文章[1]的3.3節。算法基於當前網絡狀態和碼率變化趨勢有限狀態機,採用AIMD(Additive Increase Multiplicative Decrease)方法計算目標碼率,具體計算公式請參考文章[1]。需要注意的是,當算法處於開始階段時,會採用Multiplicative Increase方法快速增加碼率,以加快碼率估計速度。</br>

此時,我們已經拿到接收端估計的目標碼率Ar。接下來以Ar爲參數調用VieRemb對象的OnReceiveBitrateChange()函數,發送REMB報文到發送端。REMB報文會推送到RTCP模塊,並設置REMB報文發送時間爲立即發送。關於REMB報文接下來的發送和接收流程,和第1節描述的RTCP報文一般處理流程是一樣的,即經過序列化發送到網絡,然後發送端收到以後,反序列化出描述結構,最後通過回調函數到達發送端碼率控制模塊BitrateControllerImpl。</br>

至此,接收端基於延遲的碼率估計過程描述完畢。</br>

4 發送端碼率計算及生效

</br>
</br>在發送端,目標碼率計算和生效是異步進行的,即Worker線程從RTCP接收模塊經回調函數拿到丟包率和REMB碼率之後,計算得到目標碼率A;然後ModuleProcess線程異步把目標碼率A生效到目標模塊如PacedSender和ViEEncoder等。下面分別描述碼率計算和生效過程。</br>

圖5 發送端碼率計算過程

碼率計算過程如圖5所示:Worker線程從RTCPReceiver模塊經過回調函數拿到RTCP RR報文和REMB報文的數據,到達BitrateController模塊。RR報文中的丟包率會進入Update()函數中計算碼率,碼率計算公式如文章[1]第2節所述。然後算法流程進入CapBitrateToThreshold()函數,和配置的最大最小碼率和遠端估計碼率進行比較後,確定最終目標碼率。而REMB報文的接收端估計碼率Ar則直接進入CapBitrateToThreshold()函數參與目標碼率的確定。目標碼率由文章[1]的3.4節所示公式進行確定。需要注意的是,RR報文和REMB報文一般不在同一個RTCP報文裏。</br>

圖6 發送端碼率生效過程

發送端碼率生效過程如圖6所示:ModuleProcess線程調用擁塞控制總控對象CongestionController週期性從碼率控制模塊BitrateControllerImpl中獲取當前最新目標碼率A,然後判斷目標碼率是否有變化。若是,則把最新目標碼率設置到相關模塊中,主要包括PacedSender模塊,RTPSender模塊和ViEEncoder模塊。</br>

對於PacedSender模塊,設置碼率主要是爲了平滑RTP數據包的發送速率,儘量避免數據包Burst造成碼率波動。對於RTPSender模塊,設置碼率是爲了給NACK模塊預留碼率,如果預留碼率過小,則在某些情況下對於NACK報文請求選擇不響應。對於ViEEncoder模塊,設置碼率有兩個用途:1)控制發送端丟幀策略,根據設定碼率和漏桶算法決定是否丟棄當前幀。2)控制編碼器內部碼率控制,設定碼率作爲參數傳輸到編碼器內部,參與內部碼率控制過程。</br>

至此,發送端碼率計算和生效過程分析完畢。</br>

5 總結

本文結合文章[1],深入WebRTC代碼內部,詳細分析了WebRTC的GCC算法的實現細節。通過本文,對WebRTC的代碼結構和擁塞控制實現細節有了更深層次的理解,爲進一步學習WebRTC奠定良好基礎。</br>
</br>
</br>
</br>

參考文獻

[1] WebRTC基於GCC的擁塞控制(上) – 算法分析
http://www.jianshu.com/p/0f7ee0e0b3be
[2] WebRTC視頻接收緩衝區基於KalmanFilter的延遲模型.
http://www.jianshu.com/p/bb34995c549a
[3] WebRTC中RTP/RTCP協議實現分析
http://www.jianshu.com/p/c84be6f3ddf3
[4] abs-send-time. https://webrtc.org/experiments/rtp-hdrext/abs-send-time/

發佈了191 篇原創文章 · 獲贊 12 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章