如何优化WebRTC提升直播体验?
全民快樂資深音視頻工程師郭奕在LiveVideoStackCon 2018音視頻技術大會的演講中從工程師的角度講述了如何利用WebRTC打造出具備實時互動能力的應用,包括從信令的交互到媒體的傳輸需要完成的工作。LiveVideoStack對演講內容進行了整理。
文 / 郭奕
整理 / LiveVideoStack
大家好,我是來自全民快樂科技有限公司的郭奕,接下來我將從一個工程師的角度為大家分享如何更好地利用WebRTC為應用賦能。本次分享將以“給音視頻實時通訊應用打分“為線索,與大家一起探索如何提升以直播連麥、傳統音視頻會議等為主要應用場景的實時互動音視頻通訊用戶體驗。
1.?給音視頻實時通訊應用打個分
如果按照顏值為上圖中的幾位人物打分,我想絕大多數人會把最高分留給右二的秋香,這其實告訴我們一定要結合實際場景才能為音視頻實時通訊應用打出客觀準確的分數。
?
本次賦能的目標APP為StarMaker——全民快樂旗下一款活躍用戶主要集中在印度、印尼、中東等地區的音樂視頻社交APP,Android端用戶規模遠大于iOS端。
?
雖然StarMaker的品類應當被定義為社交應用,但卻具備非常多的音樂基因,這就使得單純的人聲清晰已無法滿足用戶對這款APP的需求,我們需要進一步提高此款產品在音樂方面的綜合質量。
?
在我們引入RTC之前此應用就已具備直播等功能,且已經具備一套非常完善的音視頻采集、處理體系。考慮到平臺的可維護性與模塊的復用性,我們需要在對其進行改造的同時盡可能保持其核心模塊的完整與穩定。
?
一款優秀的RTC應用具備哪些特質呢?最重要的應當是實時性。優秀的RTC應用能夠給用戶近乎面對面交流的快感,用戶需要做的也僅是聯網后打開APP即可。如果不考慮服務本身與服務器架構,在良好的網絡環境與理想的終端條件下實現上述目標并不是一件十分困難的事情,而這顯然是不現實的。因此面對復雜的網絡環境與碎片化的終端情況我們能做的只有努力適應與提高兼容性,這也是實現良好用戶體驗的必由之路。
?
首先我們給自己定一個小目標:為實現70分的RTC應用我們應當做出什么努力?
?
首先從設備端到服務器的往返時延需要被控制在100ms,且在此基礎上控制丟包率在30%;唇音同步也是一項需要達到的關鍵指標,而端對端的延遲需小于400毫秒;最后一點需達到的便是能夠為2015年以后的設備提供流暢完整的服務。這些數據并非簡單的確立,經過統計我們發現北京辦公室與一個部署在印度的機房之間的RTT大約在150ms左右,而印度機房中里有80%以上的RTT都在100ms以下;30%的隨機丟包則是一個RCT SDK較為入門的達標水準,而根據Google最新的官方統計大約70%以上的Android用戶所使用的移動終端已經預裝或升級至Android 6.0以上的系統,且Android 6.0 的發布時間在2015年4月左右。由于我們的用戶多集中于 Android 平臺,這就要求我們在設計之初需要考慮到平臺可在NEXUS 6與iPhone 6以后的設備上穩定運行。
2.?集成WebRTC
我們的工作就是將WebRTC集成至應用,主要從服務器端與客戶端兩方面入手工作。雖然WebRTC是一開始是按照P2P設計的,但是為提高服務穩定性我們需要背后需要強大的服務器作為支撐;而從信令角度來說WebRTC也不能完全算作P2P。
?
作為建立通話實現控制的基礎,信令服務器在WebRTC所需服務器中至關重要,而NAT穿透服務器則是WebRTC中建立媒體過程必需的服務器支持;媒體服務器則是為完成諸如多方通訊、視頻錄制等較為繁重的媒體處理任務必不可少的關鍵一環。其中媒體服務器主要分為RTP轉發與混流,前者是我們較為熟悉的SFU而后者則是MCU。
?
上圖展示的是一個互動直播所需的基本框架,可以看到我們使用了SFU與MCU。SFU的優點在于可節省一部分寶貴的上行帶寬而MCU的優點則是可明顯節省流量成本并將多路流混成一路流,再將其轉為RTMP并轉推至CDN。結合連麥場景,上圖左側連接SFU并傳輸媒體流的三個設備可以理解為連麥的三方,SFU在接受來自連麥三方的媒體流的同時會將此三方媒體流轉至MCU并進行混流與RTMP流轉換處理,處理完成的媒體流會被推送至CDN從而讓觀眾端可從CDN上拉去相應媒體流并觀看視頻。
?
如何快速搭建可完成上述處理流程的服務器框架?這里我們就需要借助開源的力量,上圖展示了一些我們所參與社區提供的良好解決方案:SFU的開源服務器解決方案有Licode、Janus、Jitsi與Mediasoup,在選擇時我們需要考慮整個團隊的技術棧情況,若團隊技術棧偏向于底層,那么推薦選擇更多使用C++的Janus方案,而如果習慣基于Java開發那么Jitsi則是不錯的選擇;這里需要提醒的是,Licode中包含一個官方稱之為MCU的模塊,但實際上其并不具備混流的功能。
如果是MCU的開源服務器解決方案我們推薦選擇Kurento,其內部使用了GStreamer而最底層則使用glib;但Kurento的學習曲線非常陡峭這樣的好處在于其整個接口的靈活性非常出色,但出色的靈活性也意味著內部的高復雜性,這也是我們在選擇此方案是需要重點關注的方面。如果對MCU的要求沒有如此嚴苛,我們也可以使用FFmpeg自研的服務器。
?
我們的客戶端集成了WebRTC,在iOS平臺的Safari瀏覽器支持WebRTC后移動端集成WebRTC的方式主要分為以下三種:依賴手機瀏覽器的Web方式與直接將WebRTC原生代碼集成至應用端的原生方式,以及兼具二者特性的混合方式。
混合方式的好處在于其可跨越平臺限制為Web端帶來接近于原生的特性與交互體驗,其代表有Cordova與React Native;但這兩種方案還遠不能滿足我們期待的一個Web在所有平臺都能提供一致體驗的需求且Cordova的版本更新迭代頻繁,因此選擇此方案的前提是技術棧更偏向前端而底層如WebRTC則需支持,同時混合方式的試錯成本也比較高。
當然我們也可以選擇原生的方式,前提是并沒有非常強的跨平臺需求。由于我們的業務不需要PC端僅依賴移動端開展,原生自然而然成為我們集成WebTRTC的首選方式。
?
上圖展示的是我們的Android原生應用軟件框架圖,主要基于以下幾個關鍵點進行架構:首先框架需要具有一定移植性,允許我們在Android端完成開發后將平臺快速移植至iOS端;其次請觀察圖中標為橙紅色的三個基于WebRTC C++原生代碼庫建立的模塊,分別為通話管理、媒體引擎與信令模塊;而在最上層使用紅框標記的部分則是API接口。其中第一層與應用相關,根據不同應用場景區分;左側的Android API則包含傳統的RTC通話等。當我們將應用遷移至iOS時所需完成的工作量可以明顯減小,僅需要將上層接口換成OC,媒體引擎做一些適就可以了。
?
原生移動應用信令的選擇是接下來需要我們關注的工作內容。考慮到信令模塊的移植性,我們通常會通過以下三種方式完成對原生移動信令的選擇:第一種是自定義信令,之所以考慮這種信令是因為WebRTC并沒有限制信令的用途,我們只需選擇一種合適的信令類型并將足夠的信息傳遞給WebRTC即可,因此Websocket或Unix Socket都是我們考慮的方式;而Jingle也是我們考慮的一種,多用于早期WebRTC版本;SIP是滿足傳統VoIP設備兼容的不二選擇,發展至今也有許多非常成熟的開源解決方案。完成以上集成WebRTC的步驟,一個70分的RTC應用便初步構建完成。
3.?滿足現有應用需求
為了讓集成的應用初步滿足現有需求,接下來我們需要完成的工作是外部音頻與視頻的采集。
?
上圖展示了外部音頻采集的大致流程,其中紅色部分為SDK的使用者也就是APP所需完成的工作,黑色部分則是WebRTC的SDK負責的模塊,(紅框部分則是我們需要重點實現的模塊)。外部采集到的音頻會首先進入External Audio Device Module流程,隨后交由Fine Buffer處理;隨后Fine Buffer會將傳入的音頻流按照10毫秒的粒度傳送至Audio Processing Module這一WebRTC的重要模塊,其中音頻會經過回聲消除等處理;經由Audio Processing Module處理完成后的音頻數據會被推回至調用者以做音效處理最終被傳輸至編碼器進行編碼以為發送至網絡做好準備。
?
外部視頻采集流程與音頻有所不同,本地視頻的渲染工作交由外部流程完成,采集到的視頻流會首先傳輸至H.264 Video Capturer處理,通過不參與渲染與編碼的Broadcaster統計重要信息并通知Fake Video Encoder對來自Broadcaster的視頻數據流進行編碼處理。
?
為了確保音頻質量符合StarMaker的較高需求,我們選擇業界較為優秀的AAC而非Opus作為音頻編碼器。
?
為了更提高兼容性與控制轉碼負擔,我們選擇了傳統的H.264而非VP8/VP9作為視頻編碼器。雖然在性能與帶寬節省方面H.264不算最優秀的編碼方案,考慮到在我們的實際應用場景中有RTMP推流的工作,H.264對整個硬件生態的支持更佳符合我們的需求。
4.?適應網絡環境
單純的滿足現有應用需求距離我們的目標還遠遠不夠,適應網絡環境尤其如何對抗弱網是擺在我們面前的另一項關鍵命題。
?
來自于Facebook的開源工具Augmented Traffic Control是我們選擇的一個物美價廉的網絡環境模擬方案,其本質為底層基于Linux的一個TC工具,同時提供了非常完善的上下性丟包、帶寬限制、壞包、亂序等模擬方式,其簡單高效甚至可以像上圖右側那樣使用單片機進行部署。
我們使用以下兩種類型的工具箱作為對抗弱網的方案:如矛般包含擁塞算法可實現主動攻擊的ARC自動碼率控制,也被稱為GCC或Client Side BWE,主要從客戶端進行帶寬估計;而如盾般進行被動防御的有ARQ自動重傳請求(WebRTC中還有與ARQ類似的選擇性重傳)、FEC前項編碼糾錯與PLC丟包補償。可以看出WebRTC在此方面做出了大量努力,如果存在一款集成以上所有工具的編碼器是否會為我們帶來較為出色的弱網對抗效果呢?事實也的確如此,如Opus就集成了FEC與PLC。由于視頻的帶寬資源更多,ARC更多用于視頻場景,可調節空間也較為充裕。WebRTC中也集成了針對音頻的類似于ARC的模塊,其被稱為ANA(Audio Network Adaptor),作用主要是對音頻碼率進行微調,但僅針對Opus。這里需要提醒的是,PLC與ARQ、FEC有所不同,PLC主要通過信號處理的原理人為構造出流暢度較高的音頻。
但在實際應用當中我們一開始并沒有選擇FEC,主要原因如下:
?
谷歌并不推薦在H.264條件下同時使用FEC與NACK,二者只能選一運用,混合使用FEC與NACK主要針對于VP8、VP9。如上圖所示,以上7個Packet中Packet 1~Packet 3為一幀圖像而Packet 4~Packet 5則為另外一幀圖像,中間的FEC 1與FEC 2兩個包則是用于視頻恢復的冗余數據。假設FEC 1與FEC 2發生丟失現象即會出現首先我們需要知道的是RTP包中的Sequence Number必須連續,我們才能根據Sequence Number判斷哪些包丟失,而H.264即通過此方式判斷丟包;當FEC 1與FEC 2丟失之后,H.264會判斷丟失一個關鍵幀或P幀,實際上僅丟失一段冗余信息而已,但此時H.264便會發起丟包重傳,這無疑是一種對帶寬的浪費。之所以VP8、VP9不存在類似的問題,是因為VP8、VP9具有非常豐富的RTP Payload Header,不僅包括各種的邊界檢查,也攜帶了更多的額外信息。其中最重要的便是用于標識包屬于哪一幀并正確排序的Picture ID,可在出現FEC 1與FEC 2 丟失情況時偵測到Packet 3之后的Packet 4依舊存在,從而有效避免了不必要的重傳操作,這便是我們不選擇FEC的一個重要原因。
?
既然我們在音頻與視頻都使用了NACK,就需要注意以下幾個關鍵點:首先NACK僅適用于低延遲場景,所經歷的RTT時間并不長;如果用于高延遲場景便會出現重傳效果不盡如人意的狀況,因此面對高延遲場景我們應當有效控制重傳頻率,極力避免反復重傳甚至重傳風暴的現象發生。其次,丟包率的統計也易受到影響,RTCP的丟包統計嚴重依賴所收到包的數量,接收到兩個完全一樣包的概率可能會增加,其會極大影響丟包率的準確性,也許會造成丟包率數據符合要求而實際丟包控制效果卻十分糟糕的情況;加之丟包率會對擁塞控制算法產生影響,一旦相關參數設置對丟包的敏感不夠就會令擁塞控制形同虛設。
經過上述優化,獲得一個70分的RTC應用便不再成為一件困難的事情。
5.?更上一層樓
當然,70分還遠遠不夠,我們應該給自己設立更高的目標。如何實現出色的RTC應用,便是我們接下來探索的方向。
?
AEC是第一個需要改進的方面,WebRTC會優先選擇AEC處理。對于iOS而言其AEC整體性能較為出色,而對Android來說其AEC依舊具備非常大的提升空間,有些Android設備的AEC甚至并沒有發揮其應有的效果。由于Android平臺的碎片化特征,我們需要盡可能通過集成在軟件內部的AEC解決方案實現滿足較為一致的處理效果。WebRTC中的AECM處理算法專用于移動端的回聲消除,考慮到整個移動端包括CPU在內的硬件整體計算能力,AECM被簡化了許多環節,這樣帶來的副作用便如上圖展示的那樣,對比AECM處理前后的音頻頻譜我們可以發現部分音頻會被直接刪去,這一定不是我們期待的優化結果。因此,我們可以針對以上缺點對AECM優化。
?
除了AEC,上圖展示的那樣是一種基于丟包閾值與延遲閾值制定的混合抗丟包解決方案也是一個優化的方向:在高延遲條件下我們盡量采用FEC方案,當延遲低于某一閾值時則采用NACK方案;在高丟包率條件下我們采用ARC處理以實現對編碼率的控制,而在低丟包率條件下則不使用ARC。
點擊【閱讀原文】或掃描圖中二維碼了解更多LiveVideoStackCon 2019 上海 音視頻技術大會 講師信息。
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的如何优化WebRTC提升直播体验?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 音视频技术开发周刊 86期
- 下一篇: 微博客户端播放器的演进之路