webRTC中音頻相關的netEQ(三):存取包和延時計算


上篇(webRTC中音頻相關的netEQ(二):數據結構)講了netEQ里主要的數據結構,為理解netEQ的機制打好了基礎。本篇主要講MCU中從網絡上收到的RTP包是怎么放進packet buffer和從packet  buffer里取出來,以及網絡延時值(optBufLevel)和抖動緩沖延時值(buffLevelFilt)的計算。先看RTP語音包是怎么放進packet  buffer的。

 

前面說過把從網絡收到的RTP包放進packet  buffer時有個slot概念,每個slot里放一個包的屬性(比如timestamp、sequence number等)和payload。Packet buffer初始化時把numPacketsInBuffer(已有的包個數)和insertPosition(下一個包放的位置)置成0,也把屬性和payload置成合理的值(比如把payloadLengthBytes(payload長度)置成0,把payloadType(負載類型)置成-1)。當一個RTP包要放進packet  buffer時,先要看packetbuffer是否為空(即numPacketsInBuffer是否為零)。如為空,直接把包放在slot 0的位置(把包的屬性以及payload放到slot 0的位置上)。如不為空,insertPosition加1,先看這個slot上是否有包(標志是payloadLengthBytes是否為零,為零表示沒包。當這個slot上的包被取走時payloadLengthBytes會被置為零)。如有包說明packet buffer 滿了,需要reset(代碼中叫flush)packet buffer,然后把當前包放在slot 0的位置(Packet buffer中能放的包個數是一個很大的值,通常不會放滿)。如未滿,則直接放在下一個slot上。下面舉例說明。假設packet buffer最多可以放240個包,則slot范圍是0—239,如下圖。從網絡上收到的第一個包會放在slot 0 的位置,第二個包放在slot1的位置,以此類推,第240個包會放在slot239的位置。當一個包從packet buffer取出時,相應slot就又被初始化了。當第241個包來時就又放到slot0 的位置。當包放進相應slot時要check這個slot里是否有包(標准是payloadLengthBytes是否為零),有包則說明packet buffer已滿需要reset/flush,然后這個包放在slot0的位置。

 

 

接下來看怎么從pakcet buffer里取一個語音包,這要依賴於從DSP模塊帶來的timestamp值(記為timestamp_from_dsp)。遍歷packet buffer里每個slot,如果這個slot上語音包的timestamp小於timestamp_from_dsp,並且slot上有payload,就可以認為這個包來的太遲應該主動discard掉,包括reset這個slot和packet buffer里包個數減一等。遍歷完packet buffer后把離timestamp_from_dsp最近的語音包的timestamp(即語音包的timestamp減去timestamp_from_dsp的值最小)對應的slot作為將要取出來的slot,把語音包從這個slot取出來后同樣要reset這個slot以及packet buffer里包個數減一等。

 

從上面的描述可以看出這種放包的方式是比較簡單的,是按照語音包到netEQ的先后順序依次放在buffer里,而且可能是亂序的(收到包時就有可能是亂序的)。這就要求在取包時要遍歷buffer把離DSP模塊帶過來的timestamp最近的timestamp的包取出來,遍歷要用for循環,這就增加了計算量。這跟我以前做的jitter buffer的設計是有很大區別的。那種思想是把亂序的包排好序后再放在buffer里,取包時就不需要遍歷buffer,而是從頭上依次向后取。具體怎么實現的可以看我前面的文章(音頻傳輸之Jitter Buffer設計與實現)。

 

下面看怎么計算網絡延時統計值(optBufLevel),這是難點之一。假設每包20Ms,理想情況下每隔20Ms從網咯上收到一個語音包。實際情況是網絡有延時丟包抖動,導致並不是每隔20Ms收到一個包,而是有時幾十甚至100多毫秒收不到一個包,有時20Ms內收到幾個包。我們要算出網絡延時的統計值,作為產生向DSP發出控制命令的依據之一。怎么算呢?netEQ用包到的時間間隔來算,它的意思是當前收到的包相對上一個收到的包的時間間隔,以包個數為單位。當每收到一個包時就把packetIatCountSamp(已采樣點數為單位)清零,以后每取一幀數據播放就把packetIatCountSamp加上一幀的采樣點數(以AMR-WB每幀20Ms為例,每幀有320個采樣點。每取一幀,packetIatCountSamp就增加320),當下一個包到時,拿packetIatCountSamp除以320就可以 算出兩個包之間的間隔了。

 

下面給出計算網絡延時的算法:

1,  計算當前包絕對到達間隔iat(以數據據包個數為單位),計算公式如下:

根據公式,提前到達的數據包的iat均為0,正常到達的iat為1,延遲一個包時間到達的Iat為2。Iat的最大值為64,即有65種(0—64)種可能。

2,  更新iat在每個值(0—64)上的概率分布。初始化時每個值(0—64)上的概率均為0,隨着包的到來,每個值上的概率都在動態的改變着。概率更新分以下幾小步:

