實時視頻通話超低延遲架構的思考與實踐

編者按:2017年12月23日,由即構科技主辦的 ZEGO Meetup 第一期 實時音視頻開發者沙龍在深圳圓滿結束。另外,2018年3月17日(本周六),ZEGO Meetup 第二期 視頻直播+的技術實踐之道在北京舉行。此刻,讓我們把深圳 ZEGO Meetup 會上的精彩技術演講總結起來分享給業界,3月17日在北京中關村舉行的ZEGO Meetup技術沙龍總的精彩技術演講,也將會總結成技術文章來和讀者分享。

以下是即構科技音視頻核心工程師關旭就如何打造實時視頻通話超低延遲架構的精彩分享。

嘉賓簡介:

關旭,即構科技音視頻引擎核心專家,碩士畢業於南開大學數學系,先後就職於中興通訊、騰訊等公司負責音視頻相關的研發工作,在實時音視頻技術上有多年積累,當前在即構科技主要負責音視頻引擎核心開發。

精彩內容:

大家好,今天我分享的內容主要分如下幾部分:

● 實時音視頻場景——從直播到線上抓娃娃

● 實時架構的若干點思考

● 關於信源編碼的思考

● 關於信道編碼的思考

● 引入延遲的環節和降低延遲的思路

1.實時音視頻場景——從直播到線上抓娃娃

圖 1

圖1展示了實時音視頻兩種不同的應用場景——連麥互動直播和線上娃娃機。雖然這兩種都是互動,但是對於實時音視頻的要求卻不同。第一個實時連麥是語音視頻流的互動,例如其中一個說了一句話,另外一個人聽到了,再回復一句話,這個實時性只是對語音視頻流的實時性要求很高。而第二種線上抓娃娃則對信令的延遲提出了更高的要求,操縱者無需說話,看到的是娃娃機傳回來的視頻流結果。如果考量互動直播是用實時音視頻的延遲,那麼線上抓娃娃則是用信令和視頻流的延時。隨著時代的發展,我們對實時語音視頻的定義會慢慢有一些不同,將來可能還有更多的因素需要考慮。

圖 2

圖2是即構互動直播的實時架構圖,我們把互動直播分為兩部分,一個是主播側,需要更低的延遲,另一側是普通觀眾,對延時不太敏感,但對流暢性敏感,中間通過一些旁路的服務把這兩個集群(一個集群叫超低延遲集群,另外一個集群叫圍觀集群)連接起來。

在超低延時部分,我們提供的服務包括流狀態更新、房間管理等,以及一些流媒體服務,主要起到分發的作用。我們通過超低延遲伺服器集群(和觀眾側不太一樣),提供實時分發的功能。此外還提供了動態調度的服務,幫助我們在現有的資源網路上找到更好的鏈路。後面的觀眾集群是另外一個集群,把它們分開是出於一些業務方和我們自己成本上的考慮,另外會提供存儲、PCB加速、分發的功能。

中間的旁路服務包括混流、轉格式(主要是轉碼)、轉協議等。為什麼要混流?舉一個比較簡單的例子。當主播一側有9個人連麥,如果沒有混流服務,觀眾端就會同時拉9路音視頻,這樣對帶寬壓力很大。普通觀眾是通過圍觀伺服器集群(延遲相對大的集群)去拉這些流的,這個集群的延遲可控性相對比較弱,有可能會出現這9路畫面之間的不同步現象,通過混流服務,觀眾拉的都是合成好的音視頻流,就不會出現各路流之間的不同步問題。還有轉格式轉碼的服務,前面集群提供的是很低延遲的服務,裡面一些,比如說編碼碼流,不能夠在傳統的CDN網路分發,如果想在傳統的CDN網路上分發,就要服務端的轉碼。還有就是轉協議,因為前面提供一個更低延時的服務,後面要在CDN網路上分發,所以協議也需要轉。

圖 3

圖3是線上娃娃機的APP版本的架構圖,這裡的是特色是線上娃娃機可以實時推兩路視頻流,上機玩家可以隨時任意去切其中一路畫面去看。這兩路視頻流首先通過我們超低延時伺服器集群,同時上機玩家也可以推一路流上去,可以給圍觀觀眾方看到這個人在抓娃娃時候的一些表情、反應、語言,增加一種互動性。此外,玩家需要通過手機遠程操控娃娃機,因此還需要實時信令的分發。

