1. 程式人生 > 實用技巧 >webrtc實現視訊群聊系列文章(一)之基礎入門

webrtc實現視訊群聊系列文章(一)之基礎入門

前言

1.本文首先會介紹webrtc中的一些基本概念,並通過示例程式碼一步步完成一個簡單的本地視訊模擬通話,不通過網路轉接。
2.示例程式碼及其重要,是完整實踐配合基礎概念的分部介紹。
3.每一段話請注意仔細理解,我會盡可能簡單的介紹基礎概念。
4.請確保你的電腦是有攝像頭的,桌上型電腦沒有攝像頭外接一個就可以

媒體裝置

在開發Web時,WebRTC標準提供了API,用於訪問連線到計算機或智慧手機的相機和麥克風。這些裝置通常稱為媒體裝置,可以通過實現MediaDevices介面的navigator.mediaDevices物件使用JavaScript進行訪問。通過該物件,我們可以列舉所有已連線的裝置,偵聽裝置更改(連線或斷開裝置時),並開啟裝置以檢索媒體流


呼叫getUserMedia()將觸發許可權請求。如果使用者接受許可,則通過包含一個視訊和一個音軌的MediaStream來解決承諾。如果許可權被拒絕,則丟擲PermissionDeniedError 。如果沒有連線匹配的裝置,則會丟擲NotFoundError


  • 獲取媒體流示例程式碼

 // createMedia
 async createMedia() {
        let streamTep = null;
        // 儲存本地流到全域性
        streamTep = await navigator.mediaDevices.getUserMedia({audio: true, video: true})
        console.log("streamTep",streamTep)
        return streamTep;
 },

著重點:navigator.mediaDevices.getUserMedia()函式

  • 本地播放媒體流
 <div style="float: left">
      <video id="sucA"></video>
  </div>
  // 本地攝像頭開啟
async nativeMedia(){
        const that = this;
        that.localStream = await this.createMedia()
        let video = document.querySelector('#sucA');
        // 舊的瀏覽器可能沒有srcObject
        if ("srcObject" in video) {
          video.srcObject = that.localStream;
        } else {
          video.src = window.URL.createObjectURL(that.localStream);
        }
        // eslint-disable-next-line no-unused-vars
        video.onloadedmetadata = function(e) {
          video.play();
        };
        that.initPeer(); // 獲取到媒體流後,呼叫函式初始化 RTCPeerConnection
},

著重點:播放本地媒體流實質就是將上一步拿到的媒體流賦值到對應的媒體標籤元件,在這裡就是給video標籤

  • 媒體裝置約束條件
//比如設定視訊視窗的範圍
{
    "video": {
        "width": {
            "min": 640,
            "max": 1024
        },
        "height": {
            "min": 480,
            "max": 768
        }
    }
}
//獲取手機端前置攝像頭
{ audio: true, video: { facingMode: "user" } }
//後置攝像頭
{ audio: true, video: { facingMode: { exact: "environment" } } }
//具有頻寬限制的WebRTC傳輸,可能需要較低的幀速率
{ video: { frameRate: { ideal: 10, max: 15 } } }


著重點:通俗的講就是對音訊或者視訊進行約束,約束條件引數入口就是上面示例中的獲取媒體流的函式 getUserMedia({audio: true, video: true})


名詞:ICE,STUN,NAT,TURN,SDP解釋

互動式連線建立(ICE)是允許您的Web瀏覽器與對等方連線的框架。A到B兩個客戶端能夠點對點通訊的基礎就是建立通訊,但是很多情況下兩個裝置之間並沒有公網IP,而且還有防火牆這些阻斷資料傳輸,這個時候就需要STUN來為你提供一個獨一無二的地址和TURN伺服器中繼資料。

更詳細的介紹請看 MDN,具體不再介紹


RTCPeerConnection

該RTCPeerConnection介面表示本地計算機和遠端對等方之間的WebRTC連線。它提供了連線到遠端對等方,維護和監視連線以及在不再需要連線時關閉連線的方法。

RTCPeerConnection之間如何建立

  • 本地流獲取(上述已解釋)
  • 全域性引數初始化

 --------------------全域性初始化PeerConnection--------------------------
  var PeerConnection = window.RTCPeerConnection ||
          window.mozRTCPeerConnection ||
          window.webkitRTCPeerConnection;
 -----------------------iceServers  stun和turn伺服器配置-------------------------------------------
var iceServers = {
          iceServers: [
            { url: "stun:stun.l.google.com:19302"},// 谷歌的公共服務
			{
            url: 'turn:numb.viagenie.ca',
            credential: 'muazkh',
            username: '[email protected]'
          }
          ]
        };

  • 初始化兩個模擬客戶端

       //以下pc和pc2  分別代表兩個模擬客戶端的連結服務簡寫  ( pc: 代表pc->pc2連結  pc2:代表pc2->pc連結 )
        const that = this;
        that.pc = new PeerConnection(iceServers);
        that.pc2 = new PeerConnection(iceServers);
        //將全域性視訊流賦給pc連結服務
        that.pc.addStream(this.localStream);
         //監聽ice候選資訊  具體下面會說明
        that.pc.onicecandidate = function(event) {
          console.log("pc onicecandidate",event)
          if (event.candidate) {
		  //一般來說這個地方是通過第三方(socket後面會將網路端點對點)傳送給另一個客戶端,但是現在本地演示直接將候選資訊傳送到pc2連結服務
            that.pc2.addIceCandidate(event.candidate.toJSON());
          }
        };
		//監聽遠端視訊 pc充當呼叫端,所以只要監聽pc2有無視訊流資訊進來
        that.pc2.onaddstream = (event) => {
          console.log("onaddstream",event)
		  //監聽到流後將視訊流賦給另一個video標籤
          let video = document.querySelector('#sucB');
          video.srcObject = event.stream;
          video.onloadedmetadata = function(e) {
            console.log(e)
            video.play();
          };
        };

onicecandidate: 候選ICE描述了WebRTC能夠與遠端裝置進行通訊所需的協議和路由。在啟動WebRTC對等連線時,通常在連線的每一端都建議多個候選物件,直到他們相互同意描述他們認為最好的連線的候選物件為止。

  • 呼叫端模擬呼叫(pc充當呼叫端)
//建立offer
 async createOffer() {
        const that = this;
        //建立offer
        let offer_tep = await that.pc.createOffer(this.offerOption);
        console.log("offer_tep",offer_tep)
        //設定本地描述
        await that.pc.setLocalDescription(offer_tep)
        //接收端設定遠端 offer 描述
        await that.pc2.setRemoteDescription(offer_tep)
        // 接收端建立 answer
        let answer = await that.pc2.createAnswer();
        // 接收端設定本地 answer 描述
        await that.pc2.setLocalDescription(answer);
        // 傳送端 設定遠端 answer 描述
        await that.pc.setRemoteDescription(answer);
      },

      //呼叫
      async callA() {
        const that = this;
        //建立offer 並儲存本地描述
        await that.createOffer()
      },

為何呼叫會有這麼麻煩的步驟呢?這就又涉及到webrtc的會話了,具體看下面一條

webrtc會話

“當用戶(上述pc)向另一個使用者(上述pc2)發起WebRTC呼叫時,會建立一個特殊的描述,稱為offer。此描述包括有關呼叫者為呼叫建議的配置的所有資訊。然後,接收者用一個答案來回應,這是他們通話結束的描述。以此方式,兩個裝置彼此共享為了交換媒體資料所需的資訊。這種交換是使用互動式連線建立(ICE)處理的,該協議允許兩個裝置使用中介程式交換要約和答覆,即使兩個裝置之間都被網路地址轉換(NAT)隔開。然後,每個對等方都保留兩個描述:本地描述(描述自己)和遠端描述(描述呼叫的另一端)”


上面的話簡單來說就是 A呼叫B,A建立offer,在本地保留offer,然後傳送給B,B建立應答,之後本地保留應答,再將應該傳送給A,A拿到後將B的應該設定為本地的遠端描述。

  • 圖示

後續

  • 本文是webrtc實現群聊系列文章的第一篇,持續更新中
  • 關注我的公眾號及時獲取最新文章更新