實時音影片入門學習:開源工程WebRTC的技術原理和使用淺析

語言: CN / TW / HK

本文由ELab技術團隊分享,原題“淺談WebRTC技術原理與應用”,有修訂和改動。

1、基本介紹

WebRTC(全稱 Web Real-Time Communication),即網頁即時通訊。 是一個支援網頁瀏覽器進行實時語音對話或影片對話的技術方案。從前端技術開發的視角來看,是一組可呼叫的API標準。

 

在WebRTC釋出之前,開發實時音影片互動應用的成本是非常昂貴,需要考慮的技術問題很多,如音影片的編解碼問題,資料傳輸問題,延時、丟包、抖動、迴音的處理和消除等,如果要相容瀏覽器端的實時音影片通訊,還需要額外安裝外掛。

2010年5月:Google以6820萬美元收購VoIP軟體開發商Global IP Solutions的GIPS引擎,並改為名為“WebRTC”(見《了不起的WebRTC:生態日趨完善,或將實時音影片技術白菜化》)。旨在建立一個網際網路瀏覽器間的實時通訊的平臺,讓 WebRTC技術成為 H5標準之一。

2012年1月:谷歌已經把這款軟體整合到Chrome瀏覽器中,Opera初步整合WebRTC。

2013年 6月:Mozilla Firefox[5]釋出22.0版本正式整合及支援WebRTC。

2017年11月:W3C WebRTC 1.0 草案正式定稿。

2021年1月:WebRTC 被 W3C 和 IETF 釋出為正式標準(見《WebRTC 1.0: Real-Time Communication Between Browsers》)。

2、重要意義

WebRTC的出現、發展和被業內標準組織(如W3C)等普遍認可,對於當下和未來大前端技術發展具有重要的意義。

降低在web端的音影片互動開發門檻:

  • 1)以往的音影片互動開發對於Web開發者而言具有一定技術門檻;
  • 2)現在藉助於WebRTC,Web開發者通過呼叫JS介面,可快速的實現音影片互動應用。

避免依賴、外掛造成的次生問題:

  • 1)以往的音影片互動應用構建依賴於各種外掛、軟體和伺服器等;
  • 2)現在藉助於主流瀏覽器即可形成端到端的音影片互動。

統一化和標準化對傳統音影片互動環境差異性的規避:

  • 1)以往音影片互動需要面對不同的 NAT 、防火牆對媒體 P2P 的建立帶來了很大的挑戰;
  • 2)現在WebRTC 中有P2P 打洞的開源專案 libjingle ,支援 STUN,TURN 等協議。

更高效優化的演算法、技術對於音影片互動效能的提升:

  • 1)WebRTC 通過NACK、FEC技術,避免了經過服務端路由中轉,減少了延遲和頻寬消耗;
  • 2)還有 TCC + SVC + PACER + JitterBuffer 等技術對於音影片流暢性進行了優化。

3、技術特徵

WebRTC內容豐富,主要的技術特徵包含以下幾點。

1)實時通訊:

WebRTC是一項實時通訊技術,允許網路應用或者站點,在不借助中間媒介的情況下,建立瀏覽器之間點對點(Peer-to-Peer)的連線,實現影片流和(或)音訊流或者其他任意資料的傳輸。

2)無依賴/外掛:

WebRTC包含的這些標準使使用者在無需安裝任何外掛或者第三方的軟體的情況下,建立點對點(Peer-to-Peer)的資料分享和電話會議成為可能。

3)協議棧 眾多:

WebRTC並不是單一的協議,包含了媒體、加密、傳輸層等在內的多個協議標準以及一套基於 JavaScript的 API,它包括了音影片的採集、編解碼、網路傳輸、顯示等功能。通過簡單易用的 JavaScript API ,在不安裝任何外掛的情況下,讓瀏覽器擁有了 P2P音影片和資料分享的能力。

WebRTC依賴眾多協議棧圖:

 

 

同時WebRTC 並不是一個孤立的協議,它擁有靈活的信令,可以便捷的對接現有的SIP 和電話網路的系統。

4、相容覆蓋

目前大部分主流瀏覽器都正常相容WebRTC:

更詳細的瀏覽器及版本相容情況,可以看看下圖:

▲ 上圖引用自《https://caniuse.com/rtcpeerconnection