圖 4

接下來是娃娃機的H5架構圖。在推流方面和APP版本沒有太大的區別,娃娃機一側還是走的私有協議。不同的地方是因為私有協議沒有辦法直接讓H5拉到流,所以中間會加入一個媒體網關,作用是把我們的私有協議翻譯成H5可以識別的碼流格式,然後H5端通過websocket方式把這路流拉下來,這裡需要媒體網關做到超低延時的轉換。簡單來看,這裡的網關伺服器只是做了一個分發服務,好像不會引入延時,實際上不然。因為websocket拉的是TCP的流,但是我們推的是UDP的,當視頻幀很大的時候,一個幀數據就要切割成很多UDP包上行,伺服器需要將這些UDP包攢起來,湊成一個完整的幀後才下發給H5,這樣才能保證不花屏,才能跑得通,所以這個攢包組幀的過程是會有延遲的。信令部分和APP部分基本是相似的。

2、實時架構的若干點思考

剛才介紹了實時音視頻的兩種場景,下面提出一點思考:實時音視頻有什麼樣的特徵?怎麼樣去架構一個實時音視頻系統?

這是仁者見仁,智者見智的問題。你可以通過很多方式把這個系統架構起來,都會達到相對不錯的效果。但是我認為,無論怎樣,實時音視頻都有繞不過如下幾個點,只有把它們做好了,才能夠在業界有更高的知名度、更好的技術儲備。

第一是實時音視頻是不能等的,因為等了就不是實時音視頻了。不能等,這裡會引入一個矛盾。既然不能等,例如你把實時音視頻也看作一個消費模型來看,那是提前生產還是按需生產?字面上理解很簡單,肯定是按需生產,需要的時候才生產,如果提前生產就是延時了。但是並不是每一個點都做成按需生產是合理的。舉一個例子,比如你要去播放一段音頻,最好的做法是系統或者驅動告訴你,它需要數據了,然後去解一幀塞給它,這就是按需生產。但是為什麼還有提前生產一說呢?就是系統告訴你它要數據的時候,實際上它有一個對響應周期的要求。你現去生產可能就要等去解完一幀,但是這個時候來得及嗎?如果你只有一路下行,可能就來得及。但是現在要求很多路下行,在很短的時間周期內解很多幀,對硬體性能有很高的要求。通常來講,並不可取。這只是實時音視頻中一個簡單的例子。提前生產會引入延遲的,那麼到底要提前多久生產,怎麼樣動態估計我們什麼時候應該生產?這是一個開放性的問題,也是一個大家在設計系統時要重點考慮的。

第二是實時音視頻不能久等。實時音視頻中有些等待是避免不了的,例如你要做音頻編碼,它本來一定要20毫秒一幀或者40毫秒一幀去做,給一個採樣點點是編不了的。這裡既然有些延遲和等待避免不了,我們當然希望系統處理的粒度越低越好,這樣可能會帶來更低的延時。但是處理的粒度越低,整個系統在頻繁跑的時候,你可以認為它是一套循環,當循環的東西很少,這個循環就會跑很多次,對系統來說就是一個很大的開銷和負擔。所以不能久等的時候,我們當然希望它處理粒度小。另外處理粒度小還有一個優勢,在整個系統中並不能保證每一個環節的處理粒度是一致的。例如這個節點可能要求是10毫秒,下一個結點要求15毫秒,這是由於演算法的限制,可能沒有辦法避免。如果在整個系統內選一個相對小的粒度,在粒度拼接的時候,例如10-15毫秒,要兩個10毫秒才能夠15毫秒,還剩下5毫秒,剩的就比較少。如果粒度很粗,可能剩下的東西就很多。在粒度拼接的時候,這個剩餘的量代表了整個鏈路中的延遲。所以我們希望處理粒度盡量小,但是又不能小到整個系統沒有辦法接受的粒度。

第三,實時音視頻不能死等。例如你需要接收一個網路包的時候,這個包遲遲不到,這個時候你不能完全不等,完全不等就會卡。但是在等的時候有一個超時的機制,例如這個音頻包就是很久不到,就把它跳過去做一個糾幀補償,當包最終還是到了的時候,我也只能把它扔掉,而不應該把它利用起來。

