上篇(webRTC中音頻相關的netEQ(三):存取包和延時計算)講了語音包的存取以及網絡延時和抖動緩沖延時的計算,MCU也收到了DSP模塊發來的反饋報告。本文講MCU模塊如何根據網絡延時、抖動緩沖延時和反饋報告等決定發給DSP模塊的控制命令, 好讓DSP模塊先對取出的語音包做解碼處理(如果有的話)以及根據這些命令做信號處理。
MCU模塊給DSP模塊發的控制命令主要有正常播放(normal)、加速播放(accelerate)、減速播放(preemptive expand)、丟包補償(PLC,代碼中叫expand)、融合(merge),還有些次要的,如解碼器重新初始化(decoder re-init)、packet buffer 重置(packet buffer reset)等。這里講主要的控制命令是怎么決策的,次要的相對簡單就略去不講了,有興趣的可以自己去看相關代碼。DSP模塊收到這些命令后會轉化為自己的處理命令,下表列出了主要的控制命令和處理命令的映射關系。
MCU模塊給DSP模塊發什么樣的控制命令首先取決於當前幀和前一幀的接收情況。當前幀和前一幀的接收情況主要分以下四種(對當前幀和前一幀做排列組合得到四種情況):
1,當前幀和前一幀都接收正常,數據包會進入正常的解碼流程。MCU模塊會發正常播放、加速播放、減速播放三種控制命令中的一個給DSP,解碼后的數據會根據命令做相應的處理。
2,當前幀接收正常,但前一幀丟失。如果前一幀丟失,但當前幀接收正常,說明前一幀是通過丟包補償生成的。為了使前一幀由丟包補償生成的數據和當前沒有丟包的幀的數據保持語音連續,需要根據前后幀的相關性做平滑處理。這種情況下MCU模塊會發正常播放、融合兩種控制命令中的一個給DSP。DSP模塊先對當前幀解碼,然后解碼后的數據會根據命令做相應的處理。
3,僅當前幀發生丟包或延遲,這時就不需要解碼了。MCU模塊會發丟包補償命令給DSP,DSP模塊會進入丟包補償單元來生成補償數據。
4,當前幀丟失或延遲,前一幀同樣丟失或延遲。MCU模塊會連續的發丟包補償命令給DSP,DSP模塊也會連續的進入丟包補償單元來生成補償數據。不過越到后面生成的補償數據效果越差。
在上面的四種情況中,有些情況下MCU模塊只會發一種命令給DSP模塊,比如當前幀丟失,只會發丟包補償控制命令給DSP。但有些情況下MCU模塊可能會發幾種命令中的一種給DSP模塊,比如當前幀和前一幀都接收正常,MCU模塊會發正常播放、加速播放、減速播放三種控制命令中的一個給DSP。到底發哪種命令呢,這就取決於網絡延時、抖動緩沖延時以及DSP發給MCU的反饋報告等因素了。這是本文的重點,下面具體講。
在DSP發給MCU的反饋報告中有個變量playedOutTS,它表示已經播放到的PCM數據的時間戳。同時MCU中還有個變量availableTS,它表示packet buffer中能獲得的有效包的起始時間戳,顯然如果availableTS等於0表示packet buffer為空。如果playedOutTS等於availableTS,它說明語音包正常接收;如果playedOutTS小於availableTS,它說明有語音包的丟失或者延時,需要做丟包補償(PLC)。上篇文章(webRTC中音頻相關的netEQ(三):存取包和延時計算)中講了網絡延時值(optBufLevel)和抖動緩沖延時值(buffLevelFilt)的計算,MCU就是要根據時間戳(playedOutTS & availableTS)的關系和延時(optBufLevel & buffLevelFilt)的關系以及上一幀的播放模式等來決定發什么樣的控制命令給DSP。先舉個簡單的例子,如果playedOutTS = availableTS並且buffLevelFilt > optBufLevel,這說明當前包正常接收,但是抖動緩沖延時大於網絡延時,即緩沖的包多了增加了時延,需要做加速播放處理,所以MCU會發加速處理命令給DSP。下面給出各個控制命令的條件,即滿足所列條件時MCU就會發相應的控制命令給DSP。
1,正常播放控制命令 / 加速播放控制命令 / 減速播放控制命令的條件
正常播放控制命令為BUFSTATS_DO_NORMAL。加速播放控制命令為BUFSTATS_DO_ACCELERATE,加速播放的原因是要播放的數據正常到達,但是抖動緩沖延時大於網絡延時,增加了時延,因此要加速播放。減速播放控制命令為BUFSTATS_DO_PREEMPTIVE_EXPAND,又稱為優先擴展控制命令。減速播放的原因是要播放的數據正常到達,但是抖動緩沖延時小於網絡延時,會引起播放時聲音的斷續,降低音質,因此要減速播放,拉長時間,使不會出現斷續。這三種控制命令都有一個必要條件,那就是playedOutTS = availableTS, 所以把這三種控制命令放在一起講。在“playedOutTS = availableTS”條件下,只有三種控制命令可供選擇,要么正常播放,要么加速播放,要么減速播放。把加速播放和減速播放的條件搞清楚了,剩下的就是正常播放條件了。下圖更直觀的說明了正常播放、加速播放和減速播放的關系,去掉加速和減速的就是正常播放的了。
先看加速播放的條件。它的第二個條件是上一幀播放模式不為丟包補償(第一個條件為playedOutTS = availableTS),第三個條件是下列兩個之一:
1) 加速播放第三個條件之一
其中sample_rate為采樣率,如8000/16000。samples_per_packet為每個包的采樣點數,以16KHZ/每包20ms為例,samples_per_packet就為320。 timescaleHoldOff初始化為32,且每發生一次加速或減速播放就右移一位。此參數是為了防止連續的加速或減速播放惡化人耳的聽覺感受。
2)加速播放第三個條件之二
再看減速播放的條件。它的第一個條件與第二個條件與加速播放一樣,第三個條件如下:
說實話我沒有完全理解上面三個加減速的數學表達式的判據,尤其是系數值的選取。我知道不能簡單的認為“buffLevelFilt > optBufLevel"就加速“buffLevelFilt < optBufLevel"就減速,要真的這么判斷的話效果肯定是不好的。如果哪位朋友理解了,麻煩給講講,先謝謝了!
除加減速播放這些條件外,剩下的就是正常播放了。可以寫成如下的偽代碼:
If(playedOutTS == availableTS)
{
If(上一幀播放模式不為丟包補償)
{
If(加速播放第三個條件是下列之一 || 加速播放第三個條件是下列之二)
return 加速播放;
If(減速播放第三個條件)
return 減速播放;
}
return 正常播放;
}
2,丟包隱藏控制命令的條件
丟包隱藏控制命令為BUFSTATS_DO_EXPAND,又稱為擴展控制命令。丟包隱藏的原因是要播放的包已丟失或者還沒到(延時)。發生丟包隱藏的場景有:
1)availableTS = 0,即packet buffer為空,顯然這時需要做丟包補償。
2)playedOutTS < availableTS,即要播放的包丟失或者延時到,但是packet buffer里有緩沖包,需要滿足下面兩個條件之一即可:
a) 上一幀播放模式不為丟包補償
b) 上一幀播放模式為丟包補償,且前面幾幀均為丟包補償,這是連續丟包的場景,這時要看連續丟包補償的次數。netEQ設定最多可以補償100ms的數據,以每包20ms為例,最多可以補償5個包,其實100ms后的補償效果也不好了。所以連續丟包補償的次數小於5的話,還會繼續丟包補償,否則就不做丟包補償了。
3,融合控制命令的條件
融合控制命令為BUFSTATS_DO_MERGE,主要用於丟包隱藏后產生的PCM數據與從packet buffer里取出的數據的銜接過程。所以產生融合控制命令的條件是:
1)playedOutTS < availableTS (此式也表示packet buffer不為空,為空時availableTS = 0)
2)上一幀的處理模式為丟包補償
以上就是主要的控制命令的條件。可能不同版本之間可能有差異,我是根據我用的版本來講的。就像上面我說的,有些我也沒有完全理解,只是把它照本宣科的講出來。苦於沒有文檔呀,網上也沒有相關的論述,目前只能先這樣了,后面如果完全理解了再補充。