22、媒体协商中的SSRC属性:a=ssrc行详解、CNAME属性、MSID与流关联
各位同学,今天我们来聊聊SDP里一个容易被忽视、但实际坑特别多的东西——SSRC属性。
我记得刚接触WebRTC那会儿,看到SDP里一堆a=ssrc:开头的行,第一反应是“这玩意儿是啥?能不能删掉?”后来在项目里被坑了几次,才老老实实回来啃这块内容。说白了,SSRC就是媒体流的“身份证号”,没有它,接收端根本不知道谁是谁。
一、SSRC是什么?为什么需要它?
SSRC全称是Synchronization Source,同步源标识符。它是一个32位的随机数,用来唯一标识一个RTP流。
你想想看,一个WebRTC会话里可能有多个音频流、多个视频流,甚至还有屏幕共享流。接收端收到一堆RTP包,怎么知道哪个包属于哪路流?靠的就是SSRC。
核心要点:每个RTP流必须有一个唯一的SSRC。如果两个流用了相同的SSRC,接收端就会发生冲突,轻则花屏,重则断流。
我在项目中遇到过一个问题:两个PeerConnection共用了同一个SSRC,结果视频一直卡在“黑屏+绿屏”之间来回跳。查了半天才发现是SSRC冲突。嗯,从那以后我养成了一个习惯——每次创建流都手动检查SSRC是否唯一。
二、a=ssrc行详解
SDP中的a=ssrc:行长什么样?来看一个典型的例子:
a=ssrc:123456789 cname:myCname
a=ssrc:123456789 msid:stream1 track1
a=ssrc:123456789 mslabel:stream1
a=ssrc:123456789 label:track1
每一行都包含三个部分:
- SSRC值:32位无符号整数,通常用十进制表示
- 属性名:比如
cname、msid、mslabel、label - 属性值:根据属性不同,格式也不同
这些属性行不是随便写的,它们有严格的语义。我个人的习惯是,在调试SDP时,先看a=ssrc行有没有缺失,尤其是cname和msid,缺了任何一个都可能导致媒体流无法正常渲染。
三、CNAME属性:流的“真名”
CNAME(Canonical Name)是SSRC属性里最重要的一个。它用来标识同一个媒体源的多个RTP流。
举个例子:一个视频源可能同时发送一个高清流和一个低清流(Simulcast场景)。这两个流的SSRC不同,但CNAME相同。接收端通过CNAME就能知道它们来自同一个源,可以做平滑切换。
我的经验:CNAME通常用user@host的格式,或者直接用UUID。我建议用UUID,因为它在全局范围内唯一,不容易冲突。曾经有个项目用了用户名做CNAME,结果两个用户同名,媒体流全乱了。
CNAME的SDP写法:
a=ssrc:111111 cname:user123@example.com
a=ssrc:222222 cname:user123@example.com
这里两个SSRC共享同一个CNAME,表示它们来自同一个媒体源。
四、MSID与流关联
MSID(Media Stream ID)是WebRTC 1.0规范引入的属性,用来将RTP流关联到特定的MediaStream和MediaStreamTrack。
它的格式是:
a=ssrc:123456789 msid:streamId trackId
其中:
streamId:MediaStream的IDtrackId:MediaStreamTrack的ID
为什么需要MSID?因为一个MediaStream可以包含多个Track(比如音频+视频),而每个Track又可能有多个SSRC(比如Simulcast)。MSID就像一根绳子,把这些散落的SSRC串起来,告诉接收端:“这些流都属于同一个MediaStream。”
注意:有些老版本的浏览器不支持msid属性,而是用mslabel和label。如果你需要兼容旧设备,最好同时保留这两种写法。我曾经因为只写了msid,导致一个Chrome 56的用户无法正常接收视频流。
五、SSRC属性与RTP包的关系
说了这么多SDP里的SSRC属性,它们和实际的RTP包有什么关系?
每个RTP包的头部都包含一个SSRC字段。接收端收到RTP包后,根据这个SSRC去查找对应的SDP信息,从而知道这个包属于哪个流、哪个Track、哪个MediaStream。
来看一张流程图:
这个流程看起来简单,但实际实现时有很多细节。比如,接收端需要维护一个SSRC到SDP属性的映射表,而且这个表要能动态更新——因为SSRC可能会在会话中发生变化(比如发生冲突后重新协商)。
六、常见问题与避坑指南
我在多个项目中积累了一些关于SSRC属性的经验,分享给大家:
| 问题 | 现象 | 解决方案 |
|---|---|---|
| SSRC冲突 | 花屏、绿屏、断流 | 使用随机数生成器,确保全局唯一 |
| 缺少CNAME | Simulcast切换失败 | 每个SSRC必须携带CNAME属性 |
| MSID不匹配 | Track无法渲染 | 确保SDP中的MSID与JS端一致 |
| SSRC重复使用 | 流混乱、丢包 | 每个新流使用新的SSRC |
我曾经踩过的坑:有一次做SFU(Selective Forwarding Unit)开发,转发流时忘了修改SSRC,导致所有用户收到的流都是同一个SSRC。结果就是,每个用户只能看到最后一个加入者的画面。排查了整整两天才发现是SSRC没做映射。从那以后,我在SFU代码里加了一个SSRC重映射模块,每次转发都生成新的SSRC。
七、总结
SSRC属性是WebRTC媒体协商中一个看似简单、实则暗藏玄机的部分。记住三点:
- SSRC是流的唯一标识,不能冲突
- CNAME是流的“真名”,用于关联多个SSRC
- MSID是流与Track的桥梁,告诉接收端怎么组织媒体
嗯,这些内容看起来有点琐碎,但实际调试时真的能救命。下次遇到媒体流异常,不妨先看看SDP里的a=ssrc行,说不定问题就出在这里。
公众号:蓝海资料掘金营,微信deep3321