主流瀏覽器都支援 WebRTC 標準 API ,因此也讓瀏覽器之間無外掛化的音影片互通成為可能, 大大降低了音影片開發的門檻,開發者只需要呼叫 WebRTC API 即可快速構建出音影片應用。

5、技術框架

如下圖所示:的技術框架描述了WebRTC的核心內容和麵向不同開發者的API設計。

WebRTC技術框架圖:

從圖中可看到,WebRTC主要面向三類開發者的API設計:

  • 1)對於Web開發者的API:框架包含了基於JavaScript 、 經過W3C認證了的一套API標準,使得web開發者可以基於這套API開發基於WebRTC的即時通訊應用;
  • 2)對於瀏覽器廠商的API:框架同樣包含了基於C++的底層WebRTC介面,對於瀏覽器廠商底層的接入十分友好;
  • 3)瀏覽器廠商可自定義的部分:框架中還包含瀏覽器廠商可自定義的音影片擷取等擴充套件部分。

6、技術核心

從上節框架中可以看到,WebRTC主要有音訊、影片引擎和傳輸三部分組成,其中又包含眾多的協議和方法等。

1)Voice Engine(音訊引擎):

  • a、Voice Engine包含iSAC/iLBC Codec(音訊編解碼器,前者是針對寬頻和超寬頻,後者是針對窄帶);
  • b、NetEQ for voice(處理網路抖動和語音包丟失);
  • c、Echo Canceler(回聲消除器)/ Noise Reduction(噪聲抑制)。

2)Video Engine(影片引擎):

  • a、VP8 Codec(影片影象編解碼器);
  • b、Video jitter buffer(影片抖動緩衝器,處理影片抖動和影片資訊包丟失);
  • c、Image enhancements(影象質量增強)。

3)Transport。

7、技術原理

7.1 基本情況

WebRTC主要的技術特徵:

  • 1)SRTP:安全的實時傳輸協議,用於音影片流傳輸;
  • 2)Multiplexing:多路複用;
  • 3)P2P:STUN+TURN+ICE,用於NAT網路和防火牆穿越;
  • 4)DTLS:安全傳輸可能還會用到DTLS(資料報安全傳輸),用於加密傳輸和金鑰協商;
  • 5)UDP:整個WebRTC通訊是基於UDP的。

限於篇幅,本文以下章節將不細緻介紹音影片採集、編碼和處理等內容,僅介紹實時通訊的建立過程原理的核心內容。

7.2 公網IP對映:明確網路定位資訊

WebRTC是基於瀏覽器端到端的連線(P2P)實現的.

由於不需要伺服器中轉,所以獲取連線物件的網路地址的方式,是藉助於ICE、STUN、TURN等輔助內網穿透技術(NAT)得到對應主機的公網網路地址和埠等網路定位資訊。

明確網路定位是建立端與端直接通訊的基礎。

NAT穿透原理圖:

STUN伺服器用於輔助內網穿透得到對應主機的公網網路地址和埠資訊圖:

7.3 信令伺服器:網路協商與資訊交換

信令伺服器的作用是基於雙工通訊來中轉資訊。

中轉資訊包括公網IP對映後的網路定位資訊,比如:公網IP、埠和媒體資料流等。

概念圖:

信令伺服器資訊互動過程圖:

7.4 會話描述協議SDP:統一的媒體協商方式

SDP的作用:

  • 1)不同端/瀏覽器對於媒體流資料的編碼格式各異,如VP8、VP9等,參與會話的各個成員的能力不對等、使用者環境與配置不一致等;
  • 2)WebRTC通訊還需要確定和交換本地和遠端音訊和影片媒體資訊,例如解析度和編解碼器功能。交換媒體配置資訊的信令通過使用會話描述協議 (SDP) 交換Offer和Anwser來進行;
  • 3)SDP的交換一定是先於音影片流交換的。其內容包括會話基本資訊、媒體資訊描述等。

//SDP的結構體

Session description(會話級別描述)

         v=  (protocol version)

         o=  (originator and session identifier)

         s=  (session name)

         c=* (connection information -- not required ifincluded inall media)

         One or moreTime descriptions ("t="and "r="lines; see below)

         a=* (zero or moresession attribute lines)

         Zero or moreMedia descriptions

 

