Web前端的WebRTC攻略(一)基礎介紹


隨着互聯網高速發展,以及即將到來的5G時代,WebRTC作為前端互動直播和實時音視頻的利器,也是將前端開發者們不可錯過的學習領域。如果你現在只是聽過而已,那你可能要好好學習一番。

什么是WebRTC?

WebRTC 全稱是(Web browsers with Real-Time Communications (RTC)

大概2011年,谷歌收購了 GIPS,它是一個為 RTC 開發出許多組件的公司,例如編解碼和回聲消除技術。Google 開源了 GIPS 開發的技術,並希望將其打造為行業標准。

收購花了一大筆錢,谷歌說開源就開源,確實不得不佩服,但顯然對於Googl來說,打造音視頻的開源生態有着更大的價值。“瀏覽器 + WebRTC”就是 Google 給出的一個答案。其願景就是可以在瀏覽器之間快速地實現音視頻通信。

發展至今日,簡單來說:WebRTC是一個免費、開放的項目。使web瀏覽器通過簡單的JavaScript api接口實現實時通信功能。


WebRTC與架構

 

 

一般談WebRTC架構都會拿出這張圖,WebRTC從上往下架構依次是:

Web API層:面向開發者提供標准API(javascirpt),前端應用通過這一層接入使用WebRTC能力。

C++ API層:面向瀏覽器開發者,使瀏覽器制造商能夠輕松地實現Web API方案。

音頻引擎(VoiceEngine):音頻引擎是一系列音頻多媒體處理的框架,包括從視頻采集卡到網絡傳輸端等整個解決方案。

  1. iSAC/iLBC/Opus等編解碼。
  2. NetEQ語音信號處理。
  3. 回聲消除和降噪。

視頻引擎(VideoEngine): 是一系列視頻處理的整體框架,從攝像頭采集視頻、視頻信息網絡傳輸到視頻顯示整個完整過程的解決方案。

  1. VP8編解碼。
  2. jitter buffer:動態抖動緩沖。
  3. Image enhancements:圖像增益。

傳輸(Transport):傳輸 / 會話層,會話協商 + NAT穿透組件。

  1. RTP 實時協議。
  2. P2P傳輸 STUN+TRUN+ICE實現的網絡穿越。

硬件模塊:音視頻的硬件捕獲以及NetWork IO相關。


WebRTC的重要的類和API

Network Stream API

1.MediaStream(媒體流)和 MediaStreamTrack(媒體軌道)

這個類並不完全屬於WebRTC的范疇,但是在本地媒體流獲取,及遠端流傳到vedio標簽播放都與WebRTC相關。 MS 由兩部分構成: MediaStreamTrack 和 MediaStream。

  • MediaStreamTrack 媒體軌,代表一種單類型數據流,可以是音頻軌或者視頻軌。
  • MediaStream 是一個完整的音視頻流。它可以包含 >=0 個 MediaStreamTrack。它主要的作用就是確保幾個媒體軌道是同步播放。

 

 

2.Constraints 媒體約束

關於MediaStream,還有一個重要的概念叫做: Constraints(約束)。它是用來規范當前采集的數據是否符合需要,並可以通過參數來設置。

// 基本
const constraint1 = {
    "audio": true,  // 是否捕獲音頻
    "video": true   // 是否捕獲視頻
}

// 詳細
const constraint2 = {
    "audio": {
      "sampleSize": 8,
      "echoCancellation": true //回聲消除
    },
    "video": {  // 視頻相關設置
        "width": {
            "min": "381", // 當前視頻的最小寬度
            "max": "640" 
        },
        "height": {
            "min": "200", // 最小高度
            "max": "480"
        },
        "frameRate": {
            "min": "28", // 最小幀率
             "max": "10"
        }
    }
}

3.獲取設備本地音視頻

其中本地媒體流獲取用到的是navigator.getUserMedia(),它提供了訪問用戶本地相機/麥克風媒體流的手段。

var video = document.querySelector('video');
navigator.getUserMedia({
    audio : true,
    video : true
    }, function (stream) {
            //拿到本地媒體流
            video.src = window.URL.creatObjectURL(stream);
    }, function (error) {
            console.log(error);
});

 

 

以上這段demo,就是通過getUserMedia獲取stream,瀏覽器彈窗向用戶索要權限,當允許后才能拿到stream傳給video標簽進行播放。

getUserMedia的第一個參數就是Constraint,第二個參數傳入回調函數拿到視頻流。當然你可以使用如下Promise的寫法:

navigator.mediaDevices.getUserMedia(constraints).
then(successCallback).catch(errorCallback);

RTCPeerConnection

RTCPeerConnection,用於實現peer跟peer之間的NAT穿透,繼而無需服務器就能傳輸音視頻數據流的連接通道。

 

 

這么說過於抽象,為了幫助理解,可以用一個不太恰當但有助於理解的比喻:RTCPeerConnection就是一個高級且功能強大的用於傳輸音視頻數據而建立類似Websocket鏈接通道,只不過它可以用來建立瀏覽器

之所以說是高級且強大,是因為它作為WebRTC web層核心API,讓你無須關注數據傳輸延遲抖動、音視頻編解碼,音畫同步等問題。直接使用PeerConnection 就能用上這些瀏覽器提供的底層封裝好的能力。

var pc =  new RTCPeerConnection({
    "iceServers": [
        { "url": "stun:stun.l.google.com:19302" }, //使用google公共測試服務器
        { "url": "turn:user@turnserver.com", "credential": "pass" } // 如有turn服務器,可在此配置
    ]
};);
pc.setRemoteDescription(remote.offer);
pc.addIceCandidate(remote.candidate);
pc.addstream(local.stream);
pc.createAnswer(function (answer) { 
    // 生成描述端連接的SDP應答並發送到對端
    pc.setLocalDescription(answer);
    signalingChannel.send(answer.sdp);
});
pc.onicecandidate = function (evt) {
    // 生成描述端連接的SDP應答並發送到對端
    if (evt.candidate) {
        signalingChannel.send(evt.candidate);
    }
}
pc.onaddstream = function (evt) {
    //收到遠端流並播放
    var remote_video = document.getElementById('remote_video');
    remote_video.src = window.URL.createObjectURL(evt.stream);
}

你會疑問這里ice Server配置是什么? signalingChannel又是什么? answer和offer又是什么? candidate又是什么?

我們可以通過new RTCPeerConnection()創建RTCPeerConnection。以上代碼只是展示RTCPeerConnection的API和設置方法,但並不能運行。

要完成一個RTCPeerConnection需要設置ICE Server(STUN服務器或TURN服務器),在連接前還要交換信息,為此需要借助一個信令服務器(signaling server)來進行,主要交換SDP會話描述協議和ICE candidate,我們后面段落介紹。

Peer-to-peer Data API

RTCDataChannel可以建立瀏覽器之間的點對點通訊。常用的通訊方式有websocket, ajax和等方式。websocket雖然是雙向通訊,但是無論是websocket還是ajax都是客戶端和服務器之間的通訊,你必須配置服務器才可以進行通訊。

而由於RTCDATAChannel借助RTCPeerConnection無需經過服務器,就可以提供點對點之間的通訊,無需/(避免)服務器了這個中間件。

var pc = new RTCPeerConnection();
var 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");
};

