WebRTC 的三个关键技术(理论强化篇)
本文是 WebRTC 系列专栏的第四篇,将深入剖析 WebRTC 背后的三大核心技术:NAT 穿透、音视频实时传输协议、以及音频处理与带宽控制。理解这些技术原理,将帮助你更好地优化 WebRTC 应用。
目录
- NAT 穿透
- 音视频实时传输协议
- 回声消除、抗抖动与带宽控制
- 总结
1. NAT 穿透
1.1 为什么需要 NAT 穿透?
NAT 的背景
NAT(Network Address Translation,网络地址转换)是解决 IPv4 地址枯竭问题的关键技术。它允许多个设备共享一个公网 IP 地址。
┌─────────────────────────────────────────────────────────────┐ │ 互联网 │ │ 公网 IP: 203.0.113.1 │ └─────────────────────────────────────────────────────────────┘ │ │ ┌─────────┴─────────┐ │ NAT 路由器 │ │ 公网: 203.0.113.1 │ │ 私网: 192.168.1.1 │ └─────────┬─────────┘ │ ┌─────────────────────┼─────────────────────┐ │ │ │ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │ 设备 A │ │ 设备 B │ │ 设备 C │ │192.168. │ │192.168. │ │192.168. │ │ 1.100 │ │ 1.101 │ │ 1.102 │ └─────────┘ └─────────┘ └─────────┘P2P 通信的挑战
当两个位于 NAT 后面的设备想要直接通信时,会遇到问题:
设备 A (192.168.1.100) 设备 B (10.0.0.50) │ │ │ NAT A NAT B │ │ (203.0.113.1) (198.51.100.1) │ │ │ └────────────── ??? ─────────────────────┘ 问题:A 不知道 B 的公网地址和端口 B 不知道 A 的公网地址和端口 NAT 会阻止未经请求的入站连接1.2 NAT 的类型
根据 RFC 3489,NAT 可分为四种类型:
1. Full Cone NAT(完全锥形)
内部地址 192.168.1.100:5000 ↓ NAT 映射 外部地址 203.0.113.1:8000 特点:任何外部主机都可以通过 203.0.113.1:8000 访问内部设备 穿透难度:★☆☆☆☆ (最容易)2. Restricted Cone NAT(受限锥形)
内部地址 192.168.1.100:5000 ↓ NAT 映射 外部地址 203.0.113.1:8000 特点:只有内部设备曾经发送过数据的外部 IP 才能回复 限制:IP 地址限制 穿透难度:★★☆☆☆3. Port Restricted Cone NAT(端口受限锥形)
内部地址 192.168.1.100:5000 ↓ NAT 映射 外部地址 203.0.113.1:8000 特点:只有内部设备曾经发送过数据的外部 IP:Port 才能回复 限制:IP 地址 + 端口限制 穿透难度:★★★☆☆4. Symmetric NAT(对称型)
内部地址 192.168.1.100:5000 ↓ 发送到不同目标,映射不同 发送到 Server1 → 203.0.113.1:8000 发送到 Server2 → 203.0.113.1:8001 特点:每个目标地址使用不同的外部端口 穿透难度:★★★★★ (最难)NAT 类型穿透成功率
| NAT A \ NAT B | Full Cone | Restricted | Port Restricted | Symmetric |
|---|---|---|---|---|
| Full Cone | ✅ 100% | ✅ 100% | ✅ 100% | ✅ 100% |
| Restricted | ✅ 100% | ✅ 95% | ✅ 90% | ⚠️ 70% |
| Port Restricted | ✅ 100% | ✅ 90% | ✅ 85% | ⚠️ 50% |
| Symmetric | ✅ 100% | ⚠️ 70% | ⚠️ 50% | ❌ 10% |
1.3 ICE 框架
ICE(Interactive Connectivity Establishment)是 WebRTC 用于 NAT 穿透的核心框架,定义在 RFC 8445。
ICE 工作流程
┌─────────────────────────────────────────────────────────────────┐ │ ICE 工作流程 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. 候选收集 (Candidate Gathering) │ │ ├── Host 候选:本地 IP 地址 │ │ ├── Server Reflexive 候选:通过 STUN 获取的公网地址 │ │ └── Relay 候选:TURN 服务器分配的中继地址 │ │ │ │ 2. 候选交换 (Candidate Exchange) │ │ └── 通过信令服务器交换所有候选 │ │ │ │ 3. 连通性检查 (Connectivity Checks) │ │ └── 对所有候选对进行 STUN Binding 请求测试 │ │ │ │ 4. 候选对排序 (Candidate Pair Prioritization) │ │ └── 根据优先级选择最佳路径 │ │ │ │ 5. 连接建立 (Connection Establishment) │ │ └── 使用最优候选对建立连接 │ │ │ └─────────────────────────────────────────────────────────────────┘ICE 候选类型
// ICE 候选示例{// Host 候选 - 本地地址candidate:"candidate:1 1 UDP 2122252543 192.168.1.100 54321 typ host",// Server Reflexive 候选 - STUN 获取的公网地址candidate:"candidate:2 1 UDP 1686052863 203.0.113.1 12345 typ srflx raddr 192.168.1.100 rport 54321",// Relay 候选 - TURN 中继地址candidate:"candidate:3 1 UDP 41885439 198.51.100.1 3478 typ relay raddr 203.0.113.1 rport 12345"}候选优先级
ICE 按以下顺序尝试连接:
| 优先级 | 候选类型 | 说明 |
|---|---|---|
| 1 (最高) | Host | 直接使用本地 IP(局域网内最快) |
| 2 | Server Reflexive | 通过 STUN 获取的公网地址 |
| 3 | Peer Reflexive | 连通性检查中发现的地址 |
| 4 (最低) | Relay | TURN 中继(保底方案) |
1.4 STUN 协议
STUN(Session Traversal Utilities for NAT)用于发现公网地址和 NAT 类型。
STUN 工作原理
┌──────────────┐ ┌──────────────┐ │ Client │ │ STUN Server │ │ 192.168.1.100│ │ 203.0.113.50 │ └──────┬───────┘ └──────┬───────┘ │ │ │ 1. Binding Request │ │ (源: 192.168.1.100:5000) │ │ ─────────────────────────────────────────> │ │ │ │ NAT 转换 │ │ (源变为: 203.0.113.1:8000) │ │ │ │ 2. Binding Response │ │ (你的公网地址是 203.0.113.1:8000) │ │ <───────────────────────────────────────── │ │ │STUN 消息格式
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0 0| STUN Message Type | Message Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Magic Cookie | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | Transaction ID (96 bits) | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Attributes... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+常用 STUN 服务器
consticeServers=[{urls:'stun:stun.l.google.com:19302'},{urls:'stun:stun1.l.google.com:19302'},{urls:'stun:stun2.l.google.com:19302'},{urls:'stun:stun3.l.google.com:19302'},{urls:'stun:stun4.l.google.com:19302'},{urls:'stun:stun.stunprotocol.org:3478'}];1.5 TURN 协议
TURN(Traversal Using Relays around NAT)是 STUN 的扩展,当 P2P 穿透失败时提供中继服务。
TURN 工作原理
┌──────────────┐ ┌──────────────┐ │ Client A │ │ Client B │ │ 192.168.1.100│ │ 10.0.0.50 │ └──────┬───────┘ └──────┬───────┘ │ │ │ ┌──────────────────┐ │ │ │ TURN Server │ │ │ │ 198.51.100.1 │ │ │ └────────┬─────────┘ │ │ │ │ │ 1. Allocate │ │ │ ───────────────> │ │ │ │ │ │ 2. 分配中继地址 │ │ │ 198.51.100.1: │ │ │ 49152 │ │ │ <─────────────── │ │ │ │ │ │ 3. 媒体数据 │ 4. 转发媒体数据 │ │ ═══════════════> │ ═══════════════════════> │ │ │ │ │ <═══════════════ │ <═══════════════════════ │ │ 6. 转发媒体数据 │ 5. 媒体数据 │ │ │ │TURN 配置示例
consticeServers=[{urls:'stun:stun.l.google.com:19302'},{urls:'turn:turn.example.com:3478',username:'user',credential:'password'},{urls:'turn:turn.example.com:443?transport=tcp',username:'user',credential:'password'},{urls:'turns:turn.example.com:443',// TURN over TLSusername:'user',credential:'password'}];TURN 服务器选型
| 开源方案 | 特点 |
|---|---|
| coturn | 最流行,功能完整,支持 STUN/TURN/ICE |
| Pion TURN | Go 语言实现,轻量级 |
| eturnal | Erlang 实现,高并发 |
| 商业服务 | 特点 |
|---|---|
| Twilio | 全球节点,按量计费 |
| Xirsys | 专注 WebRTC,易于集成 |
| Google Cloud | 与 GCP 集成 |
1.6 ICE 状态机
┌─────────────────────────────────────────────────────────────────┐ │ ICE 连接状态 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────┐ │ │ │ new │ │ │ └────┬────┘ │ │ │ 开始收集候选 │ │ ▼ │ │ ┌─────────┐ │ │ │checking │ ←─────────────┐ │ │ └────┬────┘ │ │ │ │ │ 重新检查 │ │ ┌──────────────┼──────────────┐ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ │ ┌──────────┐ ┌───────────┐ ┌──────────┐ │ │ │ │connected │ │ completed │ │ failed │─┘ │ │ └────┬─────┘ └─────┬─────┘ └──────────┘ │ │ │ │ │ │ │ │ │ │ ▼ │ │ │ ┌─────────────┐ │ │ │ │disconnected │ │ │ │ └──────┬──────┘ │ │ │ │ │ │ │ └──────────────┴───────────────┐ │ │ ▼ │ │ ┌──────────┐ │ │ │ closed │ │ │ └──────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘状态说明
| 状态 | 说明 |
|---|---|
new | 初始状态,ICE 代理刚创建 |
checking | 正在进行连通性检查 |
connected | 至少找到一个可用的候选对 |
completed | 所有候选对检查完成,已选择最优路径 |
failed | 所有候选对检查失败 |
disconnected | 连接暂时中断 |
closed | ICE 代理已关闭 |
2. 音视频实时传输协议
2.1 协议栈概览
┌─────────────────────────────────────────────────────────────────┐ │ WebRTC 协议栈 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 应用层 │ │ │ │ 音视频数据 / DataChannel 数据 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌───────────────┴───────────────┐ │ │ ▼ ▼ │ │ ┌─────────────────────────┐ ┌─────────────────────────┐ │ │ │ SRTP │ │ SCTP │ │ │ │ (加密媒体传输) │ │ (数据通道传输) │ │ │ └───────────┬─────────────┘ └───────────┬─────────────┘ │ │ │ │ │ │ └───────────────┬───────────────┘ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ DTLS │ │ │ │ (密钥交换与加密) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ ICE │ │ │ │ (NAT 穿透) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ UDP / TCP │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘2.2 RTP(Real-time Transport Protocol)
RTP 是实时音视频传输的基础协议,定义在 RFC 3550。
RTP 包格式
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P|X| CC |M| PT | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Timestamp | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Synchronization Source (SSRC) identifier | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | Contributing Source (CSRC) identifiers | | .... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | RTP Payload | | .... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+RTP 头部字段说明
| 字段 | 位数 | 说明 |
|---|---|---|
| V (Version) | 2 | RTP 版本,固定为 2 |
| P (Padding) | 1 | 是否有填充 |
| X (Extension) | 1 | 是否有扩展头 |
| CC (CSRC Count) | 4 | CSRC 标识符数量 |
| M (Marker) | 1 | 标记位,如视频帧结束 |
| PT (Payload Type) | 7 | 负载类型(编解码器) |
| Sequence Number | 16 | 序列号,用于检测丢包和排序 |
| Timestamp | 32 | 时间戳,用于同步 |
| SSRC | 32 | 同步源标识符 |
常见 Payload Type
| PT | 编解码器 | 媒体类型 | 采样率 |
|---|---|---|---|
| 0 | PCMU | Audio | 8000 Hz |
| 8 | PCMA | Audio | 8000 Hz |
| 96-127 | 动态分配 | - | - |
WebRTC 常用动态 PT:
- 111: Opus (音频)
- 96: VP8 (视频)
- 98: VP9 (视频)
- 102: H.264 (视频)
2.3 RTCP(RTP Control Protocol)
RTCP 用于传输控制信息,与 RTP 配合使用。
RTCP 包类型
| 类型 | 名称 | 说明 |
|---|---|---|
| 200 | SR (Sender Report) | 发送端报告 |
| 201 | RR (Receiver Report) | 接收端报告 |
| 202 | SDES (Source Description) | 源描述 |
| 203 | BYE | 结束通知 |
| 204 | APP | 应用自定义 |
| 205 | RTPFB (Transport Layer FB) | 传输层反馈 |
| 206 | PSFB (Payload-specific FB) | 负载特定反馈 |
Sender Report (SR) 格式
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P| RC | PT=SR=200 | length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | SSRC of sender | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | NTP timestamp, most significant word | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NTP timestamp, least significant word | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | RTP timestamp | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | sender's packet count | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | sender's octet count | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | Report Block(s)... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+重要的 RTCP 反馈消息
NACK(Negative Acknowledgement)
用于请求重传丢失的包:
┌──────────────┐ ┌──────────────┐ │ Sender │ │ Receiver │ └──────┬───────┘ └──────┬───────┘ │ │ │ RTP #1, #2, #3, #5, #6 │ │ ─────────────────────────────────────────> │ │ │ │ 检测到 #4 丢失 │ │ │ │ RTCP NACK (请求重传 #4) │ │ <───────────────────────────────────────── │ │ │ │ RTP #4 (重传) │ │ ─────────────────────────────────────────> │ │ │PLI(Picture Loss Indication)
请求发送关键帧:
// 当检测到视频解码问题时// 接收端发送 PLI 请求关键帧REMB(Receiver Estimated Maximum Bitrate)
接收端带宽估计:
接收端估计可用带宽 → 发送 REMB → 发送端调整码率2.4 SRTP(Secure RTP)
SRTP 是 RTP 的加密版本,WebRTC 强制使用。
SRTP 加密流程
┌─────────────────────────────────────────────────────────────────┐ │ SRTP 加密流程 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ │ │ │ RTP 包 │ │ │ │ (明文) │ │ │ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ SRTP 加密 │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ │ │ │ │ 密钥派生 │→ │ AES-CM 加密 │→ │ HMAC-SHA1 认证 │ │ │ │ │ │ (DTLS 协商) │ │ (负载加密) │ │ (完整性保护) │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ SRTP 包 │ │ │ │ (密文) │ │ │ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘SRTP 包格式
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | RTP Header (12 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Encrypted Payload | | ... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Authentication Tag | | (10 bytes) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+2.5 DTLS(Datagram TLS)
DTLS 用于在 UDP 上提供 TLS 安全性,负责 SRTP 密钥交换。
DTLS 握手流程
┌──────────────┐ ┌──────────────┐ │ Client │ │ Server │ └──────┬───────┘ └──────┬───────┘ │ │ │ ClientHello │ │ (支持的加密套件、随机数) │ │ ─────────────────────────────────────────> │ │ │ │ ServerHello │ │ HelloVerifyRequest (防止 DoS) │ │ <───────────────────────────────────────── │ │ │ │ ClientHello (带 Cookie) │ │ ─────────────────────────────────────────> │ │ │ │ ServerHello, Certificate, │ │ ServerKeyExchange, CertificateRequest, │ │ ServerHelloDone │ │ <───────────────────────────────────────── │ │ │ │ Certificate, ClientKeyExchange, │ │ CertificateVerify, ChangeCipherSpec, │ │ Finished │ │ ─────────────────────────────────────────> │ │ │ │ ChangeCipherSpec, Finished │ │ <───────────────────────────────────────── │ │ │ │ ═══════ 安全通道建立,导出 SRTP 密钥 ═══════ │ │ │DTLS-SRTP 密钥导出
DTLS 主密钥 │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 密钥导出函数 (KDF) │ │ │ │ PRF(master_secret, "EXTRACTOR-dtls_srtp", │ │ client_random + server_random) │ │ │ └─────────────────────────────────────────────────────────────┘ │ ├── SRTP 加密密钥 (Client) ├── SRTP 加密密钥 (Server) ├── SRTP 认证密钥 (Client) ├── SRTP 认证密钥 (Server) ├── SRTP 盐值 (Client) └── SRTP 盐值 (Server)3. 回声消除、抗抖动与带宽控制
3.1 回声消除(AEC)
回声产生原因
┌─────────────────────────────────────────────────────────────────┐ │ 回声产生示意图 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 用户 A 用户 B │ │ ┌─────────┐ ┌─────────┐ │ │ │ 麦克风 │ │ 扬声器 │ │ │ └────┬────┘ └────┬────┘ │ │ │ │ │ │ │ 1. A 说话 │ │ │ │ ─────────────────────────────────────> │ │ │ │ │ │ │ │ ▼ │ │ │ ┌─────────────────┐ │ │ │ │ B 的扬声器播放 │ │ │ │ │ A 的声音 │ │ │ │ └────────┬────────┘ │ │ │ │ │ │ │ ┌────────▼────────┐ │ │ │ │ B 的麦克风采集 │ │ │ │ │ 扬声器的声音 │ │ │ │ └────────┬────────┘ │ │ │ │ │ │ │ 2. 回声传回 A │ │ │ │ <───────────────────────────────────── │ │ │ │ │ │ │ ▼ │ │ │ ┌─────────┐ │ │ │ │ A 听到 │ │ │ │ │ 自己的 │ │ │ │ │ 回声! │ │ │ │ └─────────┘ │ │ │ │ └─────────────────────────────────────────────────────────────────┘AEC 工作原理
┌─────────────────────────────────────────────────────────────────┐ │ AEC 工作原理 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 远端信号 (扬声器播放) │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 自适应滤波器 │ │ │ │ │ │ │ │ 估计房间的声学特性(回声路径) │ │ │ │ 生成回声估计信号 │ │ │ │ │ │ │ └────────────────────────┬────────────────────────────────┘ │ │ │ │ │ ▼ 回声估计 │ │ ┌─────────┐ │ │ 麦克风信号 ────────> │ - │ ────────> 输出信号 │ │ (语音 + 回声) │ 减法器 │ (仅语音) │ │ └─────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘WebRTC AEC3 特点
WebRTC 使用第三代回声消除算法(AEC3):
| 特性 | 说明 |
|---|---|
| 自适应滤波器 | 基于 NLMS(归一化最小均方)算法 |
| 延迟估计 | 自动检测扬声器到麦克风的延迟 |
| 非线性处理 | 处理扬声器失真产生的非线性回声 |
| 双讲检测 | 检测双方同时说话的情况 |
启用 AEC
conststream=awaitnavigator.mediaDevices.getUserMedia({audio:{echoCancellation:true,// 启用回声消除echoCancellationType:'system'// 或 'browser'}});3.2 噪声抑制(NS)
噪声类型
| 类型 | 示例 |
|---|---|
| 稳态噪声 | 空调声、风扇声、电流声 |
| 非稳态噪声 | 键盘声、咳嗽声、门铃声 |
| 背景人声 | 其他人的说话声 |
NS 工作原理
┌─────────────────────────────────────────────────────────────────┐ │ 噪声抑制流程 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 输入信号 (语音 + 噪声) │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 频谱分析 │ │ │ │ (FFT 变换) │ │ │ └────────────────────────┬────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 噪声估计 │ │ │ │ │ │ │ │ • 语音活动检测 (VAD) │ │ │ │ • 在静音段估计噪声频谱 │ │ │ │ • 持续更新噪声模型 │ │ │ │ │ │ │ └────────────────────────┬────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 频谱减法 │ │ │ │ │ │ │ │ 输出频谱 = 输入频谱 - α × 噪声频谱 │ │ │ │ │ │ │ └────────────────────────┬────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 频谱合成 │ │ │ │ (IFFT 变换) │ │ │ └────────────────────────┬────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 输出信号 (语音) │ │ │ └─────────────────────────────────────────────────────────────────┘启用噪声抑制
conststream=awaitnavigator.mediaDevices.getUserMedia({audio:{noiseSuppression:true,// 启用噪声抑制autoGainControl:true// 自动增益控制}});3.3 抖动缓冲(Jitter Buffer)
什么是抖动?
网络传输中,数据包的到达时间不均匀,这种现象称为抖动(Jitter)。
发送时间: |──10ms──|──10ms──|──10ms──|──10ms──| P1 P2 P3 P4 P5 到达时间: |──8ms──|──15ms──|──5ms──|──12ms──| P1 P2 P3 P4 P5 抖动 = 到达间隔的变化抖动缓冲工作原理
┌─────────────────────────────────────────────────────────────────┐ │ 抖动缓冲原理 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 网络接收 抖动缓冲 播放输出 │ │ │ │ P1 ──┐ ┌─────────────┐ │ │ │ │ ┌───┬───┬───┤ │ │ P3 ──┼──────────────────>│ │P1 │P2 │P3 │──────────> 均匀播放 │ │ │ │ └───┴───┴───┤ │ │ P2 ──┘ │ 缓冲区 │ │ │ └─────────────┘ │ │ │ │ 乱序到达 重新排序 平滑输出 │ │ 不均匀间隔 缓冲延迟 均匀间隔 │ │ │ └─────────────────────────────────────────────────────────────────┘自适应抖动缓冲
WebRTC 使用自适应抖动缓冲,根据网络状况动态调整缓冲大小:
| 网络状况 | 缓冲大小 | 延迟 |
|---|---|---|
| 稳定 | 小 | 低 |
| 抖动大 | 大 | 高 |
| 丢包多 | 大 | 高 |
抖动小 → 缓冲小 → 延迟低 ↑ ↓ └─────────┘ 动态调整 抖动大 → 缓冲大 → 延迟高 ↑ ↓ └─────────┘ 动态调整3.4 带宽控制(BWE)
GCC 算法概述
WebRTC 使用GCC(Google Congestion Control)算法进行带宽估计和拥塞控制。
┌─────────────────────────────────────────────────────────────────┐ │ GCC 算法架构 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 发送端 BWE │ │ │ │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ 延迟梯度 │ │ 丢包率 │ │ 带宽估计 │ │ │ │ │ │ 检测器 │───>│ 检测器 │───>│ 融合器 │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Pacer │ │ │ │ (发送节奏控制) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 编码器码率控制 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘延迟梯度检测
基于包间延迟变化检测拥塞:
发送间隔: Δs = t_send(i) - t_send(i-1) 接收间隔: Δr = t_recv(i) - t_recv(i-1) 延迟梯度: d = Δr - Δs d > 0 → 队列增长 → 拥塞 d < 0 → 队列减少 → 空闲 d ≈ 0 → 稳定带宽调整策略
┌─────────────────────────────────────────────────────────────────┐ │ 带宽调整状态机 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌───────────┐ │ │ ┌────────>│ Increase │<────────┐ │ │ │ │ (增加) │ │ │ │ │ └─────┬─────┘ │ │ │ │ │ │ │ │ 空闲检测 拥塞检测 空闲检测 │ │ │ │ │ │ │ │ ▼ │ │ │ ┌────┴────┐ ┌───────────┐ ┌───┴─────┐ │ │ │ Hold │<───│ Decrease │───>│ Hold │ │ │ │ (保持) │ │ (减少) │ │ (保持) │ │ │ └─────────┘ └───────────┘ └─────────┘ │ │ │ │ Increase: 带宽 = 带宽 × 1.08 (每秒) │ │ Decrease: 带宽 = 带宽 × 0.85 (立即) │ │ Hold: 带宽保持不变 │ │ │ └─────────────────────────────────────────────────────────────────┘Transport-wide Congestion Control
WebRTC 使用 Transport-wide CC 扩展进行更精确的带宽估计:
发送端: 每个 RTP 包添加 transport-wide 序列号 接收端: 定期发送 RTCP Transport Feedback 包含每个包的接收时间 发送端: 根据反馈计算延迟梯度 估计可用带宽获取带宽统计
// 获取连接统计信息conststats=awaitpeerConnection.getStats();stats.forEach(report=>{if(report.type==='outbound-rtp'&&report.kind==='video'){console.log('视频发送统计:',{bytesSent:report.bytesSent,packetsSent:report.packetsSent,targetBitrate:report.targetBitrate,// 计算实际码率bitrate:(report.bytesSent*8)/(report.timestamp/1000)});}if(report.type==='candidate-pair'&&report.state==='succeeded'){console.log('连接统计:',{availableOutgoingBitrate:report.availableOutgoingBitrate,currentRoundTripTime:report.currentRoundTripTime});}});3.5 前向纠错(FEC)
FEC 通过发送冗余数据来恢复丢失的包,无需重传。
FEC 工作原理
┌─────────────────────────────────────────────────────────────────┐ │ FEC 工作原理 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 发送端: │ │ ┌─────┬─────┬─────┬─────┐ │ │ │ P1 │ P2 │ P3 │ P4 │ 原始数据包 │ │ └──┬──┴──┬──┴──┬──┴──┬──┘ │ │ │ │ │ │ │ │ └─────┴─────┴─────┘ │ │ │ │ │ ▼ XOR │ │ ┌─────────┐ │ │ │ FEC │ 冗余包 = P1 ⊕ P2 ⊕ P3 ⊕ P4 │ │ └─────────┘ │ │ │ │ 传输: P1, P2, P3, P4, FEC │ │ │ │ 接收端 (假设 P3 丢失): │ │ ┌─────┬─────┬─────┬─────┐ │ │ │ P1 │ P2 │ ? │ P4 │ │ │ └──┬──┴──┬──┴─────┴──┬──┘ │ │ │ │ │ │ │ └─────┴───────────┘ │ │ │ │ │ ▼ XOR with FEC │ │ ┌─────────┐ │ │ │ P3 │ P3 = P1 ⊕ P2 ⊕ P4 ⊕ FEC │ │ └─────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘WebRTC 中的 FEC
WebRTC 支持多种 FEC 方案:
| 方案 | 适用场景 | 开销 |
|---|---|---|
| Opus FEC | 音频 | 低 |
| FlexFEC | 视频 | 可配置 |
| RED (Redundant Encoding) | 音频 | 中 |
4. 总结
核心技术回顾
| 技术领域 | 关键技术 | 作用 |
|---|---|---|
| NAT 穿透 | ICE/STUN/TURN | 建立 P2P 连接 |
| 媒体传输 | RTP/RTCP/SRTP | 实时传输与加密 |
| 音频处理 | AEC/NS/AGC | 提升音频质量 |
| 网络适应 | 抖动缓冲/BWE/FEC | 适应网络变化 |
技术选型建议
┌─────────────────────────────────────────────────────────────────┐ │ 技术选型决策树 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ NAT 穿透方案: │ │ ├── 企业内网 → 仅 STUN │ │ ├── 公网用户 → STUN + TURN │ │ └── 高可用要求 → 多 TURN 服务器 │ │ │ │ 音频处理: │ │ ├── 普通场景 → 启用 AEC + NS + AGC │ │ ├── 音乐场景 → 关闭 AEC,保留 AGC │ │ └── 专业设备 → 可关闭所有处理 │ │ │ │ 带宽控制: │ │ ├── 稳定网络 → 固定码率 │ │ ├── 移动网络 → 自适应码率 │ │ └── 弱网环境 → 启用 FEC + 降低分辨率 │ │ │ └─────────────────────────────────────────────────────────────────┘下一篇预告
在下一篇文章中,我们将全面梳理WebRTC 的 API 全景图,包括:
- getUserMedia 详解
- RTCPeerConnection 完整 API
- RTCRtpSender / Receiver
- RTCDataChannel 高级用法
参考资料
- RFC 8445 - Interactive Connectivity Establishment (ICE)
- RFC 5389 - Session Traversal Utilities for NAT (STUN)
- RFC 5766 - Traversal Using Relays around NAT (TURN)
- RFC 3550 - RTP: A Transport Protocol for Real-Time Applications
- RFC 3711 - The Secure Real-time Transport Protocol (SRTP)
- WebRTC for the Curious - Media Communication
- Google Congestion Control Algorithm