WebRTC

本页目录

WebRTC 是浏览器与其他实时通信端点协作的一组 API 和协议,支持音视频及应用数据。它可以直连,也可以经 TURN 中继,或连接 SFU/MCU 等媒体服务器;“不经过服务器”不是使用 WebRTC 的必要条件。

把一次通话拆开更容易定位问题:应用先找到对端并获得授权,协商媒体与传输参数,验证可用路径与端点密钥,随后持续编码、传送和播放。信令成功、ICE 成功、收到媒体和成功播放是不同的里程碑。

速查对应机制
谁与谁通话、如何交换 offer应用信令与身份授权
哪些媒体、方向、编解码器SDP offer/answer、transceiver
经哪个地址对传包ICE 检查、选择与重启
媒体密钥如何得到DTLS-SRTP 与证书指纹绑定
文件/游戏消息如何传DataChannel、SCTP 与可靠性选项
画面卡顿在哪里编码、网络、抖动缓冲、解码与播放统计

协议栈:DTLS 握手不等于所有媒体都装进 DTLS

媒体使用 SRTP,DataChannel 使用 SCTP over DTLS 音视频轨道 编码 → RTP / RTCP 应用消息 RTCDataChannel SRTP / SRTCP DTLS exporter 提供密钥材料 SCTP → DTLS 消息、流与可靠性策略 ICE 选择的传输路径 可直连,也可经过 TURN SDP/信令描述并协商这些关系,不是每个媒体包必须经过的一层封装。

媒体使用 RTP/RTCP 的安全形式 SRTP/SRTCP,DTLS 握手协商 SRTP 保护配置,并通过 exporter 导出方向密钥与 salt。媒体包随后按 SRTP 格式保护,不是每个 RTP 包再装进一个 DTLS application-data 记录。应用数据通道则使用 SCTP over DTLS。DTLS-SRTP RFC 5764、Data Channels RFC 8831

exporter 有规定标签、上下文和输出组织,不能写成随意对 DTLS 某个 secret 执行一次 SRTP_KDF(secret)。也不能把 DTLS 1.2 的 ClientKeyExchange/ChangeCipherSpec 时序当作所有版本通用握手。

SDP 是会话描述格式,承载媒体类型、codec、方向、ICE 凭据、DTLS 指纹等信息。它不直接运输视频帧;ICE 是候选路径检查与选择机制,不是所有业务包外面的另一层 IP 头。

信令与协商:控制好状态,才能接受后续 candidate

WebRTC 不强制应用使用某个通用信令传输,可以采用 HTTPS、WebSocket 或已有会话服务;但信令仍需身份认证、房间权限、消息顺序与会话关联。攻击者若能替换对端描述或证书指纹,单纯“媒体已加密”并不足以证明正在与正确的人通信。安全架构 RFC 8827

基本 offer/answer 过程可按四个状态变化理解:A 设置本地 offer 并交给 B;B 设置远端 offer,生成并设置本地 answer;A 接收并设置远端 answer。候选收集与这些异步步骤可以交叠。对端 candidate 若早于适用的远端描述到达,应用需排队并在描述生效后按正确媒体段/ICE 代际添加。

状态回答的问题不能推出
signalingStateoffer/answer 是否仍待完成网络路径已可用
iceGatheringState本地候选收集进展对端收到了全部候选
iceConnectionStateICE 检查与连通状态codec 正确或已收到帧
connectionStateICE/DTLS 等整体连接状态视频元素已正常播放

双方同时发起 offer 会产生协商碰撞,应用需要一致的冲突处理策略,例如区分礼让方并执行相应回滚;不能把所有 setRemoteDescription 失败都归结为网络不通。WebRTC API 与协商

ICE:候选地址不是连通证明

host 候选来自本机网络,server-reflexive 候选反映 STUN 观察到的映射,relay 候选来自 TURN 分配,peer-reflexive 候选可在检查过程中发现。浏览器可能为隐私使用 mDNS 名称表示某些 host 地址,因此日志中的候选不一定全是裸 IP。

