8. SSRC标识符的分配和使用 (SSRC Identifier Allocation and Use)
在RTP头部和RTCP数据包的各种字段中携带的SSRC标识符是一个随机的32位数,要求在RTP会话中全局唯一。选择此数字时非常重要,以防止在同一网络上或同时开始的参与者选择相同的数字。
使用本地网络地址(例如IPv4地址)作为标识符是不够的,因为该地址可能不是唯一的。由于RTP转换器和混音器可以在具有不同地址空间的多个网络之间实现互操作性,因此两个空间中的地址分配模式可能会导致冲突率远高于随机分配。
在同一主机上运行的多个源也将发生冲突。
仅仅调用random()生成一个SSRC标识符是不够的,还需要仔细初始化状态。附录A.6中演示了如何生成一个随机标识符的示例。
8.1 碰撞概率
由于标识符是随机选择的,因此可能会有两个或多个源选择相同的数字。当所有源同时启动时(例如,通过某个会话管理事件自动触发时),碰撞的概率最高。如果N是源的数量,L是标识符的长度(此处为32位),则对于较大的N,两个源独立选择相同值的概率可以近似计算为1 - exp(-N2 / 2(L+1))。对于N=1000,概率大约为10**-4。
典型的碰撞概率远低于上述最坏情况。当一个新的源加入到所有其他源已经具有唯一标识符的RTP会话中时,发生碰撞的概率仅为在空间中使用的编号比例。同样,如果N为源的数量,L是标识符的长度,则碰撞的概率为N / 2L。对于N=1000,概率大约为2*10-7。
在新源发送第一个数据包(数据或控制)之前,通过从其他参与者(按SSRC标识符)接收数据包,可以进一步减少碰撞的可能性,然后再发送其第一个数据包之前,新源可以验证其标识符是否与接收到的任何标识符冲突,否则可以重新选择。
8.2 碰撞解决和环路检测
尽管发生SSRC标识符碰撞的概率很低,但所有RTP实现都必须能够检测到碰撞并采取适当的措施来解决它们。如果源在任何时间发现另一个源使用与其自己相同的SSRC标识符,它必须为旧的标识符发送RTCP BYE数据包并选择另一个随机标识符。(如下所解释的,在循环的情况下仅执行此步骤一次。)如果接收方发现另外两个源发生碰撞,当使用不同的源传输地址或CNAME来检测到时,它可以保留一个源的数据包并丢弃另一个源的数据包。预期这两个源将解决碰撞,以便不会持续发生该情况。
由于每个RTP会话的随机SSRC标识符都是全局唯一的,因此它们也可用于检测由混音器或转换器引入的环路。循环会导致数据和控制信息的重复,无论是否经过修改,如以下示例所示:
o 某个转换器可能会错误地将数据包从其接收到该数据包的相同多播组转发到该多播组中,无论是直接还是通过转换器链。在这种情况下,相同的数据包会以多次来源于不同的网络源的方式出现。
o 两个转换器错误地设置为并行,即在两边具有相同的多播组,它们都会将一个多播组的数据包转发到另一个多播组。单向转换器将生成两份副本;双向转换器将形成环路。
o 混音器可以通过向接收数据包的相同传输目标发送数据来关闭循环,无论是直接还是通过另一个混音器或转换器。在这种情况下,源可能会同时显示在数据包的SSRC上和混合的数据包的CSRC上。
源可能会发现自己的数据包正被循环使用,或者发现另一个源的数据包正在被循环使用(第三方循环)。随机选择源标识符的数据包重叠或碰撞都会导致具有相同SSRC标识符但不同源传输地址的数据包到达,该源传输地址可能是生成该数据包的端系统或中间系统的地址。
因此,如果源更改其源传输地址,则可能还应选择一个新的SSRC标识符,以避免被解释为被循环源。 (这不是必须的,因为在RTP的某些应用中,可能预期源在会话期间更改地址。)请注意,如果转换器重新启动并因此更改了转发数据包的源传输地址(例如,更改了UDP源端口号),然后所有这些数据包都将显示为被循环,因为SSRC标识符是由原始源应用的,并且不会更改。在接收方超时后,将解决此问题。
如果转换器或混音器的另一侧发生循环或碰撞,而所有数据包的副本都通过转换器或混音器进行,则无法使用源传输地址来检测。但是,如果两个RTCP SDES数据包的块包含相同的SSRC标识符但具有不同的CNAME,则仍然可以检测到碰撞。
为了检测和解决这些冲突,RTP实现必须包括与下面描述的类似的算法,尽管实现可以选择不同的策略来保留来自发生碰撞的第三方源的数据包。下面描述的算法忽略了新源或环路的数据包与已建立源的碰撞。它通过发送与参与者的旧标识符及其源传输地址相匹配的RTCP BYE数据包来解决与参与者自己的SSRC标识符的碰撞。但是,在参与者自己的数据包循环的情况下,算法将仅选择新的标识符一次,然后忽略来自循环源传输地址的数据包。这是为了避免BYE数据包的大量泛滥。
该算法要求维护一个以源标识符为索引并包含从第一个RTP数据包和第一个带有该标识符的RTCP数据包收到的源传输地址的表,以及该源的其他状态。由于,例如,RTP和RTCP数据包上的UDP源端口号可能不同,因此需要两个源传输地址。但是,可以假设网络地址在两个源传输地址中是相同的。
在RTP或RTCP数据包中接收到的每个SSRC或CSRC标识符将在源标识符表中查找,以便处理该数据或控制信息。将从数据包读取的源传输地址与表中的相应源传输地址进行比较,以检测循环或碰撞。对于控制数据包,每个带有自己SSRC标识符的元素(例如SDES块)都需要单独查找。(接收报告块中的SSRC标识符是一个例外,因为它标识了报告者所听到的源,并且该SSRC标识符与报告者发送的RTCP数据包的源传输地址无关。)如果未找到SSRC或CSRC,则创建一个新条目。这些表条目将在接收到带有相应SSRC标识符的RTCP BYE数据包并由与之匹配的源传输地址验证之后,或者在较长时间内未收到数据包(参见第6.2.1节)时删除。
请注意,如果在接收方开始操作时,位于同一主机上的两个源正在使用相同的源标识符进行传输,则可能接收到的第一个RTP数据包来自其中一个源,而接收到的第一个RTCP数据包来自另一个源。这将导致错误的RTCP信息与RTP数据相关联,但是这种情况应该非常罕见且无害,可以忽略不计。
为了跟踪环路参与者自己的数据包,实现还必须保留一个单独的源传输地址(而不是标识符)列表,已发现存在冲突。与源标识符表一样,必须保留两个源传输地址以分别跟踪冲突的RTP和RTCP数据包。请注意,冲突地址列表应该很短,通常为空。此列表中的每个元素存储源地址以及接收到最新冲突数据包的时间。如果该来源自上次冲突数据包接收以来一段时间内没有冲突数据包到达(参见第6.2节),则可以从列表中删除元素。
对于所示的算法,假定源的自身源标识符和状态包括在源标识符表中。可以重组该算法以首先将其与参与者自己的源标识符进行单独比较。
如果(在源标识符表中未找到SSRC或CSRC标识符){ 创建一个新条目,其中存储数据或控制源传输地址、SSRC或CSRC以及其他状态; }
/* 在表中找到了标识符 */ 否则,如果(在接收到控制数据包时创建的表条目,且这是第一个数据包,或反之亦然){ 存储来自该数据包的源传输地址; } 否则,如果(数据包的源传输地址与表中为该标识符保存的源传输地址不匹配){
/* 表示发生了标识符碰撞或循环 / 如果(源标识符不是参与者自己的){ / 可选的错误计数步骤 / 如果(源标识符来自包含CNAME项与表条目中不同CNAME的RTCP SDES块){ 统计第三方碰撞; } 否则 { 统计第三方循环; } 中止数据包或控制元素的处理; / 可以选择不同的策略来保留新源 */ }
/* 参与者自己的数据包的碰撞或循环 */
否则,如果(在冲突数据或控制源传输地址列表中找到源传输地址){ /* 可选的错误计数步骤 */ 如果(源标识符不来自包含CNAME项的RTCP SDES块,并且CNAME是参与者自己的){ 统计自己的流量循环发生的频次; } 将当前时间标记为冲突地址列表中的时间; 中止数据包或控制元素的处理; }
/* 新碰撞,更改SSRC标识符 */
否则 { 记录碰撞的发生; 在冲突的数据或控制源传输地址列表中创建一个新条目并标记当前时间; 使用旧的SSRC标识符发送RTCP BYE数据包; 选择新的SSRC标识符; 在源标识符表中创建一个新条目,其中包含来自正在处理的数据或控制数据包的源传输地址以及旧的SSRC; } }
在该算法中,新冲突源地址的数据包将被忽略,原始源地址的数据包将被保留。如果从原始源接收不到数据包一段较长时间,表条目将超时,新源将能够接管。如果原始源地址是通过混音器接收到的(即,作为CSRC学习),并且稍后接收到相同源的源地址,则接收方可能会明智地切换到新源地址,除非在混音中丢失其他源。此外,对于诸如在RTP会话期间可能更改地址的移动实体等应用程序,RTP实现应修改碰撞检测算法,以接受来自新源传输地址的数据包。为了防止在发生真实碰撞时来回切换地址,算法应包括某些方式来检测这种情况并避免切换。
当由于碰撞选择了新的SSRC标识符时,应首先在源标识符表中查找候选标识符,以查看其是否已由其他某个源使用。如果是这样,则必须生成另一个候选标识符并重复该过程。
数据包到一组多播目标的循环可能会导致严重的网络洪泛。所有混音器和转换器都必须实现类似于此处所示的循环检测算法,以便它们可以打破循环。这应该将超量流量限制为原始流量的最多一个副本,这可能允许会话继续,以便可以找到并修复循环的原因。但是,在极端情况下,如果混音器或转换器未正确打破循环并导致高流量水平,则可能需要端系统完全停止传输数据或控制数据包。此决定可能取决于应用程序。应适当指示错误条件。可以在长时间的随机时间(几分钟的数量级)后定期尝试重新传输。
8.3 与分层编码的使用
对于在不同的RTP会话上传输的分层编码(参见第2.4节),应在所有图层的会话中使用单个SSRC标识符空间,并且应使用核心(基本)层来进行SSRC标识符的分配和碰撞解决。当源发现发生碰撞时,它仅在基本层上发送RTCP BYE数据包,但是在所有层中更改SSRC标识符的值。