圖 5

此外,實時音視頻在伺服器端還需要深入考慮這樣幾個問題:第一是負載均衡。第二是就近接入,第三是質量評估,第四是動態路由,第五是演算法流控。

第一,負載均衡是說讓整個伺服器的每一個節點都承擔相對均勻的服務,不至於使得某一個節點負載過高造成一些丟包,造成網路往返時的增大,這樣對任何的網路損傷來講,對實時音視頻都會造成比較大的延遲增加。

第二是就近接入,這裡的「近」並不是指地域上的近,而是「網路上的近」。很簡單的例子,我們在深圳做推流,香港離得很近,可以推到香港的伺服器,但實際上這畢竟是一個跨域的網路,有不穩定的因素在裡面,所以我們寧願推遠一點。這個近指的應該是在網路質量評估意義上的近,例如網路往返時很小、往返時很平穩、分布在延遲比較大的時刻不會還具有很大的概率,丟包率很低等。

要做到就近接入,這個近要有一個很好的質量評估體系。質量評估方法有兩種:

1)事後質量評估。在復盤的時候,例如這個網路平穩的運行了一個月,復盤看一下整個月中的質量怎麼樣,這樣的質量評估可以認為是一個相對離線的評估,它能夠給我們提供一個指標,最近一個月的網路和上個月相比是否有所改善。我們可以從中學習到一些經驗,例如這個月和上個月的調度上有些策略上的不同。這是一個系統化的經驗總結和優化的方法。

2)實時質量評估。更重要的應該是一個實時上的評估,例如我現在推流,能夠實時監控到當前的質量是怎麼樣的,就可以做到實時動態路由。實時動態路由是說某個人推流從北京推到迪拜,有很多鏈路可以選,他可能根據之前的一些經驗,假如他之前經驗告訴你,直接推到迪拜,這個鏈路是很好的,但是畢竟有個例。有動態實時的質量評估,就知道這個時候推迪拜是否好,如果不好,可以在用戶無感知的情況下更換,隨時增減整個鏈路中一些路由的節點。這就是動態路由的思路。

實時動態路由是說某個人推流從北京推到迪拜,有很多鏈路可以選,他可能根據之前的一些經驗,假如他之前經驗告訴你,直接推到迪拜,這個鏈路是很好的,但是畢竟有個例。有動態實時的質量評估,就知道這個時候推迪拜是否好,如果不好,可以在用戶無感知的情況下更換,隨時增減整個鏈路中一些路由的節點。這就是動態路由的思路。

實際情況中是結合前面這4個點,在我們的網路和伺服器資源集中,去選出質量最優或者近似最優的鏈路來保證實時音視頻的服務的。但是資源集是有限的,沒有人可以保證你的資源集中一定可以選出的這個最優具有很好的鏈路特徵。保證不了就要考慮第五點,我即使選出了一個認為是整個資源集中最優的鏈路,但是它的質量還達不到很好的標準,就要通過一些演算法才能彌補。這些演算法包括在一個不可靠的網路中怎麼樣進行可靠的音視頻傳輸的技術,這些技術在接下來我們會和大家稍微分享一下,也包括整個鏈路的一些擁塞控制。

3、關於信源編碼的思考

圖 6

信源編碼是為了減少網路中的負擔,把大量的數據壓縮成比較小的網路數據,來減少網路負擔的方式。壓縮方式有很多種,我們先以音頻來看,上面畫了一些圖(圖6),我們重點看Opus編碼器,它有幾種模式在裡面,一種是線性預測模式,還有一種是混合模式,還有一種是頻域編碼模式。混合模式是把兩種編碼模式混合在一起,根據不同的情況進行選擇。

