Janus網關的集成與優化

你們好,我是李橋平,來自學霸君上海互動產品研發中心,本次分享的主題是Janus網關的集成與優化。Janus網關是WebRTC的媒體服務器,它能夠接收來自WebRTC客戶端的音視頻數據,根據業務須要對媒體數據進行處理,再轉發到其餘WebRTC客戶端上, 以此完成音視頻互動。git

Janus網關的集成與優化

本次分享的主要內容是如何把Janus網關集成到咱們公司內部的自研RTC系統中,並對其作了一些優化,在集成以後就能夠經過瀏覽器和客戶端進行實時互動了。github

1 背景介紹

Janus網關的集成與優化

背景介紹主要從三個方面來進行切入,分別是:業務場景、自研RTC體系以及爲什麼要作集成。數組

1.1 業務場景瀏覽器

Janus網關的集成與優化

咱們所作的業務是一個多人在線實時互動的教育場景,基本需求是老師和學生之間可以進行音視頻實時互動。除了音視頻以外,還須要有一些其餘的輔助教學內容,也須要進行實時的交互,好比老師和學生的手寫筆跡、PPT課件、控制的狀態(課件翻頁)等。爲了知足這些功能,從技術上分解來看,首先須要支持多對多的音視頻連麥,其次是課件、手寫筆跡的實時同步。安全

1.2 自研RTC體系服務器

Janus網關的集成與優化

爲了實現這些功能,在袁榮喜老哥的帶領下, 咱們開發了本身的RTC系統。自研RTC系統主要包含服務端和客戶端兩大塊,它們都是經過自研實現的(語音處理藉助了WebRTC的APM模塊)。客戶端和服務器之間使用UDP協議來進行媒體通訊, 數據包採用的是私有格式, 在此基礎之上完成傳輸的控制, 好比數據包排序重組, FEC, 丟包重傳, 主動Get以及擁塞控制等. 整個體系以客戶端的形式提供給用戶,支持Windows、安卓、MAC、iOS這幾個主流平臺,在使用以前須要下載客戶端。網絡

1.3 爲什麼要作集成架構

咱們主要是從用戶接入的易用性來考慮的. 首先是咱們的客戶端須要用戶本身去下載,安裝成本是比較高,而後纔是註冊帳號、登陸這些步驟。而WebRTC能夠在瀏覽器上運行, 而大部分用戶對於瀏覽器是很是熟悉的. 其次, WebRTC的功能經過JS API進行調用,自然跨平臺, 不須要過多的考慮設備兼容性這些問題, 它們都封裝在WebRTC內部了。負載均衡

經過集成,用戶能夠經過瀏覽器來接入咱們的產品,對於沒有使用過咱們產品的用戶來講, 它提供了一種更加便捷的方式。運維

Janus網關的集成與優化

上圖是完成集成後的一個效果,左圖是瀏覽器,登陸的是學生端。右圖的窗口是咱們PC上的客戶端,登陸的是老師。老師和學生能夠進行實時的視頻互動,同時還能夠經過PPT課件和手寫筆跡來輔助課堂教學。

2 WebRTC與Janus網關

Janus網關的集成與優化

WebRTC與Janus網關部分包含三個小節:首先是P2P傳輸通道的創建,介紹WebRTC的媒體傳輸是如何創建起來的,其次是介紹WebRTC網關以及Janus網關。

2.1 P2P傳輸通道的創建

P2P是指通訊的內容能夠不通過服務器, 直接發送給對方,省去了中間服務器的開銷。WebRTC的P2P傳輸底層採用的是UDP協議,從傳輸特性上說,它是無鏈接、不可靠的協議。固然,WebRTC在進行傳輸時會有好比包確認、包重傳等措施來彌補這些問題。

圖中下方是兩臺須要進行音視頻互動的電腦,電腦中的五色圓圈圖案是WebRTC的logo,表示這個電腦上運行的WebRTC的客戶端,這種客戶端最多見的就是瀏覽器了。實際上只要實現WebRTC的模塊功能,它們均可以進行音視頻的會話,好比WebRTC網關就實現了WebRTC模塊的功能,這裏認爲這兩臺電腦上運行了支持WebRTC的瀏覽器就能夠了。這兩個瀏覽器要進行音視頻互動至少須要兩方面的信息:一是雙方採用怎樣的音視頻編解碼以及相應的編解碼參數,好比採樣率、分辨率、幀率等參數。二是使用UDP發送數據須要知道對方UDP的地址信息,主要包括IP地址和端口。要交換獲取這兩方面的信息的話, 須要藉助到一個位於外網的服務器,咱們稱之爲信令服務器。

