WEB p2p視頻聊天?! -- WebRTC 初入探究 與 實現(xiàn)

前言

本片demo中 的實現(xiàn)為純前端js實現(xiàn),所以 而且可以在本機下運行(有網(wǎng)絡(luò)即可正常鏈接服務(wù)器),所以你可以修改 代碼(界面好丑,處理時間不多....)

這里感謝一下(沒有廣告費...只是免費用了別人的資源,總得感謝下)
leancloud 的 免費云平臺(現(xiàn)在不是免費了)
openshift 的 免費平臺

本篇文章內(nèi)容:

  • WebRTC的基礎(chǔ)知識
  • WebRTC的連接基本流程
  • WebRTC的基礎(chǔ)接口介紹
  • WebRTC的注意事項
  • WebRTC的 demo

WebRTC 的基礎(chǔ)知識

借來的 webRTC結(jié)構(gòu)圖

什么是WebRTC

WebRTC : 網(wǎng)頁實時通信(Web Real-Time Communication),故名思意.
對于瀏覽器來說,它要實現(xiàn)三大模塊
音頻處理模塊,視頻處理模塊,網(wǎng)絡(luò)傳輸模塊

它主要是由瀏覽器底層實現(xiàn)的,對于我們來說,是調(diào)用它開放出來的JS api
相關(guān)的主要有

  • MediaStream 系列API(主要是獲取 媒體流的,例如攝像頭啊,麥克風(fēng),音頻輸出設(shè)備啊等等的)
  • RTCPeerConnection 系列API(主要是用來 進(jìn)行 p2p連接的)
  • RTCDataChannel (其實這個應(yīng)該也算在RTCPeerConnection系列里,不同的是,不是傳輸媒體流,而且傳輸數(shù)據(jù))

WebRTC的連接方式

RTCPeerConnection底層下實現(xiàn)了ice的支持,ICE,全名叫交互式連接建立(Interactive Connectivity Establishment)
支持stun 和 turn 服務(wù)器,先用stun來進(jìn)行nat穿透鏈接,官方數(shù)據(jù)稱85%是可以穿透的.(我是那15%,心累),如果穿不透,還可以turn服務(wù)器來進(jìn)行橋接
(由于我的韓國服務(wù)器本來網(wǎng)速就不怎么好,于是就忽略了turn)

WebRTC的連接基本流程

  1. 首先 A,B 生成 RTCPeerConnection

  2. 然后 A 調(diào)用createOffer 生成 Description,
    調(diào)用setLocalDescription,
    然后再把Description發(fā)送給B

  3. B收到A的Description后,
    調(diào)用setRemoteDescription,
    然后調(diào)用createAnswer 生成 B的Description,
    然后把B的Description發(fā)送給A

  4. A收到B的Description后,setRemoteDescription

沒錯,就是這么簡單,其實就是一個交換Description的過程
第一個生成Description的叫offer,第二個生成Description的叫answer
這個交換的過程需要你自己的信令服務(wù)器(即是通信服務(wù)器)去傳遞

還有一點,上面操作的過程中,RTCPeerConnection可能產(chǎn)生icecandidate數(shù)據(jù),需要把數(shù)據(jù)傳給對方
基本流程如下:

  1. A 實現(xiàn)peer.onicecandidate,使其可以監(jiān)聽到icecandidate數(shù)據(jù)的產(chǎn)生
  2. A 監(jiān)聽到 icecandidate數(shù)據(jù)產(chǎn)生,通過信令服務(wù)器傳輸給B
  3. B 收到來自A的icecandidate數(shù)據(jù),調(diào)用 peer.addIceCandidate(new RTCIceCandidate(message.candidate)) 設(shè)置數(shù)據(jù)

上面這個操作,A,B都需要做,雙方都會產(chǎn)生icecandidate數(shù)據(jù),然后發(fā)送給對方,這個p2p鏈接的過程

基礎(chǔ)接口介紹

由于各個瀏覽器的函數(shù)名,以及參數(shù)都不一樣,所以我采用了adapter.js,來消除瀏覽器之間的差異...(一直在chrome下調(diào)試,跑一下火狐的,各種參數(shù)報錯后...馬上用了adapter)

下面函數(shù)也是才用了adapter.js后進(jìn)行介紹的;

navigator.mediaDevices.getUserMedia()

看著上面函數(shù)名,就知道它是獲取你本機的攝像頭麥克風(fēng)的了,就是說,即使不做 視頻聊天,也可以來玩玩攝像頭→_→

//還有一個facingMode,資料不多,寫上了
var usermedia = navigator.mediaDevices.getUserMedia({
  audio:true,
  video:{
    //ideal表示期望值,希望能拿到,拿不到就按照最大最小
    //(不要亂設(shè)值,不然獲取不了攝像頭就別說我沒提醒了)赤裸裸的恐嚇你→_→
    height:{ min: 320, ideal: 1280, max: 1920 },
    width:{ min: 240, ideal: 720, max: 1080 },
    frameRate: { ideal: 10, max: 15 }  //幀率
  }
});

//經(jīng)過adapter翻譯后
chrome: 
{
  "audio":true,
  "video":{
    "optional":[
      {"minHeight":1280},
      {"maxHeight":1280},
      {"minWidth":720},
      {"maxWidth":720},
      {"minFrameRate":10},
      {"maxFrameRate":10}
    ],
    "mandatory":{
      "minHeight":320,
      "maxHeight":1920,
      "minWidth":240,
      "maxWidth":1080,
      "maxFrameRate":15
    }
  }
}