圖6是一個編碼器,橫軸是碼率,縱軸是它的質量,中間是各種音頻編解碼器的表現。你會發現線性預測的方式能夠在低碼率上提供比較好的質量,但是在20K左右的時候就沒有曲線了,因為它不支持那麼高的碼率。然後看MDCT編碼,它可以在比較高的碼率上達到近似透明的音質。音頻編碼器是有不同的編碼原理在裡面的,像這種LP Mode是模擬人的發聲模型,既然有了數學建模,它的特徵是能夠在一個比較低的碼率上提供一個比較可靠的質量。但是它的特點是容易達到一種質量上的飽和,也就是說當你碼率給它很高的時候,實際上它也就編的效果還是那樣,因為它畢竟是一種參數化的編碼。所以根據業務場景,當你需要一個很高的音質,又需要音樂場景的時候,選擇它明顯不合適。MDCT MODE沒有任何的模型在裡面,實際上就是把信號轉換成頻域,直接去量化。既然沒有模型化,它是比較消耗碼率的,但是它可以在一個較高的碼率上提供很好的質量,可是低碼率的表現遠遠不如模型化的方法。

圖 7

整體總結起來,音頻包括語音和音樂兩種,因此有適合語音的codec和適合音樂的codec。第一種codec適合語音,語音可以模型化,適用於語音的codec能夠在低碼率上提供很好的質量,提供一個相對高的壓縮比,但是它容易達到飽和,不能夠提供一個近似於透明的音質。另外一種codec的編碼原理不一樣,能夠把音樂、語音都編得很好,但是特點是不能夠提供太高的壓縮比,指望它能夠在低碼率下提供很高的編碼質量是做不到的。

圖 8

關於視頻編碼,最簡單的幾個點有I幀、P幀、B幀。I幀是自參考,P幀是向前參考,它會參考歷史幀的特性進行編碼。B幀是雙向參考,它可以參考前面的幀,也可以參考後面的幀。B幀可以帶來更高的壓縮比,提供更好的質量。但是因為它會參考將來的幀,所以會引入延遲,因此我們在實時音視頻系統中是很少用到B幀的。

想要做好實時的音視頻系統,流控是一定要做的,流控對視頻的編解碼有什麼要求?至少有一點,編解碼器的碼控一定要很穩定。為什麼?舉例說,我現在有一個很好的擁塞控制策略,帶寬估計做得很好,一點差錯都沒有,估計出某一個時刻可分配視頻的帶寬就是500kbps,就可以讓視頻編碼器設置成500kbps。但是,如果碼控不是很穩定,你設置500kbps的時候,視頻編碼器可能就跑到600kbps了,這樣就會帶來一些阻塞和延遲。因此,我們希望選擇的codec具有很好的碼控策略。

實際上一些開源代碼都是有做碼控的,但是直接拿來用並不是適合你的場景,因為這些開源代碼做起來,可能或多或少的考慮其他的場景,並不只是實時音視頻場景。比如說某個codec是用來是壓片的,希望半個小時或者一個小時之內達到預定的碼率就可以,不會管這一秒鐘或者下一秒是什麼樣子的,但是實時音視頻就是要求要把時間窗做得很小。

另外我們希望codec有分層編碼的能力。什麼是分層編碼?為什麼要有分層編碼?分層編碼也分兩種,一種是時域上的分層,一種是空域上的分層。前者是編碼的時候是當前幀不參考上一幀,而是有隔幀參考的策略;後者可以認為使用較低的碼率先編碼一個小的畫面,然後使用剩餘的碼率編碼增量的部分,得到更高解析度的畫面。 為什麼要這樣做?實時音視頻中並不是很多場景都是一對一的,當不是一對一,要做流控的時候,不可能因為某一路觀眾的下行不好,就把主播上行推流的碼率降下來,因為可能還有一千個觀眾的網路很好,這些網路好的觀眾也會因為個別觀眾網路不好,而只能看到不那麼清晰的畫面。所以要分層,可以在伺服器端選擇給用戶到底下發哪一層的,因為有分層策略,如果這個人線路不好,只要選擇其中一個比較小的層次發給他就可以了,例如核心層,這樣可以緊緊利用核心層把整個視頻還原,可能會損傷一些細節或者幀率偏低,但是至少整體可用。

最後,我想說一下,很多人認為,視頻的數據量很大,視頻的延時比音頻應該更高才對,實際上不是。因為很多的延遲實際上是編解碼自有的延遲,如果編解碼中沒有B幀的話,你可以理解為視頻編碼是沒有任何延遲的。但是音頻編碼或多或少都會參考一些將來的數據,也就是說音頻編碼器的延時一定是存在的。因此,通常來講,音頻的延時比視頻的延時更高才對。

