在WebRTC的視頻處理流水線中,接收端緩沖區(qū)JitterBuffer是關(guān)鍵的組成部分:它負(fù)責(zé)RTP數(shù)據(jù)包亂序重排和組幀,RTP丟包重傳,請(qǐng)求重傳關(guān)鍵幀,估算緩沖區(qū)延遲等功能。其中緩沖區(qū)延遲JitterDelay對(duì)視頻流的單向延遲有重要影響,很大程度上決定著應(yīng)用的實(shí)時(shí)性。本文不打算全面分析接收端緩沖區(qū)的實(shí)現(xiàn)細(xì)節(jié),只針對(duì)緩沖區(qū)延遲JitterDelay的計(jì)算這一議題進(jìn)行深入分析。</br>
1 接收端延遲的組成
</br>
WebRTC視頻接收端延遲包括三部分:緩沖區(qū)延遲JitterDelay,解碼延遲DecodeDelay和渲染延遲RenderDelay。其中DecodeDelay和RenderDelay相對(duì)比較穩(wěn)定,而JitterDelay受發(fā)送端碼率和網(wǎng)絡(luò)狀況影響較大。JitterDelay也是造成接收端延遲的最大因素。</br>
緩沖區(qū)延遲由兩部分延遲構(gòu)成:傳輸大尺寸視頻幀造成碼率burst引起的延遲和網(wǎng)絡(luò)噪聲引起的延遲。WebRTC采用卡爾曼濾波Kalman Filter估算網(wǎng)絡(luò)傳輸速率和網(wǎng)絡(luò)排隊(duì)延遲,進(jìn)而確定緩沖區(qū)延遲。本文在理論學(xué)習(xí)卡爾曼濾波的基礎(chǔ)上,結(jié)合WebRTC源代碼,詳細(xì)深入分析緩沖區(qū)延遲的計(jì)算和更新過程。</br>
2 卡爾曼濾波理論學(xué)習(xí)
</br>
本節(jié)主要參考引用文獻(xiàn)[1][2][3],以此為入門資料學(xué)習(xí)卡爾曼濾波的基本原理。簡(jiǎn)單來說,卡爾曼濾波器是一個(gè)最優(yōu)化自回歸數(shù)據(jù)處理算法,文獻(xiàn)[2]概括卡爾曼濾波的基本思想:</br>
“本質(zhì)上來講,濾波就是一個(gè)信號(hào)處理與變換(去除或減弱不想要的成分,增強(qiáng)所需成分)的過程??柭鼮V波屬于一種軟件濾波方法,其基本思想是:以最小均方誤差為最佳估計(jì)準(zhǔn)則,采用信號(hào)與噪聲的狀態(tài)空間模型,利用前一時(shí)刻的估計(jì)值和當(dāng)前時(shí)刻的觀測(cè)值來更新對(duì)狀態(tài)變量的估計(jì),求出當(dāng)前時(shí)刻的估計(jì)值。算法根據(jù)建立的系統(tǒng)方程和觀測(cè)方程對(duì)需要處理的信號(hào)做出滿足最小均方誤差的估計(jì)?!?lt;/br>
下面以文獻(xiàn)[1]為基礎(chǔ),簡(jiǎn)要分析卡爾曼濾波的基本過程。</br>
2.1 建立系統(tǒng)數(shù)學(xué)模型
</br>
首先,我們先要引入一個(gè)離散控制過程的系統(tǒng)。該系統(tǒng)可用一個(gè)線性隨機(jī)微分方程(Linear Stochastic Difference equation)來描述:</br>
X(K) = AX(K-1) + BU(k) + W(k) (2.1.1)
再加上系統(tǒng)的測(cè)量值:</br>
Z(k) = HX(k) + V(k) (2.1.2)
上兩式子中,X(k)是k時(shí)刻的系統(tǒng)狀態(tài),U(k)是k時(shí)刻對(duì)系統(tǒng)的控制量。A和B是系統(tǒng)參數(shù),對(duì)于多模型系統(tǒng),他們?yōu)榫仃?。Z(k)是k時(shí)刻的測(cè)量值,H是測(cè)量系統(tǒng)的參數(shù),對(duì)于多測(cè)量系統(tǒng),H為矩陣。W(k)和V(k)分別表示過程噪聲和測(cè)量噪聲。他們被假設(shè)成高斯白噪聲,他們的協(xié)方差分別是Q,R。</br>
2.2 卡爾曼濾波過程
</br>
首先我們要利用系統(tǒng)的過程模型,來預(yù)測(cè)下一狀態(tài)的系統(tǒng)狀態(tài)。假設(shè)現(xiàn)在的系統(tǒng)狀態(tài)是k,根據(jù)系統(tǒng)的過程模型,可以基于系統(tǒng)的上一狀態(tài)而預(yù)測(cè)出現(xiàn)在狀態(tài):</br>
X(k|k-1)=AX(k-1|k-1) + BU(k) (2.2.1)
式(2.2.1)中,X(k|k-1)是利用上一狀態(tài)預(yù)測(cè)的結(jié)果,X(k-1|k-1)是上一狀態(tài)最優(yōu)的結(jié)果,U(k)為現(xiàn)在狀態(tài)的控制量。到現(xiàn)在為止,我們的系統(tǒng)結(jié)果已經(jīng)更新??墒?,對(duì)應(yīng)于X(k|k-1)的誤差協(xié)方差還沒更新。我們用P表示誤差協(xié)方差:</br>
P(k|k-1)=AP(k-1|k-1)A’+ Q (2.2.2)
式(2.2.2)中,P(k|k-1)是X(k|k-1)對(duì)應(yīng)的誤差協(xié)方差,P(k-1|k-1)是X(k-1|k-1)對(duì)應(yīng)的誤差協(xié)方差,A’表示A的轉(zhuǎn)置矩陣,Q是系統(tǒng)過程的過程噪聲協(xié)方差。式子1,2就是卡爾曼濾波器5個(gè)公式當(dāng)中的前兩個(gè),也就是對(duì)系統(tǒng)的預(yù)測(cè)。</br>
現(xiàn)在我們有現(xiàn)在狀態(tài)的預(yù)測(cè)值,然后我們?cè)偈占F(xiàn)在狀態(tài)的測(cè)量值。結(jié)合預(yù)測(cè)值和測(cè)量值,我們可以得到現(xiàn)在狀態(tài)(k)的最優(yōu)化估算值X(k|k):</br>
X(k|k) = X(k|k-1) + Kg(k)[Z(k) - HX(k|k-1)] (2.2.3)
其中Kg為卡爾曼增益(Kalman Gain):</br>
Kg(k)= P(k|k-1) H’/ (H P(k|k-1) H’+ R) (2.2.4)
到現(xiàn)在為止,我們已經(jīng)得到了k狀態(tài)下最優(yōu)的估算值X(k|k)。但是為使卡爾曼濾波器不斷的運(yùn)行下去直到系統(tǒng)過程結(jié)束,我們還要更新k狀態(tài)下X(k|k)的誤差協(xié)方差:</br>
P(k|k)=(I-Kg(k) H) P(k|k-1) (2.2.5)
其中I 為單位矩陣。當(dāng)系統(tǒng)進(jìn)入k+1狀態(tài)時(shí),P(k|k)就是式子(2)的P(k-1|k-1)。這樣,算法就可以自回歸的運(yùn)算下去。</br>
上述式子1~5是卡爾曼濾波的核心算法所在,包括預(yù)測(cè)值計(jì)算,誤差協(xié)方差計(jì)算,最優(yōu)值估算,卡爾曼增益計(jì)算,誤差協(xié)方差更新等五個(gè)重要步驟。</br>
3 WebRTC中JitterDelay的計(jì)算過程
</br>
本節(jié)結(jié)合WebRTC源代碼,分析視頻接收端緩沖區(qū)延遲JitterDelay的計(jì)算過程。</br>
3.1 JitterDelay的計(jì)算公式
</br>
由第一節(jié)分析可知,JitterDelay由兩部分延遲造成:傳輸大幀引起的延遲和網(wǎng)絡(luò)噪聲引起的延遲。其計(jì)算公式如下:</br>
JitterDelay = theta[0] * (MaxFS – AvgFS) + [noiseStdDevs * sqrt(varNoise) – noiseStdDevOffset] (3.1.1)
其中theta[0]是信道傳輸速率的倒數(shù),MaxFS是自會(huì)話開始以來所收到的最大幀大小,AvgFS表示平均幀大小。noiseStdDevs表示噪聲系數(shù)2.33,varNoise表示噪聲方差,noiseStdDevOffset是噪聲扣除常數(shù)30。</br>
解碼線程從緩沖區(qū)獲取一幀視頻數(shù)據(jù)進(jìn)行解碼之前,會(huì)根據(jù)公式3.1.1計(jì)算當(dāng)前幀的緩沖區(qū)延遲,然后再加上解碼延遲和渲染延遲,得到當(dāng)前幀的預(yù)期渲染結(jié)束時(shí)間。然后根據(jù)當(dāng)前時(shí)刻,確定當(dāng)前幀在解碼之前需要等待的時(shí)間,以保證視頻渲染的平滑性。 </br>
3.2 JitterDelay的更新過程
</br>
解碼線程從緩沖區(qū)獲取一幀視頻數(shù)據(jù)進(jìn)行解碼之前,會(huì)根據(jù)當(dāng)前幀的大小、時(shí)間戳和當(dāng)前本地時(shí)刻,更新緩沖區(qū)本地狀態(tài),包括最大幀大小、平均幀大小、噪聲平均值、信道傳輸速率、網(wǎng)絡(luò)排隊(duì)延時(shí)等參數(shù)。 更新過程如圖1所示:</br>

