RTP被定义为传输音频、视频、模拟数据等实时数据的传输协议,与传统的注重的高可靠的数据传输的传输层协议相比,它更加侧重的数据传输的实时性,此协议提供的服务包括数据顺序号、时间标记、传输控制等。
RTP一般与传输控制协议RTCP一块工作,RTP只负责实时数据的传输,RTCP负责对RTP的通讯和会话进行带外管理(如流量控制、拥塞控制、会话源管理等)。
RTP使用一个偶数端口号,而相应RTCP流使用下一个(奇数,递增)端口号。
先来看一下rtp和rtcp网络结构图:
RTP的数据包由RTP Header + RTP Playload组成,其中RTP固定头如下图所示,字段如下:
- V:当前的协议的版本号是2,其中0和1已经在草案规范中被占用,这里基本就是固定值了;
- P:填充标记,包的末尾包含了一个或者多个填充字节,其中填充字节的第一字节包括了后面填充字节的长度,该长度字段包含自己,主要是为了一些对齐处理;
- X位,如果为1则说明有扩展头,一般默认为0,很少有场景会用到;
- CC位:是为了计算后面有多少个CSRC,四位说明则最大支持15个CSRC,一般默认为0。
- M位(Marker):特别对于视频而言就是一帧的结束,视频帧比较大,需要通过多个NALU(网络抽象层单元(Network Abstract Layer Unit))来传输,当看到M位为1时就认为是这个I帧的结束,由于音频帧比较小,一个RTP包就是一个音频帧,所以该位直接置1。当该值为1时,表示该数据包是一帧数据的最后一个数据包。
- Sequence number(序列号):16位,用于标识发送者发送的RTP报文序列号,每发送一个RTP包,则这里就增加1,当达到最大值后,则重新从0开始。刚才说了一般RTP协议是承载协议是UDP,UDP是不可靠传输协议。那我们如何保证接收端收的数据是正确的呢,就是通过这个字段进行重新排序,所以接收端一般收到RTP数据第一件事就是排序。
特别注意两点: a. 这个序列号的初始值可以为0但是也可以为其它随机值,只要符合+1就行; b. 发送端的音频和视频都是通过RTP传输的,但是他们是分别计数的,所用的序列号 是不同的。
- timestamp(时间戳):占32位四字节,这个单位要注意是采样率倒数,不是真实的时间,一般要根据采样率进行换算。这里反应的RTP报文第一个八位组的采样时刻,目的是为了接收端计算延迟、抖动和音视频同步。需要说明的是,一个视频帧的时间戳是相同的,但是一个视频帧数据量很大可能需要多个RTP包传输,这样就存在多个RTP包时间戳相同的情况,音频帧数据小,不存在音频帧跨RTP的情况,所以不存在这个问题。
- SSRC(同步源标识符):占32位四字节,用于标识同步信号源,这个值只要保证在一路音视频会话里面值不相同即可。该标识符是随机选取的 RFC1889推荐了MD5随机算法。该值的作用就是在会话中标识RTP负载流的身份,给一个唯一标记值。
- CSRC list(贡献源列表):同样是32位,四字节。一个RTP头最多可以含有0-15个,如果是1对1的流媒体传输,这个字段就不用处理,直接忽略该字段。但是混流和混音时,则需要把各方的RTP同步信号源列出来,这样接收端就能正确指出交谈双方的身份。用来标志对一个RTP混合器产生的新包有贡献的所有RTP包的源,由混合器将这些有贡献的SSRC标识符插入表中。
再看一下RTP完整的报文格式:
- 一部分是RTP Heaeder,RTP
Header占用最少12个字节(只包含固定头12个字节),最多72个字节(包含固定头12个字节+CSRC列表15*4个字节); -
一部分是RTP Payload,用来封装实际的数据负载,如封装h264编码的视频数据。
再来看看RTCP协议,RTCP协议将控制包周期发送给所有连接者,主要功能如下:
- 主要提供数据发布的质量反馈,RTCP是作为RTP传输协议的一部分,与其他传输协议的流和阻塞控制有关,反馈功能由RTCP发送者和接收者报告执行。
RTCP协议包括五种类型,这里只讨论SR和RR包。
SR包(SenderReport):发送者报告,当前活动发送者发送、接收统计;所谓发送端是指发出RTP数据报的应用程序或者终端,发送端同时也可以是接收端。由三部分组成,也可能还有第四个特定设置扩展部分。
第一部分为头:8个字节,如下:
- V:2位,标识RTP版本,需要注意的是RTP数据包中的版本号与RTCP数据包的中的版本号是一致的。
- 填充P:1位,如设置于此位,RTCP包结尾包含一些附加填充八位组,它们不属于控制信息。最后一个字节填充表示应忽略多少个填充。有些加密算法需要填充,块大小固定。在组合RTCP包内,填充仅在最后一个包中需要,因为组合包加密成一个整体。
- 接收报告计数(RC):5位,包含在包内的接收报告块数目,该字段0值为有效的,但没有实际意义。
- 包类型(PT):8位,包含常数200标识此包为RTCP的SR包。
- 长度length:16位,其中存放的是该SR包以32比特为单位的总长度减一(减去一个header)。使用4个字节为1组,长度共有几个4个字节的组,然后用该长度减去1,即为RTCP包中的长度。举个栗子:假设RTCP数据包的长度为32个字节,32/4=8,总共有8组4个字节,8-1=7,此时RTCP数据包中length的值为7。
- SSRC:32位,同步源标识,与对应rtp包中的ssrc一样。
第二部分为发送者信息:20个字节,如下:
- NTP(Network time protocol)时标:64位,表示报告发送时的时钟时间,这样它就可用于合并从其他发送者到那些接收者的接收报告返回的时标。SR包发送时的绝对时间值,NTP的作用是同步不同的RTP媒体流。
Ntp把当前时间(自1970.1.1以来的秒数)分为整数部分N和小数部分X Ntp高位=整数部分N + 2208988800UL (其中2208988800UL表示自1900.1.1到1970.1.1的秒数,70年) Ntp低位=小数部分 X 4294967296 (其中4294967296为2^32,相当于左移32位)
- RTP时标:32位,与上述NTP时标同一时间有关,与RTP数据包中的RTP时间戳具有相同的单位和随机初始值。
以sample为单位,如音频8000HZ,一个packet为20ms,则两个rtp时间戳的间隔为160. 从rtp时间戳换算成ms的公式为: rtp时间戳 x 1000/samplerate。
- 发送者包计数:32位,自开始发送起,直到SR包产生时刻,发送者发送RTP数据包总数。如改变SSRC标识符,此计数重置。
- 发送者八位组计数:32位,发送者在RTP数据包中发送的载荷八位组总数(不包括头部和填充)。从发送开始,直到产生SR包,如发送者改变SSRC标识,重置此计数。这部分可用于估计载荷数据平均速率。
第三部分包含接收报告快,大小不固定。每个接收报告块传送单个同步源接收RTP包的统计。发生冲突,当源改变SSRC标识时,接收者并不继续统计。这些统计包括:
- SSRC_n(源标识):32位,接收报告块中信息所属源的SSRC标识。
- 丢失部分:8位,前一个SR或RR包发送以来所丢失的源SSRC_n的RTP数据包中一部分,定义成所丢失包的数目。
- 丢失包累积数:24位,自接收以来所丢失的源SSRC_n的RTP数据包总数,定义成小于实际所接收包的数量,该数量包括迟到或复制的包。因此,迟到的包不计为丢失,如有复制,此数量可能为负数。
- 收到已扩展的最高系列号:32位,低16位包含从SSRC_n源RTP数据包中收到的最高系列号,最重要的16位以系列号循环相应计数扩展系列号。如开始时间明显不同,同一连接内不同接收者将对系列号产生不同扩展。
- 间隔抖动:32位,RTP数据包接受时间的统计方差估计,以时标为单位,是一个无符号整数。
- 上次SR时标(LSR):32位,取最近从SSRC_n收到的SR包中的NTP时间戳的中间32比特,如还没有收到SR,此段设为零。
- 上次SR以来的延时(DLSR):32位,延迟以1/65536秒为单位,上次从SSRC_n收到SR包到发送本报告的延时,如还没有收到SR,此段设为零。
RR包(ReceiverReport):接收者报告,非活动发送者接收统计,仅作为接收者(只接收rtp包)发送出去的包;所谓接收者是指仅接收但不发送RTP数据报的应用程序或者终端。包类型包含常数201,并删除发送者信息的20个字节。
各个字段解释如下:
- SSRC_n(32bit) : 发送端的信源标识符,与发送端的SSRC一致。
- 丢包数8(8bit) :前一个SR或RR包发送后,到当前的SR包或RR包的间隔内,来自源(共享源SSRC标识)发送的数据包的丢失个数。
丢包率,它是定义一个rr发送间隔中rtp报文的丢失率。 它是一个8bits的数据段,计算方法为,loss fraction=lost rate x 256. 举例,丢包率为25%,该字段为25%*256=64
- 累积丢包数(24bit) : 自开始接受源(共享源SSRC标识)发送的数据开始,累积丢失的数据包的个数。
理论计算方式, packet lost = 期待得到报文数量 - 实际收到报文的数量 实际计算方式, packet lost = 期待收到最新sequence - 第一次收到报文的sequence #需要注意: 1. sequence为16位,容易翻转 可以通过计算extend_sequence来区分翻转的sequence。 2. packet lost可能为负数 因为有rtp报文的重传,如果重传次数多,可能造成负数 3. 该值的测量周期是整个会话 测量周期是整个会话,而不是RR的发送间隔
- 扩展包序号(32bit) :低16位为当前接收到的来自源的(共享源SSRC标识)数据包的最大序列号;高16位表示RT包序列号的循环计数!我们都知道RTP数据包中,表示序列号的长度为2个字节,即最大的RTP序列号为65536(2的16次方),如果序列号超了65536,假设为65537,这个时候RTCP在扩展包序号中对其说明,如果没有超过65536,则高16位为0,如果超过65536,则为1,如果再来一圈,则为3。做个不恰当的比喻,我们跑步一圈为65536,高16位表示我们当前正在跑第几圈,从0开始计数。
计算方法,extended_seq_num = seq_num + (65536 * wrap_around_count) 例如:100 + 65536*2 (即2左移到高16位) 其中: seq_num为当前接收到的来自源的数据包的最大序列号; wrap_around_count为sequence翻转的次数。
- 间隔抖动(32bit) :RTP数据包间隔时间的统计估计,以时间戳为单位,用无符号整数表示。
这里的延时抖动指的是rtp报文发送方的网络传输时间的变量估计值。 计算单位 是基于时间戳的基础单位,也是32位无符号整形。 因为rtp的发送和接收方没有时 间同步系统,所以不大可能能准确的测量网络传送时间。 #相反,传输时间= |rtp的时间戳-rtp接受者本地时间| 因为没有发送和接收方的时间同步机制,所以这里关心的不是传输时间,是两次接收到rtp报文传输时间的对比,计算公式如下: D(i,j)=(Rj-Ri)-(Sj-Si)=(Rj-Sj)-(Ri-Si) R是接收到的时间戳,Rj是packet j接收到的时间戳,Ri是packet i接收到的时间戳, S是rtp报文的时间戳,Sj是packet j的rtp时间戳,Si是packet i的rtp时间戳 而Jitter的计算公式如下: # J(i) = J(i-1) + (|D(i-1,i)| - J(i-1))/16
- LSR(32bit) :last SR timestamp,表示上一个SR数据包的NTP时间戳,由于NTP时间戳为64bit,LSR为32bit,LSR取上一个SR的NTP时间戳的中间32位:如上一个SR数据包的NTP时间戳为0x 00 01 7d 6e 3b 64 5a 1c,则LSR为0x 7d 6e 3b 64。
- DLSR(32bit) :发送当前RR包的时间与上一个SR之间的时间间隔,以1/65536为单位,如,本次RR包与上一次SR的时间间隔为264ms,则本字段的值为0.264*65536=17301.54。
最后通过LSR,DLSR可以计算RTT:
RTT: Round-Trip Time,发送者计算的发送来回时间。
发送者可以通过RR报文中的LSR和DLSR来计算RTT。
计算方法: 发送者用接收到RR报文的当前时间-RR报文的LSR,得到发送SR和接收到RR所花费的网络延时。
再进行: (接收到RR报文的当前时间-RR报文的LSR) - RR中的DLSR,也就是去除了在RTP接收者方本地的SR接收和RR发送的延时,这样就得到了RTT。
RTT = 接收到RR报文的当前时间-RR报文的LSR - RR中的DLSR
经验表明,如果RTT大于300ms,这样的质量网络通话无法完成。
只能通过降低比特率,降低打包延时或应用好的错误修复机制来完成。