目的
在 WebRTC 中,每個瀏覽器或終端支持的音視頻編解碼器、分辨率、碼率、幀率等可能不同。媒體能力協商的目的就是:
- 確保雙方能“聽得懂”對方發的媒體流;
- 明確誰發送、誰接收、怎么發送;
- 保障連接的互操作性和兼容性。
P2P的基本流程
參與角色
角色 | 說明 |
---|---|
peerA | 發起連接的端(通常是主叫) |
peerB | 接收連接的端(通常是被叫) |
signal | 信令服務器,用于中轉 SDP 和 ICE 信息,但不參與媒體傳輸 |
stun/turn | STUN/TURN 服務器,用于穿透 NAT 或作為中繼,輔助建立 P2P 或 Relay 通信 |
流程詳解
連接信令服務器
peerA
和peerB
分別通過 WebSocket 或其他方式連接信令服務器,用于后續中轉 SDP 和 ICE 數據。
PeerConnection 創建 + 添加媒體流(媒體準備)
peerA
:
- 創建
RTCPeerConnection
實例。 - 通過
getUserMedia()
獲取本地音視頻流。 addTrack()
或addStream()
將媒體加入連接對象。
創建 Offer SDP(發起協商)
peerA
調用createOffer()
:- 生成包含自身媒體能力(支持的音視頻編解碼器、方向、SSRC、碼率等)的 SDP。
peerA
調用setLocalDescription(offer)
:- 表示將該 SDP 用作自己的本地描述。
- 瀏覽器會開始進行 ICE 候選收集(即尋找可用的 IP/端口路徑)。
發送 Offer SDP(信令交換)
peerA
將 Offer SDP 通過signal
發送給peerB
。- 此時可以看到
peerA → signal → peerB
的 “Send SDP Offer”。
接收方處理 Offer
peerB
:
- 創建
RTCPeerConnection
實例。 setRemoteDescription(offer)
設置遠端描述(即peerA
的 SDP)。- 瀏覽器據此了解
peerA
的媒體能力,并開始準備回答。
創建 Answer SDP(應答協商)
peerB
調用createAnswer()
,根據雙方交集生成 Answer SDP。- 調用
setLocalDescription(answer)
設置為自己的本地描述。
發送 Answer SDP(信令交換)
peerB → signal → peerA
發送 Answer SDP。peerA
調用setRemoteDescription(answer)
,設置為遠端描述,協商正式完成。
到這一步為止,媒體能力協商完成(Codec、方向等確認)。
ICE 候選協商(網絡通路探測)
- 雙方瀏覽器后臺通過 STUN 向公網發送綁定請求,收集本地的候選地址(ICE candidate)。
- 每收集到一個 ICE 候選地址,會觸發
onicecandidate
事件。
發送和添加 ICE 候選(網絡路徑建立)
- 每次
onicecandidate
被觸發,候選地址通過信令發送給對方:peerA
→signal
→peerB
peerB
→signal
→peerA
- 收到對方候選后,使用
addIceCandidate()
添加。
當 ICE 連接狀態變為 connected
或 completed
時,即可開始進行媒體傳輸。
媒體流建立
- 媒體協商完成,網絡路徑打通后:
peerB
觸發onAddStream
或ontrack
事件,表示收到遠端音視頻流。- 同樣地,
peerA
也會收到對方的流。
重點流程
階段 | 關鍵函數 | 作用 |
---|---|---|
媒體協商 | createOffer / createAnswer | 生成 SDP |
setLocalDescription / setRemoteDescription | 設置 SDP 本地/遠端描述 | |
網絡協商 | onicecandidate / addIceCandidate | 交換并使用候選地址建立連接 |
信令傳輸 | signal | 中轉 SDP 和 ICE,但不傳輸媒體 |
媒體傳輸 | addTrack / ontrack | 接收對方音視頻流 |
重要函數
createOffer()
作用
生成一個 SDP Offer,描述本地支持的音視頻媒體能力(如 codec、媒體方向、分辨率、帶寬等)。
使用示例
const offer = await pc.createOffer();
應用場景
- 發起方調用,用于開始媒體協商;
- 會觸發 ICE 候選的收集。
createAnswer()
作用
接收到對方 SDP Offer 后,根據自己的能力生成一個 SDP Answer。
使用示例
const answer = await pc.createAnswer();
應用場景
- 接收方調用,用于回應媒體協商;
- 也會觸發 ICE 候選收集。
setLocalDescription(desc)
作用
設置本地描述(Offer 或 Answer),并開始 ICE 候選收集。
使用示例
await pc.setLocalDescription(offer); // offer 或 answer 都可以
說明
- 必須在發送 SDP 之前調用;
- 必須配合
createOffer
或createAnswer
使用。
setRemoteDescription(desc)
作用
設置遠端 SDP 描述(對方發送的 offer 或 answer),用于協商建立媒體連接。
使用示例
await pc.setRemoteDescription(remoteOffer);
注意
- 必須在
createAnswer()
前調用(即先設置遠端再生成應答); - SDP 是通過信令服務器中轉接收的。
onicecandidate
作用
監聽本地收集到的每一個 ICE 候選地址(IP + port),用于網絡路徑穿透。
示例
pc.onicecandidate = (event) => {if (event.candidate) {sendCandidateToPeer(event.candidate); // 通過信令發送}
};
注意
- 每收集一個候選地址就觸發一次;
- 全部收集完畢會收到一個
null
的event.candidate
。
addIceCandidate(candidate)
作用
將從對方接收到的 ICE 候選加入本地 ICE 代理中,用于連接建立。
示例
await pc.addIceCandidate(remoteCandidate);
注意
- 一定要在
setRemoteDescription
之后調用; - 多次調用用于添加多個候選。
addTrack
作用
添加音頻或視頻軌道(MediaStreamTrack)。
示例
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
const pc = new RTCPeerConnection();stream.getTracks().forEach(track => {pc.addTrack(track, stream); // 同一個 MediaStream 確保同步
});
特性
特性 | 說明 |
---|---|
精細軌道控制 | 每次添加單個 MediaStreamTrack (音頻或視頻) |
支持多軌道發送 | 可多次調用添加多個音頻/視頻軌 |
支持軌道同步 | 若多個軌道來自同一個 MediaStream ,瀏覽器會自動嘗試同步播放 |
返回 RTCRtpSender | 可動態設置編碼參數、帶寬、分辨率等 |
支持動態添加軌道 | 連接建立后也可添加軌道,會觸發重新協商 |
可用于替代 addStream | 現代 WebRTC 標準推薦使用,addStream() 已廢棄 |
與 ontrack 配套使用 | 遠端通過 ontrack 事件接收媒體軌道 |
軌道標簽與 ID 傳遞 | SDP 會攜帶軌道的 id 和所屬流 stream id (用于標識同步組) |
支持 simulcast(多編碼) | 搭配 RTCRtpSender.setParameters() 支持多編碼流發送 |
ontrack
作用
當遠端添加了新的媒體軌(音軌/視頻軌)時觸發,一般用于播放遠程視頻或音頻。
示例
pc.ontrack = (event) => {remoteVideo.srcObject = event.streams[0];
};
特性
- 可接收多個軌道;
- 是
addTrack()
和recvonly
類型媒體方向配合使用的標準回調。
發起端與接收端
發起端
const pc = new RTCPeerConnection();pc.onicecandidate = e => sendCandidate(e.candidate);
pc.ontrack = e => remoteVideo.srcObject = e.streams[0];localStream.getTracks().forEach(track => {pc.addTrack(track, localStream);
});const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
sendOffer(offer);
接收端
const pc = new RTCPeerConnection();pc.onicecandidate = e => sendCandidate(e.candidate);
pc.ontrack = e => remoteVideo.srcObject = e.streams[0];await pc.setRemoteDescription(offer);
localStream.getTracks().forEach(track => {pc.addTrack(track, localStream);
});
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
sendAnswer(answer);
總結
- 目的:確保雙方選用兼容的音視頻編解碼器、分辨率、碼率和傳輸參數,實現高質量的實時通信。
- 載體:基于 SDP(Session Description Protocol)協議,描述媒體的相關能力。
- 流程:
- 一方創建并發送包含自身支持能力的 SDP Offer
- 對方收到后生成 SDP Answer,確認兼容參數
- 雙方通過設置本地和遠端描述完成協商
- 內容包括:
- 編解碼器列表(如 VP8、H.264、Opus)
- 媒體流方向(發送、接收、雙向)
- 帶寬限制和媒體屬性
- 網絡傳輸細節(ICE、DTLS安全層)
- 結果:協商成功后,雙方基于共同支持的參數建立安全穩定的音視頻流通道。