上一篇文章我们就FLV文件格式做了一个分析,这篇文章,我们结合实际的FLV文件对照着FLV的结构来一一分析一遍。
我们用UltraEdit打开一个FLV文件,我们看到的内容大概是这样的:
其中选中的9个字节就是FLV header 我们通过最右边的字符表述可以很清楚的知道。
头三个字节 0x46 0x4C 0x56 正好是FLV三个字母的ASCII码的值。
接下来的一个字节 0x01 就是Version字段。对于FLV版本是1
第五个字节0x05 用二进制表达就是 ‘0101’b。也就是
TypeFlagsAudio = 1
TypeFlagsVideo= 1
表明这个文件包含音频流和视频流。
第六个字节但第九个字节为 0x00000009 大端模式,所以值就是9。与协议一致。就是FLV header结构的字节长度。
接下来的就是FLV File Body了,第一个四字节是PreviousTagSize0。我们看到是 0x00000000。因为在这个字段之前是没有TAG的,所以固定为0。
好了,现在FLV Header + PreviousTagSize0 = 13 Bytes。也就是从第十四个字节开始就会是我们的第一个FLV TAG了。
我们先来一张FLV TAG的结构图
我先取到前面十一个字节的内容
第一个字节的内容是0x12 我们转换为二进制就是 ‘0001 0010’。 看得到,前三个位是0。意味着Filter ==0;
也就是TagType == 0x12 表示成10进制就是 18 (script data)。就是说这是一个脚本Tag。
后面三个字节对应DataSize == 0x0002A9 。我们把它转换为十进制数是 681。注意DataSize的定义。是StreamID之后的数据长度。所以整个TAG的长度应该是681 + 11 = 692。从地址 0x0000000D 到 0x000002C0
图片中被选中的内容就是这个ScriptTAG的内容。
因为TagType == 18所以Data字段的内容就是 SCRIPTDATA
第一个域是Name。Name又是SCRIPTDATAVALUE类型
第一个字节Type。
Type == 0x02, String 类型
ScriptDataValue 是SCRIPTDATASTRING类型。
然后再接下来的两个字节是后面的String的长度。 StringLength == 0x000A , 10个字节。
StringData的内容是 0x6F 0x6E 0x4D 0x65 0x74 0x61 0x44 0x61 0x74 0x61 也就是 “onMetaData”。
再接下来一个字节又是Type。Type ==0x08 对应的数据类型是 SCRIPTDATAECMAARRAY
0x08 后面是 ECMAArrayLength == 0x00000019。 所以这个数组长度为 25。
ECMAArrayLength后面就是SCRIPTDATAOBJECTPROPERTY类型的数字。长度25。
PropertyName 是SCRIPTDATASTRING类型,先看前两字节
Length == 0x000F。15个字节。所以这个PropeotyName就是
对应的值是 “metadatacreator”。
PropertyData是一个SCRIPTDATAVAULE类型。
Type ==0x02。ScriptDataString类型。
StringLength == 0x0003。三个字节。
StringData == 0x696B75。“iku”。
接下来看第二个SCRIPTDATAOBJECTPROPERTY
PropertyName.StringLength == 0x000C。长度为12
PropertyName.StringData == “68 61 73 4B 65 79 66 72 61 6D 65 73”H : hasKeyframes
PropertyData.Type== 0x01。Boolean
PropertyData.ScriptDataValue==0x01。表示有KeyFrames信息。
**这里我们boolean型 0x01定义为yes,boolean型的0x00 定义为no。
到现在我们得到的Propertys有
metadatacreator = “iku”
hasKeyFrames = yes
接下来我就不一个一个分析了。直接把这里所以的Propertys的结果列出来
metadatacreator = “iku”
hasKeyFrames = yes
hasVideo = yes
hasAudio = yes
hasMetadata = yes
canSeekToEnd = no
dutation = 0x 40 41 0B 22 D0 E5 60 42(double 类型) (34.087 Secends)
datasize = 0x 41 2C 78 54 00 00 00 00 (double 类型) (932906.0 bytes)
videosize =0x 41 28 0B 34 00 00 00 00 (double 类型) (787866.0 bytes)
videocodecid = 0x401C000000000000 (7.0) (AVC)
width = 0x4080000000000000 (512.0)
height =0x4072000000000000 (288.0)
framerate = 0x402DFB6B354139DD (14.99) 也就是15帧每秒
videodatarate = 0x4066691F064D11D2 (179.28kbps)
audiosize = 0x410118A000000000 (140052.0 bytes)
audiocodecid = 0x4024000000000000 (10.0) (AAC)
audiosamplerate = 0x 40 E5 88 80 00 00 00 00(44100.0 kHz)
audiosamplesize = 0x4030000000000000 (16.0 bits)
stereo = yes
audiodatarate = 0x403E3E4E7394F6F6 (30.24kbps)
filesize = 0x412C7DDE00000000 (933615.0 bytes)
lasttimestamp = 0x40410B22D0E56042 (34.087)
lastkeyframtimestamp = 0x403E000000000000 (30.0)
lastkeyframlocation = 0x412B0DC400000000 (886498.0)
keyframes.propertyName = filepositions
keyframs.filepositions.StrictArrayLength = 0x00 00 00 07 (数组大小为7)
keyframs.filepositions[0] =0x4086280000000000 709.0
keyframs.filepositions[1] = 0x4088C80000000000 793.0
keyframs.filepositions[2] = 0x41073B3800000000 190311.0
keyframs.filepositions[3] = 0x411740D400000000 380981.0
keyframs.filepositions[4] = 0x412129DA00000000 562413.0
keyframs.filepositions[5] = 0x41269FE800000000 741364.0
keyframs.filepositions[6] = 0x412B0DC400000000 886498.0
keyframs.times.StrictArrayLength = 0x00 00 00 07 (数组大小为7)
keyframs.times[0] = 0x 00 00 00 00 00 00 00 00 (0.0)
keyframs.times[1] = 0x 00 00 00 00 00 00 00 00 (0.0)
keyframs.times[2] = 0x4018000000000000 (6.0)
keyframs.times[3] = 0x 40 28 00 00 00 00 00 00 (12.0)
keyframs.times[4] = 0x 40 32 00 00 00 00 00 00 (18.0)
keyframs.times[5] = 0x 40 38 00 00 00 00 00 00 (24.0)
keyframs.times[6] = 0x 40 3E 00 00 00 00 00 00 (30.0)
注: 这里的double的值都是通过 Floating Point to Hex Converter 这个在线工具计算得出的。
最后的几个字节内容是结束符。再后面四个字节是PreviousTagSize
可以看到
previousTagSize = 0x 0002b4 = 692 正好与我们前面计算的ScriptTag的长度一致。至此,第一个FLVTAG分析完了。然后我们获得了很大信息。从onMetaData这个Script TAG中。
接下来的就是VidoeTag: 从第一个字节 0x09 获知TagType = video。
我们先取从 0x000002C5 开始的11个字节的内容。
解析之后对应字段的值如下:
Filter = 0;
TagType = 9 (Video)
DataSize = 0x00002D (45) 也就是从0x000002D0 开始后的45个字节是这个TAG余下的内容。到0x000002FC。
Timestamp = 0
TimestampExtended = 0
StreamID = 0
通过前TagType == 9 我们已经确定了是视频TAG,那么接着StreamID字段之后的就是VideoTAagHeader
先看第一个字节的内容吧
第一个字节是 0x17。即
FrameType= 0x01
CodecID = 0x07
因为CodecID == 7。所以这个时候会多出两个字段 AVPacketType 和CompositionTime。两字段共四个字节。
解析出来的值分别是:
AVCPacketType = 0 ;说明后面的内容是AVC sequence header
CompositionTime = 0;
当AVCPacketType == 0 CodecID== 7 的时候。那么VideoTagBody的 Type是 AVCVIDEOPACKET 类型
也就是这个时候应该是一个AVCDecoderConfigurationRecord结构
configurationVersion = 0x01
AVCProfileIndication = 0x4D (77) Main Profile
profile_compatibiltity = 0x40
AVCLevelIndication = 0x1F (31)
第五六字节是0xFFE1 ,写成二进制格式 ‘1111 1111 11110 00001’b
对应到AVCDecoderConfigurationRecord的语法定义
lengthSizeMinusOne = ‘11’b (3) 也就是NALUintLength字段会是4个字节
numOfSequenceParameterSets = ‘00001’b 有一个Sps结构
接下来16bits 是 sequenceParameterSetLength = 0x0019 (25 bytes)
下图选中部分就是Sps了
再往下:
numOfPictureParamterSets = 0x01
pictureParameterSetLength = 0x0004;
下图选中的就是pps了
再后面四个字节是PreviousTagSize= 0x00 00 00 38 (56) 等于这个Tag的 DataSize + 11 == (45) + 11。
再下来又是一个新的FLVTAG了。
11个字节的头部先取出来
TagType = 8 (音频)
DataSize =0x000009 ( 9bytes)
开头是一个AudioTagHeader结构
SoundFormat(4bits) = 0x0A (10 == AAC)
SoundRate(2bits) = ‘11’b (3 == 44kHz)
SoundSize(1bit) =’1’b (1 == 16-bit)
SoundType(1bit) = ‘1’b (1= Stereo)
注: 虽然这里SoundRate, SoundSize SoundType 都是 1 。但是这些都是定值,AAC格式的时候,不看这里的值,可以忽略掉。具体的真是值应该从后面的数据从获取
AACPacketType == 0x00 (AAC seqence header)
所以AACPacketType后面的就是AudioSpecificConfig了 0x13 90 56
AudioSpecificConfig.audioObjectType(5 bits) = 2 (AAC LC)
AudioSpecificConfig.samplingFrequencyIndex(4 bits) = 7
AudioSpecificConfig.channelConfiguration (4 bits)= 2
AudioSpecificConfig.GASpecificConfig.frameLengthFlag (1 bit) = 0
AudioSpecificConfig.GASpecificConfig.dependsOnCoreCoder : (1 bit) = 0
AudioSpecificConfig.GASpecificConfig.extensionFlag : (1 bit) = 1
剩下的四个字节就是extensionflag3的相关内容,这块还没有研究过。
到这一个audioTag结束。
后面的0x00000014是这个AudioTag的长度 等于 20 = 9 + 11。
再后面又是一个新的TAG。
从这前11个字节知道的是这是一个视频Tag. DataSize = 0x00099F (2463)timeStamp == 0;
然后再往后看,是一个VideoTagHeader结构,可以得到的信息如下
FrameType = 1 (是一个Key Frame)
CodecID= 7 (AVC)
AVCPacketType = 1 ;是一个普通的AVC NALUint
CompositionTime = 0x000043 (67)
然后从0x00000329 —— 0x00000CC2 就是一个完整的 Key Frame的数据了。
那么这个Key Frame包含多少个NALUint呢,我们再来一步步看下去吧。记得前面我们分析的吗?NALUint数据的开头的NALUintLength字段。由之前的分析可知。占四个字节,
NALUintLength = 0x00000222 (546 Bytes) 说明第一NALUint的长度是546字节
从0x0000032D —— 0x0000054E
接着是第二个NALUintLength = 0x00 00 05 F3 (1523 bytes)
从0x00000553 —— 0x00000B45
接下来又是一个NALUintLength = 0x00 00 01 0B (267 bytes)
从0x00000B4A —— 0x00000C54
下来又是一个NALUintLenth = 0x00 00 00 32 (50 bytes)
从0x00000C59 —— 0x00000C8A
接下来还是一个NALUintLength = 0x00 00 00 34 (52 byte)
从 0x00000C8F —— 0x00000CC2
好了,到这里我们第一个KeyFrame的所有NALUint都已经取出来了
TagDataSize (2463 Bytes)= 1 (FrameType + CodecID) + 1 (AVCPacketType) + 3 (CompositionTime) + 4 + 546 +4 + 1523 + 4 + 267 + 4 + 50 + 4 + 52
等式成立。
这个VidoeTag后面是四字节的PreviousTagSize = 0x00 00 09 AA = 0x00 00 09 9F + 0x0B;
再往回又是一个TAG了。先取开头11个字节
解析一次,我们可以知道
TagType = 8;
DataSize = 0x 00 00 27 (39bytes)
Timestamp= 0
所以余下的AudioTag的内容从 0x00000CD2 —— 0x00000CF8
分析可得以下信息
SoundFormat = 0x0A (10 AAC)
SoundRate= 1
SoundSize =1
SoundType =1
AACPacketType = 1 (AAC raw data)
所以AAC raw data为下图所示
再往下分析又是一个AudioTag 从0x00000CF8 —— 0x00000DC0
这个AudioTag的 DataSize = 0xB9 (185 byte) 除去 0xAF01 这两个字节的AudioTagHeader。
AAC raw data有185 字节
Timestamp = 0x00002E
TimestempExtended = 0x00
总起来TimeStampOffset = 0x0000002E ( 46 milcoSecends)
再后面是一个视频帧了 从0x00000DC5 —— 0x00001EA8
DataSize == 0x00 10 D9
Timetamp = 0x00 00 43 (67)
TimestampExtended = 0x00
我们稍微分析下这个Tag的VideoTagHeader吧
FrameType= 2 (inter Frame, for AVC a non-seekable frame)
CodecID = 7
AVCPacketType =1 (AVC NALU)
CompositionTime = 0x00010A (266)
NALUintLength0 = 0x000008AE (2222Bytes)
NALUint0 到 0x00001686结束
NALUintLength1 = 0x00000188 (392 bytes)
NALUint1 到 0x00001812 结束
NALUintLength2 = 0x00000513 (1299 bytes)
NALUint2 到0x00001D29 结束
NALUint3Length = 0x00 00 01 7B(379 Bytes)
NALUint3 到 0x00001EA8 结束 (这里正好是这个videoTag的结尾)
好了,后面就不再分析下去了。基本就是这个节奏一直走先去就能分析到文件的结尾了。