Janus網關的集成與優化

接下來咱們來分析一下鏈接創建的過程. 首先,左邊瀏覽器發起一個SDP offer的請求,在SDP中攜帶了它支持的音視頻編解碼和ICE參數。這裏引入了兩個概念:SDP和ICE。SDP(Session Description Protocol)是會話描述協議,這裏只需知道它封裝了協商的參數就能夠了。ICE(Interactive Connectivity Establishment)是互動鏈接創建,它負責UDP下媒體會話的創建. 在ICE參數裏包含了UDP的地址信息(訪問外網的NAT地址須要藉助STUN服務, 爲了簡單起見, 能夠先不考慮)以及創建ICE鏈接所須要的用戶名跟密碼。

右邊的瀏覽器在接收到SDP offer工做請求之後,會根據本身所支持的編碼器狀況進行匹配和篩選,而後生成SDP answer做爲響應,經過信令服務器中轉返回給左邊的瀏覽器,這樣雙方就完成了SDP的協商和交換。

在交換了SDP以後, 雙方通訊須要的信息都完備了. 隨後這兩個瀏覽器會分別初始化好各自的音視頻設備,好比麥克風、攝像頭設備。而後根據協商好的編解碼, 初始化編解碼器. 於此同時, 它們會向對方發送ICE創建請求的消息,該消息會帶上雙方協商好的ICE參數,主要是攜帶用戶名和密碼的信息(後面的單端口改造藉助了這裏的用戶名字段)。在完成ICE的請求交換後進行握手認證,這樣就創建起了ICE的鏈接,雙方隨後以P2P的方式經過ICE鏈接發送編碼後的媒體數據。

直接將媒體數據發送給對方的這種形式被稱之爲P2P直連,這種方式看似很好,由於它中間不須要通過服務器,但在一些狀況下會有問題。

Janus網關的集成與優化

首先,通常的設備都沒有公網IP地址,在訪問外網時須要通過路由器,路由器上的NAT轉換會分配相應的外網地址,再進行設備到外網的訪問工做. 這時路由器上的NAT策略直接影響到ICE鏈接是否可以創建起來。整個過程涉及到UDP穿透問題,好比在對稱型、限制型錐形NAT上,穿透是很難完成的。

其次,在P2P直連的方式下,中間鏈路咱們沒法控制,所以傳輸質量難以保證。假設圖中這兩臺電腦,一個位於電信,一個位於網通,即便它們可以完成UDP的穿透,它們之間的傳輸延遲大機率也是很高的。

最後,由於數據不通過服務器,行爲監管和媒體錄製都難以實現,, 尤爲對教育行業來說,行爲監管這塊是一個必不可少的需求。

2.2 WebRTC網關架構

Janus網關的集成與優化

這是WebRTC網關的架構圖。一般狀況下咱們將WebRTC網關部署到外網,這兩個瀏覽器分別經過NAT鏈接到網關,並經過網關來轉發相應的媒體數據。網關上的WebRTC logo表示在網關上實現了WebRTC模塊的功能. 所以它能夠和瀏覽器上的WebRTC模塊進行通訊。瀏覽器和WebRTC網關之間的紅色箭頭表示信令消息的交互,綠色箭頭表示媒體消息。

下面來看看關於上個小節中的幾個問題在WebRTC網關上是如何解決的。

首先穿透問題,由於WebRTC網關是部署到外網的,瀏覽器經過內網去訪問外網. 只要可以正常上網,訪問外網是沒有問題的,所以不會有穿透失敗的問題, 同時也能夠省去STUN服務.

其次是聯通到電信的狀況,能夠把WebRTC網關部署到BGP的多線機房, 電信和聯通到BGP的延遲能夠作到很低,經過一箇中轉,總體的中轉質量反而比P2P直連質量更好。

最後是監管和錄製,由於媒體數據會通過WebRTC網關,能夠方便地在網關上進行錄製,同時也能夠在網關上針對媒體內容進行相應的數據分析,實現對其監管的功能。

在討論WebRTC網關時,通常會根據網關對媒體消息的處理方式劃分爲兩類:SFU和MCU。

Janus網關的集成與優化

