RTP封包
一、前言
RTP(Real-time Transport Protocol),即实时传输协议,RTP协议定义了在网络上传输音频和视频的标准数据包的格式。通常RTP和RTSP协议一起用于流媒体传输系统。RTP标准中包含了两个子协议,RTP和RTCP。当网络为UDP传输方式的时候RTP通常和RTCP协议配合使用,实现流媒体音视频质量的保证,通常RTP的端口为偶数,使用的RTCP的端口号为RTP的端口加1,端口号范围通常为1024 - 65535。本文将简单介绍音视频(H264/H265/AAC)码流如何封装为RTP包的,以及如何解封装。
二、RTP基本格式介绍
在RTSP的流媒体服务器中音视频的传输通过RTP封装将音视频帧封装为若干个RTP包;每个RTP包由RTP Header和音视频载荷(Payload)组成;RTP Header包含固定头和扩展头;如下图所示,蓝色块是RTP Herder部分,绿色块是音视频载荷,外围的绿色框表示分成的多个RTP包体。
1、RTP 固定头
RTP Header分为固定头和扩展头;RTP固定头是RTP包中必不可少的部分,其大小通常为12个字节;而 RTP扩展头对RTP包来说不是必需的,其大小通常可变;RTP固定头定义如下图所示,占用至少12个字节。
RTP Fixed Header Fields
V(version) :表示RTP版本号,当前版本为2。占用2bit大小;
P(padding) :表示填充标志,为1则在该RTP尾部填充一个或多个额外的8bit数据。占用1bit大小;
X(extension) :表示扩展头标志,为1则表示在该RTP Header后面跟随一个扩展头。占用1bit大小;
CC(CSRC count) :CSRC计数器,表示CSRC的个数。占用4个bit;
M(marker) :表示一个标识,视频与音频包中该值具有不同的含义。RTP包为视频包时候,若该值为1则表示视频一帧的结束(该包是当前帧的最后一个RTP包);RTP包为音频包的时候,若该值为1则表示音频会话的开始;占用1bit大小;
PT(payload type) :表示载荷类型;用于说明RTP包中的媒体文件类型;该值的定义在RFC3551RFC 3551: RTP Profile for Audio and Video Conferences with Minimal Control。由于H264、H265、AAC编码标准出现的较晚所以没有出现在RFC3551的详细定义中,通常使用96-127(动态序号),如H264的PT使用96,PT值通常在RTSP信令SDP中指定。占用7bit的大小;
timestamp : 时间戳,表示当前数据的采样时刻;同一个帧,不同RTP包的时间戳应该相同;当RTP包为视频时候时间戳的基准则为90KH(90000)。RTP包为音频则通常按照音频采样率为时间基准,时间基准通常在RTSP信令SDP中指定;当传输的音视频帧是按照固定周期生成(固定帧率),则时间戳采用累加的时间,而不是获取系统时间,如视频固定帧率25帧,则时间戳的增量为90000/25=3600,第一帧时间戳采用系统时间或者随机时间,第二帧时间戳则在第一帧的基础上加3600;占用32bit大小;
SSRC :同步信源标识符;指产生媒体流的信源;在同一个RTP会话中不会出现两个相同SSRC标识符的信源;占用32bit大小;
CSRC 列表 :提供信源标识符列表,一共可以包含最大16(0~15)个提供信源标识符;每个CSRC标识了包含在当前RTP报文有效载荷中的所有提供信源;例如音频(混音器)RTP混合了不同的同步信源RTP包后,经过混合后产生一个新的组合RTP包,并产生新的混合RTP包的SSRC,将原来所有的SSRC都作为CSRC传送给接收者,让接受者知道组成组合RTP包的所有SSRC;每个CSRC占用32bit大小。
2、RTP 扩展头
当RTP固定头中的X字段为1则需要在RTP固定头后添加RTP扩展头 ;RTP扩展头定义如下;至少包含32bit。
RTP Header Extension
defined by profile :表示扩展数据类型,通常自定义;占用16bit大小;
length :扩展数据长度,不包含defined by profile和length所占用大小。占用16bit大小;
header extension :扩展数据内容,可以为空,即大小最小可以为0。
3、RTP 载荷
在RTSP等实时流传输中常见的编码格式是H264/H265以及音频AAC;不同的编码格式在RTP包中的封装过程有所不同,比如H264和H265需要去掉码流中的启始码(0x000001),音频AAC需要去掉AAC的头部字段ADTS数据,在H264封装RTP包过程需要去掉1个字节的NAL Unit 头,在H265封装过程需要去掉2个字节的NAL Unit 头。关于起始码和NAL Unit 头的介绍可以参考下面的文章。<H264视频码流结构分析>、<H265视频码流结构分析>。下面的章节将详细介绍H264、H265、AAC码流封装RTP以及RTP解封装过程。
RTP协议资料链接:RFC 3550: RTP: A Transport Protocol for Real-Time Applications
三、RTP封装H264
RTP封装H264的载荷结构由很多种,如单一包、聚合包、分片包等;如下图所示。
【相关学习资料推荐,点击下方链接免费报名,先码住不迷路~】
音视频免费学习地址:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发
【免费分享】音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击788280672加群免费领取~
1、封装包类型
单一包
主要包含 single NAL unit packet;对应的Type为1~23;单一包的载荷定义如下图,前8个bit为H264中Nal Unit的第一字节(在下面切片包会详细讲述),接下来的就是H264的Nal Unit原始字节流数据(不包含启始码和Nal Unit)。
聚合包:
主要包含STAP-A,STAP-B,MTAP16,MTAP24;对应的Type为24~27;聚合包的定义如下图所示,前8个bit为H264中Nal Type( 应该是不一致的,这里的Type应该是24-27),接下来的就是一个或者多个聚合单元数据;STAP-A,STAP-B,MTAP16,MTAP24个字的聚合单元定义有所不同。
切片包 FU-A
切片包类型包含FU-A、FU-B;对应的Type分别为28、29。通常H264的一帧里面包含一个或者多个Nal Unit单元(如SPS、PPS、ISlice、PSlcie),FU-A、FU-B可以将不同的Nal Unit单独打包发送。FU-A和FU-B载荷的定义有所差异。FU-A的定义如下图。FU-A的载荷主要有4部分组成:分片单元标识(FU indicator)、分片单元头(FU header)和分片载荷数据(FU payload)、RTP填充数据。
分片单元标识(FU indicator)
主要包含3个字段:F、NRI、Type;如下图所示,左侧是FU indicator的定义,右侧红色框内是H264 Nal Unit 的第一个字节的定义,两者的含义一样。F占1bit值为0;NRI表示当前NAL单元的优先级,占用2bit,取值0-3,数值越大优先级越高若当前包丢掉之后解码越容易出现异常;Type 表示包的类型,类型为FU-A时候值为28。
分片单元头(FU header) : 包含4个字段,S、E、R、Type;定义如下图所示。
S:为1表示分片的开始,即表示一个编码帧的分片第一个包;占用1bit。
E:为表示分片的结束,即表示一个编码帧分片的最后一个包;占用1bit;在H264码流中ISlice或者PSlice通常比较大,会大于一个MTU(网络最大传输单元1500字节)此时会将ISlice分成多个片,S为1表示当前的RTP包为ISlice/PSlice切的第一片数据;E为1表示当前的RTP包为ISlice/PSlice切的最后一片数据。
R:保留位,为0;占用1bit;
Type: 表示H264的Nal Unit Type。
分片载荷数据(FU payload): 去掉起始码、和Nal Unit 第一个字节之后的原始字节序列载荷。
切片包 FU-B
定义如下图所示;分为5个部分:分片单元标识(FU indicator)、分片单元头(FU header)、DON(Decoding Order Number)和分片载荷数据(FU payload)、RTP填充数据。与FU-A相比较FU-B多了一个DON( 解码顺序号);DON 用来 指示 NAL 单元的解码顺序,它允许 H264 NAL 单元的传输顺序和 NAL 单元的解码顺序不同 。在切片包 FU-B的类型包中FU indicator的Type 值固定为29。
RTP打包H254说明资料:RFC 6184 - RTP Payload Format for H.264 Video
四、RTP封装H265
RTP封装H265的载荷结构也有很多种:单一包(Single NAL unit packet);聚合包(Aggregation Packet);分片包(Fragmentation Unit)等。每一种封包类型中都有PayloadHdr(载荷头);PayloadHdr和H265视频帧的Nal Uint头的定义对比如下图; 左侧是PayloadHdr右侧是H265视频帧的Nal Uint头;F字段和forbidden_zero_bit一样都是1bit大小,正常情况值都为0;LayerId和nuh_layer_id含义一样,通常值也是0;TID和nuh_temporal_id_plus1表示当前Nal所在时域层的标识号+1;PayloadHdr中的Type值和封装类型有关,封装类型不同取值不同;当封装类型为单一包的时候Type取值和H265中的nal_unit_type的取值是相同,其他封装类型取值不同(具体看下文截图所示)。
1、封装包类型
单一包(Single NAL unit packet)
单一包是将一个Nal Unit封装到一个RTP包中,通常较小的视频帧可以采用这种方式。单一包的结构定义如下图,主要有4部分:载荷头(PayloadHdr),DONL(Decoding Order Number),Nal Unit 载荷数据,填充数据。
聚合包(Aggregation Packet)
通常是将多个很小的Nal Unit封装到一个RTP包中,来降低RTP包的开销;在聚合包中载荷头(PayloadHdr)中的Type 取值为48。聚合包的定义如下图所示,主要有载荷头(PayloadHdr)、两个或者多个聚合单元、填充数据。
分片包(Fragmentation Unit)
有些Nal Unit的大小比较大(大于MTU),如H265的ISlice/PSlice会将大的Nal Unit分成多个RTP包,分开封装发送。分片包主要包含5个部分:PayloadHdr(载荷头)、分片单元头(FU header)、
DONL(解码顺序)、载荷、填充字段。PayloadHdr(载荷头)中的Type取值为49。
分片包分片单元头(FU header) 的定义如下图所示,有3个字段:S,E、FuType。
S: 为1的时候表示当前RTP包是Nal Unit中分片的第一个片。占用1bit;
E: 为1的时候表示当前RTP包是Nal Unit中分片的最后一个片。占用1bit。通常较大的Nal Unit在封装RTP包的时候会切成多个片,每个片单独的封装为RTP;根据S和E的值来区分当前分片的位置。
FuType: 表示Nal Unit的类型、和h265中的nal_unit_type取值相同,占用用6个bit。
RTP打包H265说明资料:RFC 7798 - RTP Payload Format for High Efficiency Video Coding (HEVC)
五、RTP封装AAC
RTP封装AAC的协议采用MPEG-4格式的封装协议,RTP在封装AAC包通常和RTSP信令SDP的AAC音频信息的填充有一定关系,接下来会详细介绍。
通常根据AAC码率大小可以分为Low Bit-rate AAC以及High Bit-rate AAC模式。在Low Bit-rate下规定AAC的一帧大小最大不超过63字节。在Low Bit-rate AAC模式下其对应的SDP(示例)信息如下所示;SDP中的mode=AAC-lbr,表示RTP封包的AAC采用Low Bit-rate AAC的模式;sizeLength则表示AAC编码帧长这一参数占用的bit数,sizeLength=6则表示AAC帧长这一参数中占6bit所以编码帧长取值最大是63(取值范围0-63),即AAC编码帧长最大63字节。
m=audio 49230 RTP/AVP 96
a=rtpmap:96 mpeg4-generic/22050/1
a=fmtp:96 streamtype=5; profile-level-id=14; mode=AAC-lbr; config=
1388; sizeLength=6; indexLength=2; indexDeltaLength=2;
constantDuration=1024; maxDisplacement=5
High Bit-rate AAC下规定一帧大小最大不超过8191字节。在High Bit-rate AAC其对应的SDP(示例)信息如下;SDP中mode=AAC-hbr,表示RTP封包的AAC采用High Bit-rate AAC的模式;sizeLength则表示AAC编码帧长这一参数占用的bit数,sizeLength=13则表示AAC编码帧长这一参数中占13bit所以取值最大是8191(取值范围0-8191),即AAC帧长最大8191字节。
m=audio 49230 RTP/AVP 96
a=rtpmap:96 mpeg4-generic/48000/6
a=fmtp:96 streamtype=5; profile-level-id=16; mode=AAC-hbr;
config=11B0; sizeLength=13; indexLength=3;
indexDeltaLength=3; constantDuration=1024
AACRTP打包AAC(MPEG-4)说明资料:RFC 3640 - RTP Payload Format for Transport of MPEG-4 Elementary Streams
1、封包结构
RTP封装AAC的数据结构定义如下图;主要包含4个部分:RTP Header(前面介绍过这里就不介绍了)、AU Header、Auxiliary Section(附件信息、基本用不到)、Access Unit Data Section。
AU Header Section:主要定义了 Access Unit Data Section的信息。AU Header 的定义如下图所示;主要包含了AU Header的长度信息以及若干个AU Header。
AU-headers-length表示的是AU Header所占用的bit数,如AU-headers-length为16表示AU Header会占用2个字节的存储空间。
AU-header的定义如下图所示。AU-header的定义字段很多,但是常用的就是AU-size、AU-Index/AU-Index-delta;其他参数可以不需要。
AU-size:表示的是AAC编码帧的长度;其占用的bit数和SDP中isizeLength这个参数一致;如如在High Bit-rate AAC模式下isizeLength=13表示AU-size占用13个bit。
AU-Index/AU-Index-delta:表示AUData数据的索引参数,一个RTP中包含多个AAC的数据片时候才有意义,当一个RTP包只有一帧AAC数据载荷的时候AU-Index/AU-Index-delta的值为0。AU-Index/AU-Index-delta占用bit数和SDP中indexLength/indexDeltaLength这两个参数一致;如在High Bit-rate AAC模式下indexLength=3;indexDeltaLength=3分别表示AU-Index占用3个bit,iAU-Index-delta占用3个bit。如下代码是RTP封装AAC的代码示例。
nt rtp_pakc_aac(unsigned char *ptr,int bytes,unsigned char *rtpPkt)
{
if (0xFF == ptr[0] && 0xF0 == (ptr[1] & 0xF0) && bytes > 7)
{
// skip ADTS header
assert(bytes == (((ptr[3] & 0x03) << 11) | (ptr[4] << 3) | ((ptr[5] >> 5) & 0x07)));
ptr += 7;
bytes -= 7;
}
int rtpHeadLen = rtp_pack_header(rtpPkt);//RTP Header
unsigned char * header = rtpPkt + rtpHeadLen;
// 3.3.6. High Bit-rate AAC
// SDP fmtp: mode=AAC-hbr;sizeLength=13;indexLength=3;indexDeltaLength=3;
header[0] = 0;
header[1] = 16; // 16-bits AU headers-length
header[2] = (uint8_t)(bytes >> 5);
header[3] = (uint8_t)(bytes & 0x1f) << 3;//13bit AU-size、3bit AU-Index/AU-Index-delta
unsigned char * pPayload = rtpPkt + rtpHeadLen + 4;
memcpy(pPayload, ptr,bytes);
return bytes + rtpHeadLen + 4;