本文分享了聲網Agora 首席音訊工匠高澤華在 RTC 2018 實時網際網路大會上,以《SOLO X™:相容 WebRTC 標準的抗丟包語音編碼器》為題的演講。主要介紹了聲網推出的抗丟包語音編碼器 Agora SOLO™ 的演算法特點,以及第二代編解碼器 Agora SOLO X™ 的新特性與測試效果。以下為演講實錄。
去年,我們在 RTC 大會上分享了自主研發的抗丟包編解碼方案 Agora SOLO™ ,主要是面向抗丟包網路,解決實時通訊傳輸 last mile 的問題。
傳統的抗丟包思路
有時,大家對丟包問題會有一些誤解。很多人認為隨著 4G 的普及和 5G 的建設,是不是丟包問題變得不重要了?我認為並不是這樣,因為在現有網路條件下,不論是 server 到 server,還是 sever 到 last mile 都存在著大量的丟包問題。
我們談到丟包就涉及到丟包的概念,到底什麼叫做丟包?其實任何沒有按時到達的包都是丟包。如果把一個包的延時拉到足夠大,一般來說,我們可以通過無數次重傳來解決丟包問題。如果這個包沒有在規定的時間到達,都算丟包。而且,丟包也不一定是網路原因導致,有時候是由於系統的排程等原因造成。
舉個例子。通常,Android 系統上播放執行緒、解碼執行緒和收包執行緒都可以不是一個執行緒。Android 並不是特別實時的作業系統。一旦系統比較忙的時候,就容易產生收包執行緒和解碼執行緒的衝突,導致丟包產生。而這種丟包是由於系統產生的。甚至在解碼時,解碼器完成解碼後交給播放器播放,其中的 buffer 也會導致卡頓。
丟包也與頻寬限制相關。如果頻寬足夠大,可以通過無數次重傳來實現抗丟包效果。但如果頻寬很有限,就不能完全依靠無限制的重傳來實現抗丟包效果。
比如說,曾經聯通做過這樣的事情,流量包月,但每個月的頻寬只能在 128kbps,滿足你對網頁和音視訊的基本需求。在這種服務下,如果你開一些高效能或者大位元速率的信源通訊的時候就會產生頻寬擁塞,頻寬擁塞首先導致延時增加,下一步就會產生丟包。所以,實際上頻寬和丟包也相關。
由於我們聲網在全球佈網,非洲、東南亞、中東和印度的網路狀況與中國的網路狀況相差還比較遠。即使在中國的網路環境下,上海和偏遠地區的網路狀況也是不一樣的。所以,並不是以後上了 5G 就不存在丟包問題了,相反,我認為隨著網路的差異化會導致丟包的問題變得越來越普遍。
舉一個例子,描述一下什麼情況會產生丟包?如上圖所示為典型的丟包模型,device 1 與 device 2、device 3、device 4 進行通訊。比如,device 1,把 packet 1、packet 2、packet 3 一起發給 device 2,packet 1、packet 2、packet 3 丟了一個包是 packet 2。在這個時間視窗計算丟包率時,當前的丟包率是 33%。此時 device 2 會給 device 1 發回一個回傳控制,即 loss info,loss info 帶上丟包率是 33% 的資訊。根據這個資訊,device 1 會重新對 device 2 進行抗丟包的處理,發兩個 packet 4 和兩個 packet 5。那麼,當丟了一個 packet 4 時,還有一個 packet 4 可以起到抗丟包的效果。也就是說,在整個系統中,在第一個時間視窗,packet 1、packet 2、packet 3,在這 3 個包內算丟包率,再把丟包率回傳到 device 1,由 device 1 根據丟包率的資訊,去傳送抗丟包的冗餘。
在其中,我們會發現幾個問題。第一是時間視窗問題。如果按照 3 個時間視窗來算,丟包率是 33%。如果按照 4 個時間視窗算,丟包率是 25%。如果按照 2 個時間視窗算,丟包率是 50%。這就產生一個問題,即丟包率算的其實沒有那麼準。
第二,device 2 接收到 device 1 傳送的包之後,會回傳一個 loss info 給 device 1。一般來講,loss info 中會加入一些 FEC 的控制,保證它不會被丟。但實際上 loss info 的包仍可能會丟失。
我們假設上述情況中 loss info 沒有丟包,device 1 傳送冗餘的包給 device 2,譬如傳送兩個 packet 4 和兩個 packet 5。由於此時的丟包率是 33%。那麼 packet 4 丟掉之後,對於 packet 4 來說,起到了抗丟包的效果,但是對於 packet 5 來說,發的兩個 packet 5 並沒有起到抗丟包的效果,這對網路頻寬是浪費的。
再假設前面視窗也取準了,loss info 也沒有丟,packet 4、packet 5 也都取得了抗丟包的效果,還是會存在一個問題。在回傳的通道中已經有很長的 RTT 在裡面,發過去一個包,device 2 進行估算,估算後,再傳送一個 loss info 回到 device 1。如果 RTT 太長,發包的過程中還是會不斷出現丟包。也就是在長 RTT 的情況下,這種抗丟包方案的效果是比較有限,或者可以說存在一些缺陷。
我們還可以假設以上問題都不存在。在多人通話中,每個裝置接收端的網路環境不同,假設 device 1 與 device 2 之間是 3G 或有損的 Wi-Fi 環境,而 device 3、device 4 分別使用 4G、5G。那麼,device 1 在處理 loss info 時應該採取什麼策略呢?如果都加入 FEC 的話,就意味著浪費了 device 3 和 device 4 的頻寬,但如果不想浪費頻寬,沒有 FEC ,就意味著無法實現對 device 2 的抗丟包效果。
所以在多人環境下,網路頻寬的差異化是做網路策略時要考慮的重要因素。同時,還有一些人為或成本因素需要我們在做網路策略時進行綜合考慮。例如,在中國與印度之間進行實時通訊時,其中一個 server 在印度當地,可以直接與中國的 server 進行連線。但印度的通訊成本較高,會產生一定的資費。這時我們可以選擇通過新加坡的 server 繞個遠,但這個過程又會產生一些丟包問題。
Agora SOLO™ 的演算法思路
在多人環境下,傳統的抗丟包策略無法很好解決這類問題,所以聲網在去年 10 月份推出了一個新的編碼器叫 Agora SOLO™。
一段音訊訊號通過 Agora SOLO™ 處理後,會生成兩個成對的包 packet 1 和 packet 2 ,它們為同一幀,並且互補。即我收到一個 packet 1 的時候,我可以解碼出一個 8kbps 的窄帶音訊訊號,如果我收到一個 packet 2,也可以解碼出一個 8kbps 的窄帶音訊訊號,但如果我同時收到一個 packet 1 和一個 packet 2,我就可以恢復出一個 16kbps 的寬頻高質量音訊訊號。
一定程度上講,這個編解碼器可以解決剛才提到的 FEC 遇到的所有抗丟包的策略問題。
通常,編解碼器做的事情是壓縮、去冗餘,而抗丟包從一定程度上講是通道處理的擴大化。抗丟包是一種糾錯演算法的擴充套件,通過加冗餘實現抗丟包。而對於 Agora SOLO™ 來說,是把去冗餘和加冗餘進行結合,對重點資訊加冗餘,對非重點資訊則更多的去冗餘,以達到在通道和信源的聯合編碼的效果。
我們強調了在編碼器上加一定的冗餘去抗丟包,實際上就會帶來一些編碼效率的降低。但這種情況下,我們可以通過增加一些新的演算法,減少編碼效率的降低。並且,和原始的 codec 編碼效率相比,從結果上看還要更好一些。
上圖是我們用 ITU NTT 的中文測試序列跑的測試結果。稍微介紹一下,ITU 的 NTT 是標準的編解碼器測試序列,裡面有 26 國語言,這裡只拿出了中文部分的測試結果。那麼橫座標內的 PESQ 是窄帶編碼器的客觀測試標準,最後一列,PESQ-WB 是寬頻編解碼的客觀測試標準,滿分是 5 分,通常打到 4 分就是完美了。後面有我們這個編碼器與一些傳統編碼器在 PESQ 分數的對比。
我們可以看到,是隻收到 8kbps 的 packet 1, PESQ 的 MOS 分是 3.52 分。如果只收到 packet 2,MOS 分是 3.51 分。如果 packet 1 和 packet 2 都收到,在16kbps 時,MOS 分是 3.95 分。以上是窄帶的分數。寬頻下的 PESQ 的 MOS 分是 3.582 分。
用新的編碼器後,我們再看一下它的效果。此時就不需要再考慮是否加 loss info,我們可以直接從 device 1 給所有的 device 2、device 3、device 4 發 packet 1、packet 1’、packet 2、packet 2’、packet 3、packet 3’的包。
為什麼可以這樣做?因為實際上,如果 packet 1 或者 packet 1’ 丟包,不會有太大影響,你仍然可以恢復出一個有限的 8kbps 質量的音訊資料。如果沒有丟包,收到了 packet 1、packet 1',就可以恢復出 16kbps 的音訊資料,其中並沒有任何的冗餘資訊。這種演算法不像原來的 FEC,兩個包完全相等,或者一大一小,那會造成頻寬浪費。這種演算法不會造成頻寬浪費,那麼你就無需考慮以下問題:
第一,延時問題;
第二,回傳 loss info 的丟包問題;
第三,多人通訊中,每個網路頻寬不一致的問題。
總體來說,這個編碼器被我稱為接近完美的抗丟包編解碼,因為沒有完美的編解碼器。
Agora SOLO™ 有如下四個特點:
第一,更低延時。無需回傳通道丟包資訊,預設傳送多倍資料;
第二,更高質量。收到一個包質量可達到普通編解碼器水平,收到兩個包質量即可達到高質量編解碼的水平;
第三,面向多人環境;
第四,簡化策略。無需策略調整,不擔心策略顆粒度問題。
所以從抗丟包的四種方法(FEC、PLC、ARC、ARQ)來看,需要想盡各種辦法去做 balance。但有了這種方法後,你無需考慮太多,直接呼叫即可。另外在有限的成本下,如果同時使用一條高質量的高成本的伺服器的頻寬和一條低成本相對有丟包下的頻寬時,你可以發兩條流,分別是 packet 1 和 packet 1’,這樣可以起到更好的效果。
如上圖所示,是我們做的 Agora SOLO™ 和其他編碼器的對比。從圖中我們可以很快發現 ILBC 和 AMR-NB 的 MOS 分比較低,這是由於它們倆是上個時代的編碼器,因此屬於正常現象。
SILK 和 Opus 的 MOS 分比較高,但我們會發現 SILK+FEC20 時,基礎 MOS 分會下降得非常厲害,因為拿一部分頻寬去做冗餘就會導致基礎 MOS 分下降。
而 Opus+FEC20 時,實際上編不出在丟包 5% 時的位元速率,因為位元速率控制的設計相對比較保守。但我們可以看到它加 FEC20 後,基礎 MOS 分在下降。
而 Agora SOLO™ 的基礎 MOS 分和 SILK、Opus 幾乎一樣,下降在 0.2 之內。而它在抗丟包時,一旦發生丟包,幾乎沒有 MOS 分下降。在丟包 5%、10%、15%、20% 時,它的 MOS 分維持在一個相對穩定的情況,和競品比有時候可以達到 1 分的差距。
做過通訊 MOS 分競品比較的人應該知道,高 0.3-0.5 分已經是非常困難,高 1 分幾乎是不可能。以 20% 為例,Agora SOLO™ 的 MOS 分是 3.2,而 SILK 卻只有 2.2 和 2.3,雖然加上 FEC20 後是 3.0,但加上 FEC 後 SILK 的基礎 MOS 分會比較低。
這是 ITU NTT 的中文測試第一條序列——女聲。女聲相對來說比較難編,因為中國人說話有四聲問題,比其他語言更復雜。女聲的高頻資訊比男聲更豐富,所以編起來也會更難。
由圖可知,SILK16 的 MOS 分是 3.5,SILK16+FEC 編不出來,Opus16 的 MOS 分是 3.5,Opus16+20FEC 的 MOS 分是 3.3,Opus20+20FEC 的 MOS 分是 2.8。雖然位元速率更高了,但由於頻寬原因,給 FEC 的位元速率更高,它的核心位元速率反而更低,所以它的基礎 MOS 分變得很低。
而 Agora SOLO™ 的 基礎 MOS 分是 3.5,在不丟包的情況下它和 Opus16 和 SILK16 的 MOS 分基本一致。在丟包的情況下,以 20% 為例,它的 MOS 分是 2.5,比其他都高,這就是 Agora SOLO™。
模擬測試結果
我們不僅做了線下 ITU 的網損測試,還做了線上的模擬測試。如上圖所示,我們用兩臺 iPhone 去做模擬丟包的測試,device A 經過 Wi-Fi、內網 VOS 和 Wi-Fi,去和 device B 通訊。
這是我們的測試結果。紅色是丟包率,綠色是丟幀率。從圖中我們可以看到,丟包率原本是 50% 左右,經過 Agora SOLO™ 之後,它的丟幀率只剩 20%。NOVA 是我們的另一個編碼器,它的丟包率和丟幀率都是 50%,沒有做任何的 FEC。
當採用一包兩幀策略做抗丟包的時候,在 20% 的丟包率下,Agora SOLO™ 基本沒有丟幀的產生。而傳統的編碼器下,丟幀率還是有一定的損失。
相容 WebRTC 標準的抗丟包語音編碼器
過去一年裡,我們把 Agora SOLO™ 編碼器的抗丟包部分移植到了 OPUS 上,我們給這個技術起了一個新的名字叫分組譜互補技術,並開發了一個新的編碼器叫 SOLO X™。
SOLO X™ 演算法和 SOLO™ 是一樣的,那我們直接看結果上的差異。
16kbps 時,我們可以看到 SOLO X™ 的 MOS 分會下降到 3.2。它的抗丟包的效果在 20% 的時候是 2.4,和 Agora SOLO™ 差不多,比 Opus20 和 Opus16 都要好。
如果是 20kbps 時,它的 MOS 分是 3.46。在丟包 20% 時,它的 MOS 分是 2.6 ,還是比 Opus16+20FEC、Opus20+20FEC 的效果好,基礎 MOS 分也高。
它的優勢主要是和 OPUS 結合後,可以與 OPUS 互相相容。用聲網 Native 的 SDK 和 WebRTC 的 SDK 進行互通時,如果它支援 SOLO X™ 編碼器,它就可以解出兩條更高質量的流。而為了做到相容,會有一些質量損失,導致 SOLO X™ 在 16kbps 的 MOS 分比 SOLO™ 要下降一些,這也是我們後期優化的主要方向。
對於 8kHz 來看,為了起到相容的效果,它的基礎 MOS 分也有下降,但是它的抗丟包效果沒有明顯下降。
和 WebRTC 做相容後,我們對 WebRTC 的互通做了測試。 googDecodingPLC 的意義是加了網損後,當外邊沒有收到包時,會呼叫 googDecodingPLC 函式去解碼,做後端的抗丟包補償。圖中右側縱軸的單位是幀,即這段時間內補償了多少幀來做抗丟包。如果我們實現了抗丟包的效果,則不用調大量後端 PLC 做補償。
從上圖資料可以看到,在只使用 OPUS 編碼器時,在 20% 和 40% 的丟包模擬下,可以看到 5 分鐘以內補償幀數分別在 6 千次和 11 千次左右。用 SOLO X™ 時,20% 丟包的情況下,補償幀只有 1K 多一點。40% 丟包時,丟包補償幀只有 4K 左右,可以看到明顯下降,這意味著已經有更多的包到達解碼端,不需要呼叫 PLC 做補償。
SOLO X™ 和 WebRTC 互通中的資料表現
如上圖所示,是我們在相容模式和非相容模式下的 PESQ-MOS 分。前面給的資料都是相容 Opus 編碼器的 MOS 分,而非相容模式下 PESQ-MOS 分可以做的更高。
實際上這張圖也是 PESQ-WB 的 MOS 分,理論上,我們應該用 POLQA 去比分,那麼在 48kHz,48kbps 和 32kHz,32kps 去比,MOS 分的差異和影響對比會更大。因為對高頻的權重越高,一旦有損失就會導致分比較低。這也是為什麼幾乎相同的對比策略和解碼輸出, PESQ-WB 的分比 PESQ 的分要低的原因。
在非相容模式下,它的 MOS 分會遠遠高於相容模式。後期我們會針對相容模式進行優化,我覺得是有機會達到接近非相容模式時的效果的。
Agora SOLO™ 的下一步
2017 年,我們花了兩年時間把 SOLO™ 的編碼器做了出來,今年做 SOLO X™ 目的是與 OPUS 互通,目前主要做 Voice 這一塊,計劃 2019 年推出針對音樂的編碼器,相信效果會更突出,因為音樂在實際產生中都是使用 128k 或者 196K 的位元速率,這種位元速率下做 FEC 的代價會非常非常高。
最後我想說,對創業公司來說花大力氣做編解碼是很吃虧的事情。因為做編碼器很累,也很苦,普通公司通常不會去做。所以我很感謝聲網給我這個機會,也感謝 Tony 的鼓勵。坦白講,無數的不眠之夜,整夜整夜的失眠才做出了 Agora SOLO™ 和 SOLO X™。
掃碼觀看演講視訊回顧(觀看該演講請空降02:32分)