背景
webRTC是Google在2010年收購GIP公司之后獲得的一項技術。如下圖所示,它提供了音視頻的采集、處理(降噪,回聲消除等)、編解碼、傳輸等技術。
webRTC的目標是實現無需安裝任何插件就可以通過瀏覽器進行P2P的實時音視頻通話及文件傳輸,來看看Google的demo,是不是很酷?本文將帶你分析webRTC的原理,并逐步編寫一個簡單的demo。
原理
當然,連接建立的過程不會這么簡單。首先,提到P2P就繞不開NAT(Network Address Translation),webRTC使用ICE(Interactive Connectivity Establishment)框架,ICE是一種綜合性的NAT穿越技術,它整合了STUN、TURN。當穿越網絡時,ICE會先嘗試STUN,查出自己位于哪種類型的NAT之后以及NAT為某一個本地端口所綁定的Internet端端口從而建立UDP連接,如果失敗了ICE就會再嘗試TCP(先嘗試HTTP,再嘗試HTTPS),如果仍然失敗就使用中繼的TURN服務器。
再來看看建立連接過程中的具體步驟:
- 調用getUserMedia獲取本地的MediaStreams;
- 從STUN獲取自己的外網IP及端口,通過Signaling Server向對方發送Offer(SDP協議),并收到Answer;
- 同時webRTC會生成一些包含自己的內網、外網IP等信息的candidate,同樣通過Signaling Server相互傳輸;
- 建立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的分辨率、幀率、對移動端還可以選擇前后攝像頭: 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等。
創建連接
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的事件更容易實現。
中斷會話
function stop() {pc.stop();pc = null;
}
復制代碼
關于socket.io有關的代碼本文沒有貼出,詳情可參考socket.io的用法。
可行性
按照上面的步驟可以成功地搭建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占用率和延時的問題。
參考文章
-
WebRTC in the real world: STUN, TURN and signaling
-
How to Select a Signaling Protocol for Your Next WebRTC Project?
-
Getting Started with WebRTC
-
使用WebRTC搭建前端視頻聊天室——入門篇