WebRTC 音頻抗弱網技術(下)

融雲 發佈 2022-07-14T13:27:01.676792+00:00

上周,我們針對音頻弱網對抗技術中的前向糾錯技術、後向糾錯技術及 OPUS 編解碼抗弱網特性進行了分享。文本分享 WebRTC 使用的抗抖動模塊 NetEQ。

上周,我們針對音頻弱網對抗技術中的前向糾錯技術、後向糾錯技術及 OPUS 編解碼抗弱網特性進行了分享。文本分享 WebRTC 使用的抗抖動模塊 NetEQ。關注【融雲全球網際網路通信雲】了解更多


抖動的定義和消除原理

抖動是指由於網絡原因,到達接收端的數據在不同時間段,表現出的不均衡;或者說接收端接收數據包的時間間隔有大有小。

WebRTC 通過包到達時間間隔的變化來評估抖動,公式如下

Ji 定義為時刻 i 時測量的抖動,E(T) 表示包到達時間的間隔均值,Ti 表示時刻 i 收到的包距上一次收包的時間間隔。

Ji > 0 說明數據包提前到了,這樣抖動緩存區數據將會出現堆積,容易造成緩衝區數據溢出,導致端到端時間延遲增大;Ji < 0 說明數據包晚到或丟失,會增大延時;不管早到還是晚到,都可能造成丟包,增加時延。


NetEQ 通過測量包達到時間間隔,來預測包在網絡中的傳輸時間延時;根據收到的包但還未播放的緩衝語音數據的大小來評估抖動的大小。

原則上通過對網絡延時的測量,以其最大延時來設置抖動緩衝區的大小,然後使每個包在網絡中的延時加上該包在抖動緩衝區的延時之和保持相等,這樣可以消除抖動,就可以控制語音包從抖動緩衝區以一個相對平穩的速度播放音頻數據。


下圖[1]說明了抖動消除的核心思想:

(抖動消除的核心思路)

A、B、C、D 包在發送端分別以 30ms 間隔發送,即分別在 30ms、60ms、90ms、120ms 處發送;接收端接收到這些包對應的時間為 40ms、90ms、100ms、130ms;這樣它們在網絡中的延時分別是 10ms、30ms、10ms、10ms;包到達的間隔分別為 50ms、10ms、30ms,即抖動。

因此可以使包 A、C、D 在抖動緩存中的延時 20ms 再播放,即 A、B、C、D 的播放時間為 60ms、90ms、120ms、150ms,這樣可以保持一個平穩的間隔進行播放。

NetEQ 通過估算網絡傳輸延時,根據網絡傳輸延時 95% 分位來調整抖動緩衝區;這使得 NetEQ 在消除抖動緩衝影響時,兼顧最小延時。


下圖[2]是官方對 NetEQ 和其它技術在消除抖動帶來的延時對比,可以看出 NetEQ 在消除抖動時,可以保持很低的時間延時。

(NetEQ 與其它技術消除抖動延時對比)


NetEQ 及其相關模塊

下圖[1]概要描述了 WebRTC 的語音引擎架構。紅色區域為 NetEQ 部分,可以看出 NetEQ 位於接收端,包含了 jitter buffer 和解碼器、PLC 等相關模塊。接收端從網絡收到語音包後,語音包將先進入 NetEQ 模塊進行抖動消除、丟包隱藏、解碼等操作,最後將音頻數據輸出到音效卡播放。


NetEQ 包含的模塊見下圖[1]:


NetEQ 核心模塊有 MCU 模塊DSP 模塊,MCU 模塊負責往 jitter buffer 緩存中插入數據和取數據,以及給 DSP 的操作;DSP 模塊負責語音信息號的處理,包括解碼、加速、減速、融合、PLC 等。

同時 MCU 模塊從 jitter buffer 中取包受到 DSP 模塊相關反饋影響。


