您當前的位置是:  首頁 > 資訊 > 文章精選 >
 首頁 > 資訊 > 文章精選 >

下一代音視頻實時傳輸 SDK 的架構設計

2019-12-12 11:09:20   作者:   來源:CTI論壇   評論:0  點擊:


  在 12 月 6 日的 ArchSummit 北京站,聲網(wǎng) Agora SDK 資深架構師章真分享了 Agora SDK 的架構設計。以下為演講實錄。
  我是來自聲網(wǎng)的SDK資深架構師,負責整個前端API。聲網(wǎng)在全球部署了軟件定義的實時網(wǎng) SD-RTN?,它為開發(fā)者提供了實時音視頻專用網(wǎng)絡服務。之前有一位演講人說 API 很重要。確實是這樣的。
  我會從這 4 個方面簡要介紹一下我們的架構經(jīng)驗:
  1. RTC 場景現(xiàn)在面臨的問題和挑戰(zhàn);
  2. 重點介紹一下架構和API的設計和思想;
  3. 如何對架構上進行重構或代碼改進,從而更好地控制媒體和網(wǎng)絡;
  4. 為了 SDK 的低延遲、高性能、高并發(fā),我們做了哪些探索。
  考慮到大家對 RTC 領域不是太了解,我先簡單介紹一下。其實它是一個很傳統(tǒng)的實時音視頻場景,現(xiàn)在最主流的技術是由谷歌提供 WebRTC,利用它,你可以通過瀏覽器與另一個人進行實時音視頻的通話。聲網(wǎng)也參考了一些 WebRTC 的設計,從最開始的一對一通話,然后到一對一多通話,到現(xiàn)在一個頻道可以支持上百萬的用戶,其中也有很多技術挑戰(zhàn)。
  問題與挑戰(zhàn)
  首先,從場景角度講,我們會遇到的問題和挑戰(zhàn)有哪些呢?
  • 傳統(tǒng)的 RTC 場景:現(xiàn)在我們可以看到很多場景,例如說 4K 高清視頻,如果傳統(tǒng)的SDK不做改善的話,傳輸一個 4K 視頻,對它的內存、CPU等各方面都會帶來極大的挑戰(zhàn)。
  • 娛樂社交和在線教育:現(xiàn)在不光需要打開 Web 瀏覽器、攝像頭,還需要打開本地的播放器,傳輸本地播放器的內容。
  • 云游戲加速:現(xiàn)在很多廠商還在開發(fā)云游戲,游戲運行于服務端,數(shù)據(jù)以音視頻、指令等形式傳輸至手機,手機僅僅負責渲染,其中最大的挑戰(zhàn)就是延時,如果從服務端到手機的傳輸延時超過 200ms 的話,游戲體驗會變得很差,這就需要一個類似于聲網(wǎng)的實時碼流加速傳輸網(wǎng)絡。
  • SIP/PSTN:SIP傳統(tǒng)的網(wǎng)絡電話,在全球有大量的業(yè)務需求,通過網(wǎng)絡的流量來達到整個 RTC 的效果。
  • WebRTC 加速:如果在中國和美國之前通過公網(wǎng) P2P 溝通,卻缺少一個底層網(wǎng)絡網(wǎng)和SDK的介入的話,其實是很難工作的。一個沒有任何 QoS(服務質量)保障的連接,通話會很糟。
  這些都是我們在 RTC 領域會遇到的場景,而 WebRTC 一類的開源引擎是遠不能達到我們對場景的技術要求的,需要一個具備網(wǎng)絡傳輸、音視頻編解碼等能力的 SDK 來實現(xiàn)。
  面對這樣的場景需求,SDK 需要具備哪些特性呢?
  首先是合理的架構設計,它有兩個特點:第一點是媒體和網(wǎng)絡是獨立控制的。因為在類似 PSTN、云游戲加速傳輸?shù)膱鼍爸,它的媒體數(shù)據(jù)是由自己處理的,僅需要我們提供網(wǎng)絡傳輸加速的能力。但像 4K 音視頻的實時傳輸,從采集、編碼、渲染到傳輸,都需要 SDK 來完成。所以對于不同場景,SDK 就需要提供不同層次和不同模塊的接口。
  第二是面向對象的 API 設計。關于 WebRTC 有個小故事,P2P 連接的協(xié)商過程是通過 SDP 協(xié)議做的,而整個能力協(xié)商的過程通過交換 offer 和 answer 就可以快速握手。最初這種設計認為協(xié)商過于復雜,一般的工程師搞不懂,所以并沒有開放接口讓開發(fā)者控制SDP相關內容。微軟在進入 RTC 領域后,基于 WebRTC 貢獻了 ORTC 項目,它 API 設計則是面向對象的。他們曾經(jīng)有過這樣一個看法,如果可以開放更多面向底層、面向對象的 API,開發(fā)者可以根據(jù)自己的場景需要來搭建。這也是面向對象 API 設計的重要性。
  現(xiàn)在很多提供 API 的公司都強調一點,叫做易用性,十幾行代碼就可以讓你實現(xiàn)某個功能。因為以前開發(fā)者的能力普遍還沒有那么強,也不清楚 RTC 場景是怎樣的,所以我們通過這種簡單的方式,讓任何一個小白開發(fā)者都可以輕松做出一個 App。隨著這些年的發(fā)展,場景變得越來越復雜,開發(fā)者的能力也越來越強,我們完全可以提供面向對象的 API,讓開發(fā)者自己通過它們構建自己想要的場景。
  除了合理的架構設計,還要支持豐富的媒體傳輸能力,具備低延時、高性能、高并發(fā)的特性等。這些我稍后會詳細分析。
  架構與API設計
  先說一下傳輸 SDK 的分層。如上圖,SDK 的分層最底下是網(wǎng)絡層。最早之前的一些網(wǎng)絡傳輸都是基于 TCP 的,TCP 和 UDP 之間的區(qū)別,我就不說了,但是對于媒體的實時傳輸來講,在有網(wǎng)絡丟包時,TCP 的延時會非常大,完全不能滿足實時互動的要求,所以最核心的是說媒體其實是不需要,就是在網(wǎng)絡上丟包的情況下,TCP現(xiàn)在幾乎所有的媒體實時傳輸都是基于 UDP 實現(xiàn)的,包括比較新的 QUIC 協(xié)議,底層也是基于 UDP的。
  Transport(UDP)上面是擁塞控制與網(wǎng)絡連接控制,這是 RTC 領域最重要的一個技術環(huán)節(jié)和算法模塊。目的是要在比較復雜錯綜的網(wǎng)絡環(huán)境下,實現(xiàn)更靈活的網(wǎng)絡控制。
  然后是 Media stream 層,它類似于一個 RTP 的協(xié)議,更多是面向媒體流,這一層有時間戳和一些標準的協(xié)議。
  再上面就是 Media Engine。Media Engine有兩層,一層是編解碼器,一層是輸出編碼后的數(shù)據(jù),比如 VP8、VP9,也包括一些傳統(tǒng)的編碼碼率。
  再往上是 Frame YUV/PCM。WebRTC 一般只能傳YUV和PCM的數(shù)據(jù)。這里講一個小的故事,很多中國的開發(fā)者會把 WebRTC 當成一個 SDK 用,其實 WebRTC 根本算不上是一個 SDK,它僅僅是一個 Media Engine。Media Engine 和 SDK 最主要的差別是什么呢?Media Engine僅僅是提供了一個功能,比如說像谷歌自己也有 RTC 的功能,它僅僅是把 WebRTC 的代碼當成一個功能模塊來使用,Chromium 才是一個真正的 SDK。
  說完網(wǎng)絡與對象的簡單分層,我們來一起看一下對象的建模。
  我們去分析一個業(yè)務場景,或者是去設計一個 API,最重要是要了解你控制的對象是什么。首先,我們一般的輸入源有攝像頭、屏幕共享、錄音設備,以及文件或客戶自定義數(shù)據(jù),對于這些對象,我們通過 Audio Source 和 Video Source 作為管理,既可以管理 YUV/PCM 這種原始采集數(shù)據(jù),也可以管理類似 H264/VP8 這種編碼后數(shù)據(jù)。這些數(shù)據(jù)源可以產生媒體流,對于媒體流對象,我們用 Video Track 或者 Audio Track 來管理,對于本地發(fā)布流和遠端訂閱的流,用 local 和 remote 作為區(qū)分。而最重要的模塊自然就是網(wǎng)絡,我們抽象為一個叫 RTC Connection 的對象,負責網(wǎng)絡連接到我們的 SD-RTN? 上。每一個 Connection 都有且只有一個 local user 負責媒體流的發(fā)布和訂閱。除此以外,video 和 audio 的處理模塊也都對象化處理,如 video filter、audio filter、audio device manager 等。把媒體流發(fā)布到這個 Connection 上,你可以進行遠端的通話了。
  在這里我們可以看到面向對象 API 的一些優(yōu)點。你可以在其中創(chuàng)建多個對象,對應這個圖來講就是可以創(chuàng)建多個 Local Video Track,能同時有幾個或幾千個 RTC Connection,可以同時與多人建立連接,或者創(chuàng)建更多頻道。
  從我們的理解來講,API 的設計還有一個非常重要的地方。很多初級開發(fā)者都會覺得 API 僅僅是把 SDK 的功能體現(xiàn)給使用者。而在我們看來,好的 API 設計“能自己講故事”。當別人看過你 30%的 API 之后,就能知道你整個架構和設計理念是什么,它能成為架構師與開發(fā)者對話的一個渠道。如果發(fā)送編碼數(shù)據(jù)和發(fā)送原始數(shù)據(jù) 是完全兩套API的style,就會給開發(fā)者帶來困惑。所以在 API 的設計之中,架構要做的不僅僅是展現(xiàn)功能,還將你的API 設計理念通過 API 傳達給使用者。
  舉一個例子。我們怎么實現(xiàn)與遠端用戶的通話。首先你要創(chuàng)建一個 Connection,你作為一個 Local User 想要發(fā)布流就需要一個 Local Track,這時候你需要調用 Publish Track 把 Local Track 發(fā)送到 Connection 上,這樣遠端的用戶就能看到你了。同樣的,你也可以去訂閱遠端用戶(Remote Users)的流,他的 Remote Track 會通過 Connection 發(fā)送到 Local Users 這一端。這就是一個完整的“故事”。在聽完這個“故事”之后,如果有一天你想傳輸你的攝像頭數(shù)據(jù),對你來講,它仍然是一個 Track,只是 Source 不同了。只有會講“故事”的 API,才能讓用戶理解如何去靈活使用。
  另外,還有很重要的一點,就是不要創(chuàng)造新的名詞,應該符合全球定義的標準。我們在定義 API 的時候,就會大量地翻閱一些國際標準,比如 W3C 的,這些都是符合開發(fā)者認知體系的。
  媒體和網(wǎng)絡控制
  接下來,我們講講架構設計里面的一些具體實現(xiàn)。
  我不知道大家是否聽過 SOLID 法則。在講它之前,我們要講講為什么說 WebRTC 只是一個功能模塊。當你去玩一些開源項目,谷歌提供的能力也好,WebRTC 的開源代碼也罷,你可能會發(fā)現(xiàn)它的適用場景非常單一,它只是適合 P2P 或者跟一些服務器打交道。
  作為一個 SDK,要講功能開放給開發(fā)者,就必須要實現(xiàn)一個 Pipeline。從最簡單的 Pipeline 來講,有 5 個 SOLID 法則:
  • 單一責任法則。假如你有一個 100 人的團隊,每個團隊都有自己的任務,有做降噪的,有做視頻編碼的,好的架構是讓這些人只需要專注于自身的功能模塊的實現(xiàn),代碼如何寫,算法如何改進,而不需要去考慮其它模塊中的業(yè)務。
  • 開閉法則。當你需要開發(fā)一個新功能的時候,不需要去修改之前的代碼,這是好的架構。
  • 模塊可替換。作為一個好的 SDK 架構,SDK 中的任何接口和模塊都是可以被無縫替換的。
  • 接口隔離。用戶可以清楚找到控制對象或者接口,而不需要理解很多不感興趣的接口。
  最后,依賴反轉是特別重要的一點。任何API 都需要面向接口編程,這樣一來,用戶就不需要去理解模塊內部是如何實現(xiàn)的,只需要看接口就行了。
  我們的 Pipeline 如上圖所示。綠色的是接收端,中間通過 Agora SD-RTN?進行傳輸。我們會將一些算法、引擎等用 Pipeline 的方式進行組織; SOLID 法則,我們面向各種場景的應用,代碼會變得越來越快、越來越方便,算法專家也不用去了解其他模塊,只專注于手上的工作。
  舉個例子,我們有一個叫做 Media Player Kit 的組件,它支持本地媒體播放和多流互動(詳見我們此前的文章),如上圖是它的架構。Media Player 可以支持本地媒體播放,也可以將本地視頻流發(fā)送到遠端。如果你還記得“API需要講統(tǒng)一的故事“,就能想象到,Media Player 是一個媒體數(shù)據(jù)源,可以提供 video track 和 audio track,如果將這些 track 加上 renderer,就可以本地播放,如果把這個 track 發(fā)布到 RTC Connection 就可以和遠端用戶共享了。
  Pipeline 就像一個管道一樣,一般來說 Pipeline 都是單向的,從管道的入口到出口,但其實Pipeline 里最核心的一些控制是通過負向反饋來做的,這也是控制理論經(jīng)典的話題。
  在 RTC 領域里,有一個很核心的 Pipeline 叫“帶寬估計”,它可以實時監(jiān)控當前網(wǎng)絡是否有擁塞,當發(fā)現(xiàn)有擁塞的之后,會立即反饋預估的帶寬值到 Video Quality Controller 模塊,動態(tài)調整碼流、幀率,以保證音視頻流的實時體驗。如上圖所示,Video Quality Controller模塊同時還會監(jiān)聽 CPU 狀態(tài),因為低端手機,遇到較高幀率、分辨率的視頻會容易遇到 CPU 的性能瓶頸,從而出現(xiàn)卡頓。Video Quality Controller 模塊會基于收到的帶寬估計和 CPU 狀態(tài)信息來動態(tài)改變編碼碼率,比如你現(xiàn)在發(fā)送的是 2M 的碼流,但是遇到了網(wǎng)絡擁塞,那么就會降低一些畫質,改為發(fā)送 1M 的碼流,能保證通話是流暢的。
  在架構中,策略層和功能層是要嚴格區(qū)分的。從上圖來講,實線的部分就是數(shù)據(jù)通道,它提供了視頻的采集、編碼、傳輸功能,而下方的模塊則是策略層,負責根據(jù)網(wǎng)絡及設備情況來反饋給功能模塊,調整其中的碼率、幀率這樣的參數(shù)。
  低延時、高性能、高并發(fā)
  除此弱網(wǎng)對抗的算法等常規(guī)方法以外,我們還可以在開發(fā)工具層面來進一步優(yōu)化網(wǎng)絡延時。就好像萊特兄弟造飛機一樣。他們做的最重要的一項設計就是風洞。這飛機真正試飛前就可以進行充分的測試。我們也一樣,在此方面也花費了很多精力。我們做了配套的性能調查工具、系統(tǒng)工具,比如perf性能瓶頸的查找,熱點代碼的定位等,以此來做到 SDK 的白盒化。我們通過這些工具來不斷優(yōu)化SDK 的各項指標,包括延時、弱網(wǎng)對抗、內存優(yōu)化、CPU 優(yōu)化等。
  以分段延時為例,如果以光的速度來計算,從中國到美國直線傳輸大概需要 30ms。我們聲網(wǎng)在全球的平均延時可以達到 76ms。下圖是一個傳輸?shù)姆侄窝訒r示意圖。我們通過工具來對每段延時生成清晰的報表。這些監(jiān)測數(shù)據(jù)讓我們能有針對性地優(yōu)化不同的模塊。
 
  同時,我們還要對弱網(wǎng)對抗算法進行不斷的驗證和優(yōu)化。我們會模擬丟包、模擬延遲,我們在算法上會關注碼率跟蹤速度、帶寬預估準確度。如下圖所示,紅線是我們的預估值,黑線是驗證的數(shù)值,兩者越接近,說明碼率控制得越好。
  在高性能方面,我們提出了內存池和線程池的概念。
  我們需要根據(jù)系統(tǒng)內存情況,自動調整內存池的大小,不同大小的空閑隊列需要自動進行負載均衡,同時要有效地減少 malloc/free 調用次數(shù)、頁錯誤數(shù)量。確保 SDK 在低內存環(huán)境中的可用性。
  在某些服務器推流的場景下,高并發(fā)可以極大的降低用戶的服務器使用成本。如果每一路通話或者推流都需要一個進程實例的話,在并發(fā)情況下,CPU 會消耗在線程切換上。在我們的 SDK 中,我們可以通過線程的方式多開實例,可以極大地降低線程梳理,從而提高并發(fā)量。我們也進行了一些測試,業(yè)界其他產品在相同機器環(huán)境下,并發(fā)路只有 600 路,而我們聲網(wǎng)的最大并發(fā)數(shù)可以到達 3400 路。
  在我們的SDK中,線程是通過統(tǒng)一的線程池管理的,這種做法既讓研發(fā)功能模塊中,降低并發(fā)編程模型的復雜度,有可以讓我們的線程數(shù)目受控,比如如果模塊或者功能團隊需要新的線程,需要提出申請,SDK通過注入的方式,將線程給予模塊使用。這對于 SDK 的性能改善會很有幫助。來源:聲網(wǎng) Agora
【免責聲明】本文僅代表作者本人觀點,與CTI論壇無關。CTI論壇對文中陳述、觀點判斷保持中立,不對所包含內容的準確性、可靠性或完整性提供任何明示或暗示的保證。請讀者僅作參考,并請自行承擔全部責任。

相關熱詞搜索: 聲網(wǎng) Agora SDK

上一篇:BAT之側,小巨頭怎么活?

下一篇:最后一頁

專題

CTI論壇會員企業(yè)