webRTC——瀏覽器裏的音視頻通話

背景

webRTC是Google在2010年收購GIP公司以後得到的一項技術。以下圖所示,它提供了音視頻的採集、處理(降噪,回聲消除等)、編解碼、傳輸等技術。webRTC結構html

webRTC的目標是實現無需安裝任何插件就能夠經過瀏覽器進行P2P的實時音視頻通話及文件傳輸,來看看Google的demo,是否是很酷?本文將帶你分析webRTC的原理,並逐步編寫一個簡單的demo。前端

原理

圖片描述
如圖,瀏覽器之間媒體流的傳輸是P2P的,可是這並不意味着webRTC不須要服務器支持。創建P2P視頻鏈接須要的信息,如用來初始化通訊的session信息,雙方的ip、端口,視頻分辨率,編解碼格式等等,仍是須要經過服務器來傳輸的。webRTC沒有規定這些信息傳輸的機制,XHR、webSocket、Socket.io等都是能夠的,由於Socket.io自帶了房間的概念,便於雙向視頻的撮合,因此我在demo裏選擇了Socket.io。html5

固然,鏈接創建的過程不會這麼簡單。首先,提到P2P就繞不開NAT(Network Address Translation),webRTC使用ICE(Interactive Connectivity Establishment)框架,ICE是一種綜合性的NAT穿越技術,它整合了STUNTURN。當穿越網絡時,ICE會先嚐試STUN,查出本身位於哪一種類型的NAT以後以及NAT爲某一個本地端口所綁定的Internet端端口從而創建UDP鏈接,若是失敗了ICE就會再嘗試TCP(先嚐試HTTP,再嘗試HTTPS),若是仍然失敗就使用中繼的TURN服務器。
ICEios

再來看看創建鏈接過程當中的具體步驟:git

  1. 調用getUserMedia獲取本地的MediaStreams;
  2. 從STUN獲取本身的外網IP及端口,經過Signaling Server向對方發送Offer(SDP協議),並收到Answer;
  3. 同時webRTC會生成一些包含本身的內網、外網IP等信息的candidate,一樣經過Signaling Server相互傳輸;
  4. 創建P2P鏈接,傳輸媒體信息。

API

  • getUserMedia: 獲取本地視頻、音頻,能夠傳入constraints調整分辨率、幀率,返回一個promise;
  • RTCPeerConnection: 生成一個RTCPeerConnection實例,傳輸視頻、音頻流;
  • createOffer: 會話發起方生成SDP Offer,包含了本地媒體流信息;
  • setLocalDescription:在此方法被調用以前oncandidate事件不會被觸發;
  • setRemoteDescription: 接收到offer或者answer以後調用;
  • addIceCandidate: 接收到icecandidate以後調用;

Steps

獲取媒體流

var constraints = {
  audio: false,
  video: true
};

navigator.mediaDevices.getUserMedia(constraints)
.then(gotStream)
.catch(function(e) {
  alert('getUserMedia() error: ' + e.name);
});

function gotStream(stream) {
  localVideo.srcObeject = stream;
  localStream = stream;
}

getUserMedia存在兼容性問題,須要在項目中引用webRTC官方給出的adapter.js。constraints還能夠配置video的分辨率、幀率、對移動端還能夠選擇先後攝像頭:github

var constraints = {
    video: {
        width: { min:640, ideal: 1280, max: 1920 },
        height: { min: 480 ideal: 720, max: 1080 },
        facingMode: 'user'     // 前置攝像頭
    }
};

定義RTCPeerConnection

var serverConfig = {
 'iceServers': [{
    'urls': 'stun:stun.l.google.com:19302'
  }]
};

function createPeerConnection() {
    var pc = new RTCPeerConnection(serverConfig);
    pc.onicecandidate = function(e) {
        if (e.candidate) {
            pc.addIceCandidate(e.candidate);
        }
    };
    
    // 添加對方的媒體流
    pc.onaddstream = function(e) {
        remoteVideo.srcObeject = e.stream;
        remoteStream = stream;
    };
}

由STUN、TURN配置生成對應的RTCPeerConnection實例,再定義相關的事件處理函數,如onicecandidate、onaddstream、onremovestream等。web

建立鏈接

function start() {
  pc.addstream(localStream);

  if (isCaller) {
    pc.createOffer(function(sessionDescription) {
      pc.setLocalDescription(sessionDescription);
      send(sessionDescription);  // 根據不一樣的Signaling方式實現
    });

    if (receiveAnswer) {
      pc.setRemoteDescription(answer.sessionDescription);
    }
  } else {

    if (receiveOffer) {
      pc.setRemoteDescription(offer.sessionDescription);
    }

    pc.createAnswer(function(sessionDescription) {
      pc.setLocalDescription(sessionDescription);
      send(sessionDescription);
    });
  }
}

必須先getUserMedia後才能生成sessionDescription,而且只有在setLocalDescription後onicecandidate事件纔會觸發。上面代碼中的只是爲了說明大體流程,實際項目中結合socket.io的事件更容易實現。segmentfault

中斷會話

function stop() {
  pc.stop();
  pc = null;
}

關於socket.io有關的代碼本文沒有貼出,詳情可參考socket.io的用法。promise

可行性

按照上面的步驟能夠成功地搭建webRTC的小demo,可是可否將webRTC運用到實際項目中去呢?下面從瀏覽器兼容性和webRTC自己的性能兩個方面去分析。瀏覽器

兼容性

  • IOS: 只有最新的ios11支持webRTC,且僅限safari瀏覽器,微信內置瀏覽器尚不支持getUserMedia,不支持DataChannel,視頻編解碼格式爲H.264;
  • Android: 安卓4.4以上(不含4.4),經測試各大手機廠商自帶瀏覽器均不支持getUserMedia,但微信內置瀏覽器能夠正常運行,另外61版本以上的Chrome for Android也都支持;
  • PC: Chrome49以上,Firefox55以上,Edge支持,Safari只有11支持,IE不支持。

性能

誠然webRTC在回聲消除,圖像編解碼等方面已經作得十分出色,但它在性能上的問題仍是不可忽視的:

  • 因爲須要進行視頻編解碼,因此CPU佔用率很是高,尤爲是在移動設備上;
  • 在移動設備上獲取的視頻分辨率有限,最高只能達到640 * 480;
  • 帶寬有限時,音視頻質量較差,延時明顯;

綜上所述,雖然webRTC具備不需安裝插件或者客戶端,開源免費,強大的網絡穿透能力,出色的音視頻處理技術等等優勢,但因爲兼容性及性能上的問題,要投入到生產中還須要時間,主要是IOS11的普及以及CPU佔用率和延時的問題。

參考文章

相關文章
相關標籤/搜索