2、WebRTC核心架构:整体架构、API与信令媒体分离设计
WebRTC 的架构,说白了就是一套「浏览器原生音视频引擎」。它把采集、编码、传输、渲染这些脏活累活全包了,你只需要调 API 就行。我刚开始接触时也觉得它像个黑盒,但拆开看其实就几大块。
2.1 整体架构:三层模型
WebRTC 官方架构图分三层,我习惯这么记:
- 应用层:你写的 JS 代码,调
getUserMedia、RTCPeerConnection这些 API。 - 会话管理层:信令状态机、ICE 代理、SDP 协商。这部分浏览器帮你做了,但信令消息得你自己传。
- 传输与媒体层:DTLS、SRTP、NACK、FEC、JitterBuffer。真正的音视频数据在这跑。
嗯,这里要注意:应用层和传输层是隔离的。你调 addTrack 时,根本不用管底层是 UDP 还是 TCP,WebRTC 自己会选。
核心观点:WebRTC 不是单体的 SDK,而是一组规范 + 浏览器实现。Native 端和 Web 端底层逻辑一样,只是 API 风格不同。
2.2 Native 与 Web 端 API 差异
Web 端你写 JS,Native 端你用 C++ 或各平台封装。我做过一个项目,Web 端调 navigator.mediaDevices.getUserMedia 三行代码搞定,Android 端却要写一堆 Camera2 回调。但底层 PeerConnection 的逻辑完全一样。
| 能力 | Web API (JS) | Native API (C++/Obj-C/Java) |
|---|---|---|
| 媒体采集 | getUserMedia |
VideoCapturer / AudioDeviceModule |
| 连接建立 | RTCPeerConnection |
PeerConnectionFactory + PeerConnection |
| 数据通道 | RTCDataChannel |
DataChannelInterface |
| 统计信息 | getStats() |
PeerConnection::GetStats() |
| 自定义编解码 | 不支持(浏览器限制) | 支持(可注入自定义 encoder/decoder) |
我的经验:如果你做 RTSP 推流,建议用 Native API。Web 端对 H264 的支持有限,而且无法直接读取 RTSP 流。Native 端你可以自己写一个 VideoCapturer,把 RTSP 解码后的帧喂进去。
2.3 信令与媒体通道分离设计
这是 WebRTC 最容易被误解的地方。很多人以为 WebRTC 自带信令,其实它只定义了 信令状态机,信令消息怎么传——你自己定。
为什么会这样?因为信令没有「标准协议」。你可以用 WebSocket、HTTP、MQTT,甚至拿鸽子送信都行。WebRTC 只管两件事:
- 交换 SDP:描述媒体能力(编解码、分辨率、IP 端口)。
- 交换 ICE Candidate:告诉对方「我能从哪些地址连到你」。
媒体通道则是另一条路。信令走你的服务器,媒体走 P2P 或 TURN 中继。我见过有人把信令和媒体混在一起,结果 NAT 穿透时乱成一团。
避坑指南:我曾经在一个项目中把信令消息也塞进 RTCDataChannel,想着「省一个连接」。结果信令丢包导致连接状态不同步,排查了两天。记住:信令必须可靠,媒体可以容忍丢包。信令走 TCP/WebSocket,媒体走 UDP/DTLS-SRTP。
2.4 信令状态机:从 Offer 到 Connected
WebRTC 内部维护一个状态机。你调 createOffer 时,状态从 stable 变成 have-local-offer。我刚开始调试时老看 signalingState,后来发现只要按规范走,基本不会出问题。
标准流程是这样的:
- 发起方:
createOffer→setLocalDescription→ 发送 SDP 给信令服务器。 - 接收方:收到 SDP →
setRemoteDescription→createAnswer→setLocalDescription→ 发回 Answer。 - ICE 收集:双方各自收集 Candidate,通过信令交换。
- 连接建立:ICE 连通性检查通过,媒体开始传输。
注意:信令状态和 ICE 状态是两回事。信令状态表示 SDP 交换进度,ICE 状态表示网络连通性。我见过有人把 iceConnectionState === 'connected' 当成信令完成,结果 SDP 还没交换完就发数据,当然不行。
2.5 媒体通道:P2P 与 TURN 中继
媒体通道默认走 P2P。但现实是大部分设备在 NAT 后面,P2P 打不通。这时候就需要 TURN 服务器做中继。
我做过一个跨运营商的项目,P2P 成功率不到 30%。加了 TURN 后提到 95% 以上。但 TURN 贵啊,带宽成本翻倍。所以我的策略是:先试 P2P,不行再降级到 TURN。
| 通道类型 | 延迟 | 带宽成本 | 适用场景 |
|---|---|---|---|
| P2P (直连) | 低(10-50ms) | 无服务器成本 | 内网、公网 IP、NAT 类型友好 |
| TURN (中继) | 中(50-150ms) | 服务器带宽成本高 | 对称 NAT、防火墙限制、跨运营商 |
| 混合 (ICE 策略) | 动态切换 | 可控 | 大部分生产环境 |
我的建议:做 RTSP 推流时,如果接收端是浏览器,大概率需要 TURN。因为浏览器在用户内网,RTSP 源在服务器端,两边 NAT 类型往往不匹配。提前部署好 TURN 服务器,别等上线了再补。
2.6 小结
WebRTC 核心架构其实不复杂:三层分离,信令和媒体各走各的路。Native 端更灵活,Web 端更便捷。你只要记住信令是「你的活」,媒体是「WebRTC 的活」,就不会搞混。