简介
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⽂件最多只有⼀个⾳频流,⼀个视频流,不存在多个 独⽴的⾳视频流在⼀个⽂件的情况。
00000 1 0 1
FLV头的结构如下:
Field | Type | Comment |
---|---|---|
签名 | UI8 | ‘F’(0x46) |
签名 | UI8 | ‘L’(0x4C) |
签名 | UI8 | ‘V’(0x56) |
版本 | UB5 | FLV的版本。0x01表示FLV版本为1 |
保留字段 | UB1 | 前五位都为0 |
⾳频流标识 | UB1 | 是否存在⾳频流 |
保留字段 | UB1 | 为0 |
视频流标识 | UB1 | 是否存在视频流 |
⽂件头⼤⼩ | UI32 | FLV版本1时填写9,表明的是FLV头的⼤⼩,为后期的 FLV版本扩展使⽤。包括这四个字节。数据的起始位置 就是从⽂件开头偏移这么多的⼤⼩。 |
FLV Body
FLV Header之后,就是FLV File Body。FLV File Body是由⼀连串的back-pointers + tags构成。 Back-pointer表示Previous Tag Size(前⼀个tag的字节数据⻓度),占4个字节。
计算出⼤⼩ 383= 0x0000017F
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:audeo 9:video 18:Script data(脚本数据) all 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和TimestampExtended拼出来的是dts。也就是解码时间。 Timestamp和TimestampExtended拼出来dts单位为ms。(如果不存在B帧,当然dts等于 pts)
- CompositionTime 表示PTS相对于DTS的偏移值, 在每个视频tag的第14~16字节, 。 显示时间(pts) = 解码时间(tag的第5~8字节) + CompositionTime CompositionTime的单位也是ms
Script data脚本数据就是描述视频或⾳频的信息的数据,如宽度、⾼度、时间等等,⼀个⽂ 件中通常只有⼀个元数据,⾳频tag和视频tag就是⾳视频信息了,采样、声道、频率,编码等 信息。
Script data脚本数据就是描述视频或⾳频的信息的数据,如宽度、⾼度、时间等等,⼀个⽂ 件中通常只有⼀个元数据,⾳频tag和视频tag就是⾳视频信息了,采样、声道、频率,编码等信息。
Script Tag Data结构(脚本类型、帧类型)
该类型Tag⼜被称为MetaData Tag,存放⼀些关于FLV视频和⾳频的元信息,⽐如:duration、width、 height等。通常该类型Tag会作为FLV⽂件的第⼀个tag,并且只有⼀个,跟在File Header后。该类型Tag DaTa的结构如下所示(source.200kbps.768x320.flv⽂件为例):
第⼀个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对于库的版本
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信息)。
Field | Type | Comment |
---|---|---|
音频数据 | UI[8*n] | if SoundFormat == 10 (AAC类型) AACAUDIODATA else Sound data—varies by format |
AAC AUDIO DATA
The AudioSpecificConfig is explained in ISO 14496-3. AAC sequence header存放的是 AudioSpecificConfig结构,该结构则在“ISO-14496-3 Audio”中描述。
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 VP6FLVVIDEOPACKET If CodecID == 5 VP6FLVALPHAVIDEOPACKET If CodecID == 6 SCREENV2VIDEOPACKET if CodecID == 7 (AVC格式) AVCVIDEOPACKET |
AVCVIDEOPACKET
- CompositionTime 单位毫秒 CompositionTime 每个视频tag(整个tag)的第14~16字节(如果是tag data偏移[3]~[5], [0],[1][2:AVCPackettype] )(表示PTS相对于DTS的偏移值 )。 CompositionTime 单位为ms : 显示时间 = 解码时间(tag的第58字节,位置索引[4][7]) + CompositionTime
- AVCDecoderConfigurationRecord AVC sequence header就是AVCDecoderConfigurationRecord结构,该结构在标准⽂ 档“ISO-14496-15 AVC file format”
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字节(表示PTS相对于DTS的偏移值 )
CompositionTime 单位为ms 显示时间 = 解码时间(tag的第5~8字节) + 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…