切換語言為:簡體
音視訊通話之webRTC實現

音視訊通話之webRTC實現

  • 爱糖宝
  • 2024-09-24
  • 2043
  • 0
  • 0

前言

接上篇文章,實現了音視訊通話的轉接mqtt的實現,接下來進行音影片的具體實現,這裏採用的方案是透過webRTC進行實現,這個API的好處就是不用額外安裝其他軟體的前提下,瀏覽器直接點對點實現音視訊通話,現在來簡單介紹下具體實現過程,希望能對你有幫助。

webRTC是什麼

webRTC是一項實時通訊技術,也叫點對點通訊,它能讓 Web應用程式站點能夠流式傳輸音影片媒體,以及瀏覽器之間交換資料而無需中介軟體,建立瀏覽器資料共享和傳輸,而且通道一旦建立,可以不經過服務端,客戶端對客戶端進行實時通話。

準備工作

概念知識

  • RTCPeerConnection

簡稱peer,點對點通訊(peer-to-peer),透過這個方法建立一個peer端,可以理解成建立了一個影片視窗殼子,例如發起方建立一個peer,接收方也建立一個peer,兩端之間進行聯通,即可實現實時通話。

  • offer

透過createOffer建立的offer資訊,裡面包含SDP offer資訊,發起方將offer傳送給對方,建立影片連線。

  • answer

透過createAnswer建立的應答資訊,和offer類似,接收方接收到offer後,透過createAnswer方法建立本機的SDP資訊,然後傳送給發起方,這樣雙方的SDP交換就完成了。

  • candidate

有用過獲取本機ip的對這個應該眼熟,獲取本機ip地址,也是透過webRTCcandidate進行實現,這個的主要作用是獲取本機的ip,它會優先抽區域網中獲取,如果區域網獲取不到,然後就會從TURN/STURN獲取。

  • 信令伺服器

通常使用webSocketmqtt等進行實現,在前面我們有說過建立offeranswer傳送給對方,瀏覽器怎麼知道要發給誰?所以這裏就是使用信令伺服器傳送給指定的人。

  • TURN/STURN

這個俗稱就是打洞,網路穿透,當兩個裝置不處於同一區域網的前提下,就需要這個進行一個網路穿透,透過這個可以獲取兩個裝置各自在公網的ip,這樣就能進行連線互通。

為什麼要交換SDP和信令伺服器

有人可能會問,WebRTC不是能夠點對點直接通訊嗎?為什麼還需要交換SDP和信令伺服器?

舉個梨子:張三會中,英,韓語三門語言,李四會中、日、俄語,現在張三和李四想要對話交流,自然而然的找到兩個人共同點語言中文進行交流,同理,交換SDP其實就是找共同語言的一個過程,也就是取交集。

然後就是信令伺服器,再打個比方,現在有100個房間,我現在只想去張三的房間,但是我又不知道張三的房間號,所以這個時候就需要信令伺服器Socket,將我想去張三房間的這個想法轉達給張三,並帶我去他的房間。

常用方法

API

  • RTCPeerConnection:建立一個peer端

  • setLocalDescription:設定本地描述

  • setRemoteDescription:設定遠端描述

  • createOffer:建立一個SDP offer

  • createAnswer:建立一個SDP Answer

  • addStream: 新增影片流

  • addIceCandidate:新增對方的網路資訊

監聽

  • onicecandidate:監聽到ip資訊

  • onaddstream:監護獲取對方的影片流資訊

  • onicegatheringstatechange :監聽網路協商狀態

實現步驟

  • 交換SDP

音視訊通話之webRTC實現

  1. 發起方peerA,接收方peerB都建立一個peer

  2. 發起方獲取本機攝像頭資訊,並透過addStream新增影片流到通道

  3. 接收方獲取本機攝像頭資訊,並透過addStream新增影片流到通道

  4. 發起方透過createOffer獲取到SDP資訊,並透過setLocalDescription新增到本地影片描述資訊

  5. 接收方拿到發起方SDP資訊後,透過setRemoteDescription新增對方SDP到遠端影片描述資訊,再透過createAnswer建立應答SDP資訊,透過setLocalDescription新增到本地影片描述資訊,再將answer SDP資訊傳送給發起方

  6. 發起方拿到對方傳送的SDP資訊後,透過setRemoteDescription新增對方SDP到遠端影片描述資訊,到這裏,雙方交換SDP已經完成。

  • 建立Candidate連線

