webRTC中音頻相關的netEQ(二):數據結構


上篇(webRTC中音頻相關的netEQ(一):概述)是netEQ的概述,知道了它主要是用於解決網絡延時抖動丟包等問題提高語音質量的,也知道了它有兩大單元MCU和DSP組成。MCU 主要是把從網絡收到的語音RTP包放進packet buffer內,同時也會根據計算出來的網絡延時和抖動緩沖延時以及DSP單元反饋過來的信息決定給DSP發什么控制命令(命令主要有正常播放、加速、減速、丟包補償、融合等),也會把語音包從packet buffer里取出來給DSP單元處理。DSP主要是對取出來的語音包解碼並根據MCU給出的控制命令做信號處理。本篇我們繼續講netEQ,講主要的數據結構。

 

要研究一個模塊,首先得搞清楚它的數據結構。netEQ中最頂層的結構體是MainInst_t(也就是netEQ結構體),它主要包含兩個成員變量,一個是DSPInst_t,另一個是MCUInst_t,正好對應netEQ的兩個單元DSP和MCU。具體見圖1(這里把次要的成員變量忽略掉了,下同)。在netEQ初始化時生成netEQ的實例,實例中包含DSP和MCU兩個子實例。

 

                                                               圖 1

先看DSP結構體,見圖1。pw16_readAddress 和pw16_writeAddress用於與MCU交互數據。MCU中也有這兩個成員變量,先說一下MCU和DSP是怎么交互的。MCU會給DSP發控制命令執行何種信號處理算法,就把命令相關的數據寫在自己的pw16_writeAddress的地址上,讓 DSP到這個地址上取數據,即DSP的pw16_readAddress就是MCU的pw16_writeAddress。DSP處理完一幀后會給MCU發反饋數據,反饋數據就寫在自己的pw16_writeAddress地址上,MCU就從這個地址上去讀反饋數據, 即MCU的pw16_readAddress就是DSP的pw16_writeAddress。main_inst指向父結構體netEQ(MainInst_t),這是一種常見的操作手法,便於找到父結構體實例。speechBuffer(語音buffer)用於存放經過解碼和信號處理過的語音數據。它分兩塊,一塊是已經播放過的語音數據,另一塊是未播放過將要播放的語音數據,成員變量curPosition是分界點。另一成員變量endPosition表示語音buffer的大小,這依據采樣率來定。可以用圖2表示這三個成員變量的關系:

 

                                                              圖 2

endTimestamp用於記錄語音buffer中未播放的語音數據的最后的時間戳(MCU給DSP的控制命令中會帶當前幀的時間戳,解碼后通過換算就可以得到endTimestamp)。fs是采樣率。w16_frameLen是每幀的采樣點數。w16_mode是當前幀的處理方法(是加速處理還是減速處理等),這個值會帶給MCU,MCU根據這和網絡延時抖動緩沖延時等決定下一幀的處理命令。pw16_speechHistory和w16_speechHistoryLen用於丟包補償(PLC,控制命令是EXPAND),pw16_speechHistory放最近播放過的歷史語音數據,要做PLC時就以這些歷史語音數據作為參考數據產生補償的語音數據。w16_speechHistoryLen是放歷史語音數據的buffer(即pw16_speechHistory)的長度,是個定值,依采樣率而定。DSP結構體中還有幾個子實例,主要有decoder實例(CodecFuncInst_t)、丟包補償實例(ExpandInst_t)和背景噪聲生成實例(BGNInst_t)。

 

上面說到MCU和DSP的數據交互,我們看一下交互的是什么數據。MCU發給DSP的是控制命令,控制命令數據占3個short大小,第一個short是命令相關的,第二三個是timestamp的高16位和低16位。DSP發給MCU的是反饋數據,反饋數據的結構體如圖3:

 

                                                             圖 3

playedOutTS表示語音buffer中最后數據的時間戳,等於DSP結構體中的endTimestamp。samplesLeft表示語音buffer總未播放的數據長度。lastMode表示上一幀的處理方法,等於DSP結構體中的w16_mode。frameLen表示上一幀解碼后成長度。

 