Time description

         t=  (timethe session is active)

 

Media description(媒體級別描述), ifpresent

         m=  (media name and transport address)

         c=* (connection information -- optional ifincluded at session level)

         a=* (zero or moremedia attribute lines)

一個SDP例子如下:

v=0 //代表版本,目前一般是`v=0`.

o=- 3883943731 1 IN IP4 127.0.0.1

s=

t=0 0 //會話處於活動狀態的時間

a=group:BUNDLE audio video //:描述服務質量,傳輸層複用相關資訊

m=audio 1 RTP/SAVPF103 104 0 8 106 105 13 126 //...

a=ssrc:2223794119 label:H4fjnMzxy3dPIgQ7HxuCTLb4wLLLeRHnFxh81

7.5 一對一連線建立過程

以建立一對一的Web RTC連線過程為例來簡要講解。

一對一過程圖:

簡要過程圖:

如上圖所示,解釋一下:

  • 1)交換SDP,獲取各自媒體配置資訊;
  • 2)STUN伺服器交換網路地址和埠等網路資訊;
  • 3)Turn中轉音影片媒體流資料。

工作流程圖:

如上圖所示,解釋一下:

  • 1)A和B雙方先呼叫 getUserMedia 開啟本地攝像頭,作為本地待輸出媒體流;
  • 2)向信令伺服器傳送加入房間請求;
  • 3)Peer B 接收到 Peer A 傳送的 offer SDP 物件,並通過PeerConnection的SetLocalDescription方法儲存 Answer SDP 物件並將它通過信令伺服器傳送給 Peer A;
  • 4)在 SDP 資訊的 offer/answer 流程中,Peer A 和 Peer B 已經根據 SDP 資訊建立好相應的音訊 Channel 和影片 Channel,並開啟Candidate 資料的收集,Candidate資料(本地IP地址、公網IP地址、Relay服務端分配的地址);
  • 5)當 Peer A 收集到 Candidate 資訊後通過信令伺服器傳送給 Peer B。同樣的過程 Peer B 對 Peer A 也會再發送一次。

7.6 多對多的建立

多對多建立點到點連線概念圖,以三個使用者點對點的連線為例:

7.7 WebRTC的主要JavaScrip介面

getUserMedia()訪問資料流,例如來自使用者的相機和麥克風

//請求媒體型別

const constraints = {

  video: true

  audio:true

};

 

const video = document.querySelector('video');

 

//掛載流到相應dom展示本地媒體流

function handleSuccess(stream) {

  video.srcObject = stream;

}

 

function handleError(error) {

  console.error('getUserMedia error: ', error);

}

//利用攝像頭捕獲多媒體流

navigator.mediaDevices.getUserMedia(constraints).

  then(handleSuccess).catch(handleError);

RTCPeerConnection通過加密和頻寬管理工具啟用音訊或影片通話

// 允許 RTC 伺服器配置。

const server = {

    "iceServers":

            [{ "urls": "stun:stun.stunprotocol.org"}]

};

 

// 建立本地連線

const localPeerConnection = newRTCPeerConnection(servers);

 

// 收集Candidate 資料

localPeerConnection.onicecandidate=function(event){

    ...

}

// 監聽到媒體流接入時的操作

localPeerConnection.ontack=function(event){

    ...

}

RTCDataChannel支援通用資料的點對點通訊,常用於資料點到點的傳輸

const pc = newRTCPeerConnection();

const dc = pc.createDataChannel("my channel");

//接受資料

dc.onmessage = function(event) {

  console.log("received: "+ event.data);

};

//開啟傳輸

dc.onopen = function() {

  console.log("datachannel open");

};

//關閉傳輸

dc.onclose = function() {

  console.log("datachannel close");

};

8、應用案例

這裡以WebRTC的多人影片案例為實踐來大致演示一下。

8.1 設計框架

多人影片基本框架圖:

8.2 關鍵程式碼

8.2.1)媒體捕獲:

獲取瀏覽器影片許可權,捕獲本地影片媒體流,在Video元素中附加媒體流,顯示本地影片結果。程式碼如下。

//攝像頭相容性處理

navigator.getUserMedia = ( navigator.getUserMedia ||

               navigator.webkitGetUserMedia ||

               navigator.mozGetUserMedia ||

               navigator.msGetUserMedia);

