WebRTC自適應控制算法

該文章系總結性文章,主要參考一下文章:
1、webrtc視頻jitterbuffer原理機制(描述版) - 簡書
http://www.jianshu.com/p/bd10d60cebcd
2、WebRTC視頻接收緩衝區基於KalmanFilter的延遲模型 - 簡書
http://www.jianshu.com/p/bb34995c549a
3、WebRTC基於GCC的擁塞控制(上) - 算法分析 - 簡書
http://www.jianshu.com/p/0f7ee0e0b3be

此三篇文章共同構成WebRTC帶寬自適應的算法體系(以下簡稱自適應算法)!之所以作總結,是因爲這三篇文章針對不同的側重點進行敘述,而又互相影響,很容易搞混這裏面的相關概念、作用、關係是什麼。本篇文章通過把這三篇文章做一個串講,並加上自己的理解,進而還原一個更加清晰的完整的算法流程,而不是針對某一個點來理解。

開始之前,首先要了解RTP/RTCP協議,簡答來說就是,TCP有丟包重傳機制保證包的的可靠性,但是UDP沒有,於是引入RTP/RTCP,通過把UDP封裝成RTP包,添加各種其他信息,由RTCP的來控制RTP包的亂序、丟包、抖動等等,其實是TCP的會話的“中庸版本”,爲的就是在保證一定延時的情況下,增加數據包的可靠性!具體細節請參考其他文章,本文不做詳述!

好,開始進入正題!

一、
首先要理解自適應算法的目的是什麼?簡單來講就是“擁塞控制”!
但是具體點呢?優化延時、丟包、抖動的平衡!
再具體呢?調整發端速率、修正接收緩衝

其中優化丟包策略包括:
1、通過降低碼率,緩解擁塞來實現;
2、發起重傳來解決!——>將增大延時
3、增大接收等待延時!——>將增大延時
而優化延策略包括:
1、降低碼率,緩解擁塞;
最後優化抖動的策略包括
1、降低碼率,緩解擁塞!
2、爲了對抗已經產生的延時抖動帶來的影響,需要增加接收緩衝區。

我們可以到:優化丟包和抖動是在犧牲延時的基礎上進行的!

二、
瞭解了目的,我們開始講解實際優化的方法!

對於碼率:
GCC算法使用發端基於丟包的算法來計算當前帶寬,在接收端基於排隊延時的算法來計算當前呆卵。然後接收端把自己估計的帶寬返回給發端,發端通過比較來選取合適的算法;
對於抖動:
基於排隊延時等參數(後續詳述)計算抖動時延,從而確認接收緩衝區大小和等待渲染時間,以消除抖動帶來的影響!

所以收端其實做了兩件事:帶寬估計、抖動優化!其中帶寬估計本身就優化了發送碼率,降低下一時刻的網絡抖動,而抖動優化是爲了對上一時刻受抖動影響的數據包進行優化,使其平穩渲染!

那麼怎麼計算帶寬和抖動延時呢?
這裏面的算法的有兩層:一層是谷歌自己提出的計算公式;另一層是卡爾曼濾波!

首先谷歌建立了一個模型,認爲兩幀之間的延遲具有下列關係:

/////////////////////////////////////////////////////////////////
d(i) = t(i) – t(i-1) – (T(i) – T(i-1)) 
     = [t(i) - T(i)] - [t(i-1) - T(i-1)]
     =FrameSize(i)/c(i) - FrameSize(i-1)/c(i-1)
/////////////////////////////////////////////////////////////////

T(i)是第i個數據包組中第一個數據包的發送時間,t(i)是第i個數據包組中最後一個數據包的到達時間,它們的差就是從發端開始到收端接收完需要的時間,正好等於(數據包長度 除以 信道速率)——聯想火車過橋的數學問題就是這樣計算的;於是問題就來了,信道長度和信道速率是個時變的函數,即每時每刻都不是一個定值,這個時候就用到隨機過程了。此時我們假設鏈路長度和速率在i時刻和i-1時刻是相等的,那麼:

/////////////////////////////////////////////////////////////
           = [FrameSize(i)- FrameSize(i-1)]/C + w(i)
          = deltaFSBytes/C + m(i) + v(i)
/////////////////////////////////////////////////////////////

其中w(i)就是爲了修正我們的假設導致的不正確而引入的白色高斯過程,也就是由於鏈路狀態不穩定帶來的隨機延時:這裏我們認爲,白色高斯過程由兩部分組成:排隊時延m(i)+白色噪聲v(i)(0均值噪聲)

這裏我們就看到了一個很重要的參數:排隊時延!基於延時的帶寬估計就根據這個值來算出來的。而這個值得計算過程就是卡爾曼濾波計算出來的——卡爾曼濾波請移步相關參考文檔!

這裏面通過卡爾曼濾波計算出來的m(i)結合閾值γ等參數來確定當前網絡的三種狀態:過載、空閒、保持,從而調整速率!——具體調整算法移步相關參考文檔!

但是,卡爾曼濾波計算出來的值、以及計算的帶寬值不僅僅用在這裏,還用在抖動延時的優化計算中了!也就是JitterDelay的計算,在GCC算法中,JitterDelay由兩部分延遲造成:傳輸大幀引起的延遲和網絡噪聲引起的延遲:

///////////////////////////////////////////////////////////////
JitterDelay =  (MaxFSAvgFS)/c + noise
            =    (MaxFSAvgFS)/c + [noiseStdDevs * sqrt(varNoise) – noiseStdDevOffset]
//////////////////////////////////////////////////////////////

C已經由上述帶寬估計算法估算出來,MaxFS是自會話開始以來所收到的最大幀大小,AvgFS表示平均幀大小
noiseStdDevs表示噪聲係數2.33,varNoise表示噪聲方差,noiseStdDevOffset是噪聲扣除常數30。
所以,我們求JitterDelay實際上即使求noise——>實際上就是求varNoise!

///////////////////////////////////////////////////////////////
varNoise = alpha*varNoise + (1 – alpha)*(residual – avgNoise)^2 
residual = d – (deltaFSBytes/c + m) 
avgNoise = alpha*avgNoise + (1-alpha)*residual
alpha = pow(399/400, 30/fps)
////////////////////////////////////////////////////////////////

其中alpha表示概率係數,受幀率fps影響:當fps變低時,alpha會變小,表示當前噪聲變大,噪聲方差受當前噪聲影響更大一些。實時幀率越接近30 fps越好。residual計算延遲殘差(反映網絡噪聲的大小),avgNoise表示自開始以來的平均噪聲。deltaFS表示幀間大小差值,c表示信道傳輸速率,m表示網絡排隊延遲。( d – (c * deltaFSBytes + m)就是上文的 d(i) = deltaFSBytes/C + m(i) + v(i)引入的),通過上述計算,我們更新了“當前”JitterDelay,從而調整渲染等待時間、緩衝區大小,進而消除抖動帶來的不平滑渲染,但這是一犧牲延時爲代價的!——具體調整方法請移步參考文檔!

所以可以看出,卡爾曼濾波是極其重要的一環:一方面計算出來的m(i)值(排隊時延)用於調整發送速率c(i);另一方面要通過排隊時延m(i),和由排隊時延計算出來的發送速率c(i)計算JitterDelay,來調整等待渲染後時間!

再次注意!!!!!本文爲了減少不必要的重複性,卡爾曼濾波、帶寬估計(包括髮端和收端)、Jitterbuffer等知識都沒有敘述!本文只是搭建了這幾個重要算法的橋樑聯繫作用,用於讀者明白這幾個算法之間是如何相互影響的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章