透過監聽onicecandidate,我們能獲取當前本機的Candidate(ip,網路資訊),透過信令伺服器傳送給對方,對方接收到後透過addIceCandidate,將雙方建立連線,從而進行畫面的流式傳遞,通道建立完成。可以監聽icegatheringstatechange,當iceGatheringState變為complete時,表示雙方網路通道已經連線就緒。

簡單實現

peer接收方主要做的事:

  1. 初始化攝像頭並透過addStream新增

  2. 建立offer設定setLocalDescription併發送offer sdp

  3. 收到answer sdp設定setRemoteDescription

  4. 收集到candidate傳送給對方

// peerA 發起方
let localStream;
let localPeer;

//獲取本機攝像頭
async function getLocalVideo(){
   const stream = await getAndSelectCamera({ audio: true });
    if (stream) {
      localStream = stream;
      document.getElementById("localVideo").srcObject = stream;
      connectInit()
    }
}
getLocalVideo()

// RTC初始化
function connectInit(){
  localPeer = new RTCPeerConnection({});
  localPeer.addStream(localStream);
  sendOffer();
  localPeer.onaddstream = (e) => {
    document.getElementById("remoteVideo").srcObject = e.stream;
  };
  localPeer.addEventListener("icegatheringstatechange",(ev) => {
      if (ev.target.iceGatheringState === "complete") {
         console.log("影片連線成功")
      }
  },false);
}

// 傳送offer
function sendOffer(){
  localPeer.createOffer((offer) => {
      // 傳送Offer給對方
      mqttServer.publish("/webrtc",{type: "offer", offer})
      // 將offer設定到本地
      localPeer.setLocalDescription(offer);
  });

  localPeer.onicecandidate = (event) => {
    if (event.candidate) {
      //收集到candidate後,將candidate資訊傳送給對方
      mqttServer.publish("/webrtc",{type: "candidate", candidate: event.candidate})
    }
  };
}

// 獲取到peerB的answer sdp
function getAnswer(data){
   localPeer.setRemoteDescription(new RTCSessionDescription(data.answer))
}

// 收到對方的candidate後,配對
function handleCandidate(data) {
   localPeer.addIceCandidate(new RTCIceCandidate(data.candidate));
}

peer接收方主要做的事:

  1. 初始化攝像頭並透過addStream新增

  2. 收到對方傳送的offer設定setRemoteDescription

  3. 建立answer sdp設定設定setLocalDescription併發送answer sdp

  4. 收集到candidate傳送給對方

// peerB接收方

let localStream;
let localPeer;

//獲取本機攝像頭
async function getLocalVideo(){
   const stream = await getAndSelectCamera({ audio: true });
    if (stream) {
      localStream = stream;
      document.getElementById("localVideo").srcObject = stream;
      connectInit()
    }
}
getLocalVideo()

// RTC初始化
function connectInit(){
  localPeer = new RTCPeerConnection({});
  localPeer.addStream(localStream);
  localPeer.onaddstream = (e) => {
    document.getElementById("remoteVideo").srcObject = e.stream;
  };
  localPeer.onicecandidate = (event) => {
    if (event.candidate) {
       mqttServer.publish("/webrtc",{type: "candidate", candidate: event.candidate})
    }
  };
  localPeer.addEventListener("icegatheringstatechange",(ev) => {
      if (ev.target.iceGatheringState === "complete") {
         console.log("影片連線成功")
      }
  },false);
}

// 收到對方的offer後建立answer
function handleSendAnswer(data){
  localPeer.setRemoteDescription(new RTCSessionDescription(data.offer));
  localPeer.createAnswer(
    (answer) => {
      localPeer.setLocalDescription(answer);
      mqttServer.publish("/webrtc",{type: "answer",answer})
  });
}

//收到對方的candidate後,配對
function handleCandidate(data) {
   localPeer.addIceCandidate(new RTCIceCandidate(data.candidate));
}

TURN/STURN

這個主要是進行網路穿透,需要搭建一個TURN伺服器,在建立RTCPeerConnection的時候有一個可選引數,配置這個可以進行網路穿透,由於我目前場景沒有使用到這個,所以這裏只是簡單提一下。

{
  iceServers: [
    {
      url: "xxxx",
      username: "xxxx",
      credential: "xxxxxxxx",
    },
  ],
}

最後

到這裏webRTC音視訊通話已經完成了,其實還是挺有意思的,除此之外,WebRTC還可以用來實時傳輸檔案等,例如可以實現白板寫字同步功能等

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.