MCU 模塊包括音頻包進入到 buffer,音頻包從 buffer 中取出,通過語音包到達間隔評估網絡傳輸時間延時,以及對 DSP 模塊執行何種操作(加速、減速、PLC、merge)等。

DSP 模塊包括解碼,對解碼後的語音 PCM 數據進行相關操作,評估網絡抖動 level,向 play buffer 傳遞可播放的數據等等。

下面詳細分析 MCU 和 DSP 各相關模塊。


MCU

將收到的包插入 packet_buffer_

從網絡收到音頻包後,將其插入到抖動緩存 packet_buffer_中,最多緩存 200 個包;當緩存滿了,會刷新緩存中所有的包,同時該緩存最多緩存近 5 秒的音頻數據,太早的會被定時清除。

若收到的包是 red 包,會將 red 包中的每個獨⽴包解析出來存入到緩存隊列中,包在緩存隊列中按照 timestamp 的升序進行存儲,即最近的音頻包存儲在隊列後面,老的包存儲在隊列前面。

收到音頻包後,還會更新 NackTracker 模塊,該模塊主要是通過包的序列號來判斷是否存在丟包,若存在丟包且重傳請求開啟,需要向發送端發起 nack 請求,請求發送端重發丟失音頻包。


估算網絡傳輸延時

在將包插入到抖動緩存中時,根據包到達時間間隔對網絡延時進行估算。這裡將 WebRTC 中根據包到達時間間隔來計算網絡延時,WebRTC 計算音頻網絡延時,主要考慮如下幾點:

統計音頻包到達時間間隔,取其 95% 分為作為網絡時延估計。

以上公式通過計算當前包和上一個包的時間差,以及 seqnum 的差值,來計算每個 seq 包占有的時間戳範圍,即 packet_per_time,然後通過當前包和上一個包收包間隔 iat_ms,來計算收包時間間隔延時,用包的個數來度量。

當正常包到達時 iat = 1,iat = 0 表示提前到了,iat = 2,表示延時一個包, 最終用包個數來衡量延遲時間。

每算出一個 iat 後,會將其插入到對應的直方圖,直方圖記錄 0-64 個包,即記錄 0-64 個包的延遲概率分布情況。所有延遲概率和為 1。

計算出當前包的到達間隔 iat,將其插入到直方圖,並更新直方圖每個延遲的概率,每個延遲分布乘以一個遺忘因子 f,f 小於 1, 更新過程如下:

上面公式的意義是保證每次計算出一個包到達間隔延遲後,對應增加其概率,同時對其它延時分布的概率進行遺忘,使整個和保持為 1。

最後取其 95% 分為的延遲作為目標延時,即有 95% 的延遲都小於這個目標延時。

統計包到達時間間隔最大峰值

統計多個包到達間隔峰值,最多 8 個,在一定情況下,以峰值作為網絡延時估計。每次計算到包到達時間間隔 iat 和按 95% 分位計算到的目標延時(記為target_level)後,來判斷峰值。

認為該 iat 是⼀個延時峰值的條件是:

iat > target_level + threshold, 或 iat > 2 * target_level, 這裡的 threshold 為 3


且距上次峰值時間間隔小於閾值 5s

當判斷該 iat 為延時峰值時,將其添加到峰值容器中,峰值容器中每個元素記錄兩個值,一個是峰值 iat 值,另一個是當前峰值距離上一次峰值的時間間隔(即為 period_ms);

當峰值容器超過了兩個,且當前時間距離上一次發現峰值時已流逝的時間(記為 eplase_time)小於峰值容器元素中記錄的最大 period_ms 的兩倍時,認為該峰值是有效的,需要從峰值容器取最大的 iat(記為 max_iat),則目標時延取值:

當 target_level 取值是 max_iat 時,該峰值的理論有效時間可到達 40s 以上,此處存在優化空間。


最小延時限制

根據設置最低時延,調整目標延時估計,保證目標延時估計不低於最小時延;最小時延是音視頻同步計算的出來的結果。

目標延時小於該最小延時,會導致音視頻不同步。

