有关RTMP和FLV格式详细介绍可查看如下文章:
https://www.cnblogs.com/ssyfj/p/14684500.html
前文所述,RTMP传输音视频消息时,其RTMP负载采用的时FLV的封装格式,这个说法有点不太准确,RTMP音视频消息负载并不是完整的flv封装,其只是采用了FLV tag body的封装格式,这里为什么不采用完整的flv格式封装呢? 这是因为RTMP header中携带的信息及RTMP推拉流交互中的消息的传递,已经能够完全覆盖FLV头字段及tag头字段所携带的信息,本周最小数据的原则,这里只保留了flv的 video/audio tag body格式,有关Video tag和Audio tag封装格式,可查阅规范《video_file_format_spec_v10.pdf》,规范获取方式:
注公众号壹零仓,发送RTMP,获取规范文档
本文主要介绍RTMP发送H264和H265视频时,其RTMP视频消息的格式介绍及抓包分析。
从《流媒体之RTMP详解》这篇文章中我们详细介绍了RTMP推流和拉流的流程,这里就不做介绍,这里只介绍RTMP视频消息的格式,其他类型消息及交互过程可查阅此篇文章。
RTMP 视频消息与其他格式的消息一样,由消息头和消息体组成,根据前文介绍的消息分块传输的方式,拆分消息时,只把消息体拆分为多个chunk data,消息头融合进chunk header中,由chunk header + chunk data组成,很多文章直接把其称为RTMP Header和RTMP Body,wireshark中就这样标注。
chunk header中Chunk stream ID 是用来区分消息信道的,因为 RTMP 协议,所有的通信都是通过同一个 TCP 来完成的,因此所有类型的通信信道需要由 Chunk stream ID 来进行区分,从而判断当前收到的消息所属的信道类型,此是由用户定义的,Adobe 建议采用如下的分类:
其中obs推流、ffmpeg推流都有自己的定义方式,与这个略有不同,CSID再解析式可只作为一路流的通道,由Message Type来标识音视频,消息类型定义如下图所示:
这里可以看出视频消息的消息类型为9,当消息类型为9时,表示其消息体为video tag body(Video Data)封装格式的视频数据,有关Video Data的封装格式如下图所示(video_file_format_spec_v10 flv/f4v规范):
RTMP发送AVC sequence header,其Video data封包方式(设包为buf数组),第一个字节为buf[0]=0x17=0B00010111,其frametype为关键帧,所以高四位为1,编码类型为H264,所以低四位为7,其视频数据采用AVCVIDEOPACKET格式,AVCPacketType为AVC序列头,所以buf[1]=0,buf[2,3,4]={0,0,0},此时其Data的格式为AVCDecoderConfigurationRecord(就是AVC sequence header),AVC sequence header格式如下
版本号固定为1,因此buf[5]=1,后面三个字节表示编码规格、编码兼容性和编码等级,从SPS中即可获取,所以buf[6]=SPS[1],buf[7=SPS[2],buf[8]=SPS[3],lengthSizeMinusOne表示用多少字节来表示nalu size,一般默认为3,其值+1,表示nalu要使用4个字节来表示长度,buf[9]= 0xFC|0x3=0xFF,下一个字节表示SPS个数,H264一般SPS只有一个,因此buf[10]=E0|01=E1,接下来就是SPS size和SPS data,SPS size站2个字节,buf[11,12,13,14]=SPS size(大端模式),后面直接为SPS的数据,这里注意要去掉起始码之后的数据,假设去掉起始码之后SPS为24,则buf[11-12]=0x0018,buf[13-26]= SPS data,下一个字节为PPS个数,一般为1,buf[27]=1,之后为PPS Size和PPS Data,这里假设PPS Size=4,则buf[28-29]=0x04,buf[30-33]=PPS Data,至此RTMP传输的起始帧AVC sequence header组装完毕,此帧可每次随IDR帧重复发送
前文介绍了RTMP发送AVC序列帧方式,真正的视频帧是如何发送的呢?真正的视频帧发送很简单,如果为I帧,buf[0]=0x17,表示关键帧,H264编码;视频帧AVCPacketType类型为AVC Nalu,所以buf[1]=1,buf[2,3,4]={0,0,0},此时其Data的格式为AVCC Nalu封装方式,即是4字节nalu大小+nalu数据(去掉起始码),所以buf[5-8]=nalu size,buf[9-n]= nalu data,至此I帧的RTMP 负载就组合完了;P帧与I帧就第一个字节不同,P帧不是关键帧,因此frametype=2,buf[0]=0x27,其他组合方式一样,这里不做介绍。
RTMP协议中并没有介绍H265相关规范,可采用CDN联盟的HEVC扩展标准,将HEVC的VideoTagHeader定义为12,详见下图:
当HEVC编码时,CodecID=12,视频数据封装方式采用HEVC封装采用HVCC的封装格式,与AVCC类似,其无起始码,前面四个字节表示nalu的size大小,其客户端解码需要首先接收到HEVC的序列头,序列头格式如下:
格式中定义相关字段的值,可以直接从HEVC的SPS序列帧中解析得到,按照此格式组合成HEVC的序列头,其buf[0]=0x1C,表示关键帧,编码格式为HEVC,buf[1]=0表示HEVC的序列帧,buf[2-4]=0x000000,buf[5]=1,之后的编码信息赋值按照从H265的SPS中解析出的值赋值即可,其中numOfArrays表示后面数组的个数,一般为0x03,表示包含VPS/SPS/PPS,后面数组的封装方式示意如下(《ISO-14496-15 AVC file format》中有详细说明):
// The CodecPrivate syntax shall follow the
// syntax of HEVCDecoderConfigurationRecord
// defined in ISO/IEC 14496-15.
//
// The number zero (0) shall be written to
// the configurationVersion variable until
// official finalization of 14496-15, 3rd ed.
//
// After its finalization, this field and the
// following CodecPrivate structure shall
// follow the definition of the
// HEVCDecoderConfigurationRecord in 14496-15.
unsigned int(8) configurationVersion;
unsigned int(2) general_profile_space;
unsigned int(1) general_tier_flag;
unsigned int(5) general_profile_idc;
unsigned int(32) general_profile_compatibility_flags;
unsigned int(48) general_constraint_indicator_flags;
unsigned int(8) general_level_idc;
bit(4) reserved = ‘1111’b;
unsigned int(12) min_spatial_segmentation_idc;
bit(6) reserved = ‘111111’b;
unsigned int(2) parallelismType;
bit(6) reserved = ‘111111’b;
unsigned int(2) chromaFormat;
bit(5) reserved = ‘11111’b;
unsigned int(3) bitDepthLumaMinus8;
bit(5) reserved = ‘11111’b;
unsigned int(3) bitDepthChromaMinus8;
bit(16) avgFrameRate;
bit(2) constantFrameRate;
bit(3) numTemporalLayers;
bit(1) temporalIdNested;
unsigned int(2) lengthSizeMinusOne;
unsigned int(8) numOfArrays;
for (j=0; j < numOfArrays; j++) {
bit(1) array_completeness;
unsigned int(1) reserved = 0;
unsigned int(6) NAL_unit_type;
unsigned int(16) numNalus;
for (i=0; i< numNalus; i++) {
unsigned int(16) nalUnitLength;
bit(8*nalUnitLength) nalUnit;
}
}
详细解释如下:
bits | 描述 | 备注 |
---|---|---|
1 | array_completeness | 默认0 |
1 | reserved | 默认0 |
6 | NAL_unit_type | 帧类型 |
16 | numNalus | 此种类型的帧个数,一般为1,如果大于1,下面进入循环 |
16 | nalUnitLength | 2字节表示附加帧(VPS/SPS/PPS)的长度 |
N | NALU data | 附加帧(VPS/SPS/PPS)的数据 |
一般情况下numOfArrays=3,第一个数组为VPS相关数据,第二个数组为SPS,第三个数组为PPS
Hevc视频帧的封装式与H264类似,当为I帧时,buf[0]=0x1C,表示关键帧,H265编码;视频帧类型为HEVCNalu,所以buf[1]=1,buf[2,3,4]={0,0,0},此时其Data的格式为HEVC Nalu封装方式,即是4字节nalu大小+nalu数据(去掉起始码,需要去掉转移字节),所以buf[5-8]=nalu size,buf[9-n]= nalu data,至此I帧的RTMP 负载就组合完了;P帧与I帧就第一个字节不同,P帧不是关键帧,因此frametype=2,buf[0]=0x2C,其他组合方式一样,这里不做介绍