WebRTC基於瀏覽器的開發


WebRTC簡介


WebRTC通信原理

WebRTC需要通過長鏈接查找到通信雙方,然后通過 peer to peer 的方式傳輸音頻數據。

PeerConnection

WebRTC中最主要的就是一個叫做PeerConnection的對象,這個是WebRTC中已經封裝好的對象。每一路的音視頻會話都會有唯一的一個PeerConnection對象,WebRTC通過這個PeerConnection對象進行視頻的發起、傳輸、接收和掛斷等操作。
PeerConnection中包含的屬性如下:

  • localDescription:本地描述信息,類型:RTCSessionDescription
  • remoteDescription:遠端描述信息,類型:RTCSessionDescription
  • onicecandidate:傳入一個回調方法,該回調方法有一個返回參數,返回參數類型為:RTCIceCandidateEvent
  • onaddstream:傳入一個回調方法,該回調方法有一個返回參數,返回參數類型為:``,如果檢測到有遠程媒體流傳輸到本地之后便會調用該方法。
  • ondatachannel:(暫未用到)
  • oniceconnectionstatechange:(暫未用到)
  • onnegotiationneeded:(暫未用到)
  • onremovestream:(暫未用到)
  • onsignalingstatechange:(暫未用到)

PeerConnection中還包含了一些方法:

  • setLocalDescription:設置本地offer,將自己的描述信息加入到PeerConnection中,參數類型:RTCSessionDescription
  • setRemoteDescription:設置遠端的answer,將對方的描述信息加入到PeerConnection中,參數類型:RTCSessionDescription
  • createOffer:創建一個offer,需要傳入兩個參數,第一個參數是創建offer成功的回調方法,會返回創建好的offer,可以在這里將這個offer發送出去。第二個參數是創建失敗的回調方法,會返回錯誤信息。
  • createAnswer:創建一個answer,需要傳入兩個參數,第一個參數是創建answer成功的回調方法,會返回創建好的answer,可以在這里將這個answer發送出去。第二個參數是創建失敗的回調方法,會返回錯誤信息。
  • addIceCandidate:將打洞服務器加入到配置信息中,參數類型:RTCIceCandidate
  • addStream:向PeerConnection中加入需要發送的數據流,參數類型:MediaStream
  • close:
  • createDTMFSender:
  • createDataChannel:
  • getLocalStreams:
  • getRemoteStreams:
  • getStats:
  • getStreamById:
  • removeStream:
  • updateIce:
RTCSessionDescription

RTCSessionDescription類型中包含了兩個屬性:

  • sdp:這個包含了所有的音視頻的配置信息。
  • type:這個指明了是視頻的接收方還是發起方,這個將在之后進行討論。
通信過程:

A向B發起通信請求

  1. A鏈接socket;
  2. A獲取音頻數據;
  3. A創建一個Ice Candidate
  4. A通過創建好的Ice Candidate創建一個PeerConnection
  5. A創建一個offeroffer中包含了視頻設置sdp,將創建好的offer設置為PeerConnectionlocalDescription
  6. A同時將創建的offerIce Candidate通過socket發送給B;
  7. 將A獲取到的音頻數據存入PeerConnection
  8. 如果B先接收到A發過來的offer,那么先將offer存起來,等到接收到A發過來的Ice Candidate后通過Ice Candidate創建一個PeerConnection,再將保存好的offer設置為PeerConnectionremoteDescription
    如果B先接收到A發過來的Ice Candidate,那么通過A發過來的Ice Candidate創建一個PeerConnection,然后等待接收到A發過來的offer,再將A發過來的offer設置為PeerConnectionremoteDescription
  9. B接收到A發過來的offer后要創建一個answer,將answer設置為PeerConnectionlocalDescription。並且將創建的answer通過socket返回給A。
  10. B開始獲取音頻數據,將音頻數據存入PeerConnection中,WebRTC便會自動將音頻數據發送給A。
  11. A接收到B返回的answer,將B返回的answer設置為PeerConnectionremoteDescription
  12. 這個時候WebRTC會將音頻數據自動發送給B,A和B就建立起了實時音頻通信。

