FFmpeg从入门到精通——中级篇,FLV文件结构解析
FLV是目前主流的视频封装格式,市面上大多数直播产品的推拉流协议都是基于FLV。而FFMPEG作为多媒体处理引擎,凭借强大功能及开源易于维护等特性,被广泛应用于各种音视频相关软件中,用于完成流媒体协议及格式解析或者解码后处理等任务。金山云SDK基于FFmpeg,通过应用FFmpeg来处理FLV的复用和解复用。继上期推出《FFmpeg从入门到精通》基础篇后,本期将深入解读FLV文件结构。
FLV(FLASH VIDEO),是一种常用的文件封装格式,目前国内外大部分视频分享网站都是采用的这种格式。其标准定义为《Adobe Flash Video File Format Specification》。RTMP协议也是基于FLV视频格式的。
FLV的文件格式在该规范中已阐述清楚,本章节不再重复描述,而是结合下面的示例具体阐述如何分析FLV文件。
图1. FLV文件结构示例1
图2. FLV文件结构示例2
FLV文件的分析工具有很多,这里给大家推荐FLV Parser这个小软件,通过它可以很容易的看到文件的组成结构。
1.1 文件结构
从整个文件上看,FLV是由Header和File Body组成,如下图所示:
图3. FLV文件总体结构
1. FLV Header - 长度为9,其结构的标准定义参见 标准定义见E.2 The FLV header;
2. FLV File Body - 由一连串的PreviousTagSize + Tag构成。previousTagSize是4个字节的数据,表 示前一个tag的size。标准定义参见E.3 The FLV File Body。
以图1. FLV文件结构示例1为例分析整体结构:
1. 位置0x00000000 - 0x00000008, 共9个字节,为 FLV Header,其中:
• 0x00000000 - 0x00000002 : 0x46 0x4C 0x56分 别表示字符'F''L''V',用来标识这个文件是FLV格式 的。在做格式探测的时候,如果发现前3个字节为 “FLV”,就认为它是FLV文件;
• 0x00000003 : 0x01, 表示FLV版本号;
• 0x00000004 : 0x05, 转换为2进制是00000101, 其中第0位为1,表示存在video,第2位为1,表示存 在audio;
• 0x00000005 - 0x00000008 : 0x00 0x00 0x00 0x09,转十进制为9,表示FLV header的长度,当 FLV 版本号为1时,该值通常为9。
2. 位置0x00000009 - ,为FLV File Body:
• 0x00000009 - 0x0000000C : 0x00 0x00 0x00 0x00 PreviousTagSize0,转十进制为0,该值永远 为0;
• 0x0000000D - 0x00000209 : 0x12 ... 0x09,共 509个字节,为Tag1的具体内容;
• 0x0000020A - 0x0000020D : 0x00 0x00 0x01 0xFD,转十进制为509,表示它前面的Tag,即 Tag1的长度为509;
• 0x0000020E - :按照Tag + PreviousTagSize的结 构依次递推,此处不再举例说明。
1.2 Tag定义
FLV File Body是由一系列的PreviousTagSize + Tag组成,其中PreviousTagSize的长度为4个字节,用来表示前一个Tag的长度;Tag里面的数据可能是video、audio或者scripts,其定义参见E.4.1 FLV Tag,结构如下:
图4. FLV Tag 结构
以图1. FLV文件结构示例1为例分析Tag结构:
1. 位置0x0000020E : 0x08, 二进制为0000 1000, 第5位为0, 表示为非加扰文件;低5位01000为8, 说明这个Tag包含的数据类型为Audio;
2. 位置0x0000020F - 0x00000211 : 0x00 0x00 0x04,转十进制为4,说明Tag的内容长度为4,与 该tag后面的previousTagSize(15) - 11相同;
3. 位置0x00000212 - 0x00000214 : 0x00 0x00 0x00,转十进制为0,说明当前Audio数据的时间戳 为0;
4. 位置0x00000215 : 0x00,扩展时间戳为0,如果扩 展时间戳不为0,那么该Tag的时间戳应为: Timestamp | TimestampExtended<<24;
5. 位置0x00000216 - 0x00000218 : 0x00 0x00 0x00,StreamID,总是0;
6. StreamID之后的数据每种格式的情况都不一样, 下面会依次进行详细解读。
1.3 Audio Tags
如果TAG包中的TagType等于8,表示该Tag中包含的数据类型为Audio。StreamID之后的数据就是AudioTagHeader,其定义详见E.4.2.1 AUDIODATA。结构如下:
图5. FLV Audio Tag结构
需要说明的是,通常情况下AudioTagHeader之后跟着的就是AUDIODATA数据了,但有个特例,如果音频编码格式为AAC,AudioTagHeader中会多出1个字节的数据AACPacketType,这个字段来表示AACAUDIODATA的类型:
• 0 = AAC sequence header
• 1 = AAC raw。
以图1. FLV文件结构示例为例分析AudioTag结构:
1. 位置0x00000219 : 0xAF, 二进制表示为1010 1111:
高4位为1010,转十进制为10,表示Audio的编码格 式为AAC;
第3、2位为11,转十进制为3,表示该音频的采样 率为44KHZ;
第1位为1,表示该音频采样点位宽为16bits;
第0位为1,表示该音频为立体声。
2. 位置0x0000021A : 0x00,十进制为0,并且Audio 的编码格式为AAC,说明AACAUDIODATA中存放 的是AAC sequence header;
3. 位置0x0000021B - 0x0000021C : AUDIODATA 数据,即AAC sequence header。
1.3.1 AudioSpecificConfig
AAC sequence header中存放的是AudioSpecificConfig,该结构包含了更加详细的音频信息,《ISO-14496-3 Audio》中的1.6.2.1 章节对此作了详细定义。
通常情况下,AAC sequence header这种Tag在FLV文件中只出现1次,并且是第一个Audio Tag,它存放了解码AAC音频所需要的详细信息。
有关AudioSpecificConfig结构的代码解析,可以参考ffmpeg/libavcodec/mpeg4audio.c中的avpriv_mpeg4audio_get_config方法。
为什么AudioTagHeader中定义了音频的相关参数,我们还需要传递AudioSpecificConfig呢?
因为当SoundFormat为AAC时,SoundType须设置为1(立体声),SoundRate须设置为3(44KHZ),但这并不意味着FLV文件中AAC编码的音频必须是44KHZ的立体声。播放器在播放AAC音频时,应忽略AudioTagHeader中的参数,并根据AudioSpecificConfig来配置正确的解码参数。
1.4 Video Tag
如果TAG包中的TagType等于9,表示该Tag中包含的数据类型为Video。StreamID之后的数据就是VideoTagHeader,其定义详见E.4.3.1 VIDEODATA,结构如下:
图6. FLV Video Tag结构
VideoTagHeader之后跟着的就是VIDEODATA数据了,但是和AAC音频一样,它也存在一个特例,就是当视频编码格式为H.264的时候,VideoTagHeader会多出4个字节的信息,AVCPacketType和CompositionTime。
• AVCPacketType用来表示VIDEODATA的内容
• CompositonTime相对时间戳,如果 AVCPacketType=0x01,为相对时间戳,其它均为 0;
以图2. FLV文件结构示例2为例分析VideoTagHeader结构:
1. 位置0x0000022C : 0x17, 二进制表示为0001 0111:
• 高4位为0001,转十进制为1,表示当前帧为关键 帧;
• 低4位为0111,转十进制为7,说明当前视频的编码 格式为AVC。
2. 位置0x0000022D : 0x00,十进制为0,并且Video 的编码格式为AVC,说明VideoTagBody中存放的 是AVC sequence header;
3. 位置0x0000022E - 0x00000230 : 转十进制为0, 表示相对时间戳为0;
4. 位置0x00000231 - 0x0000021C : VIDEODATA 数据,即AVC sequence header。
1.4.1 AVCDecoderConfigurationRecord
AVC sequence header中存放的是AVCDecoderConfigurationRecord,《ISO-14496-15 AVC file format》对此作了详细定义。它存放的是AVC的编码参数,解码时需设置给解码器后方可正确解码。
通常情况下,AVC sequence header这种Tag在FLV文件中只出现1次,并且是第一个Video Tag。
有关AVCDecoderConfigurationRecord结构的代码解析,可以参考中的ff_isom_write_avcc方法。
1.4.2 CompositionTime(相对时间戳)
相对时间戳的概念需要和PTS、DTS一起理解:
• DTS : Decode Time Stamp,解码时间戳,用于告 知解码器该视频帧的解码时间;
• PTS : Presentation Time Stamp,显示时间戳,用 于告知播放器该视频帧的显示时间;
• CTS : Composition Time Stamp,相对时间戳,用 来表示PTS与DTS的差值。
如果视频里各帧的编码是按输入顺序依次进行的,则解码和显示时间相同,应该是一致的。但在编码后的视频类型中,如果存在B帧,输入顺序和编码顺序并不一致,所以才需要PTS和DTS这两种时间戳。视频帧的解码一定是发生在显示前,所以视频帧的PTS,一定是大于等于DTS的,因此CTS=PTS-DTS。
FLV Video Tag中的TimeStamp,不是PTS,而是DTS,视频帧的PTS需要我们通过DTS + CTS计算得到。
为什么Audio Tag不需要CompositionTime呢?
因为Audio的编码顺序和输入顺序一致,即PTS=DTS,所以它没有CompositionTime的概念。
1.5 Script Data Tags
如果TAG包中的TagType等于18,表示该Tag中包含的数据类型为SCRIPT。
SCRIPTDATA 结构十分复杂,定义了很多格式类型,每个类型对应一种结构,详细可参考E.4.4 Data Tags
onMetaData是SCRIPTDATA中一个非常重要的信息,其结构定义可参考E.5 onMetaData。它通常是FLV文件中的第一个Tag,用来表示当前文件的一些基本信息: 比如视音频的编码类型id、视频的宽和高、文件大小、视频长度、创建日期等。