首先,確定幀間相對(duì)延遲frameDelay和幀間大小差值deltaFS:</br>
frameDelay = t(i) – t(i-1) – (T(i) – T(i-1)) (3.2.1)
deltaFS = frameSize – prevFrameSize (3.2.2)
其中t(i)表示當(dāng)前幀本地接收時(shí)刻,t(i-1)表示上一幀本地接收時(shí)刻;T(i)表示當(dāng)前幀的時(shí)間戳,T(i-1)表示上一幀的時(shí)間戳。frameDelay表示相鄰兩幀的相對(duì)延遲,frameDelay > 0表示幀i相對(duì)于幀i-1在路上花費(fèi)的時(shí)間更長(zhǎng)。frameSize和prevFrameSize分別表示當(dāng)前幀和上一幀的大小。</br>
然后計(jì)算幀大小的平均值和方差:</br>
avgFrameSize = phi * avgFrameSize + (1-phi) * frameSize (3.2.3)
varFrameSize = phi * varFrameSize + (1-phi) * (frameSize – avgFramesize)^2 (3.2.4)
接下來計(jì)算延遲殘差(反映網(wǎng)絡(luò)噪聲的大小),并據(jù)此計(jì)算噪聲均值和方差:</br>
residual = frameDelay – (theta[0] * deltaFSBytes + theta[1]) (3.2.5)
avgNoise = alpha*avgNoise + (1-alpha)*residual (3.2.6)
varNoise = alpha*varNoise + (1 – alpha)*(residual – avgNoise)^2 (3.2.7)
alpha = pow(399/400, 30/fps) (3.2.8)
其中alpha表示概率系數(shù),受幀率fps影響:當(dāng)fps變低時(shí),alpha會(huì)變小,表示當(dāng)前噪聲變大,噪聲方差受當(dāng)前噪聲影響更大一些。實(shí)時(shí)幀率越接近30 fps越好。avgNoise表示自開始以來的平均噪聲。theta[0]表示信道傳輸速率,theta[1]表示網(wǎng)絡(luò)排隊(duì)延遲。</br>
最后一步,卡爾曼濾波器根據(jù)當(dāng)前幀間延遲和幀間大小差值,動(dòng)態(tài)調(diào)整信道傳輸速率theta[0]和網(wǎng)絡(luò)排隊(duì)延遲theta[1],具體過程在下一節(jié)進(jìn)行詳細(xì)討論。</br>
至此,JitterDelay的一次更新過程結(jié)束。當(dāng)下一幀數(shù)據(jù)到來時(shí),使用本次更新結(jié)果計(jì)算JitterDelay,并再次執(zhí)行更新過程。</br>
4 卡爾曼濾波更新緩沖區(qū)狀態(tài)
</br>
本節(jié)結(jié)合WebRTC源代碼,深入分析卡爾曼濾波更新更新信道傳輸速率theta[0]和網(wǎng)絡(luò)排隊(duì)時(shí)延theta[1]的過程,這也是緩沖區(qū)延遲估計(jì)的核心算法所在。</br>
4.1 數(shù)學(xué)符號(hào)定義
</br>
X_bar: 向量X
X_hat: 向量X的估計(jì)值
X(i): 向量X的第i個(gè)分量
[x y z]:包含元素xyz的行向量
X_bar^T:向量X_bar的轉(zhuǎn)置向量
E{X}: 隨機(jī)變量X的期望
4.2 理論推導(dǎo)過程
</br>
d(i) = t(i) – t(i-1) – (T(i) – T(i-1)) (4.2.1)
t(i)表示當(dāng)前幀本地接收時(shí)刻,t(i-1)表示上一幀本地接收時(shí)刻;T(i)表示當(dāng)前幀的發(fā)送端采樣時(shí)刻即時(shí)間戳,T(i-1)表示上一幀的采樣時(shí)刻。d(i) > 0表示幀i相對(duì)于幀i-1在路上花費(fèi)的時(shí)間更長(zhǎng)。式子4.2.1是系統(tǒng)的測(cè)量值。</br>
d(i) = dL(i) / C(i) + m(i) + v(i) (4.2.2)
dL(i)表示幀i和幀i-1的長(zhǎng)度之差,C(i)表示信道傳輸速率,m(i)表示幀i的網(wǎng)絡(luò)排隊(duì)時(shí)延,v(i)表示測(cè)量噪聲,其協(xié)方差為R。其中[1/C(i) m(i)]是我們要求的目標(biāo)值,即信道傳輸速率和網(wǎng)絡(luò)排隊(duì)時(shí)延。式子4.2.2是系統(tǒng)的預(yù)測(cè)值。</br>
theta_bar(i) = [1/C(i) m(i)]^T 為幀i狀態(tài)列向量;
h_bar(i) = [dL(i) 1]^T 為幀間長(zhǎng)度差列向量;
theta_bar(i+1) = theta_bar(i) + u_bar(i); u_bar(i)表示過程噪聲;
Q(i) = E{u_bar(i) * u_bar(i)^T} 表示過程噪聲協(xié)方差矩陣;
diag(Q(i)) = [10^-13 10^-3]^T Q(i)是對(duì)角陣;
估計(jì)過程:</br>
theta_hat(i) = [1/C_hat(i) m_hat(i)]^T; // 目標(biāo)估計(jì)值
z(i) = d(i) – h_bar(i)^T * theta_hat(i-1)
= d(i) – (dL(i)/C_hat(i-1) + m_hat(i-1)) (4.2.3)
上述式子的意義是,用上一時(shí)刻估計(jì)值估算本時(shí)刻的時(shí)間消耗:</br>
dL(i)/C_hat(i-1) + m_hat(i-1)
然后用當(dāng)前觀測(cè)值d(i)和估算值求出殘差z(i)。</br>
theta_hat(i) = theta_hat(i-1) + z(i) * k_bar(i) (4.2.4)
上述式子表示i時(shí)刻和i-1時(shí)刻的狀態(tài)迭代關(guān)系:i-1時(shí)刻的狀態(tài)加上i時(shí)刻的殘差z(i)和i時(shí)刻卡爾曼濾波的乘積,其中i時(shí)刻的kalman gain計(jì)算公式如下:</br>
k_bar(i)=[(E(i-1)+Q(i))*h_bar(i)]/[var_v_hat(i)+h_bar(i)^T*(E(i-1)+Q(i))*h_bar(i)] (4.2.5)
E(i) = (I–k_bar(i)*h_bar(i)^T)*[E(i-1)+Q(i)] (4.2.6)
var_noise(i) = max(alpha*var_noise(i-1)+(1–alpha)*z(i)^2, 1) (4.2.7)
alpha = pow(399 / 400, 30 / fps) (4.2.8)
var_v_hat(i)=(300*exp[-fabs(deltaFS)/maxFrameSize)+1]*sqrt(varNoise) (4.2.9)
其中I是2*2單位陣,E(i)是誤差協(xié)方差(它是卡爾曼濾波迭代的關(guān)鍵參數(shù))。var_noise是噪聲方差,var_v_hat是噪聲標(biāo)準(zhǔn)差指數(shù)過濾平均后取值,其實(shí)就是幀i的測(cè)量噪聲協(xié)方差R,它對(duì)最終計(jì)算出的卡爾曼增益有較大影響:var_v_hat(i)較大時(shí),卡爾曼增益較小,表示本次測(cè)量中噪聲較大,最終估計(jì)值更靠近上次估計(jì)值而較少受本次殘差的影響。</br>
4.3 WebRTC代碼實(shí)現(xiàn)
</br>
下面?zhèn)未a是對(duì)WebRTC中VCMJitterEstimator::KalmanEstimateChannel函數(shù)的精簡(jiǎn)概括,描述了卡爾曼濾波的代碼實(shí)現(xiàn)。</br>
void VCMJitterEstimator::KalmanEstimateChannel(frameDelayMS, deltaFSBytes) {
// 計(jì)算誤差協(xié)方差和過程噪聲協(xié)方差的和:E = E + Q;
_thetaCov[0][0] += _Qcov[0][0];
_thetaCov[0][1] += _Qcov[0][1];
_thetaCov[1][0] += _Qcov[1][0];
_thetaCov[1][1] += _Qcov[1][1];
// 計(jì)算卡爾曼增益:
// K = E*h'/(sigma + h*E*h')
// h = [deltaFS 1], Eh = E*h'
// hEh_sigma = h*E*h' + sigma
Eh[0] = _thetaCov[0][0] * deltaFSBytes + _thetaCov[0][1];
Eh[1] = _thetaCov[1][0] * deltaFSBytes + _thetaCov[1][1];
// sigma為測(cè)量噪聲標(biāo)準(zhǔn)差的指數(shù)平均濾波結(jié)果,即測(cè)量噪聲協(xié)方差R。
double sigma = (300.0 * exp(-fabs(static_cast<double>(deltaFSBytes)) /
(1e0 * _maxFrameSize)) +1) * sqrt(varNoise);
hEh_sigma = deltaFSBytes * Eh[0] + Eh[1] + sigma;
kalmanGain[0] = Eh[0] / hEh_sigma;
kalmanGain[1] = Eh[1] / hEh_sigma;
// 計(jì)算殘差,獲得最優(yōu)估計(jì)值
measureRes = frameDelayMS - (deltaFSBytes * _theta[0] + _theta[1]);
_theta[0] += kalmanGain[0] * measureRes;
_theta[1] += kalmanGain[1] * measureRes;
// 更新誤差協(xié)方差:E = (I - K*h)*E;
t00 = _thetaCov[0][0]; t01 = _thetaCov[0][1];
_thetaCov[0][0] = (1 - kalmanGain[0] * deltaFSBytes) * t00 -
kalmanGain[0] * _thetaCov[1][0];
_thetaCov[0][1] = (1 - kalmanGain[0] * deltaFSBytes) * t01 -
kalmanGain[0] * _thetaCov[1][1];
_thetaCov[1][0] = _thetaCov[1][0] * (1 - kalmanGain[1]) -
kalmanGain[1] * deltaFSBytes * t00;
_thetaCov[1][1] = _thetaCov[1][1] * (1 - kalmanGain[1]) -
kalmanGain[1] * deltaFSBytes * t01;
}
至此,卡爾曼濾波估計(jì)信道發(fā)送速率和網(wǎng)絡(luò)排隊(duì)時(shí)延的一次迭代完成。</br>
5 總結(jié)
</br>
本文在理論學(xué)習(xí)卡爾曼濾波基本原理的基礎(chǔ)上,結(jié)合WebRTC源代碼,深入分析了WebRTC視頻接收端緩沖區(qū)延遲的計(jì)算方法和更新過程。通過本文,更進(jìn)一步加深對(duì)WebRTC的學(xué)習(xí)和了解。</br>
參考文獻(xiàn)
</br>
[1] 卡爾曼濾波的原理說明 http://blog.sciencenet.cn/blog-1009877-784428.html
[2] 卡爾曼濾波的基本原理及應(yīng)用[J]. 軟件導(dǎo)刊, Vol.8 No.11, Nov. 2009.
[3] An Introduction to the Kalman Filter [J]. University of North Carolina at Chapel Hill Department of Computer Science
Chapel Hill, NC 27599-3175
[4] A Google Congestion Control Algorithm for Real-Time Communication
https://tools.ietf.org/html/draft-alvestrand-rmcat-congestion-03
[5] Analysis and Design of the Google Congestion Control for Web Real-time
Communication[J]. MMSys ’16 Article No.13