两端把兼容候选配对,通过带 ICE 凭据的 STUN 检查确认可达性,控制方按规则提名选定候选对。收到一个外部 IP 或拿到 TURN 分配不等于该候选已经成为业务路径。ICE RFC 8445

Trickle ICE 允许候选边收集边发送、边检查,降低等待全部收集完成的时间。它需要处理候选结束信号和每轮 ICE 的关联,不能在重启后把旧候选无条件加到新会话。Trickle ICE RFC 8838

TURN 可以通过 UDP、TCP 或 TLS 等方式接收客户端连接,选哪一种取决于支持与网络条件。“UDP 不通就让 SRTP 原样改跑任意 TCP socket”不是浏览器暴露的通用回退接口。TURN 的控制连接、分配地址和对端媒体路径应分别观察,基础机制见端口与 NAT。

连接变差时,短暂 disconnected 不一定已经永久失败;持续失败可能需要 ICE restart,以新的 ICE 凭据重新协商路径。只在本地调用重启、却不把新描述交给对端,不能完成恢复。

信任边界:TURN、SFU 与帧级加密

TURN 转发密文与 SFU 终止媒体安全,是两种边界 端点 A TURN 端点 B A—B 的 SRTP 保护仍跨过中继 端点 A SFU 端点 B 常规部署为 A—SFU 与 SFU—B 两段 SRTP SFU 不解码视频不等于不能解密;需另行部署帧级端到端加密才能缩小服务器信任。

TURN 中继通常只转发端点之间的加密数据,不持有这两个端点间的 SRTP 内容密钥。常规 SFU 是每个客户端的 WebRTC 对端,终止对应 DTLS-SRTP,再选择媒体流转给其他端点。“SFU 不解码视频”表示它通常不重建像素并重新编码,不表示它天然无法取得媒体明文。

若需求是让 SFU 也不能解读媒体内容,需要在媒体帧层增加端到端保护,并解决成员密钥分发、成员加入退出和密钥轮换。SFrame 定义相应帧保护构造;它不自动提供完整群组成员管理,也不会隐藏所有路由所需元数据。SFrame RFC 9605

延迟是预算:有时值得重传,有时来不及

实时媒体可以使用丢包反馈、RTX 重传、FEC 或丢包隐藏等手段,并不是禁止重传。选择取决于缺失包到播放截止还剩多少时间,以及重传的网络和排队代价。30 fps 的帧间隔约 33 ms,也不意味着所有包从发送到播放的总期限只有 33 ms。WebRTC RTP 使用 RFC 8834

例如采集与编码 35 ms、发送排队 10 ms、单向网络 25 ms、抖动缓冲 60 ms、解码与显示 25 ms,总预算 155 ms。抖动缓冲吸收网络变化,却增加延迟;降到 20 ms 能减少预算,同时也让迟到包更容易错过播放。

stages_ms = {'capture_encode': 35, 'send_queue': 10, 'network': 25,
             'jitter_buffer': 60, 'decode_display': 25}
assert sum(stages_ms.values()) == 155

def retry_fits(slack, feedback_to_sender, retransmit_to_receiver, decode):
    return feedback_to_sender + retransmit_to_receiver + decode < slack
assert retry_fits(80, 25, 25, 10)
assert not retry_fits(40, 25, 25, 10)
print('给定播放预算下的重传可用窗口通过')

拥塞控制根据反馈约束发送速率,编码器调节码率、分辨率或帧率,pacer 控制发送节奏。GCC 是常见实现体系,不是 WebRTC 规范要求所有浏览器只能使用同一算法。扩大的发送队列会吃掉延迟预算,即使平均吞吐看起来足够。

实时数据,什么时候值得等一次重传?

保持往返网络时间50ms和解码10ms,将剩余时间从80ms拉到40ms,重传从可用变成来不及。这里比较的是发现丢失后的剩余预算,不能直接拿帧间隔替代它。

正在呈现知识画面
实时数据,什么时候值得等一次重传?

是否来得及由剩余deadline决定,不由UDP或实时这两个标签决定。缓冲给恢复留下空间,也增加播放延迟。

多人拓扑:上行省下的流量去了哪里

