3 HTML躬行記——WebRTC視頻通話

WebRTC 在創建點對點(P2P)的連接之前,會先通過信令服務器交換兩端的 SDP 和 ICE Candidate,取兩者的交集,決定最終的音視頻參數、傳輸協議、NAT 打洞方式等信息 。
在完成媒體協商,并且兩端網絡連通之后,就可以開始傳輸數據了 。
本文示例代碼已上傳至 Github,有需要的可以隨意下載 。
一、術語在實現一個簡單的視頻通話之前,還需要了解一些相關術語 。
1)SDP
SDP(Session Description Protocal)是一個描述會話元數據(Session Metadata)、網絡(Network)、流(Stream)、安全(Security)和服務質量(Qos,Grouping)的 WebRTC協議,下圖是 SDP 各語義和字段之間的包含關系 。
換句話說,它就是一個用文本描述各端能力的協議,這些能力包括支持的音視頻編解碼器、傳輸協議、編解碼器參數(例如音頻通道數 , 采樣率等)等信息 。

3 HTML躬行記——WebRTC視頻通話

文章插圖
下面是一個典型的 SDP 信息示例,其中 RTP(Real-time Transport Protocol)是一種網絡協議,描述了如何以實時方式將各種媒體從一端傳輸到另一端 。
=================會話描述======================v=0o=alice 2890844526 2890844526 IN IP4 host.anywhere.coms=-=================網絡描述======================c=IN IP4 host.anywhere.comt=0 0================音頻流描述=====================m=audio 49170 RTP/AVP 0a=rtpmap:0 PCMU/8000================視頻流描述=====================m=video 51372 RTP/AVP 31a=rtpmap:31 H261/900002)ICE Candidate
ICE 候選者描述了 WebRTC 能夠與遠程設備通信所需的協議、IP、端口、優先級、候選者類型(包括 host、srflx 和 relay)等連接信息 。
host 是本機候選者 , srflx 是從 STUN 服務器獲得的候選者,relay 是從 TURN 服務器獲得的中繼候選者 。
在每一端都會提供許多候選者,例如有兩塊網卡,那么每塊網卡的不同端口都是一個候選者 。
WebRTC 會按照優先級倒序的進行連通性測試,當連通性測試成功后,通信的雙方就建立起了連接 。
3)NAT打洞
在收集到候選者信息后,WebRTC 會判斷兩端是否在同一個局域網中,若是,則可以直接建立鏈接 。
若不是,那么 WebRTC 就會嘗試 NAT 打洞 。WebRTC 將 NAT 分為 4 種類型:完全錐型、IP 限制型、端口限制型和對稱型 。
前文候選者類型中曾提到 STUN 和 TURN 兩種協議,接下來會對它們做簡單的說明 。
STUN(Session Traversal Utilities for NAT,NAT會話穿越應用程序)是一種網絡協議,允許位于 NAT 后的客戶端找出自己的公網地址,當前 NAT 類型和 NAT 為某一個本地端口所綁定的公網端口 。
這些信息讓兩個同時處于 NAT 路由器之后的主機之間建立 UDP 通信,STUN 是一種 Client/Server 的協議 , 也是一種 Request/Response 的協議 。
下圖描繪了通過 STUN 服務器獲取公網的 IP 地址 , 以及通過信令服務器完成媒體協商的簡易過程 。
3 HTML躬行記——WebRTC視頻通話

文章插圖
TURN(Traversal Using Relay NAT,通過 Relay 方式穿越 NAT),是一種數據傳輸協議,允許通過 TCP 或 UDP 穿透 NAT 。
TURN 也是一個 Client/Server 協議,其穿透方法與 STUN 類似 , 但終端必須在通訊開始前與 TURN 服務器進行交互 。
下圖描繪了通過 TURN 服務器實現 P2P 數據傳輸 。
3 HTML躬行記——WebRTC視頻通話

文章插圖
CoTurn 是一款免費開源的 TURN 和 STUN 服務器,可以到 GitHub 上下載源碼編譯安裝 。
二、信令服務器通信雙方彼此是不知道對方的,但是它們可以先與信令服務器(Signal Server)連接 , 然后通過它來互傳信息 。
可以將信令服務器想象成一個中間人 , 由他來安排兩端進入一個房間中 , 然后在房間中可以他們就能隨意的交換手上的情報了 。
本文會通過 Node.js 和 socket.io 實現一個簡單的信令服務器,完成的功能僅僅是用于實驗,保存在 server.js 文件中 。
如果對 socket.io 不是很熟悉,可以參考我之前分享的一篇博文,對其有比較完整的說明 。
1)HTTP 服務器
為了實現視頻通話的功能,需要先搭建一個簡易的 HTTP 服務器,掛載靜態頁面 。
注意,在實際場景中,這塊可以在另一個項目中執行,本處只是為了方便演示 。
const http = require('http');const fs = require('fs');const { Server } = require("socket.io");// HTTP服務器const server = http.createServer((req, res) => {// 實例化 URL 類const url = new URL(req.url, 'http://localhost:1234');const { pathname } = url;// 路由if(pathname === '/') {res.writeHead(200, { 'Content-Type': 'text/html' });res.end(fs.readFileSync('./index.html'));}else if(pathname === '/socket.io.js') {res.writeHead(200, { 'Content-Type': 'application/javascript' });res.end(fs.readFileSync('./socket.io.js'));}else if(pathname === '/client.js') {res.writeHead(200, { 'Content-Type': 'application/javascript' });res.end(fs.readFileSync('./client.js'));}});// 監控端口server.listen(1234);

推薦閱讀