一、Group属性到底是什么?
Group属性,说白了就是告诉浏览器:「这几个媒体流,你们得绑在一起处理」。我刚开始接触WebRTC时,看到SDP里一堆a=group行,说实话有点懵。后来做项目多了,才真正理解它的重要性。
在WebRTC的SDP协商中,group属性主要干两件事:
- BUNDLE分组:把多个媒体流合并到同一个传输通道
- FID/FEC分组:把原始流和冗余流关联起来
嗯,这里要注意,group属性的格式其实很简单:
a=group:BUNDLE audio video
a=group:FID 1 2
a=group:FEC 1 3
第一行表示audio和video要捆绑传输,第二行表示媒体流1和它的重传流2是一对,第三行表示媒体流1和它的FEC流3是一对。
二、BUNDLE分组:省带宽的利器
我个人习惯把BUNDLE叫做「多流合一」。你想想看,如果没有BUNDLE,每个媒体流都要单独开一个UDP端口,那ICE连接数就爆炸了。
BUNDLE的核心思想:
- 所有媒体流共享同一个ICE候选对
- 所有媒体流共享同一个DTLS连接
- 所有媒体流共享同一个SRTP上下文
我在项目中遇到过一个问题:有个客户反馈视频通话经常卡顿,排查了半天发现是BUNDLE没协商成功,导致每个流都走了独立的UDP端口,NAT穿透失败率飙升。
BUNDLE协商的关键点:
- 发起方在SDP中声明a=group:BUNDLE,列出所有要捆绑的媒体
- 应答方必须按相同顺序回应,否则协商失败
- 每个媒体行都要有a=mid标识,group里引用的就是mid
三、FID分组:重传流的绑定
FID,全称是Flow Identification。它解决的是「丢包重传」的问题。当网络丢包时,接收方会请求重传,但重传的数据包和原始数据包怎么区分?
答案就是FID分组:
m=video 9 UDP/TLS/RTP/SAVPF 100 101
a=mid:1
a=rtpmap:100 VP8/90000
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
m=video 0 UDP/TLS/RTP/SAVPF 101
a=mid:2
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=group:FID 1 2
这里mid:1是原始VP8流,mid:2是重传流。FID分组告诉接收方:「这两个流是一家的,重传数据请认准这个组合」。
避坑指南:我曾经在调试时发现重传一直不生效,查了半天原来是FID分组里的mid顺序写反了。记住,第一个是原始流,第二个是重传流,顺序不能乱。
四、FEC分组:前向纠错的SDP协商
FEC(前向纠错)和重传不同,它不需要等待丢包反馈,直接发送冗余数据。接收方即使丢了一些包,也能通过冗余数据恢复出来。
FEC分组的SDP长这样:
m=video 9 UDP/TLS/RTP/SAVPF 100 102
a=mid:1
a=rtpmap:100 VP8/90000
a=rtpmap:102 ulpfec/90000
m=video 0 UDP/TLS/RTP/SAVPF 102
a=mid:3
a=rtpmap:102 ulpfec/90000
a=group:FEC 1 3
这里mid:1是原始VP8流,mid:3是FEC流。FEC分组告诉接收方:「这两个流是配套的,FEC数据用来修复原始流」。
FEC的两种模式
| 模式 | 说明 | 适用场景 |
|---|---|---|
| ULP FEC | 基于异或运算,冗余率可配置 | 丢包率<10%的场景 |
| Flex FEC | 更灵活的FEC方案,支持多种编码 | 高丢包率场景 |
我个人建议,如果丢包率超过15%,单纯靠FEC就不太够了,带宽浪费太严重。这时候应该FEC和NACK(重传)配合使用。
五、Group属性的完整协商流程
让我用一张图来展示整个协商流程:
这个流程看起来简单,但实际项目中坑不少。我记得有一次调试,发起方SDP里写了a=group:BUNDLE audio video,但应答方返回的group顺序是video audio,结果Chrome直接报错,协商失败。
重要提醒:BUNDLE分组中的媒体顺序必须严格一致。发起方怎么写,应答方就怎么回。这是很多新手容易踩的坑。
六、FID和FEC的共存策略
在实际项目中,FID和FEC经常同时使用。这时候group属性会变成这样:
a=group:BUNDLE audio video
a=group:FID 1 2
a=group:FEC 1 3
a=group:FID 4 5
a=group:FEC 4 6
这里mid:1是视频原始流,mid:2是视频重传流,mid:3是视频FEC流。mid:4是音频原始流,mid:5是音频重传流,mid:6是音频FEC流。
我个人习惯的做法是:
- 视频流:FID + FEC 同时开启,FEC冗余率设10%
- 音频流:只开FID,不开FEC(音频包小,重传成本低)
为什么这么配?因为视频数据量大,FEC的冗余开销相对可以接受。音频包本来就小,重传一次也就几十字节,没必要浪费FEC带宽。
七、总结
Group属性是WebRTC媒体协商中容易被忽视但极其重要的部分。它决定了媒体流如何组织、如何关联、如何容错。
三个核心点:
- BUNDLE:多流合一,节省连接资源
- FID:原始流和重传流的绑定
- FEC:原始流和前向纠错流的绑定
嗯,这些内容看起来简单,但实际调试时每个细节都可能让你头疼。建议大家在写SDP解析代码时,一定要严格校验group的格式和顺序,否则线上出问题排查起来很痛苦。
一句话总结:Group属性就是WebRTC的「组织架构图」,它告诉系统谁和谁是一组的,谁负责干活,谁负责兜底。