Mesh 中每人向其他 N−1 人发送;SFU 中发送端上传一个或多个编码层,服务器按订阅关系复制给接收者;MCU 通常解码、合成再编码,使接收端可接收合成结果。规模上限取决于码率、设备和画面布局,不存在协议规定的“Mesh 最多四人”。

假设六人每人发送 1.5 Mbit/s 视频,所有人都看其他五人,不算音频与封装:Mesh 每人上行 7.5 Mbit/s;单层 SFU 每人上行 1.5 Mbit/s,但 SFU 总下行仍有 45 Mbit/s。SFU 降低客户端重复上传,并没有凭空消除所有接收副本。

n, rate = 6, 1.5
assert n * (n - 1) // 2 == 15
assert (n - 1) * rate == 7.5
assert n * rate == 9
assert n * (n - 1) * rate == 45
# 三路独立 simulcast 编码的上行是总和,而不是只取最高一路。
assert sum([0.2, 0.6, 1.5]) == 2.3
print('Mesh、SFU 与多档上传的流量口径通过')

Simulcast 同时生成多路编码表示;SVC 使用可扩展编码层及其依赖关系,两者不能直接等同为“三个互不依赖的视频文件”。SFU 选层还要考虑关键帧与依赖,不能任意丢弃上层依赖的基础数据。Simulcast RFC 8853

WebRTC 媒体连接持有拥塞、密钥与传输状态,不适合直接当作 HLS 静态分段缓存;规模化可采用区域媒体节点、选择性订阅、分发层与转协议等组合,不应限定为“只能 SFU 级联”。

SFU省掉的上行,为什么服务器下行还在增长?

六人全互看时,SFU接收6份上传却发送30份副本。把每人订阅数降到1,服务器下行随之降到6份,而每人的单层上传不变。带宽节省发生在哪一段,应按这个复制关系计算。

正在呈现知识画面
SFU省掉的上行,为什么服务器下行还在增长?

SFU把重复上传转移为服务器复制,不会消除接收者需要的副本。选择性订阅改变复制数,压缩算法不是这个公式的变量。

DataChannel:顺序与可靠性是两个维度

DataChannel 保留消息边界,可配置 ordered,以及 maxRetransmits 或 maxPacketLifeTime 的部分可靠策略;两个限制不能同时设置。无序不等于不可靠,可靠也不等于所有 channel 共用一个应用消息顺序。Data Channels

文件传输需要可靠性及应用分块、校验和恢复;游戏位置更新可能更在乎新状态而允许丢旧包。多 SCTP 流能隔离部分顺序约束,但仍共享拥塞资源;大消息、实现的交错支持和发送缓冲都会影响其他流,不能写成“多 stream 就完全没有队头阻塞”。

发送时按 bufferedAmount、低水位事件与协商出的最大消息大小实施背压。send() 接受数据只表示进入发送处理,不表示对端业务已持久化;需要业务确认时应另建消息 ID 与应答规则。

从 getStats 判断卡在哪里

先把统计对象关联到同一 transport、候选对和 SSRC,再比较时间差;累计 counters 不能直接当成当前码率。对象更换、计数重置或字段不可用时,要重新建立基线。WebRTC Stats

例如同一 outbound-rtp 对象 bytesSent 在两秒内从 1,000,000 增至 1,500,000,对应这项统计口径的 2 Mbit/s。它不是自动包含每层 IP/UDP/TURN 开销的网卡速率。接收侧 jitterBufferDelay / jitterBufferEmittedCount 可用于计算累计平均排队时间,观察区间变化时应对两个累计量分别求差再相除。

现象可以继续观察
SDP 完成但 ICE failed候选是否交换完整、实际检查结果、TURN 可达性
ICE 可用而 DTLS failed指纹、角色、握手与安全策略
outbound 没有增长轨道状态、协商方向、采集与编码
inbound 字节增长但无画面解码帧、codec、关键帧、播放元素与自动播放限制
画面延迟逐渐增长发送队列、RTT、抖动缓冲与解码耗时
只在特定网络变差被选中的候选对与实际中继承载

这些证据让“WebRTC 不通”拆成可以验证的阶段。应用日志中的 room ID、协商代际和时间戳应能与统计对应,而不是只保存一个最终 connectionState。