WebRTC 實戰 (3) —— SDP 交換與媒體協商
有了「信令伺服器」這個報馬仔,現在兩台電腦終於可以說話了。但它們要說什麼呢?
WebRTC 的成功連線,依賴於一場精密的**「媒體協商 (Media Negotiation)」**。這個過程就像是兩個第一次見面的外交官,在交換各自的通訊規格書。這本規格書,就是 SDP (Session Description Protocol)。
一、 核心主角:RTCPeerConnection
RTCPeerConnection 是 WebRTC API 的靈魂。它負責處理 P2P 連線的所有黑魔法:封裝、加解密、頻寬管理以及數據傳輸。
連線建立的 5 個經典步驟
- A 建立 Offer:A 呼叫
createOffer()生成自己的 SDP,並透過setLocalDescription()給自己存檔。 - A 傳送 Offer:透過「信令伺服器」把這份 SDP 傳給 B。
- B 接收 Offer:B 收到後,呼叫
setRemoteDescription()把 A 的資訊記下來。 - B 建立 Answer:B 呼叫
createAnswer()生成回應的 SDP,並透過setLocalDescription()自己存檔。 - B 傳送 Answer:透過「信令伺服器」把自己的 SDP 回傳給 A。最後 A 呼叫
setRemoteDescription()。
二、 揭開 SDP 的面紗:它到底寫了什麼?
SDP 其實是一串看似亂碼的純文字資訊。它記錄了:
- 媒體類型:我有攝影機、麥克風、資料通道。
- 編碼格式:我支援 VP8, VP9, H.264。
- 連線資訊:網路候選位址。
- 安全性:加密的金鑰指紋。
三、 實作:手寫連線握手
將我們前兩篇學過的內容串聯起來:
javascript
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
// 1. 加入本地軌道
stream.getTracks().forEach(track => pc.addTrack(track, stream));
// 2. 處理遠端軌道
pc.ontrack = (event) => {
const [remoteStream] = event.streams;
document.querySelector('#remoteVideo').srcObject = remoteStream;
};
// 3. 發起連線 (Offerer)
async function makeCall() {
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
signaling.send({ type: 'offer', sdp: offer.sdp, to: 'User_B' });
}
// 4. 回應連線 (Answerer)
signaling.onMessage = async (msg) => {
if (msg.type === 'offer') {
await pc.setRemoteDescription(new RTCSessionDescription(msg));
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
signaling.send({ type: 'answer', sdp: answer.sdp, to: 'User_A' });
} else if (msg.type === 'answer') {
await pc.setRemoteDescription(new RTCSessionDescription(msg));
}
};總結
今天我們攻克了 WebRTC 最核心的邏輯:RTCPeerConnection 的管理職責、Offer/Answer 的 5 步握手流程,以及 SDP 作為「媒體合約」的重要性。
下一篇,我們將進入網路穿透的最終戰:ICE Candidate 與 NAT 穿越。
️ 進階挑戰
為什麼 WebRTC 不直接在 createOffer 之後就連線,而要搞這麼複雜的 5 步握手?(提示:想想看如果雙方都同時想發起通話,會發生什麼事?)
延伸閱讀與資源
- MDN: RTCPeerConnection API
- WebRTC.org: Connectivity