RTP协议封装H264/H265/AAC

<RTSP实时音视频传输介绍>

目录

一、前言

二、RTP基本格式介绍

1、RTP 固定头

2、RTP 扩展头       

3、RTP 载荷

三、RTP封装H264

1、封装包类型

四、RTP封装H265

1、封装包类型

五、RTP封装AAC

1、封包结构

《RTP封装H264/H265/AAC代码实现》链接:

https://edu.csdn.net/learn/38258/606139?spm=1003.2001.3001.4157

《RTP解封装H264/H265/AAC代码实现》链接:

https://edu.csdn.net/learn/38258/606143?spm=1003.2001.3001.4157

一、前言

       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包体。

c77220bc113644a483b8320c910e972f.png

1、RTP 固定头

        RTP Header分为固定头和扩展头;RTP固定头是RTP包中必不可少的部分,其大小通常为12个字节;而 RTP扩展头对RTP包来说不是必需的,其大小通常可变;RTP固定头定义如下图所示,占用至少12个字节。

f06dd51b9f5e46498f91ddc72128f09b.png

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的大小;

sequence number : 表示当前包的序号,每次封装(发送)一个RTP包该值加一;可以被接收方用来检测数据包丢失和恢复数据包顺序;占用16bit大小;

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。

007e5405c48b4160acdc4e0741432dfd.png

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的载荷结构由很多种,如单一包、聚合包、分片包等;如下图所示。

46346d6922274e9a9555567e5bf63039.png

1、封装包类型

单一包

        主要包含 single NAL unit packet;对应的Type为1~23;单一包的载荷定义如下图,前8个bit为H264中Nal Unit的第一字节(在下面切片包会详细讲述),接下来的就是H264的Nal Unit原始字节流数据(不包含启始码和Nal Unit)。

ece0f57981ec4d09b3b2a0687f2e649a.png

聚合包:

       主要包含STAP-A,STAP-B,MTAP16,MTAP24;对应的Type为24~27;聚合包的定义如下图所示,前8个bit为H264中Nal Type(在下面切片包会详细讲述),接下来的就是一个或者多个聚合单元数据;STAP-A,STAP-B,MTAP16,MTAP24个字的聚合单元定义有所不同。

a928c8a12d1f446abd4a6efd71edf4ad.png

切片包 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填充数据。

2389f57a4f8b4656b15d1284a724144a.png

        分片单元标识(FU indicator)

        主要包含3个字段:F、NRI、Type;如下图所示,左侧是FU indicator的定义,右侧红色框内是H264 Nal Unit 的第一个字节的定义,两者的含义一样。F占1bit值为0;NRI表示当前NAL单元的优先级,占用2bit,取值0-3,数值越大优先级越高若当前包丢掉之后解码越容易出现异常;Type 表示包的类型,类型为FU-A时候值为28。

3a6cb5576e7a47af8bb6e1ee51265d3f.png

        分片单元头(FU header) :   包含4个字段,S、E、R、Type;定义如下图所示。d8c2d061fe5d45cbb3205bd2645f6e26.png

       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。

59d75178aa9f43c294ba653925dd3e13.png

      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的取值是相同,其他封装类型取值不同(具体看下文截图所示)。

8bd8621153a7456196fe69189d3db781.png

1、封装包类型

    单一包(Single NAL unit packet)

    单一包是将一个Nal Unit封装到一个RTP包中,通常较小的视频帧可以采用这种方式。单一包的结构定义如下图,主要有4部分:载荷头(PayloadHdr),DONL(Decoding Order Number),Nal Unit 载荷数据,填充数据。

7cb957279ef54a9bab52e57c7b4c8713.png

    聚合包(Aggregation Packet)

       通常是将多个很小的Nal Unit封装到一个RTP包中,来降低RTP包的开销;在聚合包中载荷头(PayloadHdr)中的Type 取值为48。聚合包的定义如下图所示,主要有载荷头(PayloadHdr)、两个或者多个聚合单元、填充数据。

fddf487c4d6b46b8b1142fac3fb9196f.png

    分片包(Fragmentation Unit)

        有些Nal Unit的大小比较大(大于MTU),如H265的ISlice/PSlice会将大的Nal Unit分成多个RTP包,分开封装发送。分片包主要包含5个部分:PayloadHdr(载荷头)、分片单元头(FU header)、

DONL(解码顺序)、载荷、填充字段。PayloadHdr(载荷头)中的Type取值为49。

14e6b5641cd54ce2810a9b71bf4162b9.png

     分片包分片单元头(FU header) 的定义如下图所示,有3个字段:S,E、FuType。

 2921f06ac2984850837526d754affa4a.png

     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。

9792e35453ec451d9b7e8792751be33a.png

     AU Header Section:主要定义了 Access Unit Data Section的信息。AU Header 的定义如下图所示;主要包含了AU Header的长度信息以及若干个AU Header

5c0bd584bf114d3b81dbf0ed4fca33b5.png

      AU-headers-length表示的是AU Header所占用的bit数,如AU-headers-length为16表示AU Header会占用2个字节的存储空间。

       AU-header的定义如下图所示。AU-header的定义字段很多,但是常用的就是AU-size、AU-Index/AU-Index-delta;其他参数可以不需要。

2f64895748994b5396f1b7a8c7f61176.png

      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;
}

猜你喜欢

转载自blog.csdn.net/u010140427/article/details/127773028