WebRTC實現

1.信令服務器

首先WebRTC需要一個信令服務器,也就是一個socket鏈接用來發起視頻通信,發送WebRTC中的offer和回復answer
如何搭建一個簡單的socket服務器,可以找我的這篇文章《》,也可以是用webSocket搭建信令服務器。

2.打洞服務器

WebRTC需要打洞服務器(一個stun,一個turn)來穿透防火牆等,我們需要配置打洞服務器:

var iceServer = { "iceServers": [{ "urls" : ["stun:stun.l.google.com:19302"] }, { "urls" : ["turn:numb.viagenie.ca"], "username" : "webrtc@live.com", "credential" : "muazkh" }] }; 
3.創建PeerConnection

WebRTC由於是未來的一種即時通信的標准,所以目前在Chrome、Firefox和Opera瀏覽器中有內置插件,均提供一個全局的PeerConnection類。

  • Chrome瀏覽器中為webkitRTCPeerConnection
  • FireFox瀏覽器中為mozRTCPeerConnection
  • Opera瀏覽器中暫時沒有特殊名稱

創建PeerConnection的對象:

var peerConnection = new webkitRTCPeerConnection(iceServer) 

創建時需要傳入打洞服務器的配置信息,如果不穿入打洞服務器的配置信息,則只可以在內網中使用實時音頻通訊。

由於PeerConnection是全局的,所以我們可以通過另外的一種方式進行創建:

window.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection; var peerConnection = new RTCPeerConnection(iceServer) 

如何判斷瀏覽器是否支持WebRTC:

if (RTCPeerConnection) (function () { console.log("瀏覽器支持實時音頻通訊"); // 這里面可以做其他操作 })();else { console.log("您使用的瀏覽器暫不支持實時音頻通訊。"); } 
4.獲取本地音視頻數據

WebRTC也提供了一個全局單例來獲取本地的音視頻信息:

  • Chrome瀏覽器中為webkitGetUserMedia
  • Firefox瀏覽器中為mozGetUserMedia
  • Opera瀏覽器中為msGetUserMedia

GetUserMedia需要傳入三個參數,第一個參數為配置信息,第二個參數為獲取成功的回調,第三個參數為獲取失敗的回調。
獲取到視頻流之后

navigator.getMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia navigator.getMedia({ audio: true, // 是否開啟麥克風 video: true // 是否開啟攝像頭,這里還可以進行更多的配置 }, function(stream){ // 獲取到視頻流stream // 綁定本地媒體流到video標簽用於輸出 document.getElementById('localVideo').src = URL.createObjectURL(stream); // 向PeerConnection中加入需要發送的流 peerConnection.addStream(stream); }, function(error){ // 獲取本地視頻流失敗 }) 
5.發起音頻通話請求

創建一個offer並發送給指定的對象:

peerConnection.createOffer(function(desc){ console.log("創建offer成功"); // 將創建好的offer設置為本地offer peerConnection.setLocalDescription(desc); // 通過socket發送offer }, function(error){ // 創建offer失敗 console.log("創建offer失敗"); }) 

創建offer時要同時發送打洞服務器配置信息,WebRTC給了一個監聽:

peerConnection.onicecandidate = function (event) { console.log("發送打洞服務器配置信息"); } 

返回的參數中有一個candidate屬性,便是打洞服務器的配置信息。

6.收到音頻通話請求

音頻通話請求是通過socket發來的,需要通過socket去監聽。

如果收到了offer,那么需要將offer存到自己的peerConnection中,並且創建一個answer發送回對方。
將offer存入peerConnection中:

peerConnection.setRemoteDescription(new RTCSessionDescription(json.data.sdp)); 

創建一個answer並返回給對方:

peerConnection.createAnswer(function(desc){ console.log("創建answer成功"); // 將創建好的answer設置為本地offer peerConnection.setLocalDescription(desc); // 通過socket發送answer }, function(error){ // 創建answer失敗 console.log("創建answer失敗"); }) 

如果收到了打洞服務器的配置信息,那么需要將打洞服務器的配置信息存入到peerConnection中:

peerConnection.addIceCandidate(new RTCIceCandidate(json.data.candidate)); 

發送給對方answer后便可以等待接受對方的數據流了:

peerConnection.onaddstream = function(event){ console.log("檢測到媒體流連接到本地"); // 綁定遠程媒體流到video標簽用於輸出 document.getElementById('remoteVideo').src = URL.createObjectURL(event.stream); }; 

至此,整個簡單的WebRTC的流程就完成了

WebRTC例子

<html> <body> Local: <br> <video id="localVideo" autoplay></video><br> Remote: <br> <video id="remoteVideo" autoplay></video> <script> console.log("開始"); // 僅僅用於控制哪一端的瀏覽器發起offer,#號后面有值的一方發起 // #號后面加true的為發起者 var isCaller = window.location.href.split('#')[1]; // 與信令服務器的WebSocket連接 var socket = new WebSocket("ws://127.0.0.1:3000"); // stun和turn服務器,打洞服務器設置 var iceServer = { "iceServers": [{ "url": "stun:stun.l.google.com:19302" }, { "url": "turn:numb.viagenie.ca", "username": "webrtc@live.com", "credential": "muazkh" }] }; // 創建PeerConnection實例 (參數為null則沒有iceserver,即使沒有stunserver和turnserver,仍可在局域網下通訊) window.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection; var peerConnection = new RTCPeerConnection(iceServer) // 發送offer的函數 var sendOfferFn = function (desc) { // 設置本地Offer peerConnection.setLocalDescription(desc); // 發送offer socket.send(JSON.stringify({ "event": "_offer", "data": { "sdp": desc } })); }; // 發送answer的函數,發送本地session描述 var sendAnswerFn = function(desc){ // 發送answer peerConnection.setLocalDescription(desc); // 設置本地Offer socket.send(JSON.stringify({ // 發送answer "event": "_answer", "data": { "sdp": desc } })); }; // 獲取本地音頻數據 navigator.getMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia navigator.getMedia({ audio: true, // 是否開啟麥克風 video: true // 是否開啟攝像頭,這里還可以進行更多的配置 }, function(stream){ // 獲取到視頻流stream // 綁定本地媒體流到video標簽用於輸出 document.getElementById('localVideo').src = URL.createObjectURL(stream); // 向PeerConnection中加入需要發送的流 peerConnection.addStream(stream); // 如果是發起方則發送一個offer信令 if(isCaller){ peerConnection.createOffer(sendOfferFn, function (error) { console.log('Failure callback: ' + error); }); } }, function(error){ // 獲取本地視頻流失敗 console.log("獲取本地視頻流失敗"); }) // 發送ICE候選到其他客戶端 peerConnection.onicecandidate = function(event){ if (event.candidate !== null) { socket.send(JSON.stringify({ "event": "_ice_candidate", "data": { "candidate": event.candidate } })); } }; // 如果檢測到媒體流連接到本地,將其綁定到一個video標簽上輸出 peerConnection.onaddstream = function(event){ document.getElementById('remoteVideo').src = URL.createObjectURL(event.stream); }; //處理到來的信令 socket.onmessage = function(event){ var json = JSON.parse(event.data); //如果是一個ICE的候選,則將其加入到PeerConnection中,否則設定對方的session描述為傳遞過來的描述 if( json.event === "_ice_candidate" ){ peerConnection.addIceCandidate(new RTCIceCandidate(json.data.candidate)); } else { peerConnection.setRemoteDescription(new RTCSessionDescription(json.data.sdp)); // 如果是一個offer,那么需要回復一個answer if(json.event === "_offer") { peerConnection.createAnswer(sendAnswerFn, function (error) { console.log('Failure callback: ' + error); }); } } }; </script> </body> </html>
 
轉自:https://www.jianshu.com/p/57fd3b5d2f80 作者:Shmily落墨


免責聲明!

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



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