协议汇总
- RTP:RFC1889,RFC3550
- rtp-parameters:https://www.ietf.org/assignments/rtp-parameters/rtp-parameters.xml
- rtp pt值定义:https://tools.ietf.org/pdf/rfc3551.pdf
- SRTP(安全):RFC3711
- h.264详细定义在ISO_IEC_14496-10-AVC-2003.pdf
- H.264的rtp payload定义在RFC6184
- transport-wide congestion control:https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
- RTCP feedback 信息定义在https://tools.ietf.org/html/rfc4585
RTP协议分析
RTP/RTCP协议诞生于1996年(RFC1889),在2004年有次大的改变(RFC3550),后来考虑到安全性变更为SRTP(RFC3711)。WebRTC用到SRTP,由于SRTP的对称秘钥需要走额外信道交换,所以WebRTC使用了DTLS,而DTLS依赖SDP的信息做校验,在P2P无法连接的场景需要用到TURN中继转发。RTP和RTCP是在一个协议里面描述的,RTCP是控制和反馈消息,比如如下知识点,都会用到RTCP
- BWE带宽估计、ARQ重传、FEC前向纠错
- FEC可以选择XOR算法或RSFEC算法
- 关于BWE和ARQ,有接收端估计的REMB,以及新的发送端估计的TransportCC
- 拥塞控制算法GCC(Google congestion control)(https://www.jianshu.com/p/0f7ee0e0b3be?spm=ata.13261165.0.0.19be4097vKwk13,https://www.jianshu.com/p/bb34995c549a?spm=ata.13261165.0.0.19be4097vKwk13)
RTP消息头
下面的的字段均是从协议RFC3550中截取的,更详细的介绍请参见RFC3550,下面仅把个人的理解阐述出来。
主要字段说明
P(padding)
1 比特,若填充比特被设置,则此包包含一到多个附加在末端的填充比特,填充比特不算作负载的一部分。填充的最后一个字节指明可以忽略多少个填充字节。填充可能用于某些具有固定长度的加密算法,或者用于在底层数据单元中传输多个RTP包。由于Opus的包大小差不太多,但又不完全一样,所以会出现很多大小差不多却又不一样的小包,这对于服务器GSO发送这些包会带来很大的困难,所以通过增加几个字节的padding,可以让多个包一起GSO处理掉。
X(扩展标识)
1比特,若设置为1,固定头后面会跟随一个头扩展,扩展头4个字节,其中长度字段表示的是扩展项的个数,而非长度
CC(CSRC的个数)
4个比特,CSRC计数包含了跟在固定头后面的CSRC的数目
M(mark)
1个比特,标识的解释有具体协议规定。它用来允许在比特流中标识重要的事件,如帧边界等。
- Opus包,一般每个包的marker都设置为true
- Single NALU packet,也就是Payload就是264的NALU,帧的最后一个NALU的mark设置为true
- STAP-A包,用来打包SPS、PPS,一般都是IDR帧前会插入这个包,所以它属于IDR的一部分NALU,按照前面规则不是最后一个NALU设置marker为false,STAP-A设置为false。
- FU-A,最后一个包设置为true,其它设置为false
timestamp
rtp时间戳的定义和一般封装格式(比如TS、mp4)不一致,像TS、MP4音视频时间戳都是定义在一个时间轴上的,分别通过音视频时间戳即可保证音视频同步。
- rtp时间戳,音视频是单独计算的,不在一个时间轴上
- 时间戳单位:是采样率的倒数,比如视频采样率是90000,则单位是1/90000,音频是8000,则单位是1/8000。
- 如何保证音视频同步:在rtcp协议上会分别将音视频的时间戳和NTP时间一起传递,接收端处理逻辑则是依据NTP时间作为统一参照时间轴,做音视频同步。
SSRC和CSRC
在rtp协议上会有个混频器和转换器的东西,其作用是将多路rtp流混频成单一rtp流(比如重新转码,降低码率)这样从混频器出来的单一rtp流其SSRC是由混频器重新定义并且会话内唯一,但是为了标识混合后的音频隶属于哪路原始音频,则会在CSRC中标识多个原始SSRC。
注意,音频和视频的SSRC是不一样的。
RTP扩展头
defined by profile
具体解释后面header extension的格式,有one byte,和two byte,header extension是标准的TLV格式,one byte则标识T+L为1个字节,其中T和L各为4个比特。two byte含义类似,T+L为2个字节,其中T和L各位1个字节。
length
length的定义比较特殊,标识的是4字节为单位的个数,如果定义为2,则标识header extension总长度为2*4=8个字节。不够4字节的需要加0填充。
RTP PayLoad
负载类型
首先要说清楚一点,在rtp payload中可以存放多种媒体类型,具体类型由rtp header中的pt值决定,具体定义见RFC3551和https://www.ietf.org/assignments/rtp-parameters/rtp-parameters.xml定义。
以视频为例,RFC3551部分定义,>=96的动态自定义的部分,需要用户在SDP协议中定义。比如常见的H264 pt值定义为96,也有定义为97的。
负载存放形式
本文仅以最常见的H264为例具体说明,放入到RTP Payload的数据是H264的NALU包,但和H264标准定义有些差异。对于一个原始的 H.264 NALU 单元常由 [Start Code] [NALU Header][NALU Payload] 三部分组成, 其中 StartCode 用于标示这是一个NALU 单元的开始, 必须是 "00 00 00 01" 或 "00 0001", NALU 头仅一个字节, 其后都是NALU 单元内容.打包时去除 "00 0001" 或 "00 00 00 01" 的开始码, 把其他数据封包的 RTP 包即可。
宏观上看,打包的形式有3中模式,单一包模式、聚合包模式、分段包模式。同时又分有交织和不交织。
交织代表发包顺序不是解码顺序,这种方式比较适合对延时没有要求的场景,当然此种方式还需要为每个NALU包携带DON(docodeing order number)
NALU
h264详细定义在ISO_IEC_14496-10-AVC-2003.pdf。H.264的rtp payload定义在RFC6184,在RTP Payload部分的第一个字节是如下定义的NAL HDR,其中最重要的就是低5个比特,标识当前rtp包的NALU包的类型,也就是前面说的单一包模式、聚合模式、分段模式。
F: 1个比特. forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0.禁止位,0表示正常,1表示错误,一般都是0;
NRI: 2个比特. nal_ref_idc. 取00~11,表示这个NALU的重要性,如00的NALU解码器可以丢弃它而不影响图像的回放.
Type: 5个比特,重要 nal_unit_type. 这个NALU单元的类型.简述如下:
NALU TYPE介绍
下面的截图分别取置ISO_IEC_14496-10-AVC-2003.pdf和RFC6184,后面RFC6184是rtp针对264做的扩展说明
RTP包中的nalu type介绍
- 1-23,这个是h264定义的nalu,里面全是媒体数据包
- 24,stap-a,single-time-aggregation packet,多个nalu的聚合包,他们时间戳相同。stap-a是没有DON(decoding order number)
- 25、stap-b,和stap-a一样包含多个nalu,他们时间戳相同,不同点在于stap-b包含DON
- 26、27,MTAP,也是存放聚合包,都需要有DON,但各个NALU时间戳不同。这种包由于时间戳不同,会在每个NALU包前面包含ts offset,而这个offset有16bit和24bit之分。
- 28、fu-a,fragmentation units,分片的包,比如一个idr帧可能有6000字节,作为一个udp包发那肯定超过mtu,一般mtu1500左右,有时候为了兼容mtu可能设置为500左右,fu-a也是不包含don的
- 29、fu-b,和fu-a一样是分片的nalu,包含don
在RTC应用场景中,由于对延时要求较高,一般不使用交织模式的打包方式,所以常用的分包模式是NAL unit、STAP-A、FU-A。像sps、pps这两个nalu由于很小,会用聚合的方式stap-a类型的rtp包来传输;针对idr帧,由于很大,会采用分片fu-a的方式来传输,同时在fu-a的起始rtp包和末尾rtp包均会带上标识。
DON
简单说下DON,全称是decoding order number,只用在交织包里面,非交织包发送顺序和解码顺序是一致的。
打包示例
协议RFC6184的打包示例如下,具体最好直接看协议会比较清楚一些。
Multiple Slices
一个帧可能是一个NALU,也可能有多个NALU,多个NALU叫多个slice(multiple slices)。打包时由于每个NALU的长度都没有超过最大长度,所以每个NALU都打成一个RTP包。这种打包方式有个问题,由于每个NALU都是一个包,如果丢包了之后,比较难处理。另外,优化UDP性能时,若使用GSO发送UDP包,这些包大小都不同,会让GSO难以处理。我们可以把这些NALU作为一个NALU打包,使用AnnexB的方式连接这些NALU,这样将小包合并为大包比较方便服务器处理。
nalu0 + 00 00 00 01 + nalu1 + 00 00 00 01 + nalu2
AnnexB一共有两种起始码:3字节的0x000001和4字节的0x00000001。3字节的0x000001只有一种场合下使用,就是一个完整的帧被编为多个slice的时候,包含这些slice的nalu使用3字节起始码。其余场合都是4字节的。
RTCP协议分析
RTCP和RTP混合一个通道传输
RTCP和RTP定义在一个RFC中,是反馈消息。可以完成预测和校正,便于WebRTC做到低延时。
RTCP和RTP可以bundle一个通道上传输,RFC协议考虑了如何区分它们,参考RFC1889.下面是RTP和RTCP的消息协议头,注意看RTP的M+PT和RTCP的PT均在第二字节。
因此,RTP和RTCP的关键区别,在于第二个字节也就是PT,RTCP的PT的定义,必须要能够避免和RTP的PT以及M被设置时冲突,参考(https://tools.ietf.org/html/rfc5761?spm=ata.13261165.0.0.19be4097vKwk13#section-4)中有详细定义。
RTP媒体的PT:[96,127]或[224,255](如果M标识位设置为1)
RTCP可用区间为:[0,95]或[128,223](如果M标识位设置为1),目前已经分配的区间有:
- [64-65]([192,193]),FIR和NACK
- [72-76]([200,204]),SR、RR、SDES、BYE
- [77-78]([205,206]),RTPFB和PSFB
- 79(207),XR
- 80(208),RSI
所以未来RTCP的分片区间是[209,223],[194,199],所以判断是否是RTCP则判断PT值为[0,95]([128,223]),目前RTCP的PT值都是从64(192)开始,所以精确判断是在[64,95](192,223)。
主要协议
rtcp协议重要的两个协议SR和RR,一个是发送端信息统计和是接收端信息统计
SR
SR协议定义中有部分是RR协议定义的,刨除RR部分的最重要的NTP和RTP timestamp,这个部分是用于音视频同步用的。
RR
fraction lost:丢包率
packets lost:丢包数
扩展包序号(32bit):低16位为当前接收到的来自源的(用源SSRC标识)数据包的最大序列号;高16位表示RT包序列号的循环计数!我们都知道RTP数据包中,表示序列号的长度为2个字节,即最大的RTP序列号为65536,如果序列号超了65536,假设为65537,这个时候RTCP在扩展包序号中对其说明,如果没有超过65536,则高16位为0,如果超过65536,则为1,如果再来一圈,则为2。
间隔抖动(32bit):到达时间差值-发送时间插值
在WebRTC场景下,有用的部分主要是SR的NTP和RTP timestamp,用于音视频同步。RR中的丢包率和丢包数,用于发送端依据丢包率进行带宽估计。其他字段暂时未发现有用