1)  用遺忘因子f對當前概率進行遺忘,計算公式如下:

這里有個遺忘因子(forgetting factor)的概念。每個值上的概率均要算一下,得到新的概率。

2)  增大本次計算到的iat的概率,計算公式如下:

   

3)  更新遺忘因子f,使f為遞增趨勢,即通話時間越長,包間隔iat的概率分布越穩定。計算公式如下:

4)  調整本次計算到的iat的概率,使整個iat的概率分布之和近似為1。假設當前概率分布之和為tempSum,則計算公式如下:

3, 統計滿足95%概率的iat值,記為B。根據下式可以算出B的值。

 

4, 統計iat的峰值

netEQ中采用兩個長度為8的數組來統計iat的峰值,一個用來存峰值幅度,另一個用來存峰值間隔。峰值間隔是結構體automode中另一個參數peakIatCountSamp,用於統計當前探測到的峰值距離上次探測到的峰值的間隔,以樣本個數為單位。當iat的值大於2B時就認為峰值出現了,把當前的iat和peakIatCountSamp值存在數組里。如果數組未滿,就放在上一個峰值位置后的空的位置上;如果滿了,就淘汰掉數組里最早的那個峰值,其他峰值左移,並把新的峰值放在數組里index為7的位置上。這里需要說明的是當兩個數組的值不足8個時,峰值數組是不起作用的。

 

5, 計算optBufLevel

當峰值數組起作用並且當前peakIatCountSamp小於等於峰值間隔數組中最大的間隔兩倍時,optBufLevel 取峰值數組中的最大值。否則optBufLevel 就為B。

 

以上是我照本宣科的把怎么算網絡延時的算法表述出來。我基本理解了算法的思想,但是不清楚算法中的一些系數是怎么得到的。用google搜了一下,沒找到相關的文檔說明,這也是好多開源軟件的通病,沒有文檔。我猜測是相關開發人員用數學建模的方法得到的系數值吧。如果有哪位朋友知道,麻煩給講講,先謝謝了。講講我對這個算法的理解吧。算網絡延時是基於概率來算的,共有65個樣本(0延時,一個包延時,2個包延時,…….64個延時)。初始化時各個樣本的概率(占的百分比)均為0。通話后某個延時值出現了它的概率值就要變大,相應的其他延時值的概率就要變小(已經為零的沒辦法再變小,依舊為零)。算法里先用遺忘因子去減小各個延時值的概率,然后再去變大本次延時值的概率,為了保證概率和為1要做一些微調(也會去更新遺忘因子)。然后從零延時開始把各個延時值的概率加起來,達到95%的值就可初步認為是延時值了。比如0延時概率為0.1, 1個包延時概率為0.7, 2個包延時概率為0.09,3個包延時概率為0.07,這時概率和為0.96,已達到0.95的線,取網絡延時最大的值3,就可初步認為網絡延時為3個包的延時。還要看當前網絡狀況,如果一段時間內頻繁出現延時的峰值,說明當前網絡環境比較糟糕,為了提高語音質量需要加大網絡延時的值,就把峰值數組里的最大值,作為最終的網絡延時值。

 

算網絡延時是在語音包放進packet buffer后。算抖動緩沖延時是在收到DSP模塊給MCU模塊發反饋信息后(要用到反饋信息)以及從packet  buffer取語音包前。下面給出計算步驟:

1,  根據packet buffer里已有的語音包的個數算出已有的樣本數,記為samples_in_packetbuffer,這依賴於采樣率和包時長,以AMR-WB為例,采樣率為16kHZ,包時長為20ms,可算出每包有320個樣本。假設packet  buffer里有5個語音包,則packet buffer里已有的樣本數為1600 (1600 = 320*5)。

2,  在speech  buffer里未播放的(即sampleLeft)樣本也要算在抖動緩沖延時內。它與packet buffer內的樣本數相加就是實時的抖動緩沖延時(samples_jitter_delay,以樣本個數為單位),即samples_jitter_delay = samples_in_packet_buffer + samplesLeft,再除以每包樣本數samples_per_packet,就可以得到實時抖動緩沖延時值(以包個數為單位)。

3, 計算bufferLevelFilt,公式如下:

   

這里計算的是抖動緩沖延時的自適應平均值,f是計算均值的遺忘因子,根據網絡狀況自適應的變化,具體取值見下式:

 

其中B為前面算網絡延時時的B值(以包個數為單位)。

4, 如果經過加速或者減速播放,則需要去修正bufferLevelFilt,公式如下:

    

其中samplesMemory表示加速或減速播放后數據長度的伸縮變化,已樣本個數為單位。若為加速,sampleMemory為正值,bufferLevelFilt減小;若為減速,sampleMemory為負值,bufferLevelFilt變大。

 

上面講了MCU中網絡延時和抖動緩沖延時的計算,MCU也收到了DSP模塊發過來的反饋報告。后面MCU就要根據這些來決定給DSP模塊發什么樣的控制命令(加速/減速等),這是下一篇的主要內容。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM