50行代碼完成視頻通話 (WebRTC + WebSocket)


開始

1.相關技術

本示例主要使用了 WebRTC 和 WebSocket:

  • WebRTC(Web Real-Time Communication)即網頁即時通信,是一個支持網頁瀏覽器進行實時語音對話或視頻對話的API。
  • WebSocket是一種在單個TCP連接上進行全雙工通信的協議。在 WebSocket 中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸。

 

2.通話建立流程

簡單說一下流程,如瀏覽器 A 想和瀏覽器 B 進行音視頻通話:

  1. A、B 都連接信令服務器(ws);
  2. A 創建本地視頻,並獲取會話描述對象(offer sdp)信息;
  3. A 將 offer sdp 通過 ws 發送給 B;
  4. B 收到信令后,B 創建本地視頻,並獲取會話描述對象(answer sdp)信息;
  5. B 將 answer sdp 通過 ws 發送給 A;
  6. A 和 B 開始打洞,收集並通過 ws 交換 ice 信息;
  7. 完成打洞后,A 和 B 開始為安全的媒體通信協商秘鑰;
  8. 至此, A 和 B 可以進行音視頻通話。

引用網上的有關WebRTC建立的時序圖,可能更加直觀:

從上述流程,可以發現通信雙方在建立連接前需要交換信息,這也就是開頭提到的 WebSocket 充當的角色:信令服務器,用於轉發信息。而 WebRTC 不借助中間媒介 的意思是,在建立對等連接后,不需要借助第三方服務器中轉,而是直接在兩個實體(瀏覽器)間進行傳輸。

資源搜索網站大全 https://www.renrenfan.com.cn 廣州VI設計公司https://www.houdianzi.com

3.代碼

第一步

獲取視頻標簽,連接信令服務器,創建 RTCPeerConnection 對象。其中 RTCPeerConnection 的作用是在兩個對等端之間建立連接,其構造函數支持傳一個配置對象,包含ICE“打洞”(由於本示例在本機進行測試,故不需要)。

const localVideo = document.querySelector('#local-video'); const remoteVideo = document.querySelector('#remote-video'); const socket = new WebSocket('ws://localhost:8080'); const peer = new RTCPeerConnection(); socket.onmessage = () => { // todo } peer.ontrack = () => { // todo } peer.onicecandidate = () => { // todo }

第二步

獲取本地攝像頭/麥克風(需要允許使用權限),拿到本地媒體流(MediaStream)后,需要將其中所有媒體軌道(MediaStreamTrack)添加到軌道集,這些軌道將被發送到另一對等方。

navigator.mediaDevices.getUserMedia({ video: true, audio: true }) .then(stream => { localVideo.srcObject = stream; stream.getTracks().forEach(track => { peer.addTrack(track, stream); }); });

第三步

創建發起方會話描述對象(createOffer),設置本地SDP(setLocalDescription),並通過信令服務器發送到對等端,以啟動與遠程對等端的新WebRTC連接。

peer.createOffer().then(offer => { peer.setLocalDescription(offer); socket.send(jsON.stringify(offer)); });

當調用 setLocalDescription 方法,PeerConnection 開始收集候選人(ice信息),並發送offer_ice到對等方。這邊補充第一步中的peer.onicecandidate和socket.onmessage

對等方收到ice信息后,通過調用 addIceCandidate 將接收的候選者信息傳遞給瀏覽器的ICE代理。

peer.onicecandidate = e => { if (e.candidate) { socket.send(jsON.stringify({ type: 'offer_ice', iceCandidate: e.candidate })); } }; socket.onmessage = e => { const { type, sdp, iceCandidate } = JSON.parse(e.data); if (type === 'offer_ice') { peer.addIceCandidate(iceCandidate); } }

第四步

接收方收到了offer信令后,開始獲取攝像頭/麥克風,與發起方操作一致。同時將收到offer SDP指定為連接的遠程對等方屬性(setRemoteDescription),並創建應答SDP(createAnswer),發送到對等端。這邊補充第一步中的socket.onmessage。

socket.onmessage = e => { const { type, sdp, iceCandidate } = JSON.parse(e.data); if (type === 'offer') { navigator.mediaDevices.getUserMedia(); // 與發起方一致,省略 const offerSdp = new RTCSessionDescription({ type, sdp }); peer.setRemoteDescription(offerSdp).then(() => { peer.createAnswer(answer => { socket.send(JSON.stringify(answer)); peer.setLocalDescription(answer) }); }); } }

注意:當 setLocalDescription 方法調用后,開始收集候選人信息,並發送 answer_ice 到對等方。與發送方同理,不贅述。

第五步

通過不斷收集ICE信息(onicecandidate),發起方和應答方最終將建立一條最優的連接方式,此時會觸發 ontrack 回調,即可獲取到對等方的媒體流。

peer.ontrack = e => { if (e && e.streams) { remoteVideo.srcObject = e.streams[0]; } };


免責聲明!

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



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