本文由B端技術中心資深開發工程師馬家憶分享,原題“B站在實時音視頻技術領域的探索與實踐”,下文進行了排版和內容優化。
1、引言
直播行業從傳統的娛樂直播發展到教育直播、電商直播等形式,產生了很多新的玩法。傳統的直播是一位主播展示才藝,觀眾通過彈幕、送禮物等方式進行互動。隨著網絡質量不斷地提高,用戶也對直播平臺產生的新的要求,實時互動直播的場景就出現了,觀眾可以同時觀看多位主播之間互動的畫面,讓直播間的氣氛更好。B站直播的連麥PK、視頻連線業務就提供了這個能力。主播看到的是對方主播實時的流(延遲400ms以內),而觀眾看到的是“準實時”的流(延遲2~5s左右)。
本文講述搭建這樣一套最新流行的實時視頻直播系統需要了解的背景知識以及系統的整體架構,希望對大家有幫助。
2、系列文章
本文是系列文章中的第 13 篇,本系列總目錄如下:
《視頻直播技術干貨(一):揭秘百萬級粉絲互動的Facebook實時視頻直播》
《視頻直播技術干貨(二):P2P技術如何將實時視頻直播帶寬降低75%?》
《視頻直播技術干貨(三):實時直播答題系統的實現思路與技術難點分享》
《視頻直播技術干貨(四):首次披露快手是如何做到百萬觀眾同場看直播仍能秒開且不卡頓的?》
《視頻直播技術干貨(五):七牛云使用QUIC協議實現實時視頻直播0卡頓》
《視頻直播技術干貨(六):新浪微博實時直播答題的百萬高并發架構實踐》
《視頻直播技術干貨(七):實時視頻直播首屏耗時400ms內的優化實踐》
《視頻直播技術干貨(八):淘寶高清、低延時的實時視頻直播技術解密》
《視頻直播技術干貨(九):千萬級直播系統后端架構設計的方方面面》
《視頻直播技術干貨(十):一文讀懂主流視頻直播系統的推拉流架構、傳輸協議等》
《視頻直播技術干貨(十一):超低延時視頻直播技術的演進之路》
《視頻直播技術干貨(十二):從入門到放棄,快速學習Android端直播技術》
《視頻直播技術干貨(十三):B站實時視頻直播技術實踐和音視頻知識入門》(* 本文)
3、關于作者
馬家憶:B端技術中心資深開發工程師。
4、實時音視頻關鍵技術概述
從0到1搭建一套實時音視頻系統并支撐現有的業務,如果沒有接觸過這方面的東西會感覺無從下手。
我們可以看到,1996年IETF就推出了RTP協議用于實時音視頻傳輸,2011年Google推出了WebRTC用于網頁端實時音視頻通話(見《了不起的WebRTC:生態日趨完善,或將實時音視頻技術白菜化》)。
從這些現有的協議和項目中,我們可以發現實時音視頻技術的關鍵點,評估自身現有的基礎組件支持情況并結合業務場景尋找適合自己的解決方案。
5、關鍵技術1:傳輸協議
RTP協議(Real-time Transport Protocol)定義了在互聯網上傳輸實時音視頻數據的標準格式,屬于應用層協議。RFC 3550描述RTP協議的傳輸層主要使用UDP,RFC 4571描述了RTP協議的TCP傳輸方式。
在我們的實時音視頻場景中應當優先選擇UDP,理由如下:
1)TCP保證數據流的可靠性和順序性。TCP的超時重傳策略為了保證通用和公平,相對比較保守,重傳超時時間(RTO)可能會變的很大。假如中途丟失一個包,后續的包即使先到達也要緩存起來等待重傳完成以后才能送到應用層。在網絡狀況不佳的情況下,使用TCP傳輸會產生較大的延遲;
2)UDP允許數據包丟失、亂序和重復。即使數據丟失也不會阻塞接收緩沖區等待重傳,這就為實時性提供了保障。在上層的RTP協議中,協議頭部包含了時間戳和序列號,可以對數據包進行重排和丟棄,解決了亂序和重復的問題。如果接收端監測到丟包,并且丟失的包是必要的且無法恢復,則發送NACK消息通知發送端重傳(下一節會詳細探討這個話題)。
UDP雖然在低延遲領域上有壓倒性的優勢,但是用戶側有可能存在防火墻攔截所有的UDP包。考慮到在網絡環境足夠好的情況下使用TCP也能達到不錯的效果,因此我們做了一個降級策略,優先使用UDP,當且僅當UDP不通的時候使用TCP。
6、關鍵技術2:丟包補償
前面講到我們的傳輸層協議優先選擇UDP,那么就需要引入一些機制解決丟包問題。
前向糾錯FEC(Forward Error Correction)指的是發送端發送原始數據的同時附加部分冗余的信息,如果接收端檢測到原始數據丟失則嘗試使用冗余的信息進行恢復。發送端發送n個數據包,同時根據原始數據生成k個冗余的數據包,將n+k個數據包發送出去,接收端只要收到至少n個數據包就可以得到全部的原始數據。
FEC算法的關鍵在于異或。異或(Exclusive OR)是一個數學運算符,數學符號為“⊕”,兩邊數值轉換成二進制按位運算,相同時為0,不同時為1。
以一階冗余算法為例,n個數據包生成1個冗余包,發送n+1個數據包。我們發送三個數值分別為a、b、c,生成冗余數據x=a ⊕b ⊕ c一起發送。假如數值b在傳輸中丟失了,計算a ⊕c ⊕ x即可得到b。
在實際應用中,FEC沒有這么簡單,WebRTC實現了UlpFEC和FlexFEC,UlpFEC可以針對數據包的重要程度實施不同程度的保護以充分利用帶寬,FlexFEC還支持對列做冗余,同時WebRTC默認的音頻編碼器Opus本身就支持FEC。
前向糾錯適合少量隨機丟包的場景,可以無視網絡延遲時間,但是增加了帶寬消耗。
后向糾錯包括ARQ(Automatic Repeat Request)和PLC(Packet Loss Concealment)。ARQ指的是接收端檢測到數據丟失的時候發送NACK報文請求發送端重傳,適合突發大量丟包的場景,沒有額外的帶寬消耗,但是時效性取決于RTT,如果存在很多接收端還要考慮避免NACK風暴造成雪崩。PLC用于音頻,當數據缺失時使用模型根據前后數據預測丟失的數據。
總之,前向糾錯和后向糾錯各有優缺點,需要搭配使用。
7、關鍵技術3:流量控制
流量控制指的是根據網絡狀況的波動估算可用帶寬,根據帶寬的變化自動調節音視頻碼率和發送速率。當網絡質量變差的時候迅速降低數據量以確保實時性,網絡較好時則慢慢提升數據量帶來更清晰的畫面。在WebRTC中提供了優秀的Google Congestion Control算法,包括基于延遲的評估和基于丟包率的評估,取兩種評估方式的最小值作為目標帶寬通知編碼器和數據發送模塊。
基于延遲的評估算法包括Transport-CC和Goog-REMB,目前最新版的WebRTC默認使用的是Transport-CC。Transport-CC在發送端進行帶寬評估,接收端通過TransportFeedback RTCP包向發送端反饋每個RTP包的到達時間,發送端在一個時間窗口內計算每個RTP包到達時間與發送時間之差,通過Trendline濾波器處理后預測網絡狀況。假設我們當前處于Hold狀態,如果檢測到網絡狀態為OverUse,此時應該減小數據量,變更為Decrease狀態;如果檢測到網絡狀態為Normal,此時可以嘗試增加數據量,變更為Increase狀態。
基于丟包的評估算法是當網絡突發大量丟包時的兜底策略:
- 1)如果丟包率在2%以下的時候說明網絡質量好,目標帶寬增加8%;
- 2)如果丟包率在在2%~10%說明當前發送數據的帶寬和網絡質量相匹配可以保持不變;
- 3)如果丟包率大于10%說明網絡質量差,目標帶寬減小到(1-丟包率*0.5)* 當前帶寬。
8、關鍵技術4:數據緩沖
如果我們只考慮實時性,那么收到數據就立刻解碼并渲染必然是最好的選擇,但是網絡并不穩定,延遲、亂序、丟包、重復包都有可能發生。如果采用上面的策略,音頻可能因為網絡的抖動變的斷斷續續,視頻可能因為丟包導致缺少參考幀從而出現黑屏或花屏,所以有必要引入一個緩沖區,增加一點可以接受的延遲來保證用戶體驗。
在WebRTC中,視頻包會被放入JitterBuffer模塊進行處理,JitterBuffer會進行視頻包的排序、組裝成完整的幀、確保參考幀有效,然后把數據送到解碼器。同時,根據網絡狀況自適應地調節緩沖區的長度。音頻包會被放入NetEQ中,它維護了音頻的緩沖區,同時負責將音頻同步到視頻。我們做播放器一般都是以音頻的時間為基準同步視頻,但是WebRTC剛好相反,它是以視頻為基準的。當音頻數據堆積的時候加速音頻播放,音頻數據不足的時候降低速度把音頻拉長。
9、關鍵技術5:回聲消除
在語音通話的場景中,麥克風采集到的聲音發送給遠端,遠端的揚聲器播放出來以后又被遠端的麥克風采集到這個聲音并傳送回來,這樣講話的人會感覺到有回聲,影響體驗。
WebRTC提供了回聲消除算法AEC,時延估計(Delay Estimation)模塊找到揚聲器信號和麥克風信號的時延,線性自適應濾波器(Linear Adaptive Filter)參考揚聲器信號估算回聲信號并將其剪去,最后通過非線性處理(Nonlinear Processing)模塊消除殘留的回聲。
10、關鍵技術6:最優路徑
實時音視頻對網絡的要求非常高,如果通話雙方距離很遠,那么通話質量是很難保證的。城市A的設備給城市D的設備發送數據,直接發送未必是最優的選擇,從城市B和城市C中轉一下有可能更快。
理想的解決方案是在全球部署加速節點,用戶就近接入。根據加速節點之間的實時網絡質量探測數據,找到一條最優傳輸路徑,避開網絡的擁堵。
11、開源音視頻框架WebRTC簡述
剛才介紹了實時音視頻系統實現過程中所需的關鍵技術,多次提到了WebRTC。顯然,對于絕大多數團隊來說,這些內容如果全部自主研發幾乎是不可能的事情,而我們站在WebRTC的基礎上去設計自己的系統是較為明智的選擇。
WebRTC的代碼非常復雜,想要把它搞清楚是一件非常困難的任務,我第一次看到WebRTC的代碼根本就不知道從哪里下手。
幸運的是,WebRTC官方提供了架構圖,可以先幫助我們對它進行一個宏觀的了解。
WebRTC整體架構大概可以分為接口層、會話層、引擎層和設備I/O層:
1)接口層包括Web API和WebRTC C++ API,Web API給Web開發者提供了JavaScript接口,這樣Web端就具備了接入WebRTC的能力;WebRTC C++ API面向的是瀏覽器開發者,讓瀏覽器開發商具備集成WebRTC的能力。當然,WebRTC C++ API也可以用于Native客戶端接入;
2)會話層主要包含信令相關的邏輯,比如媒體協商,P2P連接管理等;
3)引擎層是WebRTC最核心的功能,包括音頻引擎、視頻引擎和傳輸模塊。音頻引擎包含音頻編解碼器(Opus)、NetEQ和著名的3A(回聲消除、自動增益、降噪)算法;視頻引擎包括視頻編解碼器(VP8、VP9、H264)、JitterBufer和圖像增強(降噪)算法;傳輸模塊包含SRTP、多路復用和P2P模塊;
4)設備I/O層主要和硬件交互,包括音視頻采集和渲染,以及網絡I/O。
上面一節描述的實時音視頻關鍵技術中,WebRTC實現了除“最優路徑”之外的全部內容。WebRTC幾乎每個模塊都是可以按需替換的,便于我們增加定制的內容。我們可以根據實際需求決定如何使用WebRTC,Native客戶端可以通PeerConnection接口接入,服務端拿到RTP/RTCP包以后完全可以直接調用引擎層處理拿到最終的YUV和PCM數據,甚至只把WebRTC內部模塊摳出來用在自己的系統上也是沒問題的。
更多相關資料可閱讀:
- 零基礎入門:基于開源WebRTC,從0到1實現實時音視頻聊天功能
- 實時音視頻入門學習:開源工程WebRTC的技術原理和使用淺析
- 零基礎快速入門WebRTC:基本概念、關鍵技術、與WebSocket的區別等
12、B站視頻直播系統架構
我們回到B站的連麥PK業務場景,兩位主播進行互動PK,同時大量觀眾在直播間觀看PK的過程。
顯然,兩位主播通話要求低延遲,必須使用實時音視頻系統交互;而觀眾觀看直播的延遲要求相對沒那么嚴格,可以采取傳統直播的模式,通過RTMP或者SRT推流到CDN,用戶從CDN拉流。
然后我們要思考兩個問題。
12.1 問題1:主播之間的音視頻通話是采用P2P還是服務器中轉?
首先對P2P和服務器中轉兩種方案做個對比:
對于P2P方案來說,只需要部署STUN和TURN服務器,如果成功建立P2P連接那么后續媒體數據傳輸就不需要經過服務器,所以具有成本優勢。然而,P2P的缺點也很明顯,如果打洞失敗還是需要TURN服務器中轉,且建立連接的過程耗時較高,用戶之間距離較遠的情況下網絡質量不可控,而且現有的第三方網絡加速服務基本上都不支持P2P。
我們這里選擇服務器中轉的方案,因為實時音視頻本身對網絡的要求比較高,不會設置過高的碼率,所以網絡傳輸的數據量是可控的,成本能夠接受。而且我們的實時音視頻數據要對接AI審核,還要實現服務器混流,這是P2P方案做不到的。
12.2 問題2:推送給觀眾的流到CDN,這個工作放在主播客戶端還是服務器?
兩位主播PK對應的是兩路流,觀眾只從CDN拉一路流,所以必須有個地方做混流。這里的混流指的是把兩位主播的視頻進行拼接、音頻進行混合,然后打包成一路流。主播客戶端能收到對方的流,可以和自己的流做混流;在前面提到的服務器中轉方案中,服務器有雙方的流,同樣也可以完成混流。
我們先對比一下兩種方案的優劣:
服務器進行混流需要先解碼再編碼,這需要消耗大量計算資源,所以成本很高;主播客戶端進行混流需要額外增加一路流的編碼和上傳,對設備性能和上行帶寬來說也是很大的挑戰。
主播客戶端需要等待服務器把對方的流發送過來才能混流,所以從延遲的角度來看服務器混流稍微占據優勢,不過這個延遲相比CDN的延遲可以忽略不計。如果后期對通話質量要求變高,主播的設備性能和上行帶寬跟不上,我們可以很容易增加服務器來擴展計算資源和帶寬,所以在可擴展性方面服務器混流勝出。
另外,當主播從正常直播切換到連麥PK狀態的時候,采用服務器混流必須先把直播的流停掉再由服務器接管,中間的時間差可能會產生卡頓或黑屏影響觀眾體驗,而主播客戶端混流可以做到無縫切換。
所以,這兩種方案各有優缺點,我們采取折衷的辦法:如果主播的設備負載較低且上行帶寬比較充足,優先采用主播客戶端混流的方式,否則降級為服務器混流。
12.3 開始架構設計
上面兩個問題分析清楚了,就可以開始設計了。
這是我們的系統整體架構:
rtc-service主要提供信令、頻道管理、主播管理、公有云上媒體服務器集群的健康檢查和節點分配、同步主播狀態到業務服務器、記錄通話流水。
rtc-job是對rtc-service的補充,定期檢查當前在線主播的狀態,發現主播異常下線時觸發兜底邏輯。
rtc-router負責收發主播的音視頻數據。主播可以收到同一個頻道內其他人的音視頻流。如果需要服務器混流,則訪問注冊中心并采用Google的有界負載一致性哈希算法(Consistent Hashing with Bounded Loads)選取rtc-mixer節點,并往對應節點推送主播的音視頻流。
rtc-mixer負責混流,根據需求拼接畫面和混音,然后推送到CDN,觀眾通過CDN拉流。
主播的客戶端并沒有直接向rtc-router發送數據,而是通過第三方的四層加速網絡轉發。我們前面提到了“最優路徑”的概念,第三方的四層加速服務可以讓用戶接入最近的加速節點,然后尋找最優路徑把數據轉發到我們的公有云節點。客戶端只能看到第三方的加速節點IP,看不到我們公有云媒體服務器的IP,這在一定程度上可以防止服務器遭到攻擊;其次,我們可以在保證異地多活的前提下讓公有云集群相對比較集中,節省成本。
服務的可用性和容錯性也是一個很重要的問題,假如在主播PK即將勝利的時刻服務出現故障,彈出"PK異常終止請重新再來",這很令人絕望。我們不僅要保證服務可用,還要盡最大可能保證服務出現故障時減小對用戶的影響,讓流程能夠走下去。接下來討論系統中每個風險模塊為了實現這個目標所采取的措施:
四層加速網絡故障。這個屬于第三方廠商提供的服務,每個廠商提供的接入方式大同小異,基本上就是附加的header有差別,所以同時對接多家廠商對客戶端來說是很容易做到的。客戶端進行連通性檢查,只要存在至少一家廠商的服務可用,就不會影響業務。
公有云上的rtc-router和rtc-mixer故障。在公有云上部署服務,盡量要多廠商、多區域部署,防止單機房整體宕機。我們同樣準備了多個集群,每個集群都部署了多臺rtc-router、rtc-mixer和ZooKeeper,單個集群可以獨立工作,如果單個集群不可用或者負載達到上限則會被熔斷。核心機房的rtc-service會對公有云上的集群進行健康檢查,如果rtc-router宕機,rtc-service會通過信令通道通知客戶端切換到同集群中其他服務器,當同集群沒有可用機器時則切換集群。如果rtc-mixer宕機,rtc-router會通過ZooKeeper重新選擇一臺接管混流任務。
核心機房的rtc-service和rtc-job故障。這部分內容和B站大部分核心服務部署在同樣的集群,復用了B站比較成熟的高可用架構。這部分內容可以參考其他文章,這里不再贅述。
13、本文小結
如果我們把實時音視頻技術比作一座富麗堂皇的城池,這篇文章只能帶領大家來到城門口。我們也不會停止探索的腳步。希望大家讀到這里能夠有所收獲,如有疏漏,歡迎批評指正。
14、參考資料
[1] 實時語音通訊的回音及回音消除概述
[2] 實時語音通訊的回音消除技術詳解
[3] 實時語音通訊丟包補償技術詳解
[4] 零基礎,史上最通俗視頻編碼技術入門
[5] IM實時音視頻聊天時的回聲消除技術詳解
[6] 學習RFC3550:RTP/RTCP實時傳輸協議基礎知識
[7] 基于RTMP數據傳輸協議的實時流媒體技術研究(論文全文)
[8] 愛奇藝技術分享:輕松詼諧,講解視頻編解碼技術的過去、現在和將來
[9] 零基礎入門:實時音視頻技術基礎知識全面盤點
[10] 實時音視頻面視必備:快速掌握11個視頻技術相關的基礎概念
[11] 零基礎入門:基于開源WebRTC,從0到1實現實時音視頻聊天功能
[12] 實時音視頻入門學習:開源工程WebRTC的技術原理和使用淺析
[13] 零基礎快速入門WebRTC:基本概念、關鍵技術、與WebSocket的區別等
[14] 移動端實時音視頻直播技術詳解(五):推流和傳輸
[15] 移動端實時音視頻直播技術詳解(六):延遲優化
[16] 實時視頻直播客戶端技術盤點:Native、html5、WebRTC、微信小程序
[17] 淺談開發實時視頻直播平臺的技術要點
[18] 視頻直播技術干貨:一文讀懂主流視頻直播系統的推拉流架構、傳輸協議等
(本文已同步發布于:http://www.52im.net/thread-4785-1-1.html)