WebRTC 入門指南:實時通信完全解析
WebRTC 入門指南:實時通信完全解析
?? 簡介
WebRTC(Web 實時通信)是一項強大的技術,支持瀏覽器和移動應用實時交換音視頻與數據——無需中間服務器中轉。它是現代視頻通話、屏幕共享工具及實時協(xié)作平臺的核心底層技術。
本文將完整覆蓋 WebRTC 技術流程:從獲取用戶媒體到建立安全的點對點(P2P)連接,并提供基于 TypeScript 風格的 JavaScript 實戰(zhàn)示例。
?? 捕獲媒體流
什么是媒體流(Media Stream)?
流(Stream)是連續(xù)的數據傳輸流——在 WebRTC 中,特指實時傳輸的音頻或視頻數據。
使用 getUserMedia 捕獲音視頻
通過 navigator.mediaDevices.getUserMedia() 方法可請求訪問用戶的麥克風和攝像頭,示例如下:
const constraints = { audio: true, video: true }; // 配置:同時捕獲音頻和視頻
navigator.mediaDevices
.getUserMedia(constraints)
.then((mediaStream) => {
console.log('成功獲取媒體流:', mediaStream);
})
.catch((err) => {
console.error('獲取媒體流失敗:', err);
});
?? 注意:瀏覽器會先彈出權限請求,僅在用戶允許后才會提供音視頻訪問權限。
在 <video> 元素中顯示視頻
首先在 HTML 中定義用于顯示視頻的元素:
<video autoplay playsinline id="local-video"></video>
<!-- autoplay:自動播放;playsinline:在頁面內播放(避免全屏) -->
再通過 JavaScript 將捕獲到的媒體流綁定到視頻元素:
const videoElement = document.getElementById('local-video') as HTMLVideoElement;
navigator.mediaDevices.getUserMedia({ video: true }) // 僅捕獲視頻
.then((stream) => {
videoElement.srcObject = stream; // 將媒體流賦值給視頻元素
});
?? 枚舉與選擇設備
枚舉所有設備
通過以下方法可列出設備上所有可用的音視頻輸入/輸出設備(如麥克風、攝像頭、揚聲器):
navigator.mediaDevices.enumerateDevices()
.then((devices) => {
devices.forEach((device) => {
console.log(`${device.kind}:${device.label}`);
// 示例輸出:videoinput:USB 攝像頭、audioinput:內置麥克風
});
});
監(jiān)聽設備變化
當有新設備(如外接攝像頭)連接或設備斷開時,可通過事件監(jiān)聽實時更新設備列表:
navigator.mediaDevices.addEventListener('devicechange', () => {
console.log('設備列表已更新!');
// 可在此處重新調用 enumerateDevices() 刷新設備列表
});
?? 使用媒體約束(Media Constraints)
媒體約束允許精細化配置音視頻參數,例如指定使用某臺攝像頭、設置視頻分辨率或幀率:
const preferredDeviceId = 'abc123'; // 從 enumerateDevices() 獲取的目標設備 ID
const constraints = {
video: {
deviceId: { exact: preferredDeviceId }, // 精確指定使用某臺設備
width: { ideal: 1280 }, // 理想寬度:1280px
height: { ideal: 720 }, // 理想高度:720px(720P)
frameRate: { ideal: 30 }, // 理想幀率:30fps
},
audio: {
echoCancellation: true, // 開啟回聲消除
},
};
// 按約束條件獲取媒體流
navigator.mediaDevices.getUserMedia(constraints);
?? 捕獲屏幕
通過 getDisplayMedia() 方法可捕獲屏幕內容(如整個屏幕、特定窗口或應用),示例如下:
const screenStream = await navigator.mediaDevices.getDisplayMedia({
video: true, // 屏幕捕獲僅支持視頻(無音頻)
});
// 將屏幕流綁定到視頻元素顯示
document.querySelector('video').srcObject = screenStream;
?? 注意:瀏覽器會彈出選擇窗口,要求用戶指定要捕獲的屏幕、窗口或應用。
?? 管理媒體軌道(Media Tracks)
每個媒體流(Stream)包含一個或多個軌道(Track),分別對應音頻軌道或視頻軌道。可對軌道進行單獨禁用、停止等操作:
// 獲取僅含視頻的媒體流
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
// 提取流中的所有軌道
const tracks = stream.getTracks();
// 1. 禁用軌道(臨時關閉,可重新啟用)
tracks[0].enabled = false; // 禁用第一個軌道(此處為視頻軌道)
// 2. 完全停止軌道(釋放設備資源,無法恢復)
tracks.forEach((track) => track.stop());
?? 建立對等連接(Peer Connection)
WebRTC 的核心是 RTCPeerConnection 接口,它負責在兩個對等端(如兩臺設備)之間建立直接連接:
// 配置 ICE 服務器(用于穿透 NAT,建立 P2P 連接)
const config = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' } // 谷歌公共 STUN 服務器
],
};
// 創(chuàng)建對等連接實例
const peerConnection = new RTCPeerConnection(config);
?? ICE、STUN、TURN 概念解析
WebRTC 依賴以下三種技術實現對等端之間的網絡連接:
- ICE(Interactive Connectivity Establishment,交互式連接建立):核心框架,負責尋找對等端之間可用的網絡路徑。
- STUN(Session Traversal Utilities for NAT,NAT 會話穿越工具):幫助設備發(fā)現自身在公網中的 IP 地址和端口(解決 NAT 遮擋問題)。
- TURN(Traversal Using Relays around NAT,通過中繼穿越 NAT):當 P2P 直接連接失敗時,作為中繼服務器轉發(fā)音視頻數據(確保通信不中斷)。
含 TURN 服務器的配置示例
const config = {
iceServers: [
// 優(yōu)先使用 STUN 嘗試直接連接
{ urls: ['stun:stun.l.google.com:19302'] },
// 備用 TURN 服務器(需自行部署或使用商業(yè)服務)
{
urls: 'turn:turn.example.com', // TURN 服務器地址
username: 'user', // 認證用戶名
credential: 'pass' // 認證密碼
},
],
};
?? ICE 候選者交換
ICE 候選者(ICE Candidate)是包含設備網絡信息(如 IP、端口、傳輸協(xié)議)的數據,對等端需交換候選者才能找到可通信的路徑。交換需通過信令服務器(如 WebSocket)完成:
發(fā)送 ICE 候選者(本地 → 遠程)
// 監(jiān)聽本地 ICE 候選者生成事件
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
// 通過信令服務器將候選者發(fā)送給遠程對等端
signalingServer.send('ice-candidate', event.candidate);
}
};
接收 ICE 候選者(遠程 → 本地)
// 監(jiān)聽信令服務器的 ICE 候選者消息
signalingServer.on('ice-candidate', async (candidate) => {
// 將遠程候選者添加到本地對等連接
await peerConnection.addIceCandidate(candidate);
});
?? 提議/應答交換(SDP)
對等連接建立前,需交換 SDP(Session Description Protocol,會話描述協(xié)議)信息,用于協(xié)商媒體格式、編碼方式等參數。交換過程分為“提議(Offer)”和“應答(Answer)”兩步:
1. 發(fā)起方創(chuàng)建并發(fā)送提議
// 1. 創(chuàng)建提議(包含本地媒體配置)
const offer = await peerConnection.createOffer();
// 2. 將提議設置為本地描述(保存本地配置)
await peerConnection.setLocalDescription(offer);
// 3. 通過信令服務器發(fā)送提議給接收方
signalingServer.send('offer', offer);
2. 接收方處理提議并發(fā)送應答
// 1. 接收并設置遠程描述(保存發(fā)起方的配置)
peerConnection.setRemoteDescription(offer).then(async () => {
// 2. 創(chuàng)建應答(包含接收方的媒體配置)
const answer = await peerConnection.createAnswer();
// 3. 將應答設置為本地描述(保存接收方配置)
await peerConnection.setLocalDescription(answer);
// 4. 通過信令服務器發(fā)送應答給發(fā)起方
signalingServer.send('answer', answer);
});
?? 向連接添加本地媒體
將本地捕獲的音視頻流添加到對等連接,才能向遠程對等端傳輸媒體:
// 1. 獲取本地音視頻流
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
// 2. 將流中的所有軌道添加到對等連接
stream.getTracks().forEach((track) => {
peerConnection.addTrack(track, stream);
});
接收遠程媒體
監(jiān)聽 track 事件,獲取遠程對等端發(fā)送的媒體流并顯示:
peerConnection.addEventListener('track', (event) => {
// 從事件中提取遠程媒體流
const remoteStream = event.streams[0];
// 將遠程流綁定到視頻元素(顯示對方畫面)
document.getElementById('remote-video').srcObject = remoteStream;
});
?? 動態(tài)管理軌道
連接建立后,可動態(tài)調整媒體軌道(如關閉麥克風、切換攝像頭):
切換麥克風(禁用/啟用音頻軌道)
// 1. 找到音頻軌道的發(fā)送器(Sender)
const audioSender = peerConnection
.getSenders()
.find((sender) => sender.track?.kind === 'audio');
// 2. 禁用/啟用音頻軌道
if (audioSender) audioSender.track.enabled = false; // 禁用(靜音)
// if (audioSender) audioSender.track.enabled = true; // 啟用(取消靜音)
連接后添加新軌道
// 假設已獲取新的視頻軌道(如切換攝像頭后的軌道)
const newVideoTrack = stream.getVideoTracks()[0];
// 將新軌道添加到對等連接
peerConnection.addTrack(newVideoTrack, stream);
? 關閉 WebRTC 連接
結束通信時,需停止所有媒體軌道并關閉對等連接,釋放資源:
// 1. 停止所有發(fā)送器的軌道
peerConnection.getSenders().forEach((sender) => sender.track.stop());
// 2. 關閉對等連接
peerConnection.close();
?? 群組通話:Mesh、SFU 與 MCU 對比
當通話參與者超過 2 人時,需選擇合適的架構方案。以下是三種主流群組通話架構的對比:
Mesh 架構
- 原理:每個參與者直接與其他所有參與者建立 P2P 連接(如 3 人通話需建立 3 條連接)。
- 優(yōu)點:架構簡單,無需專用媒體服務器,延遲低。
- 缺點:參與者數量越多,CPU 占用和網絡帶寬消耗呈指數級增長(僅適合 4 人以下小規(guī)模通話)。
SFU(選擇性轉發(fā)單元)
- 原理:所有參與者將媒體流發(fā)送到 SFU 服務器,服務器不解碼媒體,僅根據需求將流轉發(fā)給其他參與者(如只轉發(fā)說話者的流)。
- 優(yōu)點:客戶端負載低(僅需處理 1 條上傳流和 N 條下載流),支持中等規(guī)模群組(20 人以內)。
- 主流方案:LiveKit、mediasoup、Mirotalk。
MCU(多點控制單元)
- 原理:所有參與者將媒體流發(fā)送到 MCU 服務器,服務器解碼并混合所有流(如將多個人的畫面合成一個分屏畫面),再將混合后的單一流轉發(fā)給所有參與者。
- 優(yōu)點:客戶端負載極低(僅需處理 1 條上傳流和 1 條下載流)。
- 缺點:服務器解碼/混合需大量計算資源,延遲較高,服務器成本高(適合大規(guī)模但對延遲不敏感的場景)。
?? 總結
WebRTC 僅需幾行 JavaScript 代碼,就能實現實時音視頻與數據傳輸。雖然入門簡單,但它涉及 ICE、SDP、信令等深層技術概念。
無論你是想開發(fā)類似 Zoom 的視頻會議工具,還是實時代碼面試平臺——理解如何捕獲、發(fā)送和接收點對點媒體流,都是邁出的第一步。
擴展鏈接
SpreadJS+GCExcel全棧解決方案--協(xié)同編輯的框架搭建(三)
浙公網安備 33010602011771號