这段时间,工作上的需要,在RTMP上做了flv流到标准h264、AAC的转换,服务器是开源项目CRTMPSERVER,客户端flex编写,视频编码h264,音频编码AAC,现将一些协议相关的东西记录如下。

 

一、分析FLV数据

    我们先拿一个flv文件来简单分析一下flv数据的格式

    本文重点不在于此,这块就跳过了。相信大家对照flv标准文档都能看懂flv数据格式,而且我也强烈建议想学习这块知识的朋友先把这步的工作完成。

二、分析RTMP上行的h264视频流

    在server上将上行视频存成二进制文件(注意,一定要用二进制形式保存数据)如下图(图片所显示的二进制数据每行有16列数据,即从0至f,如显示不完全,请单独打开图片查看)

(转)RTMP中FLV流到标准h264、aac的转换_H264

    我使用的工具是notepad++(并安装二进制查看插件)

    如果你有做第一步的工作,不难看出rtmp中flv视频流就是一个接着一个的Video tag——即FLV tag中去除头信息, 只保留video tag内容。

    我们对照flv标准文档来逐个分析

    17:1-keyframe  7-avc

    00:AVC sequence header -- AVC packet type

    00 00 00:composition time,AVC时,全0,无意义

    因为AVC packet type=AVC sequence header,接下来就是AVCDecoderConfigurationRecord的内容

    configurationVersion = 01

    AVCProfileIndication = 42

    profile_compatibility = 00

    AVCLevelIndication = 1f

    lengthSizeMinusOne = ff -- FLV中NALU包长数据所使用的字节数,(lengthSizeMinusOne & 3)+1,实际测试时发现总为ff,计算结果为4,下文还会提到这个数据

    numOfSequenceParameterSets = E1 -- SPS 的个数,numOfSequenceParameterSets & 0x1F,实际测试时发现总为E1,计算结果为1

    sequenceParameterSetLength = 00 31 -- SPS 的长度,2个字节,计算结果49

    sequenceParameterSetNALUnits = 67 42 80 1f 96 54 05 01 ed 80 a8 40 00 00 03 00 40 00 00 07 b8 00 00 20 00 00 03 01 00 01 fc 63 8c 00 00 10 00                     00 03 00 80 00 fe 31 c3 b4 24 4d 40 -- SPS,为刚才计算的49个字节, SPS中包含了视频长、宽的信息

    numOfPictureParameterSets = 01 -- PPS 的个数,实际测试时发现总为E1,计算结果为1

    pictureParameterSetLength = 00 04 -- PPS 的长度

    pictureParameterSetNALUnits = 68 ce 35 20 -- PPS 

    接下来又是新的一包videotag数据了

    17:1-keyframe  7-avc

    01:AVC NALU

    00 00 00:composition time,AVC时,全0,无意义

    因为AVCPacket type = AVC NALU,接下来就是一个或多个NALU

    每个NALU包前面都有(lengthSizeMinusOne & 3)+1个字节的NAL包长度描述(前文提到的,还记得吗),前面计算结果为4个字节

    00 00 00 02 :2 -- NALU length

    09 10:NAL包

    这里插入一点NALU的小知识,每个NALU第一个字节的前5位标明的是该NAL包的类型,即NAL nal_unit_type



(转)RTMP中FLV流到标准h264、aac的转换_aac_02

#define NALU_TYPE_SLICE 1
#define NALU_TYPE_DPA 2
#define NALU_TYPE_DPB 3
#define NALU_TYPE_DPC 4
#define NALU_TYPE_IDR 5
#define NALU_TYPE_SEI 6
#define NALU_TYPE_SPS 7
#define NALU_TYPE_PPS 8
#define NALU_TYPE_AUD 9  //访问分隔符
#define NALU_TYPE_EOSEQ 10
#define NALU_TYPE_EOSTREAM 11
#define NALU_TYPE_FILL 12


(转)RTMP中FLV流到标准h264、aac的转换_aac_02


    09&0x1f=9,访问单元分隔符

    前面我们解析的sps头字节为67,67&0x1f = 7,pps头字节为68,68&0x1f=8,正好能对应上。

    00 00 00 29:说明接下来的NAL包长度为41

    06 00 11 80 00 af c8 00 00 03 00 00 03 00 00 af c8 00 00 03 00 00 40 01 0c 00 00 03 00 00 03 00 90 80 08 00 00 03 00 08 80:06&0x1f=6 -- SEI

    00 00 3c d0:接下来的NAL包长度

    65 88 80……:65&0x1f=5 -- I帧数据

    这包video tag分析到此结束了,下面会紧接着来一些该I帧对应的P帧数据, 见下图

(转)RTMP中FLV流到标准h264、aac的转换_rtmp_04 

    看00003d80那行,前面的内容一直到53 4f 7f都是上一个video tag的内容,即前面说的65 88 80那个I帧的数据拉,27开始是新的一个video tag

    27:2-inter frame即P帧,7-codecid=AVC

    01:AVCPacket type = AVC NALU

    00 00 00:composition time,AVC时,全0,无意义

    00 00 00 02 09 30:跟上面分析的一样拉,2个字节的nal包,访问单元分隔符

    00 00 00 11:17字节的NAL包

    06 01 0c 00 00 80 00 00 90 80 18 00 00 03 00 08 80:06&0x1f=6-SEI

    00 00 46 85: NAL包数据长度

    41 9a 02……: 41&0x1f=1,P帧数据

 

三、转换

    大致总结下flv h264流,按顺序依次是

    1、一个video tag,包含的信息:SPS,PPS,访问单元分隔符,SEI,I帧包

    2、一个或多个video tag,包含的信息:访问单元分隔符,SEI,P帧包可为多个

    循环1、2

    这里需要说明下,做转换这一步时,我们只需要从videotag中获取到所有的、一个一个的NAL包就可以了,至于它是I帧、P帧及其他类型,实际上我们并不需要关心,这里只是为了更好的分析数据。

    h264的NALU和NALU之间是由00 00 01(也可以是00 00 00 01)分隔开的,我们组成h264之后的格式为

    1、00 00 00 01 SPS 00 00 00 01 PPS 00 00 00 01 访问单元分隔符 00 00 00 01 SEI 00 00 00 01 I帧 00 00 00 01 P帧 00 00 00 01 P帧……(P帧数量不定)

    循环1

    其中的访问单元分隔符和SEI不是必须的,将h264以二进制的形式写入文件,使用​