4、關於信道編碼技術的思考

圖 9

信道編碼分幾個部分。一種是根據先驗知識的網路冗餘編碼技術——前向糾錯技術。以RS(4,6)編碼為例,我要發一個分組,這個分組有六個包,其中有四個包是實際媒體數據,有兩個包是冗餘包。那麼在解碼端收到六個包中任意的四個,就可以完全恢復所有攜帶媒體內容的包。例如這裡2、3都丟了,收到了1、4、r1、r2,也能夠完全恢復2和3。這樣看來很好,任意兩個丟掉都可以完全恢復。但是這樣的演算法也有它的弱點,不太適合突發性的丟包。因為這個分組不宜太大,如果分組很大,分組就有很大的延時。分組如果很小,很可能整個分組都丟掉了。實際上這種做法就沒有任何意義。所以它不太適合突發性丟包,而且它畢竟是根據先驗知識去做的一種冗餘,也就是說它永遠是根據上一時刻網路的狀態作出的判斷,下一時刻網路是什麼樣的,是預測的東西。網路是實時發生變化的,這種預測的東西並不完全可靠。所以它恢復的效率在實際網路中相對比較低,而且這樣的演算法複雜度相對比較高。當然它也有優勢,例如我們是提前算好的,一次性發過去,不需要等到你發現丟包時我再做怎樣的冗餘傳輸,所以不受網路往返的影響。而且這種分組可以任意、隨機調整大小冗餘度,比較適合均勻丟包的場景。

圖 10

另外一項技術是丟包重傳技術。相對來說,丟包重傳相對RS來講,更有針對性,所以恢復效率比較高。第一個Go Back N技術是類似於TCP的傳輸技術,發送端在不斷的發包,接收端要負責告訴發送端我現在收到包的情況是怎麼樣,收到的連續的幀的是序列號什麼樣的。發送端發現發了10個幀,接收端只正確收到8,不管9號包或者10號包是否收到,都會丟包重傳。所以Go Back N技術有一定的目的性,維護的是丟包狀態,它知道哪些包是沒有收到的,但是並不精準。

接下來是自動選擇重傳技術(Selective ARQ)。選擇性的重傳,是在接收端發現了哪個包丟了,然後才會讓發送端重新發送這個包。聽起來是非常好的一個技術,效率很高,丟了哪個包就重傳哪個包。但是它的弱點在於,你必須要假定這個包是頻密的發送才可以。例如發送端發出1、2、3、4這樣的包,但是一秒鐘才發一個包,什麼時候發現2丟了呢?收到3的時候。如果2作為最後一包,永遠發現不了丟掉了。也就是如果發包不頻密,至少需要1秒鐘才發現它丟。這個時候再讓它重傳,就很晚了。

所以在一個真實的系統中,選擇性重傳是首選,因為音視頻的大部分場景是頻密的,但是可能也要結合一些Go-Back -N的做法。發一些確認機制,這樣才能把重傳做得更加完備。另外所有的重傳都要至少等一個網路往還時,因為無論是確認丟包還是反饋收包情況,都需要一個網路往返時,所以它的弱點是,它受網路往返時影響比較大,如果控制不好,有可能造成重傳風暴。優勢是演算法計算複雜比較低,且容易實現。另外,因為它有很大的針對性,無效的重傳包會比較少,針對突發性的丟包會有比較好的效果。

剛才講了針對不可靠網路的兩種傳輸技術,前向糾錯和丟包重傳,它們都有各自的優點和缺點。實際上一個好的網路分發技術應該是將這兩種結合在一起的,根據不同的信道情況把這兩種技術結合在一起。

圖 11

圖11來自於網路,首先從左下角藍色部分看起,當網路往返時很小,丟包率不高的時候就用重傳。但是當網路RTT很高的時候,在這個圖裡面去看,就沒有選用重傳策略。從我個人的角度來看,我認為這並不是一個非常合理的做法。因為剛才提到了,FEC是一個無目的性的、根據先驗知識去做的一種冗餘技術,雖然當RTT很高,重傳很耗時,但如果沒有重傳,要加很多冗餘包,才能把丟掉的包完全恢復,實際就會帶來很大的資源浪費。而且當你丟包率很高的時候,可能還並不能夠完全恢復所有包。視頻只要丟幀就會很卡,視頻丟包率應該控制在千分之幾以下,才可以達到順暢的可以觀看的水平。

