1、FLV 简介
FLV(Flash Video) 是 Adobe 公司推出的一种流媒体格式,由于其封装后的音视频文件体积小、封装简单等特点,非常适合于互联网上使用。目前主流的视频网站基本都支持FLV。采用 FLV 格式封装的文件后缀为.flv。直播场景下拉流比较常见的是 http-flv 直播流,具有延时低,易传输等特点。
------------------------
2、FLV 格式
总体上看,FLV 由文件头(file header)和(file body)组成,其中 FLV body 由一对对的 Tag 和 Previous Tag Size(占用4个字节,记录前一个 Tag 的大小,用于逆向读取处理,FLV header 后的第一个 Previous Tag Size 为 0)组成。而 Tag 一般分为三种类型:脚本数据、视频数据、音频数据,有 Tag header 和 Tag Data 组成。FLV 数据以大端序进行存储,在解析时需要注意。一个标准 FLV 文件结构如下图:
FLV 文件的详细内容结构如下:
---------------------------
2.1 FLV header(文件头)
从上图中可以看到,FLV 头占用 9 个字节,用来标识文件类型为 FLV 类型,以及后续的音视频标识。一个 FLV 文件,每种类型的 tag 都属于一个流,也就是一个 flv 文件最多只有一个音频流,一个视频流,不存在多个独立的音视频流在一个文件的情况。FLV 头的结构如下:
上面的 UI 表示无符号整型,后面跟着的数字代表其长度是多少位;UB 表示位域,表示一个字节的多少位。可以参考C语言中的结构体位域。
------------------------------------
2.2 FLV Body(文件体)
FLV Header之后,就是 FLV File Body。FLV File Body 是由一连串的 back-pointers + tags 构成。
back-pointer
back-pointer 表示 Previous Tag Size(前一个 tag 的字节数据长度),占4个字节,
第一个 back-pointer 数据为0。
Tag
音视频数据,每一个 tag 由两部分组成:tag header 和 tag data。
----------------------------------------
3、FLV Tag 详解
3.1 Tag 数据结构
flv_tag_header + flv_tag_data,其中 flv_tag_header 占 11 个字节。
3.2 FLV_tag_header
存放当前 tag 的类型、数据区长度、时间戳等信息。tag header 一般占 11 个字节的内粗空间。
3.3 FLV_Tag_Data
FLV Tag 的类型可以是视频、音频和 Script(脚本类型)
3.3.1 Script Tag Data(脚本类型、帧类型)
脚本 Tag 一般只有一个,是 flv 的第一个 Tag,跟在 flv header 后,用于存放 flv 视频和音频的元信息,比如 duration、audiodatarate、creator、width 等。一般来说,Script Tag Data结构包含两个 AMF 包(AMF(Action Message Format)是 Adobe 设计的一种通用数据封装格式,在 Adobe 的很多产品中应用,简单来说,AMF 将不同类型的数据用统一的格式来描述),结构如下:
第一个 AMF 包(封装字符串类型数据):
第 1 个字节表示 AMF 包类型,一般总是 0x02,表示字符串; 第 2-3 个字节为 UI16 类型值,表示字符串的长度,一般总是 0x000A(“onMetaData”长度); 后面字节为字符串数据,一般总为 “onMetaData”(6F,6E,4D,65,74,61,44,61,74,61)。
---------------------------------------
第二个 AMF 包(封装一个数组类型,这个数组中包含了音视频信息项的名称和值):
第 1 个字节表示 AMF 包类型,一般总是 0x08,表示数组。 第 2-5 个字节为 UI32 类型值,表示数组元素的个数。 后面即为各数组元素的封装,数组元素为元素名称和值组成的对。表示方法如下: 第 1-2 个字节表示元素名称的长度,假设为 L。后面跟着为长度为 L 的字符串。第 L+3 个字节表示元素值的类型。后面跟着为对应值,占用字节数取决于值的类型。
常见的数组元素表示如下表:
=======================
3.3.2 Video Tag Data(视频类型)
视频 Tag Data 开始的第一个字节包含视频数据的参数信息,从第二个字节开始为视频流数据。结构如下:
第一个字节包含视频信息:前4位表示帧类型;后4位表示编码类型。
第二个字节开始为视频数据
AVC VIDEO PACKET
视频的格式(CodecID)是 AVC(H.264)的话,VideoTagHeader 会多出 4 个字节的信息,AVCPacketType 和 CompositionTime,所以是 H264 编码的情况下 VideoHeader 长度是 5 个字节。 AVC VIDEO PACKET 结构如下:
AVCDecoderConfigurationRecord
包含着 H.264 解码相关比较重要的 sps 和 pps 信息,再给 AVC 解码器送数据流之前一定要把 sps 和 pps 信息送出,否则的话解码器不能正常解码。 而且在解码器 stop 之后再次 start 之前,如 seek、快进快退状态切换等,都需要重新送一遍 sps 和 pps 的信息。 AVCDecoderConfigurationRecord 在 FLV 文件中一般情况也是出现 1 次,也就是第一个 video tag。
sps pps
第一个 video tag 一般存放的是 sps 和 pps。存储的格式: 0x01 + sps[1] + sps[2] + sps[3] + 0xFF + 0xE1 + sps size + sps + 01 + pps size + pps 其中 sps size 和 pps siz e各占两个字节。 data 数据结构:
关于 CTS:需要和 pts,dts 配合一起理解。 pts:presentation time stamps 显示时间,也就是接收方在显示器显示这帧的时间。单位为1/90000 秒; dts:decoder timestamps 解码时间,也就是rtp包中传输的时间戳,表明解码的顺序。单位为1/90000 秒。——根据后面的理解,pts 就是标准中的 CompositionTime; cts偏移:cts = (pts - dts) / 90。cts 的单位是毫秒; pts 和 dts 的时间不一样,应该只出现在含有 B 帧的情况下,也就是 profile main 以上。baseline 是没有这个问题的,baseline 的 pts 和 dts 一直相同,所以 cts 一直为 0 。
=================================
3.3.3 Audio Tag Data(音频类型)
音频 Tag Data 区域开始的第一个字节包含了音频数据的参数信息,从第二个字节开始为音频流数据。结构如下:
第一个字节包含音频信息:前 4bit 表示音频格式;第 5、6bit 表示采样率;第 7bit 表示采样精度;第 8bit 表示音频声道。
第二个字节开始为音频数据
其中如果音频格式为 10,即是 AAC 格式的,AudioTagHeader 中会多出一个字节 AACPacketType,这个字段来表示 AACAUDIODATA 的类型:0 = AAC sequence header,1 = AAC raw。
AAC sequence header 也就是包含了 AudioSpecificConfig,AudioSpecificConfig 包含着一些更加详细音频的信息。
AAC raw 这种包含的就是音频 ES 流了,也就是 audio payload。
12 00 01 17 00 00 00 00 00 00 00 (tag header 11 字节)
0x12 表示这是一个 scripts tag, 00 01 17 三子节表示 tag data 长度为 279 个字节,Timestreamp、TimestampExtended、stream id 均为0。 下一个 back-pointers,表示该 scripts tag 的 size,即 279 + 11 = 290 = 0x122,即 00 00 01 22,可以看到最后4字节刚好是的。
02 00 0a 6f 6e 4d 65 74 61 44 61 74 61 表示第一个 AMF 包,02 表示类型为 string type,后面两个字节 00 0A 表示长度是 10,值 onMetaData; 03 表示 ObjectType,此处应该一般是 08 才对,表示数组类型;表示有8个键值对。 00 05 表示键长度,77 69 64 74 68 表示 width,00 表示类型为 Number,后面 8 字节表示值; 00 06 表示键长度, 68 65 69 67 68 74 表示 height,00 表示类型为 Number,后面 8 字节表示值。 以此类推解析到最后 end marker 00 00 09,表示解析完毕。
4、举例子
根据真实数据来解析 FLV 格式。
4.1 FLV header
<464c5601 05000000 09000000 00120001 17000000 00000000 02000a6f
6e4d6574 61446174 61030005 77696474 68004084 00000000 00000006
68656967 68740040 7e000000 00000000 0c736f75 7263655f 77696474
6800409e 00000000 0000000d 736f7572 63655f68 65696768 74004090
e0000000 00000009 6672616d 65726174 6500402e 00000000 0000000c
76696465 6f636f64 65636964 00401c00 00000000 00000f61 7564696f
73616d70 6c657261 74650040 bf400000 00000000 0f617564 696f7361
6d706c65 73697a65 00403000 00000000 00000673 74657265 6f010000
0c617564 696f636f 64656369 64004024 00000000 0000000c 63726561
74696f6e 64617465 02001732 3032312d 31312d30 33203037 3a32373a
33302055 54430006 61757468 6f720200 034c4c4c 00000900 000122>
46 4C 56 01 05 00 00 00 09:表示 FLV header(9 字节),音视频都有; 0x 00 00 00 00 表示第一个 back-pointers(前一个 tag 的 size)。因为前面没有 tag,所以为 0。
4.2 脚本 Tag
12 00 01 17 00 00 00 00 00 00 00 (tag header 11 字节) 0x12 表示这是一个 scripts tag, 00 01 17 三子节表示 tag data 长度为 279 个字节,Timestreamp、TimestampExtended、stream id 均为0。 下一个 back-pointers,表示该 scripts tag 的 size,即 279 + 11 = 290 = 0x122,即 00 00 01 22,可以看到最后4字节刚好是的。 02 00 0a 6f 6e 4d 65 74 61 44 61 74 61 表示第一个 AMF 包,02 表示类型为 string type,后面两个字节 00 0A 表示长度是 10,值 onMetaData; 03 表示 ObjectType,此处应该一般是 08 才对,表示数组类型;表示有8个键值对。 00 05 表示键长度,77 69 64 74 68 表示 width,00 表示类型为 Number,后面 8 字节表示值; 00 06 表示键长度, 68 65 69 67 68 74 表示 height,00 表示类型为 Number,后面 8 字节表示值。 以此类推解析到最后 end marker 00 00 09,表示解析完毕。
4.3 第一个 video tag
一般包含 sps、pps
<09000022 00000000 00000017 00000000 014d401e ffe1000e 674d401e
a680a03d a6e02020 20400100 0468ee3c 80000000 2d>
09 00 00 22 00 00 00 00 00(tag header 11 字节) 09 表示视频 tag; 00 00 22 表示长度为 0x22 = 34,加上头部长度 11 字节,为 45。下一个 back-pointers 是 00 00 00 2D(上面数据的最后的四字节), Timestreamp、TimestampExtended、stream id 均为 0; 下面就是 tag data 数据: 0x17 即 0001 0111,前 4 位表示帧类型,1 为关键帧;后 4 位表示编码 ID,7 表示 AVC。 视频格式是AVC(H.264)类型的话,后面 1 个字节表示 AVCPacketType,再后三个字节表示 CompositionTime: 00 AVCPacketType 为 0,表示是 AVCDecoderConfigurationRecord。就表示包含着 sps 和 pps 了。这个东西要第一个发给解码器,要不然不能正常解码。 00 00 00 CompositionTime 为 0。接下来就是 sps 和 pps 的信息了。 按照 0x01 + sps[1] + sps[2] + sps[3] + 0xFF + 0xE1 + sps size + sps + 01 + pps size + pps 的格式,参照二进制数据: sps[1] = 4d;sps[2] = 40;sps[3] = 1e;sps size = 000e,表示 sps 的长度为 14,14 字节的数据读取完,读到 01;pps size = 0004,表示 pps 的长度为 4,读取完后该 tag 结束。下一个back-pointers。
4.4 第二个 video tag 以及后续的 video tag
<09002374 00000000 00000017 01000000 0000236b 65b82129 7f7ae073
74574ffe 0fd1ebcc 6851014a d25aa986 6daee3a1 6f0d7b77 734e17a9
0fe00e56 a92b864b 3d423839 c229a2ab 23208467 ... ...00 00237f>
下一个 video 长度为 9076 的视频数据,0x17 后一位 AVCPacketType 01,表示 NALU 数据,三个 CompositionTime 字节过后,就是 NALU 数据了。
4.5 第一个 audio tag 以及后续的 audio tag
<08000004 00005d00 000000ae 00158800 00000f>
08 00 00 04 00 00 5d 00 00 00 00ae
0x08 表示音频;
00 00 04 表示长度为 4,时间戳为 93;
加上头部长度 11,为 15,下一个 back-pointers 是 0x 00 00 00 0F(最后四子节);接下来就是 tag data: AE: 即 0b10101110,前 4 位为 10,表示音频格式是 AAC;10 会多一个字节 AACPacketType,表示该 AACAUDIODATA 的类型;第 5、6 位为 11,十进制 3,表示采样率为 44kHz;第 7 位为1,表示 16 位采样精度;第 8 位为 0,表示 sndMono 单声道。 00: 表示 AACPacketType,为 0 表示该 Tag 是 AAC sequence header。接下来两个字节表示 AudioSpecificConfig,包含更加详细音频的信息。读取完后该 tag 结束。下一个 back-pointers。
4.6 第二个 audio tag 以及后续的 audio tag
<080002fb 00005d00 000000ae 01010e34 14564a2d 92154484 d0602e72
f7cab6d6 af82156c cae24cba a6f8d2b9 d2d512d3 db8b5632 ef76c8a9
95225c3b 69e84d7d 03a6fa66 7b475759 0ead71f5 37c73379 fd124cd5
1ba64d95 48d7aa3b c558863e 090fc9ae ... ...0000 0306>
下一个长度为 752 的音频数据,时间戳为 93,0xae 后一位为 01,表示 ACC raw,即音频 NALU