白話解讀 WebRTC 音頻 NetEQ 及優(yōu)化實踐,webrtc 人聲優(yōu)化白話解讀 WebRTC 音頻 NetEQ 及優(yōu)化實踐NetEQ 是 WebRTC 音視頻核心技術(shù)之一,對于提高 VoIP 質(zhì)量有明顯的效果,本文將從更為宏觀的視角,用通俗白話介紹 WebRTC 中音頻 NetEQ 的相關(guān)概念背景和框架原理,以及......
NetEQ 是 WebRTC 音視頻核心技術(shù)之一,對于提高 VoIP 質(zhì)量有明顯的效果,本文將從更為宏觀的視角,用通俗白話介紹 WebRTC 中音頻 NetEQ 的相關(guān)概念背景和框架原理,以及相關(guān)的優(yōu)化實踐。
作者| 良逸審校| 泰一
為什么要 “白話” NetEQ
隨便搜索一下,我們就能在網(wǎng)上找到很多關(guān)于 WebRTC 中音頻 NetEQ 的文章,比如下面的幾篇文章都是非常不錯的學(xué)習(xí)資料和參考。特別是西安電子科技大學(xué) 2013 年吳江銳的碩士論文《WebRTC 語音引擎中 NetEQ 技術(shù)的研究》,非常詳盡地介紹了 NetEQ 實現(xiàn)細節(jié),也被引用到了很多很多的文章中。
《WebRTC 語音引擎中 NetEQ 技術(shù)的研究》
NetEQ 算法
WebRTC 中音頻相關(guān)的 NetEQ
這些文章大部分從比較 “學(xué)術(shù)” 的或 “算法” 的角度,對 NetEQ 的細節(jié)做了非常透徹的分析,所以這里我想從更宏觀一些的角度,說一下我個人的理解。白話更容易被大家接受,爭取一個數(shù)學(xué)公式都不用,一行代碼都不上就把思路說清楚,有理解不對的地方,還請大家不吝賜教。
丟包、抖動和優(yōu)化的理解
在音視頻實時通信領(lǐng)域,特別是移動辦公(4G),疫情下的居家辦公和在線課堂 (WIFI),網(wǎng)絡(luò)環(huán)境成了影響音視頻質(zhì)量最關(guān)鍵的因素,在差的網(wǎng)絡(luò)質(zhì)量面前,再好的音視頻算法都顯得有些杯水車薪。網(wǎng)絡(luò)質(zhì)量差的表現(xiàn)主要有延時、亂序、丟包、抖動,誰能處理和平衡好這幾類問題,誰就能獲得更好的音視頻體驗。由于網(wǎng)絡(luò)的基礎(chǔ)延時是鏈路的選擇決定的,需優(yōu)化鏈路調(diào)度層來解決;而亂序在大部分網(wǎng)絡(luò)條件下并不是很多,而且亂序的程度也不是很嚴重,所以接下來我們主要會討論丟包和抖動。
抖動是數(shù)據(jù)在網(wǎng)絡(luò)上的傳輸忽快忽慢,丟包是數(shù)據(jù)包經(jīng)過網(wǎng)絡(luò)傳輸,因為各種原因被丟掉了,經(jīng)過幾次重傳后被成功收到是恢復(fù)包,重傳也失敗的或者恢復(fù)包過時的,都會形成真正的丟包,需要丟包恢復(fù) PLC 算法來無中生有的產(chǎn)生一些假數(shù)據(jù)來補償。丟包和抖動從時間維度上又是統(tǒng)一的,等一會來了的是抖動,遲到很久才來的是重傳包,等一輩子也不來的就是 “真丟包”,我們的目標就是要盡量降低數(shù)據(jù)包變成 “真丟包” 的概率。
優(yōu)化,直觀來講就是某個數(shù)據(jù)指標,經(jīng)過一頓猛如虎的操作之后,從 xxx 提升到了 xxx。但我覺得,評判優(yōu)化好壞不能僅僅停留在這個維度,優(yōu)化是要 “知己知彼”,己是自己的產(chǎn)品需求,彼是現(xiàn)有算法的能力,己彼合一才是最好的優(yōu)化,不管算法是簡單還是復(fù)雜,只要能完美的匹配自己的產(chǎn)品需求,就是最好的算法,“能捉到老鼠的就是好貓”。
NetEQ 及相關(guān)模塊
NetEQ 的出處
《GIPS NetEQ 原始文檔》,這是由 GIPS 公司提供的最原始的 NetEQ 的說明文檔(中文翻譯),里面介紹了什么是 NetEQ 以及對其性能的簡單說明。NetEQ 本質(zhì)上就是一個音頻的 JitterBuffer(抖動緩沖器),名字起的非常貼切,Network Equalizer(網(wǎng)絡(luò)均衡器)。大家都知道 Audio Equalizer 是用來均衡聲音的效果器,而這里的 NetEQ 是用來均衡網(wǎng)絡(luò)抖動的效果器。而且 GIPS 還給這個名字注冊了商標,所以很多地方看到的是 NetEQ (TM) 。上面的官方文檔中,有一條很重要信息,“最小化抖動緩沖帶來的延時影響”,這說明 NetEQ 的設(shè)計目標之一就是:“追求極低延時”。這個信息很關(guān)鍵,為我們后續(xù)的優(yōu)化提供了重要線索。
NetEQ 在音視頻通訊 QoS 流程中的位置
音視頻通訊對于普通用戶來說,只要網(wǎng)絡(luò)是通的,WIFI 和 4G 都可以,一個呼叫過去,看到人且聽到聲音,就 OK 了,很簡單的事情,但對于底層的實現(xiàn)卻沒有看起來那么簡單。單 WebRTC 開源引擎的相關(guān)代碼文件數(shù)量就有 20 萬個左右,代碼行數(shù)不知道有沒有人具體算過,應(yīng)該也是千萬數(shù)量級的了。不知道多少碼農(nóng)為此掉光了頭發(fā) :)。
下面這張圖,是對實際上更復(fù)雜的音視頻通訊流程的抽象和簡化。左邊是發(fā)快遞 (推流) 側(cè):經(jīng)過采集、編碼、封裝、發(fā)快遞;中間經(jīng)過網(wǎng)絡(luò)傳輸;右邊是接收 (拉流) 側(cè):接收、解包、解碼、播放;這里重點體現(xiàn)了 QoS(Quality of Service,服務(wù)質(zhì)量)的幾個大的功能,以及跟推拉流數(shù)據(jù)主要流程的關(guān)系。可以看到 QoS 功能分散在音視頻通訊流程中的各個位置,導(dǎo)致要了解整個流程之后才能對 QoS 有比較全面的理解。圖上看起來左邊發(fā)快遞側(cè)的 QoS 功能要多一些,這是因為 QoS 的目的就是要解決通訊過程中的用戶體驗問題,要解決問題,最好就是找到問題的源頭,能從源頭解決的,都是比較好的解決方式。但總有一部分問題是不能從源頭來解決的,比如在多人會議的場景,一個人的收流側(cè)網(wǎng)絡(luò)壞了,不能影響其它人的開會體驗,不能出現(xiàn) “一顆老鼠屎壞掉一鍋粥” 的情況,不能污染源頭。所以收流也要做 QoS 的功能,目前收流側(cè)的必備功能就是 JitterBuffer,包括視頻的和音頻的,本文重點分析音頻的 JitterBuffer NetEQ。
上面這張圖是對 NetEQ 及其相關(guān)模塊工作流程的抽象,主要包含 4 個部分,NetEQ 的輸入、NetEQ 的輸出、音頻重傳 Nack 請求模塊、音視頻同步模塊。為什么要把 Nack 請求模塊和音視頻同步模塊也放進 NetEQ 的分析中因為這兩個模塊都直接跟 NetEQ 有依賴,相互影響。圖里面的虛線,標識每個模塊依賴的其它模塊的信息,以及這些信息的來源。接下來介紹一下整個流程。
1. 首先是 NetEQ 的輸入部分:
底層 Socket 收到一個 UDP 包后,觸發(fā)從 UDP 包到 RTP 包的解析,經(jīng)過對 ***C 和 PayloadType 的匹配,找到對應(yīng)的音頻流接收的 Channel,然后從InsertPacketInternal輸入到 NetEQ 的接收模塊中。
收到的音頻 RTP 包很可能會帶有 RED 冗余包(redundance),按照 RFC2198 的標準或者一些私有的封裝格式,對其進行解包,還原出原始包,重復(fù)的原始包將會被忽略掉。解出來的原始 RTP 數(shù)據(jù)包會被按一定的算法插入到 packet buffer 緩存里面去。之后會將收到的每一個原始包的序列號,通過UpdateLastReceivedPacket函數(shù)更新到 Nack 重傳請求模塊,Nack 模塊會通過 RTP 收包或定時器觸發(fā)兩種模式,調(diào)用GetNackList函數(shù)來生成重傳請求,以 NACK RTCP 包的格式發(fā)快遞給推流側(cè)。
同時,解完的每一個原始包,得到了時間軸上唯一的一個接收時刻,包和包之間的接收時間差也能算出來了,這個接收時間差除以每個包的打包時長就是 NetEQ 內(nèi)部用來做抖動估計的 IAT(interarrival time),比如,兩個包時間差是 120ms,而打包時長是 20ms,則當前包的 IAT 值就是 120/20=6。之后每個包的 IAT 值經(jīng)過核心的網(wǎng)絡(luò)抖動估計模塊(DelayManager)處理之后,得到最終的目標水位(TargetLevel),到此 NetEQ 的輸入處理部分就結(jié)束了。
2. 其次是 NetEQ 的輸出部分:
輸出是由音頻硬件播放設(shè)備的播放線程定時觸發(fā)的,播放設(shè)備會每 10ms 通過GetAudioInternal接口從 NetEQ 里面取 10ms 長度的數(shù)據(jù)來播放。
進入GetAudioInternal的函數(shù)之后,第一步要決策如何應(yīng)對當前數(shù)據(jù)請求,這個任務(wù)交給操作決策模塊來完成,決策模塊根據(jù)之前的和當前的數(shù)據(jù)和操作的狀態(tài),給出最終的操作類型判斷。NetEQ 里面定義了幾種操作類型:正常、加速、減速、融合、拉伸(丟包補償)、靜音,這幾種操作的意義,后面再詳細的說。有了決策的操作類型,再從輸入部分的包緩存(packet buffer)里面取出一個 RTP 包,快遞給抽象的解碼器,抽象的解碼器通過DecodeLoop函數(shù)層層調(diào)用到真正的解碼器進行解碼,并把解碼后的 PCM 音頻數(shù)據(jù)放到DecodedBuffer里面去。然后就是開始執(zhí)行不同的操作了,NetEQ 里面為每一種操作都實現(xiàn)了不同的音頻數(shù)字信號處理算法(DSP),除了 “正常” 操作會直接使用DecodedBuffer里的解碼數(shù)據(jù),其它操作都會結(jié)合解碼的數(shù)據(jù)進行二次 DSP 處理,處理結(jié)果會先被放到算法緩存(Algorithm Buffer)里面去,然后再插入到 Sync Buffer 里面。Sync Buffer 是一個循環(huán) buffer,設(shè)計的比較巧妙,存放了已經(jīng)播放過的數(shù)據(jù)、解碼后未播放的數(shù)據(jù),剛剛從算法緩存里插入的數(shù)據(jù)放在 Sync Buffer 的末尾,如上圖所示。最后就是從 Sync Buffer 取出最早解碼后的數(shù)據(jù),快遞出去給外部的混音模塊,混音之后再國際快遞音頻硬件來播放。
另外,從圖上可以看出決策模塊(BufferLevelFilter)會結(jié)合當前包緩存 packet buffer 里緩存的時長,和 Sync Buffer 里緩存的數(shù)據(jù)時長,經(jīng)過算法過濾后得到音頻當前的緩存水位。音視頻同步模塊會使用當前音頻緩存水位,和視頻當前緩存水位,結(jié)合最新 RTP 包的時間戳和音視頻的 SR 包獲得的時間戳,計算出音視頻的不同步程度,再通過 SetMinimumPlayoutDelay 最終設(shè)置到 NetEQ 里面的最小目標水位,來控制 TargetLevel,實現(xiàn)音視頻同步。
NetEQ 內(nèi)部模塊
NetEQ 抖動估計模塊(DelayManager)
1. 平穩(wěn)抖動估計部分:
將每個包的 IAT 值,按照一定的比例(取多少比例是由下面的遺忘因子部分的計算決定的),累加到下面的 IAT 統(tǒng)計的直方圖里面,最后計算從左往右累加值的 0.95 位置,此位置的 IAT 值作為最后的抖動 IAT 估計值。例如下圖,假定目標水位 TargetLevel 是 9,意味著目標緩存數(shù)據(jù)時長將會是 180ms(假定打包時長 20ms)。
2. 平穩(wěn)抖動遺忘因子計算:
遺忘因子是用來控制當前包的 IAT 值取多少比例累加到上面的直方圖里面去的系數(shù),計算過程用了一個看起來比較復(fù)雜的公式,經(jīng)過分析,其本質(zhì)就是下面的黃色曲線,意思是開始的時候遺忘因子小,會取更多的當前包的 IAT 值來累加,隨著時間推移,遺忘因子逐漸變大,會取更少的當前包 IAT 值來累加。這個過程搞的有點復(fù)雜,從工程角度看完全可以簡化成直線之類的,因為測試下來 5s 左右的時間,基本就收斂到目標值 0.9993 了,其實這個 0.9993 才是影響抖動估計的最主要的因素,很多優(yōu)化也是直接修改這個系數(shù)來調(diào)節(jié)估計的靈敏度。
3. 峰值抖動估計:
DelayManager 中有一個峰值檢測器 PeakDetector 用來識別峰值,如果頻繁檢測到峰值,會進入峰值抖動的估計狀態(tài),取最大的峰值作為最終估計結(jié)果,而且一旦進入這個狀態(tài)會一直維持 20s 時間,不管當前抖動是否已經(jīng)恢復(fù)正常了。下面是一個示意圖。
NetEQ 操作決策模塊(DecisionLogic)
決策模塊的簡化后的基本判定邏輯,如下圖所示,比較簡潔不用解釋。這里解釋一下下面這幾個操作類型的意義:
ComfortNoise:是用來產(chǎn)生舒適噪聲的,比單純的靜音包聽起來會更舒服的靜音狀態(tài);
Expand(PLC):丟包補償,最重要的無中生有算法模塊,解決 “真丟包” 時沒數(shù)據(jù)的問題,造假專業(yè)戶 ;
Merge:如果上一次是 Expand 造假出來的數(shù)據(jù),那為了聽起來更舒服一些,會跟正常數(shù)據(jù)包做一次融合算法;
Accelerate:變聲不變調(diào)的加速播放算法;
PreemptiveExpand:變聲不變調(diào)的減速播放算法;
Normal:正常的解碼播放,不額外引入假數(shù)據(jù);
NetEQ 相關(guān)模塊優(yōu)化點
NetEQ 抗抖動優(yōu)化
由于 NetEQ 的設(shè)計目標是 “極低延時”,不能很好的匹配,視頻會議,在線課堂,直播連麥等非極低延時場景,需要對其敏感度進行調(diào)整,主要調(diào)整抖動估計模塊相關(guān)的靈敏度;
直播場景,由于對于延時敏感度可以到秒級以上,所以需要啟用 StreamMode 的功能(新版本中好像去掉了),而且也需要對其中參數(shù)進行適配;
服務(wù)于極低延時目標,原始的包緩存 packetbuffer 太小,容易造成 flush,需要按業(yè)務(wù)需要調(diào)大一些;
還有一些業(yè)務(wù)會根據(jù)自己的業(yè)務(wù)場景主動識別網(wǎng)絡(luò)狀況,然后直接設(shè)置最小 TargetLevel,簡單粗暴的控制 NetEQ 的水位。
NetEQ 抗丟包優(yōu)化:
原始的 WebRTC 的 Nack 丟包請求的觸發(fā)機制是用包觸發(fā)的,在弱網(wǎng)下會惡化重傳效果,可以改為定時觸發(fā)來解決;
丟包場景會有重傳,但如果 buffer 太小,重傳也會被丟棄,所以為了提高重傳效率,增加 ARQ 延時預(yù)留功能,可明顯降低拉伸率;
比較算法級的優(yōu)化是對丟包補償 PLC 算法的優(yōu)化,調(diào)整現(xiàn)有 NetEQ 的拉伸機制,優(yōu)化聽感效果;
開啟 Opus 的 Dtx 功能之后,在丟包場景會導(dǎo)致音頻 Buffer 變大,需要單獨優(yōu)化 Dtx 相關(guān)處理邏輯。
下面是 ARQ 延時預(yù)留功能開啟后的效果對比,平均拉伸率降低 50%,延時也會相應(yīng)增加:
音視頻同步優(yōu)化:
原始的 WebRTC 的 P2P 音視頻同步算法是沒有問題的,但是目前架構(gòu)上面一般都有媒體轉(zhuǎn)發(fā)服務(wù)器(SFU),而服務(wù)器的 SR 包生成算法可能會由于某些限制或者錯誤會不完全正確,導(dǎo)致無法正常同步,為規(guī)避 SR 包生成錯誤,需要優(yōu)化音視頻同步模塊的計算方式,使用水位為主要參考來同步,即在接收端保證音視頻的緩存時間是差不多大小的。下面是優(yōu)化效果的對比:
還有一種音視頻同步的問題,其實不是音視頻同步機制導(dǎo)致的,而是設(shè)備性能有問題,不能及時處理視頻的解碼和渲染,導(dǎo)致視頻數(shù)據(jù)累積,從而形成的音視頻不同步。這種問題可以通過對比不同步時長的趨勢,跟視頻解碼和渲染時長的趨勢,兩者匹配度會很高,如下圖所示:
總結(jié)
NetEQ 作為音頻接收側(cè)的核心功能,基本上包含了各個方面,所以很多很多音視頻通訊的技術(shù)實現(xiàn)里都會有它的蹤跡,乘著 WebRTC 開源快 10 年的東風,NetEQ 也變的非常普及,希望這篇白話文章能幫大家更好的理解 NetEQ。
作者最后的話:需求不停歇,優(yōu)化無止境
特別聲明:以上文章內(nèi)容僅代表作者本人觀點,不代表ESG跨境電商觀點或立場。如有關(guān)于作品內(nèi)容、版權(quán)或其它問題請于作品發(fā)表后的30日內(nèi)與ESG跨境電商聯(lián)系。
二維碼加載中...
使用微信掃一掃登錄
使用賬號密碼登錄
平臺顧問
微信掃一掃
馬上聯(lián)系在線顧問
小程序
ESG跨境小程序
手機入駐更便捷
返回頂部