概述
RTP,即实时传输协议,英文全称为Real-Time Transport Protocol,是一种用于在互联网上传输视频、音频等实时数据的网络协议。RTP本身不提供任何服务质量保证,而是依赖于底层传输协议(比如:UDP)来进行数据传输。RTP的主要功能是为实时数据提供时间戳和序列号,以便接收端能够按照正确的顺序和时间重建数据流。
RTP与RTSP的关系
RTP专为在IP网络上传输实时音视频等连续媒体数据而设计,承担着数据封装、传输与同步的任务。相较于负责会话控制和媒体描述的RTSP,RTP更侧重于实时数据的实际传输过程。
RTSP与RTP形成互补关系:RTSP用于建立、控制和终结媒体会话,定义媒体类型、传输参数等;RTP则依据RTSP设定的参数,实际传输封装好的媒体数据。二者通过交互消息,比如:RTSP的SETUP、PLAY命令与RTP数据流,实现无缝对接。
总的来说,RTP与RTSP之间的关系是相辅相成的。RTP提供了实时数据传输的基础,而RTSP则在此基础上实现了对媒体流的控制和管理。两者共同协作,使得实时流媒体的传输和控制变得更加高效和可靠。
RTP数据包
RTP数据包由RTP头部(RTP Header)和RTP负载(RTP Payload)两部分组成。RTP头部包含了关于数据包的重要信息,比如:版本号、负载类型、序列号、时间戳等。RTP负载包含实际的媒体数据,其格式和内容取决于载荷类型(PT)字段所指示的编码或格式。比如:如果载荷类型是H.264视频,那么RTP负载将包含H.264编码的视频帧。
关于RTP数据包,这里不再深入,我们会在后续的专栏文章中专门进行介绍。
基于UDP的传输特性
RTP使用UDP来进行实时音视频等流媒体数据的传输,故其具有UDP的一些传输特性。
1、无连接性:RTP利用UDP的无连接特性,无需预先建立连接即可发送数据。这简化了会话建立过程,降低了延迟,尤其适用于实时媒体流,其中快速启动传输至关重要。
2、多播支持:由于UDP支持多播,RTP可以通过单一数据发送操作到达多个接收者,这对于大规模直播场景非常有利,减少了服务器负载和网络带宽消耗。
3、低延迟:UDP避免了TCP的握手、确认、重传等机制,使得RTP数据包几乎可以立即发送,从而保持较低的端到端延迟,符合实时流对时延敏感的要求。
虽然UDP不提供可靠传输,但RTP通过以下机制增强了传输的可靠性。
1、校验和:RTP头部包含一个校验和字段,用于检测数据包在传输过程中是否发生错误。接收端计算收到数据包的校验和,并与包内校验和比较,若不一致则丢弃该包。
2、前向纠错(FEC):通过RTP扩展,可以添加前向纠错码,发送冗余数据。这允许接收端在一定比例丢包的情况下恢复原始数据,提高传输的稳健性。
3、RTCP反馈与控制:与RTP配套的RTCP协议定期发送控制报文(比如:Sender Report、Receiver Report),提供丢包率、抖动、延迟等统计信息,用于监测会话质量并触发相应的补偿措施(比如:重新发送请求、码率调整等)。
重组数据包
重组数据包是指客户端接收一系列RTP数据包后,根据它们的序列号、时间戳等信息,将这些包按照发送时的原始顺序重新组织起来的过程。重组数据包的目的是:确保解码器能够按照正确的顺序处理这些数据,从而正确解码并输出音视频流。在重组过程中,客户端需要处理丢包、乱序到达的包以及可能存在的分包数据。
重组数据包的过程主要涉及以下5个步骤。
1、接收与存储RTP数据包
客户端通过UDP接收来自服务器的RTP数据包。每当接收到一个RTP包时,客户端首先检查其头部中的序列号字段。这个序列号是递增的16位整数,每发送一个新的RTP数据包,发送端就会将其增加1。客户端需要将接收到的RTP包及其序列号存储在一个适当的结构(比如:队列、环形缓冲区等)中,以便后续处理。
2、检测与处理丢包
根据RTP数据包的序列号,客户端可以检测是否存在丢包。如果连续接收的两个RTP包之间的序列号差值大于1,则表明在这两个包之间存在一个或多个丢包。客户端可以根据需要通过RTCP的Receiver Report报文向服务器报告丢包情况,或者使用其他应用层重传机制(比如:RTCP的NACK请求)请求丢失的数据包。
3、基于序列号排序
存储在客户端内部结构中的RTP包需要按照其序列号进行排序,以恢复原始的发送顺序。排序算法可以是简单的插入排序、快速排序、堆排序等,也可以是针对RTP包特性优化的排序方法。排序的目标是确保RTP包按照其在发送端生成的顺序排列,以便后续解码器正确处理。
比如:对于一个简单的环形缓冲区,可以设置一个指向当前已排序部分最后一个包的指针。每当接收到一个新包时,将其与指针处的包进行序列号比较。如果新包的序列号大于指针处包的序列号且小于下一个预期包的序列号(当前已排序部分最大序列号+1),则将新包插入到适当位置。如果新包的序列号小于等于指针处包的序列号,说明收到了旧包或重复包,可以直接丢弃。如果新包的序列号大于下一个预期包的序列号,说明存在未接收到的中间包,此时可以暂时将新包存放在待排序区域,等待缺失的包到达后再进行插入排序。
4、处理乱序包
在实际网络环境中,RTP包可能会因为网络延迟不均等原因出现乱序到达的情况,客户端在排序过程中应能妥善处理乱序包。当发现新包的序列号大于下一个预期包的序列号时,说明该包是乱序到达的。此时,客户端不应立即插入该包,而应将其暂存于乱序包队列,等待所有位于其前序的中间包到达后再插入到正确位置。乱序包队列应有限定的容量,防止内存过度消耗。超出容量的乱序包可以选择丢弃,或者根据具体应用需求采取其他处理策略。
5、数据包交付给解码器
当RTP包按照序列号正确排序后,客户端可以将连续的一段无丢包、无乱序的RTP包提交给解码器进行解码。解码器按照包的顺序进行解码处理,生成可供播放的音视频数据。
通过上述步骤,客户端能够基于RTP包的序列号有效地重组数据包,确保音视频流的正确解码和播放。在整个过程中,客户端需要实时监测网络状况,根据丢包率、延迟、抖动等指标调整接收策略,比如:调整缓冲区大小、请求重传、切换码率等,以优化用户体验。同时,与服务器之间的RTCP通信也是必不可少的,用于交换会话控制信息、反馈接收质量以及请求丢包恢复等。
分包与组包
分包是指将一个较大的编码单元(比如:H.264/AVC、H.265/HEVC的NAL单元)分割成多个较小的数据块(分包),以便通过RTP协议进行传输。分包通常发生在编码器端,当原始编码单元过大,不适合单个RTP包承载时,会选择将其拆分成多个RTP包发送。每个分包都有相应的标识信息(比如:分包索引、分包类型等),以便接收端识别并正确重组。
可能很多初学者会有疑问:为什么需要分包呢?主要有以下两点原因。
1、MTU限制
网络传输中存在最大传输单元MTU(Maximum Transmission Unit)的限制,比如:以太网的MTU通常是1500个字节。考虑到IP头部、UDP头部和RTP头部的开销,实际留给RTP有效载荷的空间更小,通常约1400字节。如果一个编码单元超过了这个有效载荷限制,就需要进行分包。
2、带宽管理
分包有助于更有效地利用带宽,尤其是在网络条件不稳定或带宽有限的情况下,通过将大编码单元拆分为较小的部分,可以更灵活地调整传输速率,降低丢包风险。
常见的分包方法有如下两种,下面分别进行介绍。
单个NAL单元打包:对于大小适中的NAL单元,可以直接将其放入一个RTP包中,去除NAL单元起始码(Start Code),将剩余数据作为RTP的有效载荷。
FU-A分片:当NAL单元过大时,可以采用Fragmentation Unit (FU)分片机制(比如:H.264/AVC中定义的FU-A模式)。这种机制将一个大的NAL单元切割成若干个较小的片段,每个片段称为一个FU。每个FU封装在一个单独的RTP包内,且包含分片相关的标识信息。更详细的信息,我们会在后续的专栏文章中进行介绍。
组包则是指在接收端或客户端,将属于同一原始编码单元的所有分包重新组合成完整编码单元的过程。组包是重组数据包过程中的一个子步骤,专门针对那些经过分包处理的RTP数据。客户端需要根据分包标识信息,将属于同一编码单元的分包收集在一起,并按照正确的顺序拼接起来,最终还原成完整的编码单元,供解码器进行解码。
组包主要包含如下的4个步骤。
1、接收端(客户端或解码器)接收到包含分片数据的RTP包后,依据RTP包头的序列号、时间戳以及FU Header中的分片信息进行重组。
2、根据RTP包的序列号,确保分片按照发送顺序进行重组。如果有乱序或丢包情况,可能需要通过重传请求或前向纠错等机制进行恢复。
3、对于FU-A分片,依据FU Header中的分片索引和结束标志,将属于同一NAL单元的所有片段按照正确的顺序拼接起来,恢复成完整的NAL单元。拼接时需要去除FU Header,还原NAL单元起始码。
4、组包过程中,接收端可能需要缓存未完成重组的分片数据,直到收到所有分片或达到重组超时。同时,还需要考虑时间戳信息,确保解码时的同步。