WebRTC 實戰 (3) —— SDP 交換與媒體協商
有了「信令伺服器」這個報馬仔,現在兩台電腦終於可以說話了。但它們要說什麼呢?
WebRTC 的成功連線,依賴於一場精密的 「媒體協商 (Media Negotiation)」。這個過程就像是兩個第一次見面的外交官,在交換各自的通訊規格書。這本規格書,就是 SDP (Session Description Protocol)。
一、 核心主角:RTCPeerConnection
RTCPeerConnection 是 WebRTC API 的核心。它負責媒體協商、ICE 連線、加密、壅塞控制與資料傳輸。
連線建立的 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",
from: "User_A",
to: "User_B",
payload: pc.localDescription,
});
}
// 4. 回應連線 (Answerer)
signaling.onMessage = async (msg) => {
if (msg.type === 'offer') {
await pc.setRemoteDescription(msg.payload);
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
signaling.send({
type: "answer",
from: "User_B",
to: msg.from,
payload: pc.localDescription,
});
} else if (msg.type === 'answer') {
await pc.setRemoteDescription(msg.payload);
}
};範例使用整個 pc.localDescription,而不是只傳 sdp 字串,接收端才能同時取得 type 與 sdp。
同時發起 Offer:Glare
真實產品不能假設永遠只有一方發起協商。雙方同時新增 track 或 DataChannel 時,可能同時產生 Offer,造成 Glare。正式實作應採用 Perfect Negotiation 模式,依 signalingState 與 polite/impolite 角色決定接受、忽略或 rollback Offer。
總結
今天我們攻克了 WebRTC 最核心的邏輯:RTCPeerConnection 的管理職責、Offer/Answer 的 5 步握手流程,以及 SDP 作為「媒體合約」的重要性。
下一篇,我們將進入網路穿透的最終戰:ICE Candidate 與 NAT 穿越。
進階挑戰
試著把範例改成監聽 negotiationneeded,並研究 Perfect Negotiation 如何處理雙方同時發起 Offer 的情況。
延伸閱讀與資源
- MDN: RTCPeerConnection API
- WebRTC.org: Connectivity