信令Signaling

我們說WebRTC的RTCPeerConnection是可以做到瀏覽器間(無服務)的通信。

但這里有個問題,當兩個瀏覽器不通過服務器建立PeerConnection時,它們怎么知道彼此的存在呢?進一步講,它們該怎么知道對方的網絡連接位置(IP/端口等)呢?支持何種編解碼器?甚至於什么時候開始媒體流傳輸、又該什么時候結束呢?

因此在建立WebRTC的RTCPeerConnection前,必須建立️另一條通道來交這些協商信息,這些也被稱為信令,這條通道成為信令通道(Signaling Channel)。

 

 

兩個客戶端瀏覽器交換的信令具有以下功能:

  • 協商媒體功能和設置
  • 標識和驗證會話參與者的身份(交換SDP對象中的信息:媒體類型、編解碼器、帶寬等元數據)
  • 控制媒體會話、指示進度、更改會話、終止會話等

其中主要涉及SDP(offer、answer)會話描述協議,以及ICE candidate的交換。

這里需要注意的一點:

WebRTC標准本身沒有規定信令交換的通訊方式,信令服務根據自身的情況實現

一般會使用websocket通道來做信令通道,比如可以基於來搭建信令服務。當然業界也有很多開源且穩定成熟的信令服務方案可供選擇。

WebRTC建立連接的關鍵-ICE連接

在交換SDP后,webrtc就開始真正的連接來傳輸音視頻數據。這個建立連接的過程相當復雜,原因是webrtc既要保證高效的傳輸性,又要保證穩定的連通性。

由於瀏覽器客戶端之間所處的位置往往是相當復雜的,可能處於同一個內網段內,也可能處於兩個不同的位置,所處的NAT網關也可能很復雜。因此需要一種機制找到一條傳輸質量最優的道路,而WebRTC正具備這種能力。