再看MCU結構體,見圖1。first_packet在初始化時置成1,后來收到包后置成0。用它主要是對收到第一個包后給MCU的一些成員變量(比如SSRC)賦值。pw16_readAddress 和pw16_writeAddress用於與DSP交互數據,同DSP中的一樣。main_inst也同DSP中的一樣。MCU中也有兩個主要的實例,一個是PacketBuffer_inst,它用於存放從網絡收到的語音包。另一個是BufferStat_inst,它用於統計網絡延時等。這兩個均是非常重要的結構體。先說PacketBuffer_inst,它的定義如圖4:

 

                                                            圖 4

初始化時會分配一塊可以存放最大個數(最大個數事先定義好了)語音包的buffer。存放的內容有timeStamp/payloadLocation(包的payload放的地址,指向payload)/seqNumber/payloadType/payloadLengthBytes/rcuPlCntr/waitingTime/payload等(見上圖的紅框內部分)。存放時並不是每個包的timestamp/payload等放在一起,而是所有包的timestamp放在一起,所有包的sequence number放在一起,其他也是,這樣就得到了如下的buffer分布圖5:

 

                                                            圖 5

圖5看起來不直觀。netEQ中有slot的概念,每個包的timestamp/payload等放在同一個slot內,這樣圖5就可以表示成圖6(圖中每塊 buffer都是連續的,上一塊buffer的尾部就是下一塊buffer的首部,比如timestamp的尾部就是payload location的首部),這樣看起來就直觀多了。要獲取某個包的屬性或者payload,就可以通過slot_index得到。比如要獲取第0個包的timestamp,就可以表示成timestamp[0]。怎樣存放包搞清楚了就可以很好的理解這個結構體內的其他成員變量了。packSizeSamples表示上一解碼的包有多少個采樣點。startPayloadMemory表示放payload的起始地址。memorySizeW16表示分配的buffer還剩下的memory size。currentMemoryPos表示放下一個包的payload的起始地址,這個值會放在下一個包的對應的payloadLocation里。等下一個包來后,currentMemoryPos會加上這個包的payload長度重新賦給currentMemoryPos,並作為放下下個包的payload的起始地址。numPacketsInBuffer表示packet buffer里放了多少個包。insertPosition表示下一個包要放的位置。maxInsertPositions表示packet buffer最大可放的包個數。discardedPackets表示主動丟棄的包個數。

 

                                                              圖 6

再說BufferStat_inst,它的定義如圖7:

 

                                                              圖 7

w16_noExpand表示上一包的處理是不是EXPAND。avgDelayMsQ8表示平均緩沖延時。maxDelayMs表示最大緩沖延時。AutomodeInst_t是BufferStat_inst的子實例,,主要用於計算網絡延時和抖動緩沖延時。它的定義如圖8:

 

                                                           圖 8

成員變量主要分三部分,一是iat(inter-arrival time,相鄰包到的時間間隔)統計相關的, iat以包個數為單位,假設一個包20Ms,兩個相鄰包到的時間間隔是40Ms,iat就為2。有一個大小為65的數組來存放iat(iat從0到64)個數統計的值,基於這些值算網絡延遲統計值。二是iat峰值統計相關的,用兩個長度為8的數組來存放iat的峰值,一個用來存放峰值幅度,另一個用來存放峰值間隔。峰值間隔是automode結構體中另一個參數peakIatCountSamp,它用於統計當前探測到的峰值距離上次峰值的時間間隔,以采樣點個數為單位。三是包相關的,有lastSeqNo(上一個收到包的sequence number)、lastTimeStamp(上一個收到包的timestamp)等。這些都是為了計算optBufLevel(網絡延時)和buffLevelFilt(抖動緩沖延時)。MCU發給DSP的控制命令就是根據網絡延時和抖動緩沖延時以及上一次的處理方式等得到的。需要注意的是這些變量中一些是以Q格式(Q格式相關的可以見我前面的文章Android手機上Audio DSP頻率低 memory小的應對措施)表示的,算網絡延時和抖動緩沖延時都是用Q格式算的,這加大了理解的難度。

 


免責聲明!

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



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