// 獲取本地音訊和影片流

navigator.mediaDevices.getUserMedia({

                "audio": false,

                "video": true

}).then( (stream)=> {

//顯示自己的輸出流,掛到頁面Video元素上

    document.getElementById("myVido").srcObject=stream

})

捕獲本地影片媒體流的顯示結果截圖:

為每個新的客戶端連線建立RTCPeerConnection物件:

// stun和turn伺服器  const iceServer = {

    "iceServers": [{

        urls:"stun:stun.l.google.com:19302"

    }]

};

//為點到點的連線建立RTCPeerConnection

const peerRTCConn=newRTCPeerConnection(iceServer);

8.2.2)網路協商:

主要任務就是:建立對等連線,收集ICE候選,等待媒體流接入時掛載到dom。

互動式連通性建立(Interactive Connectivity Establishment — ICE)是一個允許實時對等端發現對方並且彼此連線的框架。此技術允許對等方發現有關彼此拓撲的足夠資訊,從而有可能在彼此之間找到一條或多條通訊路徑。ICE 代理負責:收集本地IP,埠元組候選、在同級之間執行連線檢查和傳送連線保持活動。(關於ICE的介紹,見《P2P技術之STUN、TURN、ICE詳解

// 傳送ICE候選到其他客戶端 peerRTCConn.onicecandidate = function(event){

    if(event.candidate) {

        //向信令伺服器轉發收集到的ICE候選          socket.send(JSON.stringify({

            "event": "relayICECandidate",

            "data": {

                'iceCandidate': {

                    'sdpMLineIndex': event.candidate.sdpMLineIndex,

                    'candidate': event.candidate.candidate

                }

            },

            "fromID":signalMsg['data']['peerId']

        }));

    }

}

//有媒體流介入就掛載dom peerRTCConn.ontrack=function(event){

    let v=document.createElement("video")

    v.autoplay=true

    v.style="width:200px"

 

    document.getElementById("peer").appendChild(v)

    v.srcObject=event.streams[0]

}

8.1.3)媒體協商:

發起時建立Offer。peer利用setLocalDescription方法將會話資訊加到RTCPeerConnection(),並由信令伺服器中轉。其他Peer會返回相應的Answer。SDP過程:

//新加入節點發起offer  if(canOffer){

    peerRTCConn.createOffer(

        function(localDescription) {

             peerRTCConn.setLocalDescription(localDescription,

                function() {

                    //傳送描述資訊給信令伺服器                         socket.send(JSON.stringify({

                        "event":"relaySessionDescription",

                        "data":localDescription,

                        "fromID":peerId

                    }))

                 },

                function() { alert("offer failed"); }

            );

        },

        function(error) {

            console.log("error sending offer: ", error);

        }

    )

}

響應時建立Answer。會話描述包括音影片資訊等內容,當發起者向響應者發出offer型別的描述後,響應者會返回answer型別的描述:

//建立Answer會話

peer.createAnswer(

function(_remoteDescription) {

     peer.setLocalDescription(_remoteDescription,

        function() {

               //傳送描述資訊給信令伺服器                  socket.send(JSON.stringify({

                    "event":"relaySessionDescription",

                    "data":_remoteDescription,

                    "callerID":signalMsg['fromId'],

                    "fromID":signalMsg['fromId']

                }))        },

        function() { alert("answer failed"); }

    );

},

function(error) {

    console.log("error creating answer: ", error);

});

當收到ICE候選共享後,會把ICE候選新增到遠端對等點描述中:

//對應的RTCPeerConnection

const peer = peers[signalMsg["fromID"]];

//ICE候選新增到遠端對等點描述

peer.addIceCandidate(newRTCIceCandidate(signalMsg["data"].iceCandidate));

多人影片結果截圖<本地模擬效果>:

8.2.4)信令中轉:

信令服務部分關鍵程式碼:

