因为 RTCP 报文间隔是根据已知的 Session 参与者情况计算的,所以当有新的人要加入到 Session 时,可能会错估整个 Session 的规模,而是用了较短的 RTCP 间隔,尤其是当大批量的人一齐加入 Session 时这种现象更加明显。所以,可能会有一个 "发送时机重整" 算法,它实现了一个简单的撤回机制,可以在 Session 规模持续增长时,适当的撤回一些 RTCP 报文。
当有人通过发送 BYE 报文或者因为超时退出 Session 时,RTCP 的发送间隔应该缩短。
BYE 报文和其他 RTCP 报文相比,有一些特殊的地方。当有人想要退出,并发送 BYE 报文时,它可以在下一个发送周期到来之前就发送。当然,如果一大批人同时退出时,也会受到前面提到的 RTCP 报文撤回机制的影响。
维护 Session 成员的数量我们已经知道了,计算 RTCP 发送间隔是需要清楚整个 Session 中成员数量的,当一个新的节点被监听到时,它就会被加入到 Session 总数中,并且大家要把它加入到一个 SSRC(CSRC)身份识别表中然后持续追踪。大家只有收到这个新节点的多个数据包,或者收到他的 SDES 包(CNAME)时才觉得这个新节点是靠谱的。当某个节点发了一个 BYE 之后,它的信息可能就会被大家删了,但是考虑到可能有丢包或者网络拥堵的情况,所以大家会先把它标记为 "收到 BYE",然后等一段时间,如果还没收到它的别的报文,这时候才会把它删了。
如果一个节点超过一个 RTCP 周期都没收到另一个节点的报文,它可能就会将其标记为不活跃,或者删了它,这就需要丢包的情况尽可能别发生。但是不丢包是不可能的,所以大家一般会将 RTCP 传输间隔乘以一个系数(大于 1 的数)作为超时时间。
对于那些参与者很超级多的 Session,可能没法去维护一个 SSRC 表来存储所有参与者的信息。通常大家都会简化这个 SSRC 表,但是需要注意的是无论怎么简化这个表都不能低估了参与者的总数,可以允许高估参与者总数。
RTCP 报文的收发规则首先,无论是组播还是多个节点的单播都必须遵循前面提到的 RTCP 间隔。为了正常完成 RTCP 报文的收发操作,Session 中的每个参与者都会维护如下信息:
TP:最后 RTCP 报文的发送时间;
TC:当前时间;
TN:下一个要发送报文的时间点;
P-Members:计算上一个 TN 时参考的 Session 成员总数;
Members:当前的 Session 成员总是;
Senders:数据发送者总数;
RTCP_BW:RTCP 的目标带宽;
WE_Sent:从倒数第二个 RTCP 报文发送后,到现在为止,是否发送过数据;
AVG_RTCP_Size:平均 RTCP 复合包大小,包括传输层和网络层的头;
initial:是否一个 RTCP 报文都没发过。
计算 RTCP 发送间隔为了让 RTP 协议具有可伸缩性,RTCP 的发送间隔需要随着 Session 总人数的变化而适当的缩放。结合上述的部分状态,我们按如下方式计算 RTCP 报文间隔:
如果媒体流发送者的数量小于总人数的 25% 时,这个间隔和当前节点是否是媒体流发送者有关(通过 WE_Sent 判断)。如果是媒体流发送者,计算公式为 Senders * AVG_RTCP_Size / (25% * RTCP_BW),如果是媒体流的接收者,计算公式为:(Members - Senders)* AVG_RTCP_Size / (75% * RTCP_BW)。当媒体流发送者的数量超过 25% 时,发送者和接收者会被同等对待,即它们的 RTCP 周期公式为:Members * AVG_RTCP_Size / RTCP_BW。
如果某个参与者一个 RTCP 包都还没发送,最小发送间隔间隔(Tmin)为 2.5 秒,否则为 5 秒。
决定的发送间隔(Td)会是第一步计算的值和 Tmin 中较大的那个。
发包时会在 Td 的基础上随机缩放 0.5~1.5 倍。
最终这个间隔还要除以 e-3/2=1.21828,这是为了弥补因为 "发送时机重整" 算法带来的影响(因为这个算法会导致最终 RTCP 使用的实际带宽比预计使用的带宽低)。
初始化当一个人刚加入到 Session 中时,tp=0,tc=0,senders=0,p-members=0,members=1,we_sent=false,rtcp_bw = 5% * Session 带宽,initial=true,avg_rtcp_size 被设置为之后会发送的首个 RTCP 包的大小,然后计算发送间隔 T 时,会根据上述初始状态进行计算,并以此作为参考发送第一个包,最后将自己的 SSRC 加入到成员列表中。
接收 RTP 和 Non-BYE RTCP 包当 RTP 或者 RTCP 包被另一个人(A)接收到了,如果对 A 来说这个包的 SSRC 他没见过,那么他就会将其加入到 SSRC 表中,并更新 Session 总人数(Members)。对每个 CSRC 也会做同样的操作。
如果收到了一个 RTP 报文,并且其对应的 SSRC 没在发送者 SSRC 表中,那他就会把它加进发送者 SSRC 表中,并更新发送者的总数(Senders)。