圖 12

關於信道編碼的思考。信道編碼和網路吞吐呈反比關係。無論是重傳性編碼還是冗餘性編碼,都會佔用帶寬,從而減低實際媒體信息的吞吐量。現實的生活中,信道都有限制。當你傳輸的時候,就要根據信道的特徵去做一些策略。信道如果有擁塞,我們就需要有一個擁塞控制的演算法,去估計應該把整個信道怎麼樣做合理分配。

另外,在做一個系統的時候,想清楚如何去評價一個系統的效果是很重要的一個點。在信道編碼的時候,一個很重要的指標是,信道編碼的有效性是什麼樣子的。有效性分為兩種,一種是重傳或者冗餘能否真的把丟掉的包補回來,這是一個有效性。即使這個包補回來了,但是如果經過一個信道編碼策略之後,還有一些丟包。例如原來的丟包是20%,補回來變成1%,那麼這個重傳在我們的評價當中實際上是沒有效果的,因為1%的丟包對音頻來講是無所謂的,但是對視頻來講是很卡的。在這樣的評價系統中,補回來還有1%的丟包,那麼所有的編碼都是沒有太大意義的。舉這個例子,如果在這時信道也發生擁塞,再進行這樣的信道編碼,就不會達到很好的效果。這個時候是否應該停止所有的信道編碼呢?

還有信道編碼有效性的判斷,衡量它是否好,就是加了多少冗餘,冗餘中有多少沒有被利用好,如果這些冗餘像剛才那個例子那樣,6包帶2包的冗餘,剛好丟掉2包,整個包都恢復出來了都使用到了,那就是百分之百的冗餘都有效。如果4包信息丟了1包,卻帶了2包榮譽,其中1包就沒有效果。所以想要做一個好的系統,應該先想到如何評價這個系統的好壞。

5、引入延遲的環節和降低延遲的思路

延遲的引入主要分三部分,一個是採集/渲染。這好像是很簡單一個部分,但是它引入延遲可能是最大的,可能是整個分發過程中最大的環節。有很多人不是特別理解,但實際上在即構現有的網路結構中,網路往返時的延遲都控制在50毫秒以內,但是渲染和採集,尤其是渲染,幾乎沒有任何移動端系統可以保證它百分之百的50毫秒,這是一些硬體上的限制。如何去降低這些延遲?剛才我已經舉了一個生產消費模型的思路,到底是按需生產還是提前生產,這些都是可以仔細去考慮的。

還有編解碼會帶來一些延遲,尤其是音頻會帶來一些延遲。這些延遲中有些是避免不了的,我們就要根據實際的使用場景去減少這些延遲,這些都是要在具體形態上做一些權衡的東西。還有處理粒度上的考慮,也會影響整個系統的延遲。

還有一個延遲,大家都能看到的,就是網路分發延遲。如何去減小?除了在資源集中找到一個最優子集之外,還有信道編碼的東西,要做一個很好的信道編碼系統,我們如何評價信道編碼系統的好壞。有了這些思路之後,可以指導我們去做更好的下一步的開發工作。

圖 13

圖13是我們即構科技用戶的分布圖。亮點的部分代表用戶比較多的部分,稍微暗一點是用戶相對少的部分。從圖中可以看到,即構的音視頻服務遍布各大洲,尤其在歐洲和非洲北部地區、北美地區都有大量用戶。

關於即構ZEGO

即構科技於2015年由QQ前總經理林友堯創立,A輪獲得IDG投資,核心團隊來自騰訊QQ,匯聚了來自YY和華為等廠商的頂尖語音視頻人才。即構ZEGO致力於提供全球最清晰最穩定的實時語音視頻雲服務,助力企業業務創新,改變用戶線上溝通方式。即構ZEGO深耕視頻直播、視頻社交、遊戲語音、線上抓娃娃和在線教育等領域,贏得了映客、花椒直播、一直播、喜馬拉雅FM、陌陌遊戲、自由之戰2、和好未來等頂級廠商託付和信賴。

推薦閱讀:

如何搭建音視頻通信的伺服器架構

TAG:架構 | 視頻通話 | 網路延遲 |