wss.on('connection', function(ws) {

    ws.on('message', function(message) {

        let meeageObj=JSON.parse(message)

        //交換ICE候選         if (meeageObj['event'] =='relayICECandidate') {             wss.clients.forEach(function (client) {

                console.log("send iceCandidate")

                    client.send(JSON.stringify({

                        "event": "iceCandidate",

                        "data": meeageObj['data'],

                        "fromID": meeageObj['fromID']

                    }));         

            });

        }

        //交換SDP

         if (meeageObj['event'] =='relaySessionDescription') {

            console.log(meeageObj["fromID"],meeageObj["data"].type)

            wss.clients.forEach(function(client) {

                if(client!=ws) {

                    client.send(JSON.stringify({

                        "event": "sessionDescription",

                        "fromId":meeageObj["fromID"],

                        "data": meeageObj["data"],

                    }));

                }

            });

        }

    })

})

9、小結一下

WebRTC的優點主要是:

1)方便:對於使用者來說,在WebRTC出現之前想要進行實時通訊就需要安裝外掛和客戶端,但是對於很多使用者來說,外掛的下載、軟體的安裝和更新這些操作是複雜而且容易出現問題的,現在WebRTC技術內置於瀏覽器中,使用者不需要使用任何外掛或者軟體就能通過瀏覽器來實現實時通訊。對於開發者來說,在Google將WebRTC開源之前,瀏覽器之間實現通訊的技術是掌握在大企業手中,這項技術的開發是一個很困難的任務,現在開發者使用簡單的HTML標籤和JavaScript API就能夠實現Web音/影片通訊的功能。

2)免費:雖然WebRTC技術已經較為成熟,其集成了最佳的音/影片引擎,十分先進的codec,但是Google對於這些技術不收取任何費用。

3)強大的打洞能力:WebRTC技術包含了使用STUN、ICE、TURN、RTP-over-TCP的關鍵NAT和防火牆穿透技術,並支援代理。

WebRTC的缺點主要是:

1)缺乏伺服器方案的設計和部署。

2)傳輸質量難以保證。WebRTC的傳輸設計基於P2P,難以保障傳輸質量,優化手段也有限,只能做一些端到端的優化,難以應對複雜的網際網路環境。比如對跨地區、跨運營商、低頻寬、高丟包等場景下的傳輸質量基本是靠天吃飯,而這恰恰是國內網際網路應用的典型場景。

3)WebRTC比較適合一對一的單聊,雖然功能上可以擴充套件實現群聊,但是沒有針對群聊,特別是超大群聊進行任何優化。

4)裝置端適配,如回聲、錄音失敗等問題層出不窮。這一點在安卓裝置上尤為突出。由於安卓裝置廠商眾多,每個廠商都會在標準的安卓框架上進行定製化,導致很多可用性問題(訪問麥克風失敗)和質量問題(如回聲、嘯叫)。

5)對Native開發支援不夠。WebRTC顧名思義,主要面向Web應用,雖然也可以用於Native開發,但是由於涉及到的領域知識(音影片採集、處理、編解碼、實時傳輸等)較多,整個框架設計比較複雜,API粒度也比較細,導致連工程專案的編譯都不是一件容易的事。

10、參考資料

[1] 開源實時音影片技術WebRTC的現狀

[2] 簡述開源實時音影片技術WebRTC的優缺點

[3] 訪談WebRTC標準之父:WebRTC的過去、現在和未來

[4] 良心分享:WebRTC 零基礎開發者教程(中文)[附件下載]

[5] WebRTC實時音影片技術的整體架構介紹

[6] 新手入門:到底什麼是WebRTC伺服器,以及它是如何聯接通話的?

[7] WebRTC實時音影片技術基礎:基本架構和協議棧

[8] 淺談開發實時影片直播平臺的技術要點

[9] 基於開源WebRTC開發實時音影片靠譜嗎?第3方SDK有哪些?

[10] 開源實時音影片技術WebRTC在Windows下的簡明編譯教程

[11] 網頁端實時音影片技術WebRTC:看起來很美,但離生產應用還有多少坑要填?

[12] 了不起的WebRTC:生態日趨完善,或將實時音影片技術白菜化

[13] 零基礎入門:基於開源WebRTC,從0到1實現實時音影片聊天功能

[14] P2P技術詳解(一):NAT詳解——詳細原理、P2P簡介

[15] P2P技術詳解(二):P2P中的NAT穿越(打洞)方案詳解(基本原理篇)

本文已同步釋出於:http://www.52im.net/thread-3804-1-1.html

「其他文章」