Skip to content
Flash Video

Flash Video(简称 FLV),是一种容器文件格式,用作流媒体格式,用于使用 Adobe Flash Player 6 和更新版本通过互联网传送数字视频内容(如电视节目、电影等)。

它的出现有效地解决了视频文件导入 Flash 后,使导出的 SWF 文件体积庞大,不能在网络上有效使用等缺点。

FLV 文件通常包含采用 Sorenson Spark 或 VP6 视频压缩格式的编解码器编码的内容。截至 2010 年,Flash Player 的公开发行版也支持 H.264 视频和 HE-AAC 音频。

文件结构

FLV 文件主要由 FLV Header 和 FLV Body 两部分组成(后者由一系列交替的反向指针和 FLV tags 组成):

Field类型说明
Header----
PreviousTagSize0UI32前一个 Tag 的大小,因为前面无 Tag 所以总为 0
Tag1FLVTAG第一个 Tag
PreviousTagSize1UI32前一个 Tag 的大小(包括 Tag 的头和内容)
Tag2FLVTAG第二个 Tag
.........
PreviousTagSizeN-1UI32
TagNFLVTAG最后一个 Tag
PreviousTagSizeNUI32最后一个 Tag 的大小

FLV Header

FLV 头部(Header)主要包括签名字段、版本号和包含媒体类型等信息,详细解析如下表所示:

Field类型说明
SignatureUI8签名, 总是 'F' (0x46)
SignatureUI8签名, 总是 'L' (0x4C)
SignatureUI8签名, 总是 'V' (0x56)
VersionUI8版本, 总是 0x01, 表示 FLV version 1
TypeFlagsReservedUB[5]全为 0
TypeFlagsAudioUB[1]1 = 有音频
TypeFlagsReservedUB[1]全为 0
TypeFlagsVideoUB[1]1 = 有视频
DataOffsetUI32整个文件头长度, 总是 9

以示例文件为例,前 9 个字节的以 16 进制表示为:

plaintext
00000000h: 46 4C 56 01 05 00 00 00 09
00000000h: 46 4C 56 01 05 00 00 00 09

前三个字节总是固定的签名字段 FLV,然后是文件的版本为 1;接下来 0x05 转二进制表示为 0b00000101,其中第 6 位和第 8 位都为 1,表示其中包含音频和视频。

最后四个字节表示文件头数据的偏移位置,也就是整个头文件的长度为 9 个字节。

FLV Body

接下来到了 FLV Body 部分,首先是表示前一个 Tag 长度的部分。

Field类型说明
PreviousTagSizeUI32前一个 Tag 长度,以字节为单位

该部分采用四个字节表示,如前所述,第一个表示 Tag 大小的部分总为零:

plaintext
00000009h: 00 00 00 00
00000009h: 00 00 00 00

Tag Header

再接着就是 FLVTAG 部分,每个 FLVTAG 主要分为 Header 和 Body 两部分,在 FLV 中 Tag 主要分为三类:Audio、Video、Script,它们的头部基本一致:

Field类型说明
ReservedUB2保留位,应该为 0
FilterUB1主要用作文件加密处理,为 0 表示不预处理,否则表示预处理
TagTypeUB5类型, 8 = audio, 9 = video, 18 = script
DataSizeUI24数据字段中数据的字节数
TimestampUI24相对于第一个 tag 的时间戳(unit: ms), 第一个 tag 总是 0
TimestampExtendedUI8Timestamp 的高 8 位,扩展 Timestamp 为 SI32 类型(以毫秒为单位)
StreamIDUI24总是 0

可见 Tag 的头部总体大小为 11 字节,在示例文件中的数据如下:

plaintext
0000000dh: 12 00 01 74 00 00 00 00 00 00 00
0000000dh: 12 00 01 74 00 00 00 00 00 00 00

此处的 0x12 表示该标签类型为 Script,0x000174 表示数据部分包含了 372 个字节的数据,后面几部分都为零。

Script Tag Body

ScriptTagBody 里面存储的数据一般为 AMF 数据,主要由多个 Name 和 Value 对组成,Name 为 Method 或对象的名称,Value 为 AMF 参数或对象属性,两者均为 SCRIPTDATAVALUE 格式。

