上篇(webRTC中音頻相關的netEQ(二):數據結構)講了netEQ裏主要的數據結構,爲理解netEQ的機制打好了基礎。本篇主要講MCU中從網絡上收到的RTP包是怎麼放進packet buffer和從packet buffer裏取出來,以及網絡延時值(optBufLevel)和抖動緩衝延時值(buffLevelFilt)的計算。先看RTP語音包是怎麼放進packet buffer的。html
前面說過把從網絡收到的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的位置。web
接下來看怎麼從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(以數據據包個數爲單位),計算公式以下:post
根據公式,提早到達的數據包的iat均爲0,正常到達的iat爲1,延遲一個包時間到達的Iat爲2。Iat的最大值爲64,即有65種(0—64)種可能。google
2, 更新iat在每一個值(0—64)上的機率分佈。初始化時每一個值(0—64)上的機率均爲0,隨着包的到來,每一個值上的機率都在動態的改變着。機率更新分如下幾小步:url
1) 用遺忘因子f對當前機率進行遺忘,計算公式以下:spa
這裏有個遺忘因子(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模塊發什麼樣的控制命令(加速/減速等),這是下一篇的主要內容。