FLV
内部结构
FLV(Flash Video)是Adobe公司推出的一种媒体封装格式,适合流媒体传输。优点是封装简单,文件体积小。后缀为.flv。总体上看,FLV包括文件头(File Header)和文件体(File Body)两部分,其中文件体由一系列的Tag及Tag Size对组成。
1. FLV Header
– 内部结构图
– 关键栏位
- SigNature:开头三个字节,固定为 FLV 的ascii码(0x46 0x4c 0x56)
- Version:表示FLV的版本号,比如FLV版本1,则该栏位是0x01
- FlagsAudio:1表示有audio tag,0表示没有
- FlagsVideo:1表示有video tag,0表示没有
- DataOffset:表示FLV header的大小,单位为字节。对于FLV版本1,固定是9,也就是header总共9字节,有效字节有5个。
2. FLV file body
- 由一系列tag和previousTagSize组成(如图: FLV结构图);
- Tag的大小 = 11(tag header) + n(tag body),其中previousTagSize为4字节。
- PreviousTagSize0总是0;其它PreviousTagSizeN的值表示TagN的大小,用于逆向读取处理(因为flv没有同步字节)。
FLV tag
- 由tag header 和 tag body组成,tag header固定为11字节。如下图:
1. 类型
FLV tag分为3种类型:
- Video Tag:存放视频相关数据
- Audio Tag:存放音频相关数据
- Script Tag:存放音视频元数据(metadata),一般会是第一个tag,紧跟着FLV header,有且只有一个。
2. FLV tag header
对于FLV版本1,tag header固定为11字节。
– 内部结构图
– 关键栏位
- TagType:
tag类型,1字节tag类型 含义 0x8 该tag为audio tag 0x9 该tag为video tag 0x12 该tag为script tag 其它 保留 - DataSize:
tag body的大小,3字节(不包含tag header的11字节),所以整个tag大小 = DataSize + 11。 - TimeStamp:
与第一个tag的时间戳差值,单位为毫秒,3字节。对于视频来说,就是DTS,单位为ms。
第一个audio和video tag的Timestamp分别为0,亦表示解码顺序。 - TimeStampExtended:
时间戳的扩展字段,当Timestamp栏位不够用时,会启用这字段,表示最高八位。 - StreamID:
总是0。
3. Audio Tag Body
音频tag第一个字节包含音频数据的参数信息(固定的),从第二个字节开始为音频流数据。
– 内部结构图
– 关键栏位
- SoundFormat:音频格式。
ID format 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 logrithmic PCM 9 reserved 10 AAC 11 Speex 14 MP3 8 kHz 15 Device-specific sound - SoundRate:采样率。
ID 采样率 0 5.5 kHz 1 11 kHz 2 22 kHz 3 44 kHz - SoundSize:采样位宽。对于压缩过的音频,一般是16位。
ID 位宽 0 8 Bits 1 16 Bits - SoundType:声道类型。
ID 声道数 0 单声道 1 双声道 - SoundData:音频数据,
对于AAC音频,也就是SoundFormat为10的case。
从Tag的第二个字节起被称为AACAudioData,定义如下:字段 字段类型 字段含义 AACPacketType UI8 0: AAC sequence header 1: AAC raw data Data UI8[n] AACPacketType为0,AudioSpecificConfigAACPacketType为1,AAC帧数据
– Audio config tag
即AACPacketType为0,Tag body第1个字节为简略的参数信息,第2个字节为AAC packet type,从第3个字节开始为audio specific config data。相比于tag body第一个字节中的参数信息,AudioSpecificConfig提供了更加详细的音频信息,它的定义在ISO14496-3中1.6.2.1。
一般的AudioSpecificConfig有2字节定义如下:
|AAC Profile 5bits | 采样率 4bits | 声道数 4bits | 其他 3bits |
对于AAC audio tag,FLV文件中第一个audio tag是audio config tag。
如下图为一个audio config tag的例子:
audio config tag结构图如下:
– Audio raw data tag
即AACPacketType为1的case,Tag body第1个字节为简略的参数信息,第2个字节为AAC packet type,从第3个字节开始为AAC raw data,如下图:
4. Video Tag Body
视频tag body第一个字节包含视频数据的参数信息(固定),从第二个字节开始为视频数据。
– 内部结构图
– 关键栏位
- FrameType:帧类型
ID 帧类型 1 Keyframe;对于H264,IDR帧 2 Inter frame;对于H264,普通I帧 3 Disposable inter frame(H.263 only) 4 generated keyframe 5 video info (如sps、pps);
command frame,普通的帧 - Codec ID: 编解码器类型
ID 编解码器类型 1 JPEG 2 Sorenson H.263 3 Screen video 4 On2 VP6 5 On2 VP6 with alpha channel6 7 AVC - VideoData:视频数据
CodecID VideoDataType 2 H263VideoPacket 3 ScreenVideoPacket 4 VP6FlvVideoPacket 5 VP6FlvAlphaVideoPacket 6 ScreenV2VideoPacket 7 AVCVideoPacket 字段 字段类型 字段含义 AVCPacketType UI8 0:AVC sequence header,也就是sps和pps
1:AVC NALU
2:AVC end of sequenceCompositionTime SI24 AVCPacketType == 1,该栏位有效,表示pts与dts的差值,单位ms;
否则,为0Data UI8[n] AVCPacketType:
0–> AVCDecoderConfigurationRecord
1–> NALU(一个或多个)
2–> 空
– Video config tag
即AVCPacketType为0的case,Tag body第一个字节固定为0x17(key frame,AVC),packet data里面是该视频的SPS和PPS。
下图为实例:
– Video raw data tag
即AVCPacketType为1的case,Packet data里面是NALU。
下图是实例:
– video tag例子
– 关于CompositionTime(cts)
CompositionTime表示pts相对于该帧dts的差值,CompositionTime= (pts – dts) ms
当B帧存在时,视频帧的pts和dts可能不同,dts在flv中是指tag header中的timestamp。
所以视频帧的pts = CompositionTime + timestamp。
如果B帧不存在,则CTS固定为0,也就是CompositionTime这个栏位0,
即pts = timestamp。
5.Script Tag Body
- Script Tag通常用来存放跟FLV中音视频相关的元数据信息(onMetaData),比如时长、分辨率等。
采用AMF(Action Message Format)封装了一系列数据类型,如字符串,数值,数值等。 - AMF格式的有一系列Object和SCRIPTDATAOBJECTEND(0x9,表示script data结束),示意图如下:
- 在Script tag中一般包含2个AMF包。
解析出来的script tag为:1> 第一个AMF包,内容固定,分别为: ▪ 第1个字节:固定为0x02,表示字符串类型 ▪ 第2-3个字节:UI16类型,固定为0x000A,表示字符串的长度为10(onMetaData的长度); ▪ 第4-13个字节:字符串onMetaData对应的16进制数字(0x6F 0x6E 0x4D 0x65 0x74 0x61 0x44 0x61 0x74 0x61); (这个包与Adobe的一些API调用有关) 2> 第二个AMF包: ▪ 第1个字节:固定为0x08,表示数组类型; ▪ 第2-5个字节:UI32类型,表示数组的长度,onMetaData中具体包含哪些属性是不固定的。 ▪ 第6个字节+: 比如该AMF包表示duration,则: ○ 第6-9个字节:0x0008,表示长度为8个字节; ○ 第10-17个字节:0x64 75 72 61 74 69 6F 6E,表示 duration 这个字符串; ○ 第18个字节:固定为0x00,表示为数值类型; ○ 第19-26个字节:0x...,表示具体的时长;
那说明总共有26组元数据信息,比如duration为391.495s,分辨率为1280X720
实际应用遇到的问题
-
如何probe一个数据流为FLV类型?
满足以下三个条件:i. "FLV"。 数据流开头前3个字节是'F', 'L', 'V' --> FLV Header前3个字节固定是'F', 'L', 'V' ii. 保留位。 数据流第5个字节 & 0xf2的值为0 --> FLV Header前5个字节的高5位和第7位为保留位,固定为0 iii. 大小。 数据流第6个字节到第9个字节固定为 0x 00 00 00 09 --> FLV Header的长度固定为9个字节,记录与FLV Header最后4个字节。
-
如何获得FLV文件的duration?
首先,从Script tag中的duration metadata中获取duration。
然后,拿到第一个video tag的timestamp 和 最后一个video tag的timestamp,两者的差值就是duration(单位ms) -
如何识别一个tag,并且一个一个tag的读取数据?
识别方法是:先找到一个字节为0x08、0x09或者0x12,如果是tag的话,那么后面三个字节为datasize,在往后跳(datasize + 11 + 4)bytes,如果第一个字节还是0x08、0x09或者0x12,再往后跳(datasize + 11 + 4)bytes,如此循环判断五次,每次都找到0x08、0x09和0x12的话,那说明第一个找到的字节为一个tag的开始。如果有一个找不到的话,那就寻找下一个字节为0x08、0x09或者0x12,按照上述方式再进行判断。 -
如何seek?
▪ FLV官方标准对seek的支持不好,每次seek都必须从当前位置加载tag数据,直到目标位置,从而导致缓冲时间长,效果极差。
▪ 而较为常用但非官方的做法是在Script Tag中加入keyframe字段,用来建立关键帧时间点和文件位置的映射表。
▪ keyframe字段包含file positions和times两个数组,times为关键帧时间点数组,filepositions数组为关键帧在文件中偏移量数组。两个数组的长度相同,类型为double类型,且fileposition和time一一对应。借助keyframe字段,seek过程是:i. 根据用户想要seek的时间点,在times数组中比对最接近值,拿到该值对应数组索引,记为KeyframeIndex ii. 根据keyframeIndex,在fileposition数组中取得对应的偏移量,记为keyframeOffset iii. 从该偏移量开始读取数据即可完成seek播放。