FLV概述
-
FLV(Flash Video)是Adobe公司推出的⼀种流媒体格式,由于其封装后的⾳视频⽂件体积⼩、封装简单等特点,⾮常适合于互联⽹上使⽤。⽬前主流的视频⽹站基本都⽀持FLV。采⽤FLV格式封装的⽂件后缀为.flv。
-
FLV封装格式是由⼀个⽂件头(
file header
)和 ⽂件体(file Body
)组成。其中,FLV body由⼀对对的(Previous Tag Size字段 + tag)组成。Previous Tag Size字段 排列在Tag之前,占⽤4个字节。Previous Tag Size记录了前⾯⼀个Tag的⼤⼩,⽤于逆向读取处理。FLV header后的第⼀个Pervious Tag Size的值为0。 -
Tag⼀般可以分为3种类型:脚本(帧)数据类型、⾳频数据类型、视频数据。FLV数据以⼤端序进⾏存储,在解析时需要注意。
⼀个标准FLV⽂件结构如下图:
FLV⽂件的详细内容结构如下图:
大体的解析框架
FLV header
注:在下⾯的数据type中,UI表示⽆符号整形,后⾯跟的数字表示其⻓度是多少位。
-
⽐如UI8,表示⽆符号整形,⻓度⼀个字节。UI24是三个字节,
UI[8*n]
表示多个字节。 -
UB表示位域,UB5表示⼀个字节的5位。可以参考c中的位域结构体。
-
FLV头占9个字节,⽤来标识⽂件为FLV类型,以及后续存储的⾳视频流。
-
⼀个FLV⽂件,每种类型的tag都属于⼀个流,也就是⼀个flv⽂件最多只有⼀个⾳频流,⼀个视频流,不存在多个独⽴的⾳视频流在⼀个⽂件的情况。
FLV头的结构如下:
Field Type | Comment |
---|---|
UI8 | 签名 ‘F’ (0x46) |
UI8 | 签名 ‘L’ (0x4C) |
UI8 | 签名 ‘V’ (0x56) |
UI8 | FLV 版本。0x01 表示 FLV 版本为 1 |
UB5 | 保留字段,前五位均为 0 |
UB1 | 音频流标识:1 表示存在音频流,0 表示不存在 |
UB1 | 保留字段,固定为 0 |
UB1 | 视频流标识:1 表示存在视频流,0 表示不存在 |
UI32 | 文件头大小。 FLV 版本 1 时填写 9(包括这四个字节),用于后续版本扩展 |
FLV Body
FLV Header
之后,就是FLV File Body
。FLV File Body
是由⼀连串的back-pointers + tags
构成。Back-pointer
表示Previous Tag Size
(前⼀个tag的字节数据⻓度),占4个字节。
FLV Tag
- 每⼀个Tag也是由两部分组成:
tag header
和tag data
。 Tag Header
⾥存放的是当前tag
的类型、数据区(tag data
)的⻓度等信息。
tag header
tag header
⼀般占11个字节的内存空间。FLV tag
结构如下:
Field | Type | Comment |
---|---|---|
Tag类型 Type | UI8 | 8: audio 9: video 18: Script data(脚本数据) Others: reserved(其他所有值未使用) |
数据区大小 | UI24 | 当前 tag 的数据域的大小,不包含 tag header。 Length of the data in the Data field |
时间戳 Timestamp | UI24 | 当前帧时戳,单位是毫秒。相对值,第一个 tag 的时戳总是为 0 |
时戳扩展字段 TimestampExtended | UI8 | 若时戳大于 0xFFFFFF,将使用该字节。该字节是时戳的高 8 位,前三个字节是低 24 位。 |
StreamID | UI24 | 总是为 0 |
数据域 | UI[8*n] | 数据域数据 |
**注意:
-
flv
⽂件中Timestamp
和Timestamp Extended
拼出来的是dts
。也就是解码时间。Timestamp
和Timestamp Extended
拼出来dts
单位为ms
。(如果不存在B
帧,当然dts
等于pts
) -
CompositionTime
表示PTS
相对于DTS
的偏移值, 在每个视频tag
的第14~16
字节, 显示时间(pts
) = 解码时间(tag
的第5~8
字节) +CompositionTime
,CompositionTime
的单位也是ms
-
Script data脚本数据就是描述视频或⾳频的信息的数据,如宽度、⾼度、时间等等,⼀个⽂件中通常只有⼀个元数据,⾳频
tag
和视频tag
就是⾳视频信息了,采样、声道、频率,编码等信息。
Script Tag Data
结构(脚本类型、帧类型)
该类型Tag⼜被称为MetaDataTag,存放⼀些关于FLV视频和⾳频的元信息,⽐如:duration
、width
、height
等。通常该类型Tag
会作为FLV
⽂件的第⼀个tag
,并且只有⼀个,跟在File Header
后。该类型Tag Data
的结构如下所示(source.200kbps.768x320.flv⽂件为例):
1. AMF 的定义与本质
- 全称:Action Message Format(动作消息格式),是 Adobe 为 Flash 技术栈设计的二进制数据交换格式。
- 作用:在 FLV 中,AMF 包用于封装脚本数据(对应 FLV 中的
Script Data Tag
,即类型为18
的 Tag),例如视频元数据、播放控制指令、自定义脚本逻辑数据等。
2. FLV 中 AMF 包的典型应用场景
- 元数据传输:
存储视频的元信息,如视频标题、作者、时长、视频宽高、音频采样率等。例如,FLV 文件中常通过 AMF 包传递onMetaData
数据,播放器解析这些数据后,可展示视频相关信息。 - 脚本逻辑交互:
承载 ActionScript 代码需要的结构化数据,支持复杂对象(如数组、对象嵌套)的传输,实现播放流程控制(如跳转关键帧、获取播放状态等)。
-
第⼀个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
类型值,表示数组元素的个数。后⾯即为各数组元素的封装,数组元素为元素名称和值组成的对。常⻅的数组元素如下表所示。
值 | Comment | 例如 |
---|---|---|
duration | 时长(秒) | 210.732 |
width | 视频宽度 | 768.000 |
height | 视频高度 | 320.000 |
videodatarate | 视频码率 | 207.260 |
framerate | 视频帧率 | 25.000 |
videocodecid | 视频编码ID | 7.000 (H264为7) |
audiodatarate | 音频码率 | 29.329 |
audiosamplerate | 音频采样率 | 44100.000 |
stereo | 是否立体声 | 1 |
audiocodecid | 音频编码ID | 10.000 (AAC为10) |
major_brand | 格式规范相关 | isom |
minor_version | 格式规范相关 | 512 |
compatible_brands | 格式规范相关 | isomiso2avc1mp41 |
encoder | 封装工具名称 | Lavf54.63.104 |
filesize | 文件大小(字节) | 6636853.000 |
注:Lavf54.63.104即是 Libavformat version 54.63.104. 即是ffmpeg对于库的版本
在 AMF 数据结构中,每个数据项(包括键值对中的值)的格式都是 字符串名字(2字节)+类型标识(1 字节) + 数据内容。具体来说:
1.字符串名字(2字节)
- 表示每个key的名字,如
duration
等等
1. 类型标识(2 字节)
- 每个数据项的第一个字节固定为 类型标识,用于明确后续数据的格式。例如:
0x02
→ 字符串类型0x00
→ 数值类型(双精度浮点数)0x01
→ 布尔类型0x03
→ 对象类型- …(其他类型见 AMF 规范)
3. 数据内容
- 根据类型标识的不同,后续数据的格式和长度也不同。例如:
- 字符串类型:
- 第 1 字节:
0x02
- 第 2 - 3 字节:UI16 表示字符串长度(如
0x000A
表示 10 字节) - 后续字节:具体字符(如
onMetaData
共 10 字节)
- 第 1 字节:
- 数值类型(双精度浮点数):
- 第 1 字节:
0x00
- 后续 8 字节:IEEE 754 双精度浮点数(如
0x4048000000000000
表示 640.0)
- 第 1 字节:
- 布尔类型:
- 第 1 字节:
0x01
- 第 2 字节:
0x00
(false)或0x01
(true)
- 第 1 字节:
- 字符串类型:
4. 键值对的结构
- 在元数据数组中,每个键值对的格式为:
- 键(字符串):类型标识
0x02
+ 字符串长度 + 字符串内容。 - 值:根据值的类型,以对应类型标识开头,后跟数据内容。
- 键(字符串):类型标识
示例解析
假设元数据数组中有一个键值对 "width": 640
,其二进制结构如下:
- 键(“width”):
- 字符串长度:2字节
- 类型标识:
0x02
- 长度:
0x0005
(5 字节) - 内容:
77,69,69,6B,74
(ASCII 字符 “width”)
- 类型标识:
- 值(640):
- 类型标识:
0x00
- 双精度浮点数:
4048000000000000
(对应十进制 640.0)
- 类型标识:
总结
每个数据项(无论是键还是值)都严格遵循 类型标识 + 数据内容 的格式,解析时只需按顺序读取类型标识,再根据类型读取对应长度的数据即可
Audio Tag Data结构 (音频类型)
⾳频Tag Data
区域开始的:
- 第⼀个字节包含了⾳频数据的参数信息
- 第⼆个字节开始为⾳频流数据。
(这两个字节属于tag的data部分,不是header部分)
第⼀个字节为⾳频的信息(仔细看spec发现对于AAC⽽⾔,⽐较有⽤的字段是SoundFormat),格式如下:
Field | Type | Comment |
---|---|---|
音频格式 SoundFormat | UB4 | 0 = Linear PCM, platform endian 1 = ADPCM 2 = MP3 3 = Linear PCM, little endian 4 = Nellymoser 16−kHz mono 5 = Nellymoser 8−kHz 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 8−Khz 15 = Device−specific sound |
采样率 SoundRate | UB2 | 0 = 5.5kHz 1 = 11kHz 2 = 22.05kHz 3 = 44.1kHz 对于AAC总是3。但实际上AAC是可以支持到48khz以上的频率(这个参数对于AAC意义不大)。 |
采样精度 SoundSize | UB1 | 0 = snd8Bit 1 = snd16Bit 此参数仅适用于未压缩的格式,压缩后的格式都是将其设为1 |
音频声道 SoundType | UB1 | 0 = sndMono 单声道 1 = sndStereo 立体声,双声道 对于AAC总是1 |
第⼆个字节开始为⾳频数据(需要判断该数据是真正的音频数据,还是音频config信息)
Filed | Type | Comment |
---|---|---|
音频数据 | UI[8*n] | if SoundFormat == 10 (AAC类型) AAC AUDIO DATA else Sound data—varies by format |
AAC AUDIO DATA
如果是AAC数据,如果他是AAC RAW
, tag data[3]
开始才是真正的AAC frame data。
- 配置信息
这两张表格定义了 AAC 音频配置信息(AudioSpecificConfig
)及其相关解析逻辑的语法结构,具体解析如下:
Table 1.15 - AudioSpecificConfig() 语法
语法元素 | 位数 | 记忆符 | 说明 |
---|---|---|---|
audioObjectType | - | - | 通过 GetAudioObjectType() 函数获取,用于标识音频对象类型(如 AAC 编码配置)。 |
samplingFrequencyIndex | 4 | bslbf | 采样率索引,占 4 位。若值为 0xf ,则使用扩展的 samplingFrequency (24 位)。 |
samplingFrequency | 24 | uimsbf | 仅在 samplingFrequencyIndex == 0xf 时出现,存储具体采样率数值。 |
channelConfiguration | 4 | bslbf | 声道配置,占 4 位,标识音频的声道数(如单声道、立体声等)。 |
sbrPresentFlag /psPresentFlag | - | - | 预留标志位(示例中暂未赋值,实际用于标识 SBR、PS 等扩展功能是否存在)。 |
作用:AudioSpecificConfig()
用于存储 AAC 音频的核心配置参数,包括编码类型、采样率、声道数等,是解码器解析音频数据的关键依据。
Table 1.16 - GetAudioObjectType() 语法
语法元素 | 位数 | 记忆符 | 说明 |
---|---|---|---|
audioObjectType | 5 | uimsbf | 音频对象类型,占 5 位。若值为 31 ,则通过扩展字段 audioObjectTypeExt 进一步解析。 |
audioObjectTypeExt | 6 | uimsbf | 仅在 audioObjectType == 31 时使用,扩展音频对象类型(最终值为 32 + audioObjectTypeExt )。 |
作用:GetAudioObjectType()
用于精确解析音频编码的具体配置文件(如 AAC LC、HE-AAC 等),通过 5 位基础值和可能的 6 位扩展值,覆盖更广泛的编码类型。
Video Tag Data结构(视频类型)
视频Tag Data开始的:
- 第⼀个字节包含视频数据的参数信息,
- 第⼆个字节开始为视频流数据。
第⼀个字节包含视频信息,格式如下:
Field | Type | Comment |
---|---|---|
帧类型 | UB4 | 1: keyframe (for AVC, a seekable frame)——h264的IDR,关键帧 2: inter frame (for AVC, a non-seekable frame)——h264的普通帧 3: disposable inter frame (H.263 only) 4: generated keyframe (reserved for server use only) 5: video info/command frame |
编码ID | UB4 | 使用哪种编码类型: 1: JPEG (currently unused) 2: Sorenson H.263 3: Screen video 4: On2 VP6 5: On2 VP6 with alpha channel 6: Screen video version 2 7: AVC |
第⼆个字节开始为视频数据 |
Field | Type | Comment |
---|---|---|
视频数据 | UI[8*n] | If CodecID == 2 H263VIDEOPACKET If CodecID == 3 SCREENVIDEOPACKET If CodecID == 4 VP6FLVIDEOPACKET If CodecID == 5 VP6FLVALPHAVIDEOPACKET If CodecID == 6 SCREENV2VIDEOPACKET if CodecID == 7 (AVC格式) AVCVIDEOPACKET |
AVCVIDEOPACKET
1. AVCPacketType(UI8,无符号 8 位整数)
- 作用:标识 AVC 数据包的类型。
- 取值及含义:
0
:表示 AVC 序列头(AVC sequence header),通常包含解码器配置信息(如AVCDecoderConfigurationRecord
)。1
:表示 AVC NALU(网络抽象层单元),即实际的视频编码数据(如视频帧的切片数据)。2
:表示 AVC 序列结束(AVC end of sequence),此时底层 NALU 序列结束标记可能不需要或不被支持。
2. CompositionTime(SI24,有符号 24 位整数)
- 作用:用于标识视频帧的合成时间偏移(仅在特定类型下有效)。
- 逻辑:
- 当
AVCPacketType == 1
时,该字段表示 NALU 对应的视频帧合成时间偏移。 - 其他情况下(
AVCPacketType
为0
或2
),该字段值为0
。
- 当
3. Data(UI8[n]
,无符号 8 位整数数组)
- 作用:存储不同类型 AVC 数据包的具体数据内容。
- 逻辑:
- 当
AVCPacketType == 0
时,存储AVCDecoderConfigurationRecord
(解码器配置记录,包含编码参数等信息)。 - 当
AVCPacketType == 1
时,存储一个或多个 NALU(可以是单个切片,不强制要求完整帧)。 - 当
AVCPacketType == 2
时,该字段为空(Empty
)。
- 当
(1)CompositionTime
- CompositionTime 每个视频
tag
(整个tag
)的第14~16
字节(如果是tag data偏移位置索引[2]~[4]
)(表示PTS
相对于DTS
的偏移值 )。 - CompositionTime 单位为ms : 显示时间 = 解码时间(
tag
的第5~8
字节,位置索引[4]~[7]
)+CompositionTime
(2)AVCDecoderConfigurationRecord
-
AVC sequence header
就是AVCDecoderConfigurationRecord
结构,该结构在标准⽂档“ISO-14496-15 AVC file format”
-
video
配置信息
1. AVCDecoderConfigurationRecord
基础字段
字段名称 | 字节数 | 说明 |
---|---|---|
configurationVersion | 1 | 配置版本,通常为 1 。 |
AVCProfileIndication | 1 | 指示 H.264 Profile(如 0x66 表示 Baseline Profile)。 |
profile_compatibility | 1 | 兼容性标识。 |
AVCLevelIndication | 1 | 指示 H.264 Level(如 0x1E 表示 Level 3.1)。 |
lengthSizeMinusOne | 1 | NALU 长度字段的字节数减 1(如 3 表示 NALU 长度用 4 字节存储)。 |
numOfSequenceParameterSets | 1 | SPS 的数量,通常为 1 。 |
2. SPS 数据
sequenceParameterSetLength
:2 字节,标识 SPS 数据的长度。sequenceParameterSetNALUnit
:长度由sequenceParameterSetLength
定义,存放实际的 SPS 字节数据。
3. PPS 数据
numOfPictureParameterSets
:1 字节,PPS 的数量,通常为1
。pictureParameterSetLength
:2 字节,标识 PPS 数据的长度。pictureParameterSetNALUnit
:长度由pictureParameterSetLength
定义,存放实际的 PPS 字节数据。
FLV时间戳计算
题记:时间戳将每⼀秒分成90000份,即将每⼀毫秒分成90份 在flv中直接存储的都是毫秒级,在TS存储的是时间戳级
- 其中TS、flv⼀般按照编码顺序排列
- ⼀个视频tag⼀般只包含⼀帧视频的码流
- 其中视频tag的时间戳对应的是解码时间戳(DTS/90)
当前序列:
- 编码顺序 I P P B B B…
- 对应帧号 0 1 5 3 2 4…
flv对每⼀个tag都规定了它将要播放的时间戳,每个时间戳都可以对应转换特性的时间
-
其中script(脚本)、video(视频)、audio(⾳频)的第⼀个tag的时间戳值都为0
-
时间戳占4个字节 其中第四个字节是⾼位 前三个字节是低位(每个tag的5~8字节):如
6E 8D A8 01 = 0x 01 6E 8D A8 = 24022440
-
CompositionTime 每个视频tag的第14~16字节(共3字节)(表示PTS相对于DTS的偏移值 )
-
CompositionTime 单位为ms 显示时间 = 解码时间(tag的第5~8字节(共3字节)) + CompositionTime
例如(注意显示时间最后⼀个字节是⾼位)
- tag0 (脚本) :时间戳为0
- tag1 (视频) :第⼀个视频时间戳 值为0 ⽆CompositionTime (头信息)
- tag2 (⾳频) :第⼀个⾳频时间戳 值为0
- tag3 (视频) :00 00 00 00 值:0 00:00:00:00 (解码时间) CompositionTime:0x 00 00 50 值:80 00:00:00:80 I帧 显示时间: 00:00:00: 80 poc=0
- tag4 (视频) :00 00 28 00 值:40 00:00:00:40 (解码时间) CompositionTime:0x 00 00 50 值:80 00:00:00:80 P帧 显示时间: 00:00:00: 120 poc=1
- tag5 (视频) :00 00 50 00 值:80 00:00:00:80 (显示时间) CompositionTime:0x 00 00 C8 值:200 00:00:00:200 P帧 显示时间: 00:00:00: 280 poc=5
- tag6 (⾳频) :00 00 50 00 值:80 00:00:00:80(显示时间)
- tag7 (⾳频) :00 00 67 00 值:103 00:00:00:103(显示时间)
- tag8 (视频) :00 00 78 00 值:120 00:00:00:120 (解码时间) CompositionTime:0x 00 00 50 值:80 00:00:00:80 B帧 显示时间: 00:00:00: 200 poc=3
- tag9 (⾳频) :00 00 7E 00 值:126 00:00:00:126(显示时间)
- tag10 (⾳频) :00 00 96 00 值:150 00:00:00:150(显示时间)
- tag11 (视频) :00 00 A0 00 值:160 00:00:00:160(解码时间) CompositionTime:0x 00 00 00 值:00 00:00:00:00 b帧 显示时间: 00:00:00: 160 poc=2
- tag12 (⾳频) :00 00 AD 00 值:173 00:00:00:173(显示时间)
- tag13 (⾳频) :00 00 C4 00 值:196 00:00:00:196(显示时间)
- tag14(视频) :00 00 C8 00 值:200 00:00:00:200(解码时间) CompositionTime:0x 00 00 28 值:40 00:00:00:40 b帧 显示时间: 00:00:00: 240 poc=4 我们可以看到 每个视频tag相差约40ms 刚好是25fps视频 每帧视频的播放时⻓
在上例中,我们会看到按照解码时间排列
- 编码顺序
I P P B B B......
- 对应帧号
0 1 5 3 2 4......