SFU在收到媒體數據之後,不會對媒體數據自己進行處理,只作一些基本處理(SSRC, timestamp等轉換)和轉發。左圖是SFU的示意圖,不一樣顏色所表示的媒體數據在進入SFU以後,它是以原來的形態發送到其餘瀏覽器上的。

右圖是MCU的示意圖,媒體數據在進入MCU之後,MCU會對媒體內容進行深度處理,好比把多路的聲音合併成一路或者把多路的頭像合併成一個大頭像,再根據須要作轉碼,並轉發到其餘瀏覽器上。合流的一個好處是能夠節省相應的帶寬,同時能夠在發送媒體數據的時候, 根據瀏覽器所支持的編解碼狀況進行轉碼,所以它的適應性會比較好。

2.3 Janus網關

Janus網關的集成與優化

Janus網關是SFU. 它是用C語言來實現的。其次, 在Janus上,業務模塊以插件的形式實現,部署是以SO動態庫的形式進行部署的,因此它的主程序和插件開發是一個分離的方式。最後,Janus Demo很是簡單直觀,很容易上手。

接下來這部分介紹Janus網關的軟件架構。從層級上分析,Janus網關主要分爲三層,從上至下分別是插件層、核心層和傳輸層。

Janus網關的集成與優化

插件層主要是決定SFU的轉發邏輯,好比決定轉發給房間裏面的全部人,仍是隻轉發給其中的一部分人,是轉發音頻或者視頻,仍是音視頻同時轉發。一個完整的插件方案,除了Janus網關服務器上的插件實現以外,還包括瀏覽器上的JS SDK。JS SDK處理的邏輯主要包括進出房間、訂閱相應的媒體流等. 除此以外, 調用WebRTC的API獲取麥克風和攝像頭的數據,還有播放音頻和視頻數據,都是經過JS SDK來完成的。

核心層主要負責SDP的協商以及ICE鏈接的創建,UDP媒體數據的接收和轉發也在覈心層裏完成。而插件和JS SDK的通訊使用的是TCP協議, 它是經過傳輸層來完成的.

傳輸層主要負責在JS SDK和網關之間傳輸控制數據, 插件自定義消息等。傳輸層支持多種常見的傳輸協議,好比HTTP、WebSoket等。

3 Janus與自研RTC的集成

Janus網關的集成與優化

第三部分是Janus與自研RTC的集成,主要包含三個小節,分別是系統架構、音視頻互通、集成效果。

3.1 系統架構

Janus網關的集成與優化

這張圖片是高度簡化後的結果,像自研RTC集羣裏的媒體調度、負載均衡、線性擴展等內容都沒有在這裏表達出來,主要是但願能突出與集成相關的內容。圖中大體包含三個部分:自研RTC系統、Janus網關以及中間綠色箭頭表明的媒體通道。

咱們按上圖從左至右, 來看一下通訊流程。

首先是用戶A經過任意一個平臺的客戶端鏈接到自研RTC集羣,經過中間的媒體通道,間接地和鏈接到網關上的瀏覽器用戶B進行音視頻互動。在Janus網關和瀏覽器用戶B之間主要傳輸RTP格式的音視頻數據和自定義格式的筆跡數據。其中的音視頻數據走的是P2P的傳輸通道,筆跡數據走的是WebSocket通道。整個集成核心的部分是位於Janus網關和自研RTC集羣中間的綠色箭頭所表明的音視頻轉換,更具體的來講, 就是自定義封裝格式和RTP封裝格式的轉換。

前面介紹P2P媒體傳輸通道時提到RTP最終是經過UDP的傳輸協議發送出去的。

爲了不IP分片, 發送的UDP包不能太大, 具體一點是不能超過路徑上MTU的限制,通常來講,以太網上的MTU的最大限制是1500個字節。實際過程當中須要除去IP協議頭和UDP協議頭開銷,剩下大概也就1400多個字節, 所以RTP包不能超過這個限制, 這個限制會影響到RTP的封包過程。

3.2 音視頻互通

在咱們的系統中音頻採用Opus編碼,視頻採用H.264編碼,WebRTC(主要是Chrome瀏覽器)也支持這兩種編碼,所以不須要在網關上進行轉碼了。

Janus網關的集成與優化

圖中展現的是音頻數據的轉換, 包含了音頻數據從採集到封裝成RTP的過程。從上往下, 首先是聲卡採集到PCM數據,通常是按10毫秒或者20毫秒這種固定長度進行組織. 通過Opus編碼器, 根據PCM數據的內容特徵, 編碼成長度不同的編碼數據. 編碼後的音視頻數據通常是幾十到幾百個字節左右。這樣的數據量能夠直接在單個RTP包中進行攜帶,所以聲音的RTP封裝很是簡單,只須要在數據的前面追加上RTP頭部就行。

RTP頭部中主要的兩個字段是sequence number和timestamp, 即序列號和時間戳。由於UDP傳輸是一個不可靠的協議,在傳輸的過程當中可能會發生丟包或者亂序到達。序列號能夠幫助接收端正確地組織接收到的數據, 根據序號的缺失狀況能夠知道哪些數據包丟失,根據丟失包的序號能夠要求發送端進行重傳,從而保證傳輸質量。時間戳主要是輔助播放端進行聲音的同步播放。

整個過程倒過來看,就是如何從瀏覽器發過來的RTP數據中提取編碼數據的過程。在提取出編碼數據之後就能夠封裝成自研RTC格式,經過自研RTC集羣再轉發到客戶端上,並在客戶端上進行播放。

接下來是視頻的轉換。

H.264視頻轉換在RFC6184文檔裏有詳細的規定和說明。相對於音頻來講,視頻轉換要複雜一些,這是由於圖像數據編碼後,它的數據幀每每比較大,會超過RTP包的大小限制。

Janus網關的集成與優化

該圖是視頻數據轉換成RTP包的示意圖。仍是從上往下看,首先攝像頭採集原始的視頻圖像,通常是YUV格式的,通過H.264編碼後生成H.264的數據幀。數據幀自己是有內部結構的,它包含一個起始碼,後面跟着NAL單元,由多個這樣的結構組成編碼後的數據幀,在轉換的過程當中,第一步是要把起始碼去掉,再提取出單個的NAL單元數據。而後根據NAL單元數據可否封裝到單個RTP包中,分別封裝成三種不一樣的封裝格式。

圖中左邊是單個NAL單元的封裝, 在NAL單元比較小的狀況下使用. 中間是單元片斷的封裝, 在單個NAL單元大小超過RTP包限制的狀況下,採用該封裝格式。

右邊是多個NAL單元彙集到一個RTP包的封裝過程,這裏主要針對NAL單元很小,RTP包能夠同時攜帶多個NAL單元的狀況,封裝到一個包裏,能夠減小發包的數量。一樣,封包過程須要正確的填充RTP頭部的時間戳和序列號。

整個圖從下往上看,就是從RTP數據流中提取出來H.264編碼數據的過程,完成提取後再封裝成自研RTC系統的格式,發送到客戶端上進行數據的還原,再通過H.264解碼器的解碼,獲得原始的視頻數據並在界面上渲染出來.

3.3 集成效果

Janus網關的集成與優化

這個測試主要是想知道中間轉換部分的開銷, 所以這裏不考慮客戶兩端到服務器的弱網狀況. 首先是穩定WiFi,到服務器RTT是30毫秒,視頻分辨率是320×240,幀率是20幀。整個過程下來音視頻流暢,媒體延遲小於100毫秒。

Janus網關的集成與優化

測試方法藉助了一個在線秒錶的時間跳動的畫面,虛擬攝像頭採集在線秒錶的動畫,經過PC端進行編碼,而後上傳到自研RTC服務器, 轉換成RTP格式, 經過RUDP通道傳輸到Janus網關, 再經過網關發送到瀏覽器上還原出視頻畫面。對比PC端和Web端看到的視頻畫面,就能夠得出他們觀看的時間差。

圖中能夠看出PC客戶端的畫面時間和Web的畫面時間相差大概幾十個毫秒。因爲PC端有一些相應的處理(如美顏),並且存在渲染的時間消耗, 實際的差值會比這個大一些, 總體的時間延遲估計是100毫秒左右,效果仍是不錯的。

4 Janus網關優化

Janus網關的集成與優化

這部分我會從現象入手,介紹集成過程當中所作的一些優化,這裏主要介紹CPU優化和端口優化。

首先在CPU方面,在測試時咱們發現,在同一個房間裏進入12我的,八我的開麥進行音視頻互動的話,Janus近程的CPU大概佔到30%多。若是是一個四核的CPU, 算打到300%的話,也只能支撐120多我的,這樣的話承受能力會很是有限。所以須要對CPU進行優化。

