本篇是 WebRTC 101 系列 第 4 篇,讲媒体平面:ICE 选好路之后,数据是怎么被加密、打包、抗丢包、自动调速地送到对方的。原理为主。
媒体平面传输:DTLS→SRTP、RTP/RTCP 与自适应
第 3 篇结束时,ICE 选出了一条能通的路径(比如 host 直连或 TURN 中继)。这一篇回答:路有了,字节怎么在上面安全地、流畅地跑起来。
先给一张全局图,接下来的小节都挂在这条链上:
你的摄像头/麦克风
│ 采集 + AEC/降噪(第1篇)
▼
编码器 (Opus / VP8 / VP9 / H.264 / AV1) ← 协商出的 payload(第2篇 m=)
▼
RTP 打包(时间戳/序号) ──┐
▼ │ 加密
SRTP (Secure RTP) │ 密钥来自 DTLS 握手(RFC 5764 扩展)
▼ ▼
同一份传输: RTP+RTCP 复用(RTCP-mux) → 走第3篇那条 ICE 通道
│ └── 控制面: 丢包反馈NACK/码率反馈
▼
对端: 解SRTP → 抖动缓冲(jitter buffer) → 解码 → 播放
1. 一切加密的前提:DTLS 握手(先把"钥匙"谈好)
ICE 打通的是明文传输通道,但 WebRTC 强制内容加密,于是先在这条通道上做 DTLS 握手(DTLS = 给 UDP 用的 TLS):
- 每次连接,浏览器生成一个临时自签名证书,把它的指纹(
a=fingerprint)放进 SDP(第 2 篇的 SDP 里见过); - 指纹通过信令传到对端,对端用它对 DTLS 证书做绑定校验 → 防止中间人(即使信令被篡改,只要指纹对不上就握手失败);
- DTLS 握手完成后,用约定的 keying 材料派生出两组密钥:
- SRTP 密钥(RFC 3711 + RFC 5764 的 DTLS-SRTP 扩展)→ 加密音视频(RTP);
- SCTP 密钥(RFC 8261,SCTP over DTLS)→ 加密数据通道(第 5 篇的 DataChannel)。
一个容易混淆的点:媒体和数据共用同一条 DTLS(在 BUNDLE 后是同一条传输)。所以你通常只看到一个"加密隧道",隧道里既有音视频也有数据通道。
2. RTP 与 SRTP:媒体长什么样
- RTP(Real-time Transport Protocol) 是媒体承载格式:每个包带 序号 + 时间戳 + 载荷类型 + 同步源(SSRC)。序号用来"知道丢了哪个",时间戳用来"知道该什么时候播"。
- SRTP 就是把 RTP 载荷加密 + 加认证后的版本。WebRTC 里你永远拿不到明文 RTP——想抓包分析内容,只能靠浏览器内部工具(见第 5 节),普通
tcpdump抓到也是密文。 - RTCP 是与 RTP 相伴的控制包:接收方周期性回报质量(丢包率/抖动/往返时间),还承载反馈(NACK/码率控制)。默认 RTCP-mux:RTCP 和 RTP 走同一端口(不再开第二个端口)。
3. 音视频编码:协商出什么就传什么
第 2 篇说 m= 行里最终敲定一组 payload type,这里展开编码侧。音频几乎总落在 Opus(RFC 6716)上——自适应码率、抗丢包内带 FEC,是实时通话的默认答案。视频则因浏览器而异:
| 编码 | 谁默认支持 | 定位 |
|---|---|---|
| VP8 | 全浏览器通吃 | WebRTC 的事实基线(Safari 也支持) |
| VP9 / AV1 | Chrome / Firefox | 更省码率;AV1 是新一代 |
| H.264 | Safari / 兼容硬件 | 走硬件编码,端侧省电 |
线上见到的多是 VP8/VP9 混着 H.264:协商出来的永远是"两边交集里浏览器偏好最高的那个"。想锁定某编码,可用
RTCRtpSender/transceiver 的codecPreferences调,但一般没必要。
4. 对抗"网络不听话":抖动缓冲、丢包恢复、码率自适应
这一节是 WebRTC 工程味道最重的地方——网络会抖动、丢包、带宽变化,而音视频必须在几百毫秒内"尽力流畅"。
抖动缓冲(jitter buffer):网络到达间隔不均匀,接收端先缓冲一点、按时间戳平滑后播放。延迟和质量是矛盾的:缓冲越大越稳但越慢。浏览器在"尽量低延迟"和"少卡顿"之间自动权衡——这解释了为什么弱网时会觉得延迟变大:缓冲在变大。
丢包恢复(按代价从小到大):
| 手段 | 原理 | 代价 |
|---|---|---|
| NACK 重传 | 接收方报"我缺序号 X",发送方补发(rtx 伴随流) | 要等一个 RTT,丢包多时跟不上 |
| 内带/冗余 FEC | 载荷里带冗余纠错(如 Opus useinbandfec) | 抬高码率 |
| 解码端隐藏 | 丢的帧用前一帧猜(PLC/错误隐藏) | 质量略降但"不卡" |
码率自适应(拥塞控制):发送端根据接收端反馈动态调码率——网络好就升(更清晰),网络差就降(保流畅),最差会降低分辨率/帧率但尽量不断流。机制上是接收端把逐包到达时间反馈回去(transport-cc,RFC 8888),发送端跑拥塞控制算法(如 GCC)决定目标码率。这也是为什么"视频自动变糊"往往不是 bug 而是它在自适应。
5. 观察媒体平面的手段
因为内容全加密,"眼见为实"得靠这几条路:
getStats():可查到发/收字节、丢包、抖动、目标码率、currentRoundTripTime、协商用的 codec;chrome://webrtc-internals:浏览器把本会话的 RTP 统计、ICE 候选、协商日志都 dump 在这里,是排查"为什么糊/卡/断"的第一现场;- 想可视化调试媒体本身:
pc.getSenders()里的轨道配合<video>本地环回,或接 SDP 到第三方抓包服务(内容已是密文,服务端只能看统计)。
6. 多点场景才需要的东西:Simulcast / SVC
1 对 1 直连不需要它;当有 SFU 服务器做分发(第 6 篇)时才有意义:
- Simulcast(RFC 8853):发送端同时发多个分辨率的"层"(如 720p + 360p + 180p),SFU/接收端按带宽挑一层转发——这是视频会议的主流做法;
- SVC:单流内做可分级的层(如 VP9 SVC/AV1),SFU 可"截断"成低层而不重编码。
理解这两者只需要一个判断:谁来决定"给你哪一层"——1:1 没有分发方,不需要;有 SFU 的会议里,它就是省带宽的关键。
7. 本篇结论
- 所有字节先过 DTLS 握手拿密钥(证书指纹靠信令绑定,防中间人);
- 音视频是 SRTP,控制是 RTCP,两者复用同一端口/同一传输;
- 协商出什么编码就传什么,音频几乎总是 Opus,视频看浏览器交集;
- 流畅性靠三件事:抖动缓冲 + NACK/FEC/隐藏 + 码率自适应,看到"变糊"先想自适应;
- 看媒体平面全靠
getStats/chrome://webrtc-internals(内容密文,别想抓明文)。