SCRIPTDATAVALUE 包含一个字节的数据类型和后面紧跟着的特定类型的数据实体,对数据类型的定义如下:

类型ScriptDataValue
0NumberDOUBLE (8 个字节)
1BooleanUI8 ((ScriptDataValue ≠ 0)
2StringSCRIPTDATASTRING
3ObjectSCRIPTDATAOBJECT
4MovieClip (reserved, not supported)
5Null
6Undefined
7ReferenceUI16
8ECMA arraySCRIPTDATAECMAARRAY
9Object end marker
10Strict arraySCRIPTDATASTRICTARRAY
11DateSCRIPTDATADATE
12Long stringSCRIPTDATALONGSTRING

数据实体部分根据不同类型进行对应的 AMF 解析并可以取得数据,SCRIPTDATAVALUE 主要分为 8 种,下面列举了其中 4 中(其它四种可见附录)。

SCRIPTDATAECMAARRAY 类型:

Field类型说明
ECMAArrayLengthUI32ECMA 数组元素的数量
VariablesSCRIPTDATAOBJECTPROPERTY [ ]变量名和值的列表
List TerminatorSCRIPTDATAOBJECTEND列表终止符

SCRIPTDATAOBJECTPROPERTY 类型:

Field类型说明
PropertyNameSCRIPTDATASTRING对象属性或变量的名称
PropertyDataSCRIPTDATAVALUE对象属性或变量的值和类型

SCRIPTDATASTRING 类型:

Field类型说明
StringLengthUI16字符串的长度(字节)
StringDataSTRING字符串,最大长度 65535 字节,不含 NULL 结束符

SCRIPTDATAOBJECTEND 类型:

Field类型说明
ObjectEndMarkerUI8 [3]必须是 0,0,9

通常,ScriptTagBody 中包含两个 AMF 包。第一个包封装了字符串类型数据,用来装入一个“onMetaData”标志:

plaintext
00000018h: 02 00 0A 6F 6E 4D 65 74 61 44 61 74 61
00000018h: 02 00 0A 6F 6E 4D 65 74 61 44 61 74 61

如上,第一个字节 0x02 表示 String 类型,根据其对应的 SCRIPTDATASTRING 类型的定义,接下来两个字节 0x000A 表示字符串的长度为 10 个字节。

所以再之后的 10 个字节表示具体的字符串值,根据每个字节表示的数字对照 ASCII Code 表并可以得到相应的值(此处也就是“onMetaData”标识)。

第二个 AMF 包封装了一个数组类型,其中包含了具体的元数据信息。首先第一个字节仍然表示了对应的类型:

plaintext
00000025h: 08
00000025h: 08

0x08 表示 ECMA array 类型,根据其对应的 SCRIPTDATAECMAARRAY 的类型的定义,接着 4 个字节表示 ECMA 数组元素的数量:

plaintext
00000026h: 00 00 00 10
00000026h: 00 00 00 10

也就是说借下来将会出现 16 对 SCRIPTDATAOBJECTPROPERTY(PropertyName: PropertyData),那么后续就会按照 SCRIPTDATASTRING 和 SCRIPTDATAVALUE 成对出现的方式解析。

首先是表示属性名称的部分:

plaintext
0000002ah: 00 08 64 75 72 61 74 69 6F 6E
0000002ah: 00 08 64 75 72 61 74 69 6F 6E

对于 SCRIPTDATASTRING 类型上面已经解析过了,前两个字节 ox0008 标识字符串长度, 由此可知接下来 8 个字节表示具体的属性名称。

依然是对照 ASCII Code 表可知该对象属性名称为“duration”,接着是表示对象属性值的部分:

plaintext
00000034h: 00 3F F7 CE D9 16 87 2B 02
00000034h: 00 3F F7 CE D9 16 87 2B 02

作为 ScriptDataValue 类型,它的第一个字节 0x00 表示接下来的数据类型为 DOUBLE,根据 DOUBLE 类型的定义接下来 8 个字节表示其具体的值,也就是 1.488(s)。

然后就是重复这样的解析,直到解析到列表终止符,最后得到所有的元数据信息,常见的元数据属性可见附录。

Video Tag Body

到此 Script 标签已经解析完成了,接下来的 4 个字节又将表示的是前一个标签的大小:

plaintext
0000018ch: 00 00 01 7F
0000018ch: 00 00 01 7F

然后是标签的头信息部分:

plaintext
00000190h: 09 00 00 38 00 00 00 00 00 00 00
00000190h: 09 00 00 38 00 00 00 00 00 00 00

回到标签头的解析部分,第一个字节 0x09 表明这个标签的类型为 Video,0x000038 表示数据部分有 56 个字节,其余部分为零。

视频标签的 Body 部分主要由 VideoTagHeader 和 VideoData 两部分组成,其头部主要包括:

Field类型说明
FrameTypeUB[4]视频帧的类型,详情见附录
CodecIDUB[4]编码标识,常见为 7 表示 AVC,详情见附录
AVCPacketTypeUI8(CodecID==7)包的类型:0=AVC 序列头,1=AVC NALU,2=序列的 AVC 结束
CompositionTimeSI24 (CodecID==7)AVCPacketType = 1 时为 PTS 相对于 DTS 的偏移值,其它均为零

对于 VideoTag 的 VideoData 部分会根据帧类型、编码格式和包类型的不同而不同:

  • 如果 FrameType 值为 5,则视频数据体包含具有以下含义的 UI8:
    • 为 0 表示 Start of client-side seeking video frame sequence;
    • 为 1 表示 End of client-side seeking video frame sequence。
  • 如果 FrameType 不为 5 且当 CodecID 为 7 时:
    • AVCPacketType 为 0,表示 AVCDecoderConfigurationRecord;
    • AVCPacketType 为 1,视频数据内容为一个或多个 NALU。

继续从示例文件中截取 5 个字节来看:

plaintext
0000019bh: 17 00 00 00 00
0000019bh: 17 00 00 00 00

0x17 即为 0b00010111,前四位 0b0001 表示当前帧为关键帧,后四位 0b0111 表示当期视频采用了 AVC 编码格式。

由于 CodecID 为 7,所以再往后的 0x00 表示接下来的包类型是一个 AVC 序列头,最后的 0x000000 表示 CTS 为零。

同时因为 AVCPacketType 为 0,所以接下来的视频数据体为 AVCDecoderConfigurationRecord:

Field类型说明
configurationVersionUI8版本号
AVCProfileIndicationUI8包含 ISO/IEC 14496-10 中定义的配置文件代码
profile_compatibilityUI8定义与 ISO/IEC 14496-10 中定义的 SPS 中 profile_IDC 和 level_IDC 之间发生的字节完全相同的字节
AVCLevelIndicationUI8包含 ISO/IEC 14496-10 中定义的等级代码
保留的,‘111111’b
lengthSizeMinusOneUB[2]指示相关流的 AVC 视频样本或 AVC 参数集样本中 NALUnitLength 字段的字节长度减壹
保留的,‘111’b
numOfSequenceParameterSetsUB[5]SPS 的个数,指示用作解码 AVC 基本流的初始 SPS 集的 SPS 数
sequenceParameterSetLengthUI16SPS 的长度,表示 ISO/IEC 14496-10 中定义的 SPS NAL 单元的字节长度
sequenceParameterSetNALUnitUI8[n]SPS 的数据,包含 ISO/IEC 14496-10 中规定的 SPS NAL 单元。SPS 应按参数集标识符升序出现,允许有间隙
......存在多个 SPS 时,重复 SPS 的长度和数据两项
numOfPictureParameterSetsUI8PPS 的个数,指示用作解码 AVC 基本流的 PPS 初始集的图片参数集(PPS)的数量
pictureParameterSetLengthUI16PPS 的长度,表示 ISO/IEC 14496-10 中定义的 PPS NAL 单元的字节长度
pictureParameterSetNALUnitUI8[n]PPS 的数据,包含 ISO/IEC 14496-10 中规定的 PPS NAL 单元。PPS 应按参数集标识符升序出现,允许有间隙
......存在多个 PPS 时,重复 PPS 的长度和数据两项

下面是示例文件中对应 AVCDecoderConfigurationRecord 部分的数据:

plaintext
000001a0h: 01 64 00 1E FF E1 00 1E 67 64 00 1E AC D9 40 BC
000001b0h: 35 E7 F0 16 A0 20 20 28 00 00 03 00 08 00 00 03
000001c0h: 01 90 78 B1 6C B0 01 00 06 68 EB E3 CB 22 C0 FD
000001d0h: F8 F8 00
000001a0h: 01 64 00 1E FF E1 00 1E 67 64 00 1E AC D9 40 BC
000001b0h: 35 E7 F0 16 A0 20 20 28 00 00 03 00 08 00 00 03
000001c0h: 01 90 78 B1 6C B0 01 00 06 68 EB E3 CB 22 C0 FD
000001d0h: F8 F8 00

那么如果 AVCPacketType 为 1 的话,视频数据部分的结果会是怎样的呢?此时,其视频数据体部分会包含一系列的 Nalu 数据:

类型说明
UI32(具体由 lengthSizeMinusOne 决定,若 lengthSizeMinusOne 值为 3,则 NALUnitLength 占 4bytes,也就是 UI32)Nalu 长度
UI8[n]Nalue 数据
......

可见,相应的数据结构要简单很多。

Audio Tag Body

顺着示例文件,现在又来到了一个新的 Tag 面前,在解析之前还是先读取一下表示上个 Tag 长度的四个字节:

plaintext
000001d3h: 00 00 00 43
000001d3h: 00 00 00 43

然后就是 Tag 的头部部分:

plaintext
000001d7h: 08 00 00 07 00 00 00 00 00 00 00
000001d7h: 08 00 00 07 00 00 00 00 00 00 00

第一个字节 0x08 表明这个标签的类型为 Audio,0x000007 表示数据部分有 7 个字节,其余部分为零。

音频标签的 Body 部分主要由 AudioTagHeader 和 AudioData 两部分组成,其头部主要包括音频的描述信息:

Field类型说明
SoundFormatUB[4]音频编码格式,10 = AAC,详情见附录
SoundRateUB[2]采样率:0 = 5.5 kHz,1 = 11 kHz,2 = 22 kHz,3 = 44 kHz
SoundSizeUB[1]采样大小:0 = 8-bit samples,1 = 16-bit samples
SoundTypeUB[1]音频类型:0 = Mono sound,1 = Stereo sound
AACPacketTypeUI8(SoundFormat == 10)AAC 包类型:0=AAC sequence header,1=AAC raw

对于 AudioTag 的 AudioData 部分会根据编码格式和包类型的不同而不同:

  • SoundFormat 为 10 时:
    • AACPacketType 为 0,表示在 ISO 中定义的 AudioSpecificConfig;
    • AACPacketType 为 1,表示 AAC RAW Frame 的 UI8 数组数据。
  • SoundFormat 不为 10 时,Varies by format

根据上面的描述,继续从示例文件中读取数据分析:

plaintext
000001e2h: AF 00
000001e2h: AF 00

0xAF 即 0b10101111,前四位 0b1010 表示当期音频的编码格式为 AAC,紧跟在后面两位 0b11 表示采样率为 44kHz,第七位表示位深为 16bit,最后一位表示音频类型为 Stereo sound。

由于 SoundFormat 为 10 且 AACPacketType 为 0,所以接下来的部分表示 AudioSpecificConfig:

Field类型说明
audioObjectTypeUB[5]音频对象类型
samplingFrequencyIndexUB[4]使用的采样率下标,根据下标可以查出采样率
channelConfigurationUB[4]声道数
frameLengthFlagUB[1]标志位,表明 IMDCT 窗口长度
dependsOnCoreCoderUB[1]标志位,表明是否依赖 corecoder
extensionFlagUB[1]为零

那么如果这里 AACPacketType 为 1 的话,音频数据部分的结果会是怎样的呢?此时,其音频数据体部分皆为 AAC 裸数据。

总结

FLV 文件由 FLV header 和 FLV File Body 两部分组成,后者由一系列的 Tag 组成,每个 Tag 前又有一个 previousTagSize 字段,标记着前面一个 Tag 的大小。

FLV Framework

其中 Tag 根据类型可分为 “Audio Tag”、”Video Data”、”Script Data” 三种。

FLV Tag

每个 Tag 的 Tag Header 结构基本相同,但 Tag Data 部分的组成各异,更多详细信息可参考 FLV 标准文档。

附录

包括 SCRIPTDATAVALUE 数据类型、FLV 元数据对象属性和视频帧类型等信息的说明。

SCRIPTDATAVALUE 类型(其它 4 种)

SCRIPTDATAVALUE 记录了包含类型化的 ActionScript 值。

SCRIPTDATADATE 类型:

Field类型说明
DateTimeDOUBLE1970 年 1 月 1 日以来的毫秒数(UTC)
LocalDateTimeOffsetSI16与 UTC 的本地时间偏移(以分钟为单位),东时区为正,西时区为负

SCRIPTDATALONGSTRING 类型:

Field类型说明
StringLengthUI32StringData 长度(多少个字节)
StringDataSTRINGString 数据,没有 NULL 结束符

SCRIPTDATAOBJECT 类型:

Field类型说明
ObjectPropertiesSCRIPTDATAOBJECTPROPERTY [ ]对象属性列表
List TerminatorSCRIPTDATAOBJECTENDList 结束符

SCRIPTDATASTRICTARRAY 类型:

Field类型说明
StrictArrayLengthUI32数组中的长度
StrictArrayValueSCRIPTDATAVALUE [ StrictArrayLength ]List of typed values

常见的元数据属性

FLV 元数据对象应包含在名为 onMetadata 的 SCRIPTDATA 标记中。可用属性根据创建 FLV 文件的软件而有所不同。典型属性包括:

Field类型说明
audiocodecidNumber音频编码方式
audiodatarateNumber音频码率
audiodelayNumber音频的延时(单位:秒)
audiosamplerateNumber音频采样率
audiosamplesizeNumber音频采样精度
canSeekToEndBoolean是否可定位到最后的关键帧
creationdateString创建日期和时间
durationNumber时长
filesizeNumber文件大小
framerateNumber视频帧率
heightNumber视频高度
stereoBoolean是否为立体声
videocodecidNumber视频编码方式
videodatarateNumber视频码率
widthNumber视频宽度

视频帧类型和编码

在 VideoTag 中 FrameType 指明了视频帧的类型,下面的值为主要的定义:

  • 1: 关键帧
  • 2: P 或 B 帧
  • 3: 仅用于 H.263
  • 4: 生成关键帧(服务器端使用)
  • 5: 视频信息/命令帧

CodecID 的类型定义,下面是对应的编码值与对应的编码:

  • 2: Sorenson H.263
  • 3: Screen video
  • 4: On2 VP6
  • 5: On2 VP6 with alpha channel
  • 6: Screen video version 2
  • 7: AVC

音频编码格式

在 AudioTag 中 SoundFormat 指明了音频编码格式,下面的值为主要的定义:

  • 0: Linear PCM, platform endian
  • 1: ADPCM
  • 2: MP3
  • 3: LInear PCM, little endian
  • 4: Nellymoser 16kHz mono
  • 5: Nellymoser 8kHz mono
  • 6: Nellymoser
  • 7: G.711 A-law logarithmic PCM
  • 8: G.711 mu-law logarithmic PCM
  • 9: reserved
  • 10: AAC
  • 11: speex
  • 14: MP3 8kHz
  • 15: Device-specific sound

传递方式

FLV 视频格式具有占有率低、视频质量良好、体积小等特点适合网络发展,并可以通过以下几种不同的方式传递:

  • 作为独立的 FLV 文件,可以从本地存储(如硬盘或 CD)播放或转换为其他格式;
  • 使用 Flash 创作工具(Flash Player 6 及更高版本支持)嵌入到 SWF 文件中;
  • 使用 Flash 媒体服务器、VCS 或开源 Red5 服务器,通过 RTMP 流式传输到 Flash 播放器;
  • 通过 HTTP 渐进式下载(Flash Player 7 及更高版本支持)。此方法使用 ActionScript 包含外部托管的 FLV 文件客户端进行回放。

渐进式下载有几个优点,包括缓冲、使用通用的 HTTP 服务器,以及对多个 FLV 源重用单个 SWF 播放器的能力。

参考

Developed by Kisstar & Powered by VitePress.