WebRTC 的三个关键技术(理论强化篇)

本文是 WebRTC 系列专栏的第四篇,将深入剖析 WebRTC 背后的三大核心技术:NAT 穿透、音视频实时传输协议、以及音频处理与带宽控制。理解这些技术原理,将帮助你更好地优化 WebRTC 应用。


目录

  1. NAT 穿透
  2. 音视频实时传输协议
  3. 回声消除、抗抖动与带宽控制
  4. 总结

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 服务器
const iceServers = [
    { 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 配置示例
const iceServers = [
    { 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 TLS
        username: '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
const stream = await navigator.mediaDevices.getUserMedia({
    audio: {
        echoCancellation: true,      // 启用回声消除
        echoCancellationType: 'system' // 或 'browser'
    }
});

3.2 噪声抑制(NS)

噪声类型
类型 示例
稳态噪声 空调声、风扇声、电流声
非稳态噪声 键盘声、咳嗽声、门铃声
背景人声 其他人的说话声
NS 工作原理
┌─────────────────────────────────────────────────────────────────┐
│                      噪声抑制流程                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  输入信号 (语音 + 噪声)                                          │
│       │                                                         │
│       ▼                                                         │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    频谱分析                              │   │
│  │                   (FFT 变换)                             │   │
│  └────────────────────────┬────────────────────────────────┘   │
│                           │                                     │
│                           ▼                                     │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    噪声估计                              │   │
│  │                                                         │   │
│  │  • 语音活动检测 (VAD)                                    │   │
│  │  • 在静音段估计噪声频谱                                   │   │
│  │  • 持续更新噪声模型                                      │   │
│  │                                                         │   │
│  └────────────────────────┬────────────────────────────────┘   │
│                           │                                     │
│                           ▼                                     │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    频谱减法                              │   │
│  │                                                         │   │
│  │  输出频谱 = 输入频谱 - α × 噪声频谱                       │   │
│  │                                                         │   │
│  └────────────────────────┬────────────────────────────────┘   │
│                           │                                     │
│                           ▼                                     │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    频谱合成                              │   │
│  │                   (IFFT 变换)                            │   │
│  └────────────────────────┬────────────────────────────────┘   │
│                           │                                     │
│                           ▼                                     │
│                      输出信号 (语音)                             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
启用噪声抑制
const stream = await navigator.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
  包含每个包的接收时间

发送端:
  根据反馈计算延迟梯度
  估计可用带宽
获取带宽统计
// 获取连接统计信息
const stats = await peerConnection.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 高级用法

参考资料

  1. RFC 8445 - Interactive Connectivity Establishment (ICE)
  2. RFC 5389 - Session Traversal Utilities for NAT (STUN)
  3. RFC 5766 - Traversal Using Relays around NAT (TURN)
  4. RFC 3550 - RTP: A Transport Protocol for Real-Time Applications
  5. RFC 3711 - The Secure Real-time Transport Protocol (SRTP)
  6. WebRTC for the Curious - Media Communication
  7. Google Congestion Control Algorithm

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