15、SSRC属性与RID:RID的SDP定义、Simulcast流标识、a=rid行解析

各位同学,今天我们来聊一个在WebRTC Simulcast场景下绕不开的话题——RID。说实话,我刚开始接触RID的时候,也被它和SSRC的关系搞得有点晕。但后来在项目中踩过几次坑,才真正理解了它的设计意图。

RID的全称是RestrictId,翻译过来就是“限制标识符”。嗯,这个名字其实挺抽象的。你想想看,它到底限制了什么?其实它限制的是——每个RTP流只能携带一个特定的RID值。说白了,RID就是给每个Simulcast层打的一个标签。

RID的SDP定义

我们先看看RID在SDP里长什么样。在WebRTC的SDP协商中,RID是通过a=rid行来定义的。我习惯把它理解为“流的身份证”。

a=rid:1 send recv
a=rid:2 send recv
a=rid:3 send recv

上面这个例子,定义了三个RID值:1、2、3。每个RID代表一个Simulcast层。注意这里的sendrecv,它们表示这个RID是用于发送方向还是接收方向。

在实际项目中,我见过不少同学把RID和SSRC搞混。这里我给大家一个简单的区分方法:

  • SSRC:是RTP层的同步源标识,每个RTP流一个,全局唯一
  • RID:是应用层的流标识,用于区分Simulcast的不同层,同一个流的不同层可以有相同的SSRC空间

核心要点:RID是逻辑层的标识,SSRC是传输层的标识。RID告诉接收方“这是哪一层”,SSRC告诉接收方“这是哪个流”。

Simulcast流标识与a=rid行解析

我们来看一个完整的Simulcast SDP片段。这是我之前在一个视频会议项目中实际用过的配置:

a=group:BUNDLE 0 1 2
a=msid:example-msid

m=video 9 UDP/TLS/RTP/SAVPF 96 97 98
a=rid:1 send recv
a=rid:2 send recv
a=rid:3 send recv
a=simulcast:send 1;2;3

a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:2 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id

这里有几个关键点,我逐一解释:

  1. a=rid行:定义了三个RID值,分别是1、2、3。每个RID代表一个Simulcast层。通常RID值越小,分辨率或码率越低。
  2. a=simulcast行:声明了Simulcast的发送方向,send 1;2;3表示发送端会发送三个层,RID分别为1、2、3。
  3. a=extmap行:定义了RTP头部扩展,用于在RTP包中携带RID信息。rtp-stream-id就是RID的扩展头,repaired-rtp-stream-id是修复用的备用扩展头。

避坑指南:我曾经在一个项目中,只配置了a=rid行,但忘了加a=extmap。结果接收端虽然知道有三个层,但无法从RTP包中提取RID信息,导致无法正确路由。记住,a=extmap是必须的,它告诉接收端“RID信息放在RTP扩展头的哪个位置”。

RID与SSRC的映射关系

现在我们来画一张图,看看RID和SSRC在Simulcast场景下是如何配合工作的。

RID与SSRC映射关系(Simulcast场景) 发送端(SFU/Peer) RID: 1 (低) RID: 2 (中) RID: 3 (高) SSRC: 0x10000001 SSRC: 0x10000002 SSRC: 0x10000003 RTP包(SSRC + RID扩展头) 扩展头中携带RID值 RTP包(SSRC + RID扩展头) 扩展头中携带RID值 RTP包(SSRC + RID扩展头) 扩展头中携带RID值 接收端(SFU/Peer) 每个RID层对应一个独立的SSRC,RTP扩展头中携带RID值用于接收端识别层

从这张图可以看出,每个RID层都对应一个独立的SSRC。但这里有个细节:RID是逻辑层的标识,而SSRC是传输层的标识。接收端通过RTP扩展头中的RID值,就能知道这个RTP包属于哪个层。

RID的扩展头定义

在RTP层面,RID是通过扩展头来传递的。标准定义了两个扩展头URI:

扩展头URI 用途 长度
urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id 主RID标识 1字节(最多255个字符)
urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id 备用RID标识(修复用) 1字节

为什么需要两个?嗯,这里有个历史原因。早期的实现中,有些浏览器只支持repaired-rtp-stream-id,后来才统一到rtp-stream-id。我建议在SDP中两个都声明,兼容性更好。

注意事项:RID值在同一个媒体流中必须唯一。如果你有两个层都叫"1",接收端就分不清了。另外,RID值建议使用数字或简短字符串,不要超过255字节。

实际项目中的RID配置

最后,我分享一个我在项目中实际用过的RID配置模板。这个配置支持三层Simulcast,分别对应360p、720p和1080p:

// SDP中的RID配置示例
a=rid:0 send recv
a=rid:1 send recv  
a=rid:2 send recv
a=simulcast:send 0;1;2

// RTP扩展头映射
a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id

// 每个RID对应的编码参数(可选)
a=rid:0 send pt=96;max-width=640;max-height=360
a=rid:1 send pt=97;max-width=1280;max-height=720
a=rid:2 send pt=98;max-width=1920;max-height=1080

注意最后三行,我习惯在每个RID后面加上编码参数。这样接收端不用解析RTP包,光看SDP就知道每个层的分辨率范围。这在做自适应码率选择时特别有用。

好了,关于RID的SDP定义和Simulcast流标识,我们就讲到这里。记住,RID是Simulcast的核心,它让接收端能够智能地选择接收哪个层。下一节我们会深入SSRC的生成和冲突处理,到时候你会看到RID和SSRC是如何配合完成整个媒体协商的。


公众号:蓝海资料掘金营,微信deep3321