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

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

什麼是WebRTC?

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

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

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

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


WebRTC與架構

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

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

C++ API層:面向瀏覽器開發者,使瀏覽器製造商可以輕鬆地實現Web API方案。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通道來作信令通道,好比能夠基於socket.io來搭建信令服務。固然業界也有不少開源且穩定成熟的信令服務方案可供選擇。

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代碼。能夠參考 github.com/webrtcHacks…

關於標準的另外一個關鍵點是:W3C在2018年發佈的 WebRTC 1.0標準(candidate recommendation) www.w3.org/TR/webrtc 使得WebRTC也將成爲視頻通訊商業應用場景爆發的主要技術推進力。因此你能看到,目前絕大多數的實施通信廠商,在web瀏覽器側的方案基本都是WebRTC了。

兼容性

標準的發展,必然推進兼容支持性的提高。 本人在大概2017年作H5在線夾娃娃的預研,當時發現不少瀏覽器,尤爲移動端和IOS徹底不可用的狀態,所以不得不放棄了WebRTC方案。

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

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

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

WebRTC學習攻略

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

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

參考文章

webrtc.org/architectur… developer.mozilla.org/zh-CN/docs/…

相關文章
相關標籤/搜索