首先簡單了解以下三個概念。

  • ICE Canidate(ICE 候選者):包含遠端通信時使用的協議、IP 地址和端口、候選者類型等信息。
  • STUN/TURN:STUN實現P2P型連接,TRUN實現中繼型連接。兩者實現均有標准協議。(參考下圖)
  • NAT穿越:NAT即網絡地址轉換,由於客戶端並不能分配到公網IP,需要內網IP與公網IP端口做映射才能與外網通信。而NAT穿越就是位於層層Nat網關背后的客戶端之間發現對方並建立連接。

 

 

ICE連接大致的原理及步驟如下:

  1. 發起收集ICE Canidate任務。
  2. 本機能收集host類型(內網IP端口)的candidate。
  3. 通過STUN服務器收集srflx類型(NAT映射到外網的IP端口)的candiate。
  4. 通過TUN服務器收集relay類型的(中繼服務器的 IP 和端口)的candidate。
  5. 開始嘗試NAT穿越,按照host類型、srflx類型、relay類型的優先級去連接。

以上,WebRTC便能找到一條傳輸質量最優的連接道路。 當然實際情況並不是這么簡單,整個過程包含着更復雜的底層細節。

WebRTC使用步驟 Demo代碼

通過以上了解了,結合WebRTC的API,信令服務,SDP協商、ICE連接等內容。我們用一段代碼來說明WebRTC的使用流程步驟。

var signalingChannel = new SignalingChannel();
var pc = null;
var ice = {
    "iceServers": [
        { "url": "stun:stun.l.google.com:19302" }, //使用google公共測試服務器
        { "url": "turn:user@turnserver.com", "credential": "pass" } // 如有turn服務器,可在此配置
    ]
};
signalingChannel.onmessage = function (msg) {
    if (msg.offer) { // 監聽並處理通過發信通道交付的遠程提議
        pc = new RTCPeerConnection(ice);
        pc.setRemoteDescription(msg.offer);
        navigator.getUserMedia({ "audio": true, "video": true }, gotStream, logError);
    } else if (msg.candidate) { // 注冊遠程ICE候選項以開始連接檢查
        pc.addIceCandidate(msg.candidate);
    }
}
function gotStream(evt) {
    pc.addstream(evt.stream);
    var local_video = document.getElementById('local_video');
    local_video.src = window.URL.createObjectURL(evt.stream);
    pc.createAnswer(function (answer) { // 生成描述端連接的SDP應答並發送到對端
        pc.setLocalDescription(answer);
        signalingChannel.send(answer.sdp);
    });
}
pc.onicecandidate = function (evt) {
    if (evt.candidate) {
        signalingChannel.send(evt.candidate);
    }
}
pc.onaddstream = function (evt) {
    var remote_video = document.getElementById('remote_video');
    remote_video.src = window.URL.createObjectURL(evt.stream);
}
function logError() { ... }

WebRTC的現狀

標准

一開始各個瀏覽器廠商,都會實現自己的一套API,諸如webkitRTCPeerConnectionmozRTCPeerConnection 這樣的差異,對於前端開發者當然是苦不堪言。

而adapter.js正是為了消除這種差異,幫助我們可以按照規范來寫我們的WebRTC代碼。可以參考 https://github.com/webrtcHacks/adapter。

關於標准的另一個關鍵點是:W3C在2018年發布的 WebRTC 1.0標准(candidate recommendation)  使得WebRTC也將成為視頻通信商業應用場景爆發的主要技術推動力。所以你能看到,目前絕大多數的實施通訊廠商,在web瀏覽器側的方案基本都是WebRTC了。

兼容性

標准的發展,必然推動兼容支持性的提升。 本人在大概2017年做H5在線夾娃娃的預研,當時發現很多瀏覽器,尤其移動端和IOS完全不可用的狀態,因此不得不放棄了WebRTC方案。

 

 

目前看來瀏覽器支持的很不錯了,除了IE仍然不支持外,PC瀏覽器基本已經支持。移動端上IOS在11以上已經支持。

這里有個關鍵在於:別光看caniuse的瀏覽器,還要看移動端各定制瀏覽器是否支持,我這里沒有廣泛的兼容性測試數據。

但可以給出一點結論,WebRTC在最新的IOS和安卓的手Q和微信都是可以使用的。

WebRTC學習攻略

 

 

上圖給的大致的學習攻略,可以從webRTC核心API開始着手,按照demo實現諸如本地音視頻獲取及展示。 其次搭建簡單信令服務,在內網實現簡單的瀏覽器間的通訊,是個不錯的嘗試。 當用起來后,再深入李珏其連接穿越、傳輸的原理和相關協議,最后再嘗試深入挖掘webrtc內部音視頻相關知識。

以上就是對於web前端而言比較容易理解且全面的webrtc基礎介紹。

參考文章

 https://developer.mozilla.org/zh-CN/docs/Web/API/WebRTC_API


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM