SDP 在 WebRTC 中的角色与工作原理(深入指南)
本文面向实时音视频与前端工程师,系统讲解 SDP(Session Description Protocol)在 WebRTC 中的职责、Offer/Answer 协商模型、媒体与数据通道的关键字段、与 ICE/DTLS/RTP 的协作机制,以及实践中的常见坑与最佳实践。内含 Mermaid 图示与示例片段(需支持 Mermaid 渲染)。
整体概览:SDP 是"会话说明书"
- 职责:用文本描述一次实时会话的参数与能力,包括媒体类型、编解码、网络传输、加密指纹、ICE 参数与候选等。
- 作用:供双方通过 Offer/Answer 模型进行协商,最终达成一致后建立传输通道。
- 关系:
- SDP 本身不传输媒体,只描述"怎么传"。
- 媒体由 RTP/SRTP 传输;密钥协商由 DTLS 完成;可达性由 ICE(STUN/TURN)决定。
SDP: 会话描述 ICE: 候选与选路 DTLS: 密钥与指纹 媒体编解码与RTP参数 SRTP: 加密传输
SDP 基础结构
- 会话层(Session-level):
v=、o=、s=、t=、以及全局a=属性(如a=group:BUNDLE)。 - 媒体层(Media-level):每个
m=行描述一个媒体流或数据通道:m=audio/m=video/m=application(数据通道)- 端口通常设置为占位
9(实际由 ICE/DTLS 决定),传输如UDP/TLS/RTP/SAVPF或UDP/DTLS/SCTP。
- 关键属性:
a=mid:媒体标识,用于 BUNDLE 与路由。a=rtpmap/a=fmtp:编解码名称与参数(如 Opus、VP8/VP9/H.264)。a=setup/a=fingerprint:DTLS 握手角色与证书指纹。a=ice-ufrag/a=ice-pwd/a=candidate:ICE 凭据与候选。a=sendrecv/a=sendonly/a=recvonly/a=inactive:传输方向。a=msid/a=ssrc:标识媒体轨(Unified Plan 中以msid为主;旧栈可能出现ssrc)。
Offer/Answer 模型(JSEP 语境)
- 流程:
- 主叫端
createOffer()生成本地 SDP,setLocalDescription()后通过信令发送给对端。 - 被叫端
setRemoteDescription()后createAnswer()生成应答,setLocalDescription()并回发。 - 双方
setRemoteDescription()完成后,进入 ICE 检查、DTLS 握手,最终开始 SRTP 传输。
- 主叫端
- 重要特性:
- SDP 中包含双方能力与偏好(编解码、扩展、带宽)。选择共同支持的子集。
- 现代浏览器采用 Unified Plan:每个 Track 对应一个
m=行,通过a=mid、a=msid路由。
Peer A Peer B createOffer (生成本地 SDP) setLocalDescription + 发送 Offer setRemoteDescription(Offer) createAnswer (生成本地 SDP) setLocalDescription + 发送 Answer setRemoteDescription(Answer) ICE 检查 + DTLS 握手 ->> SRTP 传输 Peer A Peer B
SDP 与 ICE/DTLS/RTP 的关键字段
- ICE(可达性与选路):
a=ice-ufrag/a=ice-pwd:ICE 短期凭据,用于 STUN 完整性校验。a=candidate:候选地址(host/srflx/relay);Trickle ICE 下也可通过信令单独传输。a=ice-options:trickle:指示支持 Trickle ICE。
- DTLS/SRTP(加密与握手):
a=setup:actpass|active|passive:DTLS 握手角色协商(浏览器常用actpass)。a=fingerprint:sha-256 ...:对端证书指纹,防止中间人攻击。
- RTP(媒体类型与编码):
a=rtpmap:<pt> <codec>/<clockrate>[/channels]:编解码映射(如opus/48000/2)。a=fmtp:<pt> <params>:编码参数(如 Opus 的stereo=1;maxaveragebitrate=510000)。a=extmap:RTP 扩展(如音量提示、绝对时间戳)。
- BUNDLE 与路由:
a=group:BUNDLE <mid list>:多媒体流共享同一个 5 元组(节省资源)。a=mid:<id>:媒体标识,用于在单连接上复用与路由。
- 方向与轨:
a=sendrecv等决定媒体方向;a=msid标识关联的 Track。
数据通道(SCTP over DTLS)在 SDP 中的表达
- 媒体行:
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
- 常见属性:
a=sctp-port:5000或a=sctpmap:5000 webrtc-datachannel 1024(旧格式)
- 作用:在与媒体共用的 ICE/DTLS 栈上,提供可靠/不可靠的双向数据传输。
示例:简化的 SDP 片段(语义化)
sdp
v=0
o=- 46117326 2 IN IP4 127.0.0.1
s=-
t=0 0
# 复用所有 m= 行到一个 BUNDLE 组
a=group:BUNDLE 0 1 2
# 支持 Trickle ICE
a=ice-options:trickle
m=audio 9 UDP/TLS/RTP/SAVPF 111 0
c=IN IP4 0.0.0.0
# 标识此媒体行 ID
a=mid:0
# 音频方向(也可能是 sendonly/recvonly)
a=sendrecv
# 编码与参数
a=rtpmap:111 opus/48000/2
a=fmtp:111 maxplaybackrate=48000;stereo=1;maxaveragebitrate=510000
# DTLS 握手
a=setup:actpass
a=fingerprint:sha-256 3A:...:7F
# ICE 凭据与候选
a=ice-ufrag:u1Ab
a=ice-pwd:9kD3...
a=candidate:1 1 udp 2122260223 192.168.1.5 54321 typ host
a=msid:streamId audioTrackId
m=video 9 UDP/TLS/RTP/SAVPF 96
c=IN IP4 0.0.0.0
a=mid:1
a=sendrecv
# 典型视频编码(示意)
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;profile-level-id=42e01f
# 可能包含 simulcast/rid 扩展(省略)
a=msid:streamId videoTrackId
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
c=IN IP4 0.0.0.0
a=mid:2
a=sctp-port:5000
浏览器侧实践:如何生成与处理 SDP
- 典型流程:
js
// 1) 建立 PeerConnection
const pc = new RTCPeerConnection({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'turns:turn.example.com:443?transport=tcp', username: 'user', credential: 'pass' }
]
});
// 2) 添加媒体轨或数据通道
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
for (const track of stream.getTracks()) pc.addTrack(track, stream);
const dc = pc.createDataChannel('chat');
// 3) 创建与设置 Offer
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
// 通过你的信令通道把 offer.sdp 发给远端
// 4) 远端应答(假设通过信令收到 answer)
await pc.setRemoteDescription({ type: 'answer', sdp: remoteAnswerSdp });
// 5) Trickle ICE(候选边收集边发送)
pc.onicecandidate = (e) => {
if (e.candidate) sendSignal({ type: 'candidate', candidate: e.candidate });
};
- 注意事项:
- 不要手动"改写"浏览器生成的 SDP,除非完全理解其副作用;优先用标准 API(如
RTCRtpTransceiver)控制方向与编码。 - 现代浏览器默认 Unified Plan;旧库可能需要指定
sdpSemantics: 'unified-plan'(已逐步淘汰)。
- 不要手动"改写"浏览器生成的 SDP,除非完全理解其副作用;优先用标准 API(如
常见坑与排障
- 编解码不匹配:
- 端到端需有共同的编码集合;如 H.264 需对齐
profile-level-id与packetization-mode。
- 端到端需有共同的编码集合;如 H.264 需对齐
- ICE 凭据或候选缺失:
- 检查
a=ice-ufrag/a=ice-pwd与candidate是否正确传递;Trickle ICE 中记得调用addIceCandidate。
- 检查
- 方向错误(无声或黑屏):
a=sendonly/recvonly配置不当导致单向流;使用sendrecv或在Transceiver上设置正确方向。
- BUNDLE/MID 错误映射:
a=group:BUNDLE与a=mid不一致会导致路由失败;确保每个 m-line 的 mid 唯一且匹配。
- mDNS 主机候选:
- 浏览器可能在 SDP 中显示
xxxx.local而非内网 IP;属隐私保护正常现象。
- 浏览器可能在 SDP 中显示
最佳实践
- 统一使用 Unified Plan 与 BUNDLE;一个连接承载多媒体流与数据通道。
- 开启 Trickle ICE 缩短建链时延;必要时使用 ICE 重启应对网络切换。
- 优先使用开放的高效编码(Opus/VP8/VP9);H.264 需要更谨慎的
fmtp参数对齐。 - TURN 中继部署使用 TLS/443 与临时凭证;保障苛刻网络下的可用性。
- 使用调试工具:
chrome://webrtc-internals、浏览器 DevTools、Wireshark(SRTP/DTLS)、SDP 分析器。
参考与延伸阅读
- RFC 4566: SDP 基本规范
- RFC 3264: Offer/Answer 模式
- RFC 8839: JSEP(WebRTC 的 SDP 使用方法)
- RFC 5764: DTLS-SRTP(媒体加密)
- RFC 5389/8445/5766: STUN/ICE/TURN(连通性)
- MDN/JSEP:
RTCPeerConnection,RTCRtpTransceiver,webrtc-datachannel
总结:SDP 是 WebRTC 协商的"语言"。它把媒体、网络与安全的参数串成一份说明书,让双方据此完成 ICE 检查与 DTLS 握手并达成一致的编解码配置。掌握关键字段与 Offer/Answer 流程、谨慎处理 Trickle 与 BUNDLE 映射,再结合可靠的 STUN/TURN 部署,才能在复杂网络下稳定地建立实时连接。