目標延時不能超過 0.75 * 最大抖動緩衝區大小

WebRTC 中默認最大抖動緩衝區為 200,所以目標延時為:

即 target_level 不大於 150 個包,也就是目標延時不大於 3s 延時。


從 packet_buffer_中取包

播放線程嘗試每次獲取 10ms 數據來播放,要是抖動緩衝中沒有數據,則播放靜音數據。

從 packet_buffer_ 中獲取的數據首先需要經過解碼器解碼,然後再根據反饋的操作進行對應的 DSP 操作(加速、減速、丟包隱藏、PLC、融合等)。最後將 DSP 處理完的數據填入到 sync_buffer_ 中待揚聲器播放,並從 sync_buffer_ 中取 10ms 音頻數據進行播放。


計算抖動延時

根據抖動緩衝區 packet_buffer_ 和 sync_buffer_ 中未播放完的音頻數據總延時 total_dealy,來更新計算抖動緩存延時 filtered_level

若經過加減速操作,需要從 filtered_level 中消除加減速操作後引入的延時變化,如下所示:

獲取對應的操作

下圖中的 packet_buffer_ 和 sync_buffer_ 中未播放的數據大小可以理解為抖動緩衝區大小,從圖中可看出數據從 packet_buffer_ 取出後,經解碼器解碼、DSP 處理,最後進入 sync_buffer_中待播放。

