實現WebRTC P2P連線
WebRTC是為了解決實時音視訊傳輸問題,致力於提供免安裝、免外掛、免專利費,人人可用的高效便捷的實時流媒體傳輸。
1. 3種實時流媒體實現比較
目前實時流媒體主流有三種實現方式:WebRTC、HLS、RTMP,當你看直播網站的時候會發現很多采用了HLS(HTTP Live Streaming,http直播),它是一種把流媒體拆分成多個獨立小檔案的技術,按照播放時間請求不同檔案,把hls的檔案進行解複用之後取出音視訊資料然後丟給video去播放(Safari和安卓版的Chrome能直接播放hls)。它的優點是:使用了傳統http協議,所以相容性和穩定都非常好,服務端可以把hls檔案上傳到cdn,進而能夠應對百萬級別觀眾的直播,缺點是延時比較大,通常在10s以上,適合觀眾和主播沒有什麼互動的場景。因為一個hls檔案時間長度通常在10s以上,再加上生成檔案的時間就導致延遲很大。
它是蘋果推出的一種標準,而另一種RTMP是Adobe推出的,使用長連線,是一套完整的流媒體傳輸協議,使用flv視訊容器,原生瀏覽器不支援(flash外掛支援),不過可以使用websocket + MSE的方式,相關的類庫比較少,在Android/IOS客戶端上的直播應該用得比較多一點。相對於HLS請求分片的形式,RTMP由於使用長連線,接收不間斷的資料流,它的延遲要比HLS小很多,通常是1~3秒,所以如果觀眾和主播之間有通話或者視訊互動,這種方式的延遲是可以接受的。
第3種WebRTC(Web Real Time Communication)是谷歌在2012年推出的,到現在已經有6年的發展。今年2018年3月份WebRTC 1.0正式定稿,並得到了Safari在內的所有主流瀏覽器的支援(Edge弄了一個ORTC),WebRTC致力於高效的實時音視訊通訊,做到比RTMP提供更低的延遲和更小的緩衝率。並且官方還提供了配套的native的Andorid/IOS的庫,不過實際的實現可能是套一個webview,由webview啟動webrtc,再把資料給native層渲染。
先介紹下WebRTC的組成。
2. WebRTC的組成
WebRTC由三大塊組成,如下圖所示:
(1)getUserMedia是負責獲取使用者本地的多媒體資料,如調起攝像頭錄影等。
(2)RTCPeerConnection是負責建立P2P連線以及傳輸多媒體資料。
(3)RTCDataChannel是提供的一個信令通道,在遊戲裡面信令是實現互動的重要元素。
3. getUserMedia
getUserMedia負責獲取使用者本地的多媒體資料,包括調起麥克風錄音、攝像頭捕獲的視訊和螢幕錄製這三種,我已經在《如何實現前端錄音功能》用到了這個API——藉助WebRTC的getUserMedia實現錄音。調攝像頭錄製視訊也是類似,方法很簡單,如下程式碼所示:
window.navigator.mediaDevices .getUserMedia({video: true}) .then(mediaStream => { // 畫到一個video元素上面 $('video')[0].srcObject = mediaStream; });複製程式碼
如果想實現錄屏(螢幕共享)的話,就把獲取媒體的引數改一下,如下程式碼把引數由預設的攝像頭改成螢幕:
navigator.mediaDevices .getUserMedia({video: {mediaSource: 'screen'}}) .then(stream => { videoElement.srcObject = stream; });複製程式碼
然後就會彈一個框詢問要錄製的應用視窗,如下圖所示:
例如可以選PPT應用,就可以開始演講了。這個目前只有firefox支援,Edge有一個類似叫getDisplayMedia,Chrome還在開發之中,但是可以裝一個官方提供的瀏覽器外掛。可見這個demo。
通過getUserMedia調起之後拿到流物件mediaStream,這個流可以在本地渲染,同時通過RTCPeerConnection可以傳給對方。
4. RTCPeerConnection
為了實現客戶端的點到點連線(資料不需經過伺服器轉發),RTCPeerConnection做了很多工作。首先需要解決的問題是區域網穿透。
(1)NAT穿牆打洞
要建立一個連線需要知道對方的IP地址和埠號,在局域網裡面一臺路由器可能會連線著很多臺裝置,例如家庭路由器接入寬頻的時候寬頻服務商會分配一個公網的IP地址,所有連到這個路由器的裝置都共用這個公網IP地址。如果兩臺裝置都用了同一個埠號建立套接字去連線服務,這個時候就會衝突,因為對外的IP是一樣的。因此路由器需要重寫IP地址/埠號進行區分,如下圖所示:
有兩臺裝置分別用了相同的埠號建立連線,被路由器轉換成不同的埠,對外網表現為相同IP地址不同埠號,當伺服器給這兩個埠號傳送資料的時候,路由器再根據地址轉換對映表把資料轉發給相應的主機。
所以當你在本地監聽埠號為55020,但是對外的埠號並不是這個,對方用55020這個埠號是連不到你的。這個時候有兩種解決方法,第一種是在路由器設定一下埠對映,如下圖所示:
上圖的配置是把所有發往8123埠的資料包到轉到192.168.123.20這臺裝置上。
但是我們不能要求每個使用者都這麼配他們的路由器,因此就有了穿牆打洞,基本方法是先由伺服器與其中一方(Peer)建立連線,這個時候路由器就會建立一個埠號內網和外網的對映關係並儲存起來,如上面的外網1091就可以打到電腦的55020的應用上,這樣就打了一個洞,這個時候伺服器把1091埠加上IP地址告訴另一方(Peer),讓它用這個打好洞的地址進行連線。這就是建立P2P連線穿牆打洞的原理,最早起源於網路遊戲,因為打網路遊戲經常要組網,WebRTC對NAT打洞進行了標準化。
這個的有效性受制於使用者的網路拓撲結構,因為如果路由器的對映關係既取決於內網的IP + 埠號,也取決於伺服器的IP加埠號,這個時候就打不了洞了,因為伺服器打的那個洞不能給另外一個外網的應用程式使用(會建立不同的對映關係)。相反如果地址對映表只取決於內網機器的IP和埠號那麼是可行的。打不了洞的情況下WebRTC也提供瞭解決方法,即用一個伺服器轉發多媒體資料。
這套打洞的機制叫ICE(Interactive Connectivity Establishment),幫忙打洞的伺服器叫TURN服務,轉發多媒體資料的伺服器叫STUN服務。谷歌提供了一個turn server,在我家的網路下只能拿到區域網的地址:
(2)建立P2P連線
為此筆者寫了一個demo,可開啟這個連結嘗試P2P聊天(可以用兩個tab或者兩臺電腦),效果如下圖所示:
除了預設提供的TURN服務打洞之外,還需要有一個websocket服務交換互連雙方的資訊。所以需要寫一個websocket服務,我用Node.js簡單寫了一個,程式碼已經傳到github: ofollow,noindex">webrtc-server-client-demo ,包括瀏覽器端的程式碼。
這個過程如下圖所示:
首先開啟攝像頭獲取到本地的mediaStream,並把它新增到RTCPeerConnection的物件裡面,然後建立一個本地的offer,這個offer主要是描述本機的一些網路和媒體資訊,採用SDP( Session Description Protocol)格式,如下所示:
v=0 o=- 4809135116782128887 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE audio video a=msid-semantic: WMS 6ReMVBFmnh4JhjzqjNO2AVBc26Ktg0R5jCFB m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126 ...複製程式碼
然後把這個offer通過websocket服務傳送給要連線的對方,對方收到後建立一個answer,格式、作用和offer一樣,傳送給呼叫方告知被呼叫方的一些資訊。當任意一方收到對方的sdp資訊後就會調setRemoteDescription記錄起來。而當收到預設的ice server發來的打洞資訊candidate之後,把candidate傳送給對方(在setRemoteDesc之後),讓對方發起連線,成功的話就會觸發onaddstream事件,把事件裡的event.stream畫到video上面即可得到對方的影像。
這就是整一個連線過程。
如果連線成功就開始傳輸多媒體資料,這裡面WebRTC做了很多工作。
(3)WebRTC P2P傳輸
WebRTC整體的架構如下圖所示(可見官網):
主要的工作包括:
(1)音視訊的編解碼(VP8/VP9/AV1)
(2)抗丟包和擁塞控制
(3)回聲和噪音消除
WebRTC一個很大的作用就體現在這裡了——提供可靠的傳輸、優質的編解碼以及回聲問題消除,筆者曾經還用了一個叫h323 plus的包做了一個專案,也是P2P連線。而現在這種實時多媒體傳輸功能直接內嵌到瀏覽器裡面,對於開發人員來說無疑大大地提高了開發效率。
在實際的線上專案裡面,由於P2P連通率和穩定性並不是特別樂觀,所以更多地是採用P2SP的架構,S代表Server,如下圖所示:
一方面能夠提高穩定性,另一方面能夠解決一對多和多對多視訊聊天的問題。因為WebRTC比較適用於一對一的,在一對多場景讓一個使用者的流推給幾個使用者不管是效能還是上傳頻寬都可能會有問題。
可以做一個相容方案,當P2P不行的時候就切到P2SP.
關於RTCDataChannel這裡不展開討論,實際場景還是使用Socket/">WebSocket比較多。
5. WebRTC的未來
WebRTC已經被W3C釋出了1.0標準,但是暫未成為RFC標準。WebRTC也在逐漸地發展,包括:
(1)Chrome 69使用了新的回聲消除演算法AEC3
(2)VP9編碼提升了35%的質量,新的AV1編碼可以在Chrome裡面使用
(3)包括RTCRtpSender等更加豐富的操縱API
未來的RTC將會提供更多功能:
(1)直接操作媒體流資料的能力(現在得通過CaptureStream間接操作)
(2)自定義編解碼引數的能力RTCRtpEncodeingParameters(Chrome 70)
等等。
相信WebRTC的未來是非常光明的。
【人人網招聘中高階前端】
1. 專案背景: 我們在做一個企業級海外的SAAS CRM(客戶管理系統)產品, 前端的技術挑戰很大, 比如在我們的網站讓客戶直接打網路電話(直接打手機那種), 發email, 自動根據使用者場景處理業務等。
2. 技術棧背景: 也是採用比較流行的vue, vuex等框架, 通訊是WebRTC, 訊息分發系統用Google的FCM和蘋果的APN。服務部署在亞馬遜或谷歌雲上。服務全球客戶。
3. 另外因為產品是一個企業級使用者產品所以個方面要求比較高(比如效能,安全,多工處理等)。所以對候選人技術要求比較高,如果您對技術特別在意,那麼我們的空缺提供了很好的才能發揮空間和鍛鍊成長的機會。
請將簡歷發至[email protected]