usermedia.then(funcion(stream){
  //stream就是你獲得的媒體流
})

相關(guān)的還有以下等等,就不詳細(xì)介紹了:
navigator.mediaDevices.enumerateDevices() //獲取媒體設(shè)備(video可以用它拿到的id,來決定在那個音頻輸出設(shè)備中輸出哦~)
MediaRecorder //記錄媒體流(可以錄影,轉(zhuǎn)化成編碼再傳輸,再轉(zhuǎn)為流,再播放,挺好玩的東西)

RTCPeerConnection 系列

就是建立p2p連接那個,這里說明部分api的用法

new RTCPeerConnection()
//生成一個peer連接,但還沒開始鏈接
var peer = new RTCPeerConnection({
  "iceServers": [  //ice服務(wù)器,可以設(shè)置stun,turn服務(wù)器
    {"url": "stun:stun.l.google.com:19302"}
  ]
});
peer.onaddstream
//獲取到對方發(fā)送的媒體流,event.stream,至于要干嘛....你看著辦
peer.onaddstream = function (event) {    
  Vue.set(client, "stream", event.stream);
};
peer.onicecandidate
//獲取到icecandidate信息,要發(fā)送給p2p的對方
//不然p2p鏈接就進(jìn)行不下去了!
//(我發(fā)現(xiàn)是一般setLocalDescription,setRemoteDescription后得到)
peer.onicecandidate = function (event) {    
  if (!event.candidate) return;
  messageServer.sendRoomMessage({        
    action: "client_icecandidate",
    target: client.id,
    candidate: event.candidate
  })
};
peer.oniceconnectionstatechange
//簡單的可以理解成連接狀態(tài)事件
//這部分建議還是看文檔
//https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState
peer.oniceconnectionstatechange = function(){
  switch (peer.iceConnectionState) {
    case "failed":
      //顯然是失敗
    case "checking":
      //連接中...
    case "connected":
     //連接成功
    case "completed":
      //鏈接完成
    case "disconnected":
      //連接斷開
  }
}
peer.createOffer
//部分瀏覽起會因為沒有這個config而報錯的,所以還是加上吧
//測試一下config中兩個值設(shè)置為0的時候,發(fā)送offer的,沒有拿到stream
//目測是用來之指定需要怎么樣的資料的
//更多的資料找不到
var config = {    
  offerToReceiveAudio: 1,
  offerToReceiveVideo: 1
}
peer.createOffer(config).then(function (dp) {    
  //獲取到的Description,應(yīng)該發(fā)送給對方設(shè)置
  client.peer.setLocalDescription(dp);    
  messageServer.sendRoomMessage({        
    action: "client_offer",        
    target: client.id,        
    sdp: dp    
  });
});
peer.createAnswer
//這個又很奇怪,加上config就會報錯,不加問事....好奇怪
//其實這個函數(shù)就是獲取到對方的offer后進(jìn)行的應(yīng)答
client.peer.createAnswer().then(function (dp) {    
  //獲取到本地Description后,記得發(fā)送給對方設(shè)置
  messageServer.sendRoomMessage({       
    action: "client_answer",        
    target: client.id,        
    sdp: dp    
  });    
  client.peer.setLocalDescription(dp);
}).catch(function () {    
  console.log(client.id + ":生成 answer 失敗")  
});

RTCDataChannel在這里就不介紹
更詳細(xì)的接口信息,可以查看 https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API

WebRTC的注意事項

  1. 在部分瀏覽器中,getUserMedia 需要有 https才能打開,例如chrome,所以要如果掛帶外網(wǎng),需要https的支持
  2. 在實際開發(fā)中,請才用上面的adapter.js來控制好不同瀏覽器之間的區(qū)別
  3. 在getUserMedia的時候,如果亂設(shè)置,可能獲取不到設(shè)備的情況哦!

WebRTC的 demo

由于代碼過長就不直接貼了

代碼縮略圖

demo地址:https://static.meislzhua.online/
手機打開的話 需要手機chrome才可以(沒測試哪部分不兼容的)
桌面瀏覽器在chrome 51 和 ff48中測試成功
代碼地址:https://github.com/meislzhua/webrtc-demo

后語

在8月10的時候,突然想寫一個webrtc的demo...
于是
10 號上午看了webrtc的相關(guān)資料,下午復(fù)習(xí)一下leancloud的通信
11 號一直在調(diào)leancloud的通信...(試過推送和實時通信)
12 號正式開始寫demo
13 號 demo完成,但成功率很低..(發(fā)現(xiàn)是原來我的網(wǎng)絡(luò)不行)..然后準(zhǔn)備方案二
14 號 研究了一整天的視頻錄制與傳輸,后被leancloud實時磨得沒耐心(5K/s限制),放棄了
15-16號,醬油了一下,把demo打包成了electron應(yīng)用,并在linux,window,mac osx下測試
17號 隨意重構(gòu)了一下代碼,寫了本篇文章....

我本來打算demo用electron應(yīng)用來代替的..這樣可以保密一些東西,后來想想,還是算了~相信讀者素質(zhì)
歡問題迎留言 或者指出本文的錯誤

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容