low_limit = 3/4 *target_level 
high_limit = max(target_level, low_limit + window_20ms


但進行何種 DSP 操作處理呢?

NetEQ 將根據 packet_buffer_和 sync_buffer_中未播放的音頻數據總延時(記為 total_delay),以及 sync_buffer_ 中最後待播放的音頻數據的時間(記為 endtimestamp),和 packet_buffer_首包的時間(記為 avaibleTimestamp),以及 target_level 和 filtered_level 的關係,來綜合判斷接下來執行何種 DSP 操作。


下面將對幾種核心的 DSP 處理操作條件進行簡要說明:

norml 操作

滿足以下條件之一,將執行 normal 操作

1. 若原本要做 expand 操作,但是 sync_buffer_ 待播放的數據量大於 10ms。

2. 連續 expand 操作次數超過閾值。

3.當前幀和前一幀都正常到達,且 fitlered_level 在 low_limit 和 higth_limit 之間。

4. 上一次操作是 PLC,當前幀正常,但當前幀來得太早,執行 normal 操作。

5. 當前幀和前一幀都正常到達, 原本通過 filtered_level > high_limit 判斷,要加速操作,但是 sync_buffer_ 待播放的音頻數據大於小於 30ms。

expand 操作條件

當前數據包丟失或者還未到達時,同時在 sync_buffer_ 待播放的音頻數據小於 10ms 時,則滿足下面 4 個條件中任何一個,都將執行 expand 操作。

1. packet_buffer_ 沒有可獲取的音頻數據。

2.上一次是 expand 操作,且當前 total_delay < 0.5* target_level。

3. 上一次是 expand 操作,且當前包(即availbeTimestamp - endtimestamp 大於一定閾值)來的太早,同時當前 filtered_level 小於 target_level。

4. 上一次包非 expand(加速、減速或 normal),且 availbeTimestamp - endtimestamp >0,即中間存在丟包。

加速操作

上一個包和當前包都正常到達,filtered_level 大於 hight_limit,且 sync_buffer_ 中待播放的數據量大於 30ms。

減速操作

上一個包和當前包都正常到達,filtered_level 小於 low_limit。

merge 操作

上一次為 expand 操作,當前包正常到達。


DSP

基音[3]

基音是指發濁音時聲帶振動所引起的周期性對應的信號基本諧波,基音周期等於聲帶振動頻率的倒數。一般的聲音都是由發音體發出的一系列頻率、振幅各不相同的振動複合而成的。

這些振動中有一個頻率最低的振動,由它發出的音就是基音,其餘為泛音。基音攜帶了大部分能量,決定了音高。

基音的周期提取,一般使用短時自相關函數,因為自相關函數一般在基於周期上表現出有極大值的特點。在 NetEQ 的 DSP 信號處理中,基音的提取是一個至關重要的步驟。


語音的拉伸[4]

對語音的拉伸變速,有時域方法和頻域方法。時域方法計算量相對頻域方法少,適合 VoIP 這種場景;頻域方法適合頻率激烈變化的場景,如音樂。

NetEQ 中對語音的拉伸(加速或減速)使用的是 WSOLA 算法,即通過波形相似疊加的方法來變速;該算法在拉伸語音時能夠保證變速不變調。同時該算法是時域算法,對語音有比較好的效果,下圖是其大致原理和流程:


WSOLA 算法大致流程:

解碼

從 packet_buffer_ 中獲取數據,正常解碼,解碼數據存儲到 decoded_buffer_ 中。

加速

NetEQ 中,當 sync_buffer_ 和 packet_buffer_ 中的待播放數據總延時堆積過多,且前一幀和當前幀都正常時,需要通過加速播放操作,來降低抖動緩衝區待播放的數據量,以達到降低時延的目的,否則容易導致抖動緩衝區溢出,出現丟包。而加速是要求變速不變調的,WSOLA 算法通過尋找相似波形並對相似波形進行疊加來實現變速目的。


這裡對 NetEQ 中的加速過程做簡要描述:

⾸先做加速要求最少需要 30ms 數據,數據一般來源於從 decoder_buffer_, 當 decoded_buffer_中得的數據不足 30ms,需要從 sync_buffer_中借一部分數據,湊滿 30ms。


30ms 數據對應 240 個樣本,將這 240 個樣本下採樣到 110 個樣本;將下採樣 110 個樣本分成兩部分 seq1 和seq2,seq2 為固定末尾 50 個樣本,即[60,110]; seq1 也為 50 個樣本,滑動範圍[0,100],計算 seq1 和 seq2 的自相關性。


根據計算出來的自相關性結果,以拋物線擬合來找到自相關性峰值和位置,極大值出現的位置,即為上圖中的 offset,即 seq1 窗口向左滑動的距離,所以基音周期 T = offset + 10。


在要加速的 30ms 樣本中,取 15ms 和 15ms - T 這兩個基音周期信號,分別記為 XY 計算這兩個基音周期信號的匹配度。

當 best_correlation 大於 0.9 時,則執行將兩個基音周期信號合併為⼀個基音周期信號,起到加速作用


加速完的數據,如下圖的 output 將存儲到 algorithm_buffer_中,要是之前因為 decoded_buffer_中的數據量不夠,從 sync_buffer_中借了部分數據,則需要從 algorithm_buffer_中將借的數據 copu 回 sync_buffer_對應的位置(目的是保證音頻的平滑過渡),同時將 algorithm_buffer_中剩下的數據 copy 到 sync_buffer_末尾,如下圖所示:

減速

當 NetEQ 抖動緩衝中待播放的數據延時水平小於目標延時下限時,說明待播放的數據量小,為了達到播放端的最佳音質體驗,需要將現有數據拉伸,適當增加播放數據量;這和加速是相反的操作,底層技術相同。同樣下面對其做簡要分析:

前 4 步和加速基本上是一樣的,這裡不再贅述


最後一步,是將 15ms - T 和 15ms 兩個基音周期交叉漸變合併的混合基音插入到 15ms 後一個周期前。達到增加一個基音周期數據的目的,以此來增加播放數據量,同時需要從算法緩衝區中將借的數據返還給 sync_buffer_。


具體如下圖所示:

丟包補償

當前包丟失時,在 NetEQ 中會觸發丟包補償來預測丟失的包;丟包補償有兩種方式,一種是通過編解碼器來預測重構丟失的包,另一種是通過 NetEQ 模塊來預測重構丟失的包;對應分別是 kModeCodecPlc 和 kModeExpand。

不論是何種模式,前提要求是 sync_buffer_ 中待播放的音頻數據量小於當前請求要播放的數據量,才能執行該操作;丟包補償通過最近的歷史數據,來重構 PLC 相關參數,然後通過歷史數據和 PLC 相關參數進行線性預測,恢復丟失的包,最後再疊加一定的噪聲。

當連續多次 PLC 操作時,失真將會加大,所以多次操作後,將會降低 PLC 語音能量值。


下圖是 NetEQ expand 操作的核心步驟。



PLC 操作中,首要任務是計算基音周期,這裡使用了自相關計算信號失真計算兩種方式來計算基音周期,可通過下圖簡要說明。

A 為末端 60 個樣本信號

B 為滑動窗口,窗口包含 60 個樣本信號,窗口起始位置滑動範圍[0, 54]

通過計算 A 和 B 的自相關係數,得到 54 個自相關結果,對這 54 個自相關結果使用拋物線擬合找到三個最大值的位置 peak1_idx1、peak_idx2、peak_idx3 為三個自相關係數最大的值的位置


認為基音是周期信號,極值出現的位置為周期上

故基音周期 T

T1 = peak_idx3 + 10

T2 = peak_idx2 + 10

T3 = peak_idx1 + 10

根據上面通過自相關及拋物線擬合找到的三個極值,得到了三個基音周期


取末端 20 個信號作為 A,滑動窗口 B 也是 20 個樣本(2.5ms),B 在距 A 一個基音周期前後 4 個樣本範圍內(0.5ms)滑動


故有個窗口滑動範圍,計算三個範圍內的窗口 A 和窗口 B 的失真度


取最小失真度三個極值,這三個極值作為通過失真最小計算的基音周期 T'

1, T'2, T'3

最小失真的衡量是以 A 和 B 對應元素差值絕對值之和最小為依據的


在得到基於自相關計算出來的三個基⾳周期 T1,T2,T3 和基於失真度最小得到的三個基⾳周期 T'1,T'2,T'3

通過比較這三對的 ratio = 自相值/失真度,當 ratio 最大時,則認為這對基音周期最佳

PLC 操作中,expand_vector0 和 expand_vector1 的計算細節如下圖:


構造 AR 濾波器參數,通過獲取一組(7 個)自相關值,對這組自相關值通過 LevinsonDurbin 算法計算,預測出 AR 濾波器參數。

AR 濾波器是線性預測器,通過歷史數據來預測當前數據;在構建 PLC 包時,通過對歷史數據進行 AR 濾波,來預測丟失的包信息。


下圖是對這個過程的簡要說明:

AR 濾波器公式如下所示:

這裡 k = 7;e(n)為預測值和實際值的誤差;AR 濾波就是通過使用最近的歷史數據來預測當前時刻數據;LevinsonDurbin 算法通過自相性值的計算來預測 ck,使得 e(n) 最小。

自相關性越大,說明 e(n) 越小;

LevinsonbDurbin 算法通過使用自相關性值來預測 AR 濾波器參數,可以理解為使⽤ e(n)來預測。


融合

融合操作一般是上一幀丟失了,但當前幀正常;

上一幀是通過預測生成的 PLC 包,和當前幀需要做平滑處理,防止兩種銜接出現明顯變化;

融合就是完成該功能,融合的組要過程有:

需要 202 個擴展樣本信號,主要是向 sync_buffer_ 未播放的信號借用 sync_buffer_ 未播放的信號不足 202,通過生成足夠 PLC 數據來湊滿


通過擴展樣本信號和輸入信號,計算期它們的自相關性值,及通過拋物線擬合來獲取基音周期


對 input 數據進行 Ramp 信號變換

對擴展信號和 input 信號部分段進行混合

從緩存算法取歸還借用的信號數據到 sync_buffer_

將算法緩衝區剩餘的平滑處理的信號追加到 sync_buffer_

下圖對 NetEQ 中融合過程的簡要說明:

(基音周期和 mute_factor,點擊查看大圖)

(混合 expand 和 input 信號,點擊查看大圖)

normal

當前幀可以正常播放,也就是可以直接將這段信號送⼊到 sync_buffer_ 中, 但是因為上一幀可能是 expand 等,需要進行相關的平滑操作。


主要步驟如下:

將輸入信號拷貝到算法緩衝區生成一段 PLC 數據包


根據背景噪聲和輸入語音信號的能量比值,計算 mute_factor


根據 mute_factor 對算法緩衝區數據按照能量從弱到強的趨勢修正


將 expand 和算法緩衝區的頭 1ms 音頻數據進行平滑混合,結果存在算法緩衝區;


相關混合平滑公式如下:

將最終的算法緩衝區數據最加到 sync_buffer_ 後


下圖對 normal 的操作流程做補充說明:


NetEQ 相關 buffer

NetEQ 為了消除抖動、解碼音頻數據、對解碼數據進行 DSP 處理(加減速、PLC、融合、平滑數據),以及平滑播放等,使用了多個緩存 buffer,下面是對這些 buffer 的簡要說明:

packet_buffer_: 用來接收網絡中收到的音頻包數據,也可稱為抖動緩存,會定時刪除當前時間 5 秒前的包,同時最多緩存 200 個包,即 4 秒(這⾥以每個包 20ms 計算)。


decoded_buffer_: 當播放線程每次獲取音頻數據來播放時,會根據目標延時和抖動緩存延時來判斷是否需要從 packet_buffer_中獲取音頻數據進行解碼,解碼後的數據存放到 decoded_buffer_中。最多可緩存 120ms 數據。


algorithm_buffer_: decoded_buffer_中的數據經過 DSP 處理後,將存放到該緩存,一般每處理一次會清空一次。


sync_buffer_: 一般是從算法緩存拷貝過來的數據,是待播放的數據;sync_buffer_中有兩個變量,一個是 next_index,表示當前播放的位置,next_index 前的數據表示已經播放,後面的數據表示待播放;另一個是 endtimestamp, 表示 sync_buffer_中最後一個待播放的數據,也就是最近的音頻數據量。


該 buffer 可最多緩存 180ms 數據,是一個循環緩存;播放線程每次會從 sync_buffer_中取 10ms 數據進行播放。

關於何時從 packet_buffer_中取數據解碼說明:

一般情況下每次從 packet_buffer_中取 10ms 數據進行解碼。


當要執行 expand 操作時,但 sync_buffer_中有 10ms 以上數據,不從 packet_buffer_中取數據解碼。


當做加速操作時,若 sync_buffer_中有 30ms 以上數據,不從 packet_buffer_中取數據;或若 sync_buffer_中有 10ms 以上待播放數據,同時上次解碼了 30ms 數據,則不從 packet_buffer_中獲取。


當加速操作,若 sync_buffer_待播放數據小於 20ms,同時上次解碼的數據小於 30ms,從 packet_buffer_中獲取 20ms 數據待解碼。


當要做減速操作時,若 sync_buffer_中待播放的數據大於 30ms;或待播放的數據小於 10ms,但上一次解碼的數據多餘 30ms, 則不從 packet_buffer_中獲取數據。


當要做減速操作,sycn_buffer_中待播放的數據量小於 20ms, 且上次解碼的數據量少於 30ms,獲取 20ms 數據進行解碼。


NetEQ 能很好地跟蹤網絡抖動,同時在消除抖動時保證延時儘量小,對音頻體驗提升明顯;結合上一篇關於弱網對抗的一些技術,可明顯提升音頻在弱網環境下的體驗。

參考資料:

[1]: WebRTC 語音引擎中 NetEQ 技術的研究

[2]:http://www.gipscorp.alcatrazconsulting.com/files/english/datasheets/NetEQ.pdf

[3]: https://baike.sogou.com/v7850130.htm

[4]:A Review of Time-Scale Modification of Music Signals

關鍵字: