12、REMBB与Transport-CC:Transport-wide CC报文格式、REMBB反馈机制、接收端与发送端角色
这一章我们来聊聊WebRTC拥塞控制里两个最核心的协议组件——REMBB和Transport-CC。说实话,很多刚接触WebRTC的同学会把这两个东西搞混,觉得它们都是做带宽估计的。嗯,其实它们分工很明确:Transport-CC负责把传输层的丢包和延迟信息从接收端反馈给发送端,而REMBB则是接收端主动告诉发送端“我觉得你应该用多少码率”。
我个人习惯把Transport-CC看作是“数据采集器”,把REMBB看作是“建议书”。发送端最终听谁的?两个都听,但权重不同。下面我们一个一个拆开讲。
Transport-wide CC报文格式
Transport-wide Congestion Control,简称Transport-CC。它和传统的RTCP不同,不是每个RTP流单独反馈,而是把同一个传输通道上的所有RTP流打包在一起反馈。说白了,就是接收端每隔一段时间,给发送端发一个报文,告诉它:“你这段时间发的包,哪些到了,哪些丢了,延迟是多少。”
这个报文格式长什么样?我直接给你看结构:
// Transport-CC RTCP报文结构(简化版)
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P| FMT=15 | PT=205 | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of packet sender |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| base sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| packet status count | reference time |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| reference time (cont.) | ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| packet status chunk 1 | packet status chunk 2|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| recv delta chunk 1 | recv delta chunk 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
这里有几个关键字段我要强调一下:
- base sequence number:起始序列号。接收端从这个号开始报告,发送端就知道对应的是哪个包。
- packet status count:本次报告覆盖了多少个包。注意,不是所有包都要报告,接收端可以只报告一个窗口内的包。
- reference time:参考时间戳。接收端用这个时间作为基准,后续的延迟增量都是相对于这个时间的。
- packet status chunk:每个包的状态。有三种:未收到、已收到(小延迟)、已收到(大延迟)。
- recv delta chunk:每个已收到包的延迟增量,单位是微秒。
我在项目中遇到过一个问题:有些实现把reference time的单位搞错了,导致发送端算出来的延迟全是负数。嗯,这里要注意,reference time的单位是64毫秒的倍数,不是毫秒也不是微秒。
REMBB反馈机制
REMBB全称是Remote Bitrate Estimation,说白了就是接收端自己算一个带宽,然后告诉发送端。它的报文格式比Transport-CC简单得多:
// REMBB RTCP报文结构
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P| FMT=15 | PT=206 | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of packet sender |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of media source |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Unique identifier 'R' 'E' 'M' 'B' |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Num SSRC | BR Exp | BR Mantissa |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of flow 1 (if any) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
REMBB的核心就两个东西:BR Exp(指数)和BR Mantissa(尾数)。它们组合起来表示一个浮点数,就是建议的码率。公式是:
bitrate = mantissa * 2^exp // 单位是bps
举个例子,如果exp=10,mantissa=800,那么建议码率就是800 * 1024 = 819200 bps,约800kbps。
我曾经踩过一个坑:REMBB报文里的SSRC of media source字段,有些实现写的是发送端的SSRC,有些写的是接收端的SSRC。标准规定是写发送端的SSRC,但有些老版本Chrome写反了。如果你在做互操作性测试,这个字段一定要做兼容处理。
接收端与发送端角色
现在我们来聊聊这两个角色在拥塞控制中的分工。你想想看,整个拥塞控制流程其实就是一个闭环:
- 发送端:负责发送RTP包,同时记录每个包的发送时间和序列号。
- 接收端:收到包后,记录到达时间,然后定期生成Transport-CC和REMBB反馈。
- 发送端:收到反馈后,计算延迟梯度、丢包率,然后调整发送码率。
我画了一张图,帮你理清这个流程:
从这张图你可以看到,接收端其实做了两件事:
- Transport-CC:被动反馈。接收端只是如实报告每个包的到达情况,不做任何计算。发送端拿到数据后自己算延迟和丢包。
- REMBB:主动建议。接收端自己跑一个带宽估计算法(比如Google Congestion Control算法),然后把算出来的码率告诉发送端。
那么问题来了:发送端到底听谁的?
我个人习惯的做法是:以Transport-CC为主,以REMBB为辅。为什么?因为Transport-CC的数据更原始、更准确,发送端可以根据自己的算法做更精细的调整。而REMBB只是一个建议,接收端的算法可能和发送端的不一致,甚至可能因为实现bug给出错误的建议。
核心要点:在WebRTC的拥塞控制中,Transport-CC是“事实依据”,REMBB是“参考意见”。发送端最终做决策时,应该优先信任Transport-CC的原始数据,而不是直接采纳REMBB的建议。
避坑指南
我曾经在项目中遇到过几个典型问题,分享给你:
- Transport-CC报文丢失:如果接收端发送的Transport-CC报文在网络中丢失了,发送端就收不到反馈。这时候发送端应该怎么做?我建议设置一个超时定时器,如果超过一定时间(比如500ms)没有收到任何反馈,就主动降低码率,避免网络进一步恶化。
- REMBB和Transport-CC冲突:有时候REMBB建议降低码率,但Transport-CC显示延迟很低、丢包率为0。这时候该信谁?我的经验是:如果Transport-CC数据正常,就忽略REMBB的建议。因为REMBB可能基于过时的数据或者错误的算法。
- 多流场景下的SSRC映射:如果你有多个RTP流(比如视频+屏幕共享),每个流都有自己的SSRC。Transport-CC报文里只报告传输层的包状态,不区分SSRC。发送端需要自己维护一个映射表,把传输层序列号映射到具体的RTP流上。
重要提醒:REMBB在WebRTC标准中已经被标记为“可选实现”,而Transport-CC是“必须实现”。如果你在做新的WebRTC项目,建议优先实现Transport-CC,REMBB可以作为备选方案。Chrome从M75版本开始,已经默认使用Transport-CC作为主要的拥塞控制反馈机制。
总结
这一章我们聊了Transport-CC和REMBB的报文格式、反馈机制,以及接收端和发送端各自的角色。说白了,Transport-CC是“数据采集器”,REMBB是“建议书”。发送端拿到这两份数据后,自己做决策。
下一章我们会深入发送端的带宽估计算法,看看它到底是怎么根据Transport-CC的数据算出码率的。嗯,那个算法才是真正的核心。
公众号:蓝海资料掘金营,微信deep3321