其次是端口,Janus在服務部署的過程當中須要開放大量的端口. 這是由於Janus對於每一路上傳和每一路觀看都須要爲它分配一個外網端口。分配過多的端口無論從安全管理上仍是運維部署上都會帶來不便。在咱們實驗室實際開發過程當中就遇到過,當同時開三、4個視頻時,整個視頻的數據下發不來, Web上看到的畫面是黑的. 通過找相應的IT人員一塊兒定位分析後,發現是辦公室交換機出口對UDP訪問端口作了限制致使的,由於每一路視頻上傳下載都須要分配端口, 在交換機策略看來, 多個內網的機器訪問了同一個外網IP(janus網關的IP)的大量不一樣端口, 被斷定是異常狀態了。所以,無論從安全性、運維部署,仍是服務質量上來說,最好是用少許的端口來完成一樣的事情。

4.1 CPU優化

Janus網關的集成與優化

這部分介紹針對上述兩個問題的分析和相應的解決方法。CPU問題的緣由主要有3個:一是SFU的轉發關係複雜度爲M*N,其中M是上麥人數,N是房間內的總人數。假設房間裏有100我的,其中10我的上麥,那麼轉發的複雜度就是10×100,由於對每一路上傳的視頻都須要轉給其餘99我的,一路上傳加上99路轉發就是100處理量,10我的就是1000。

二是對於每一路上傳和轉發,Janus都分配一個對應的UDP端口和socket描述符,該分配行爲是Janus所使用的網絡庫Libnice決定的。

三是Libnice的內部採用poll作事件處理,在描述符量很大時,它的效率很低。由於poll在調用時, 須要把全部描述符以數組的形式傳遞到內核, 內核須要對每一個描述符進行查詢處理,而且還要註冊相應的事件監聽。若是當前此次調用沒有收集到任何事件的話, 它會進行等待, 在等待過程當中, 它會把當前線程註冊到全部描述符的通知等待隊列裏,而後被動等待相應事件的喚醒。在事件到達喚醒後, 返回的過程當中又須要把當前線程移出全部描述符的等待隊列,這其中涉及到大量鎖操做。以上三個緣由疊加起來,就形成了高CPU的狀況。

Janus網關的集成與優化

CPU優化的對策主要是從兩方面入手: 減小端口的使用, 以及把glib內的poll調用改成epoll。

在使用上,端口的問題的使用能夠採用如下一些辦法來緩解:

一是經過ice_enforce_list限定ICE收集candidate的網卡。默認狀況下,Janus會對全部的網卡都作端口收集。咱們在開發的過程當中所部署的機子上正好有兩個網卡,測試時發現,它所收集的端口數量比單網卡下多了一倍,在開啓這個的配置後,數據數量立馬減半,CPU也下降了不少。

二是確保Janus服務配置中, ice_tcp=false。這是在使用TCP穿透時所須要收集的端口,在實際應用中不多用到,因此將其設置爲「false」禁止掉就能夠。

其次,把glib內的poll調用改成epoll. 能夠採用兩種方式 :一是修改glib代碼,把事件處理的poll調用替換成epoll. 這種方式須要把glib代碼拉下來修改並測試,整個工做須要比較長的時間;二是採用github上第三方的擴展實現 。

4.2 端口優化

Janus網關的集成與優化

對於端口優化,咱們採用了端口複用方案. 實現端口複用的狀況下, 能夠作到減小端口使用, 同時下降CPU使用率。具體的方法以下, 首先在Janus上接管ICE的處理,經過SDP中的ICE用戶名參數來識別發送端身份。在上文提到的P2P鏈接創建的過程當中,首先要經歷ICE認證的過程,在認證消息裏包含了用戶名信息,而用戶名信息是經過SDK的的ICE參數來傳遞給對方的,所以能夠在用戶名中添加業務標識的內容,而後在ICE握手的過程當中識別出對方的身份,而後將身份和發送的IP地址關聯,這樣只要對方發送消息咱們就能夠知道是誰發送的,從而實現端口複用。

在實現單端口方案的過程當中, 採用epoll來實現描述符事件管理,去掉對libnice和glib的依賴。最終能夠經過單一(或少許)的端口對外提供網關的服務,同時下降CPU的消耗。

在方案實施後, 一樣的場景下, CPU佔用從30%降到了10%左右, 仍然有點高, 不過已經好不少了。

相比前面的幾種方案,這種方案會複雜不少,首先須要實現ICE邏輯並在Janus Core中把libnice替換成自實現方案,同時還須要實現相關的輔助結構,如ICE定時器等, 整體來看有必定的工程複雜度,但從效果上來講是值得一作的。

相關文章
相關標籤/搜索