FLV结构详解,FLV协议详解(泰山鲁 转载加修正)
FLV这种格式大家一定不会陌生,知道他的文件结构了,才可能读的懂前辈的代码,正好项目碰到了flv文件结构,我这里引用了网上的一些结构,加上自己翻译和改进,整理了这个文章,希望对对家有帮助
FLV是一个二进制文件,由文件头(FLV header)和很多tag组成。tag又可以分成三类:audio,video,script,分别代表音频流,视频流,脚本流(关键字或者文件信息之类)。出自百度-
FLV Header
一般比较简单,包括文件类型之类的全局信息
文件类型 | 3bytes | 总是FLV(0x46(F的ASC码,原注释看着让人头疼) 0x4C 0x56),否则..就不是 |
版本 | 1byte | 一般是0x01,表示FLV version 1 |
流信息 | 1byte | 倒数第一bit是1表示有视频,倒数第三bit是1表示有音频,其他都应该是0(有些软件如flvtool2可能造成倒数第四bit是1,不过也没发现有什么不对) |
header长度 | 4bytes | 整个文件头的长度,一般是9(3+1+1+4),有时候后面还有些别的信息,就不是9了 |
FLV Body
FLV body就是由很多tag组成的,一个tag包括下列信息:
文件头后紧跟的是文件内容,如下图所示(无耻的剽窃自别人的博客)
由上图可以看出,body部分由一个个Tag组成,每个Tag的下面有一块4bytes的空间用来记录这个tag的⻓度,这个后置用于逆向。当然第一个tag的长度肯定是0了(PreviosTagSize0为4个字节的0)
Tag 解析
每一个tag也是由两部分组成:tag头和tag数据区。
好了至此FLV格式分析结束,嘿嘿(裤子都脱了你就给看这个)。其实只是想强调下每个tag都是由 头+数据 组成。如下表所示,很明显tag头为11个字节。
tag头
tag头为下表中的前11个字节,剩下的为tag数据区(具体分析见2.1.2)
名称 | ⻓度 | 介绍 |
TagType(tag类型) | UI8 | 8: audio(音频) 9: video(视频) 18: script data(脚本数据) all others: reserved(其他保留) |
DataSize(tag数据区大小) | UI24 | tag的数据区大小以字节为单位,注意字节序 |
Timestamp(时间戳) | UI24(原文漏写了) | 本tag相对于FLV文件第一个tag的时间,以毫秒为单位,当然第一个tag的时间戳肯定为0(很重要) |
TimestampExtended(时间戳扩展) | UI8 | 将时间戳扩展至32位,该字节代表高8位(很重要) |
StreamID(信息流ID) | UI24 | 恒为0 |
Data(tag数据区) | If TagType == 8 AUDIODATA If TagType == 9 VIDEODATA If TagType == 18 SCRIPTDATAOBJECT | 由第一个字节的tag类型决定: 为8时代表音频 为9时代表视频 为18时代表脚本数据 |
TAG结构图示,整体的就看做一个结构的各成员(又一篇,tag头很重要我这引用了另外一篇方便大家比对tag头和)
previoustagsize | 4bytes | 前一个tag的长度,第一个tag就是0 |
tag类型 | 1byte | 三类:
|
数据区长度 | 3bytes | |
时间戳 | 3bytes | 单位毫秒,如果是脚本tag就是0 |
扩展时间戳 | 1byte | 作为时间戳的高位 |
streamsID | 3bytes | 总是0(不知道干啥用) |
数据区 | If TagType == 8 AUDIODATA If TagType == 9 VIDEODATA If TagType == 18 SCRIPTDATAOBJECT (这个原文一直不明什么东西,后来 查资料才知道是自定义的东西)
|
根据不同的tag类型就有不同的数据区
先说脚本数据吧,每个脚本数据都是flv的第一包 首当其冲的东西,尽管他是最复杂的
一般来说脚本数据都是第一帧,所以先分析脚本数据吧,这个是FLV文件中最复杂的部分(音频和视频数据比较简单),脚本数据包含了一个FLV文件的信息数据如:分辨率,视频长度,码率,采样率等信息...
脚本数据的数据类型有:
0 = Number type (double型)
1 = Boolean type(bool型)
2 = String type(字符串型)
3 = Object type
4 = MovieClip type
5 = Null type
6 = Undefined type
7 = Reference type
8 = ECMA array type
10 = Strict array type
11 = Date type(时间类型)
12 = Long string type(长字符串)
所有的数据可由公式来表达, 公式 = 数据类型 + 数据长度(根据类型决定) + 数据 三部分组成
注意:公式中可以确定长度的数据长度就会省略(bool , double),有些类型确定的类型就省略(下面介绍)看来真是抠门到家了,为了弄清这点我拿了一个16禁止编辑器(非常推荐QtCreator或者Vc 编辑器,可以直接打开)一个字节一个字节对比才得出这点。
连官方文档都没有细说,如果本人理解有误请在留言中指出!!!
举个例子最能说明这个类型,看这个例子什么都明白了
泰山鲁不生产矿泉水,哥们也只是大自然的搬运工,下面转自网上一篇flv包头分析
下面贴出FLV文件的一部分,包括FLV头和一个脚本数据
46 4c 56 01 05 00 00 00 09(FLV文件头)
00 00 00 00 (第一帧数据的长度)
12 00 04 f0 00 00 00 00 00 00 00(tag头,可以知道类型为18脚本数据,长度为 0x 00 04 f0 = 1264字节)
02 00 0a 6f 6e 4d 65 74 61 44 61 74 61(类型为字符串,长度00 0a = 10个内容为onMetaData)
08 00 00 00 0b (为ecma array类型,项数为不靠谱的11)
00 0f
6d 65 74 61 64 61 74 61 63 72 65 61 74 6f 72(key:string类型,类型字节省略, 长度为00 0f ,数据为metadatacreator)02 00 21
6d 6f 64 69 66 69 65 64 20 62 79 20 79 6f 75 6b 75 2e 63 6f 6d 20 69 6e 20 32 30 31 31 31 32 30 32(value:类型为02字符串,长度为00 21,数据为modified by youku.com in 20111202(00 21后面的33个字节)
00 0c
68 61 73 4b 65 79 66 72 61 6d 65 73(key:string类型,长度00 0c,数据hasKeyframes)01 01(value:类型为01 bool类型,数值为01)
00 08 68 61 73 56 69 64 65 6f 01 01 00 08 68 61 73 41 75 64 69 6f 01 01 00 0b 68 61 73 4d 65 74 61 64 61 74 61 01 01 00 05 77 69 64 74 68 00 40 91 c0 00 00 00 00 00 00 06 68 65 69 67 68 74 00 40 83 10 00 00 00 00 00 00 09 66 72 61 6d 65 72 61 74 65 00 40 37 f9 dc b5 11 22 87 00 0f 61 75 64 69 6f 73 61 6d 70 6c 65 72 61 74 65 00 40 d5 88 80 00 00 00 00 00 08 64 75 72 61 74 69 6f 6e 00 40 67 7b 56 b2 db d1 94 00 09 6b 65 79 66 72 61 6d 65 73
03(object类型)
00 0d 66 69 6c 65 70 6f 73 69 74 69 6f 6e 73(key:类型为string类型(省略)长度00 0d,值:filepositions)
0a 00 00 00 37 (value:类型为Strict array,数组的项数为00 00 00 37 = 55项)
00 40 94 30 00 00 00 00 00 (类型00为double类型,所以长度省略,接下来的8个字节代表值 1292.000000)
00 40 95 a8 00 00 00 00 00(类型00为double类型,所以长度省略,接下来的8个字节代表值 1386.000000)
00 40 f9 63 30 00 00 00 00 00 41 10 83 1c 00 00 00 00 00 41 1c 18 38 00 00 00 00 00 41 21 83 9e 00 00 00 00 00 41 29 29 8e 00 00 00 00 00 41 34 d7 04 00 00 00 00 00 41 3b 19 dd 00 00 00 00 00 41 3d 71 aa 00 00 00 00 00 41 3e ef 0b 00 00 00 00 00 41 47 78 7f 00 00 00 00 00 41 4e f8 c0 80 00 00 00 00 41 51 cf 99 80 00 00 00 00 41 55 70 e2 c0 00 00 00 00 41 58 fb fa 80 00 00 00 00 41 5c 9f ea 80 00 00 00 00 41 5d b7 d8 00 00 00 00 00 41 5e b8 b9 80 00 00 00 00 41 60 5e e9 80 00 00 00 00 41 60 d6 48 40 00 00 00 00 41 61 29 4d c0 00 00 00 00 41 61 86 44 60 00 00 00 00 41 62 0e 49 00 00 00 00 00 41 62 6f b6 60 00 00 00 00 41 62 e1 c3 00 00 00 00 00 41 63 68 59 40 00 00 00 00 41 64 1a 54 a0 00 00 00 00 41 64 d0 c9 60 00 00 00 00 41 66 0a 50 a0 00 00 00 00 41 66 98 d6 20 00 00 00 00 41 66 c1 67 e0 00 00 00 00 41 66 cd 8e 20 00 00 00 00 41 66 e4 b3 c0 00 00 00 00 41 67 3e 92 80 00 00 00 00 41 68 1a ed 00 00 00 00 00 41 69 25 f9 00 00 00 00 00 41 69 9a 09 e0 00 00 00 00 41 69 e7 0d 40 00 00 00 00 41 6a 02 ef 20 00 00 00 00 41 6a 9b 54 20 00 00 00 00 41 6c 4c db 40 00 00 00 00 41 6d 08 bf 20 00 00 00 00 41 6e 39 83 20 00 00 00 00 41 70 83 14 f0 00 00 00 00 41 71 04 77 e0 00 00 00 00 41 71 c3 82 60 00 00 00 00 41 72 77 6a 40 00 00 00 00 41 72 c5 e1 40 00 00 00 00 41 73 6a 92 30 00 00 00 00 41 73 eb cf 20 00 00 00 00 41 74 25 d7 70 00 00 00 00 41 74 52 66 00 00 00 00 00 41 74 97 38 50 00 00 00 00 41 75 41 a2 40 00 00 00 00 05 74 69 6d 65 73 0a 00 00 00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 14 05 1e b8 51 eb 85 00 40 23 84 fd f3 b6 45 a1 00 40 2f 88 10 62 4d d2 f1 00 40 33 3a 40 2b b0 cf 87 00 40 36 c5 d2 f1 a9 fb e7 00 40 3c c7 5c 28 f5 c2 8f 00 40 41 64 72 b0 20 c4 9b 00 40 42 84 bc 6a 7e f9 db 00 40 43 c5 0e 56 04 18 93 00 40 46 c5 d2 f1 a9 fb e7 00 40 49 c6 97 8d 4f df 3b 00 40 4c c2 05 76 19 f0 fb 00 40 4e 4d 15 29 a4 85 cd 00 40 50 81 8d fe a2 79 83 00 40 52 01 f0 4c 75 6b 2d 00 40 53 3a 40 2b b0 cf 87 00 40 54 62 8b f2 58 bf 25 00 40 55 e2 ee 40 2b b0 cf 00 40 56 bb 25 8b f2 58 bf 00 40 57 1b 3e 1f 67 15 29 00 40 57 ee 1e b8 51 eb 85 00 40 58 73 96 2f c9 62 fc 00 40 58 f1 0b 9a f7 20 15 00 40 59 7e 85 1e b8 51 eb 00 40 5a 59 67 c3 ec e2 a5 00 40 5b 51 a7 40 da 74 0d 00 40 5c 5c 96 2f c9 62 fc 00 40 5d bf 9b a5 e3 53 f7 00 40 5e 47 be 76 c8 b4 39 00 40 5e 62 6f f5 13 cc 1d 00 40 5e 6a 72 01 5d 86 7c 00 40 5e 7a 76 19 f0 fb 38 00 40 5e bf dd 2f 1a 9f be 00 40 5f 2f f9 db 22 d0 e5 00 40 5f 9d 6b 2d bd 19 42 00 40 5f e0 26 e9 78 d4 fd 00 40 60 04 18 93 74 bc 6a 00 40 60 0a c4 f3 07 82 63 00 40 60 36 d0 36 9d 03 69 00 40 60 c8 4a c0 83 12 6e 00 40 61 0f 07 82 63 ab 59 00 40 61 68 73 b6 45 a1 ca 00 40 62 28 a4 dd 2f 1a 9f 00 40 62 97 6b dc 80 57 61 00 40 63 2b 91 bf d4 4f 30 00 40 63 eb c2 e6 bd c8 05 00 40 64 59 34 39 58 10 62 00 40 65 19 65 60 41 89 37 00 40 65 8d 83 12 6e 97 8d 00 40 65 ae e0 f0 4c 75 6b 00 40 65 d2 ea 27 98 3c 13 00 40 66 1c 52 42 e6 bd c8 00 40 66 bb 25 8b f2 58 bf
00 00 09 (object的结尾)
00 00 09(ecma array的结尾)
有兴趣的同学可以自行分析,看到这么复杂的脚本“数据区”是不是晕蛋了呢,如果没有的话恭喜你厉害,本菜可是分析了将近10分钟才得出这些结论的(反正吹牛又不用缴税,擦劳资的两个不眠夜啊)。接下来毁三观:
必须用的数据只在tag头中,必须用的数据只在tag头中,必须用的数据只在tag头中....
所以如果晕菜的话没关系,在实际应用中(比如视频合并)本菜的亲身经历告诉你其实这么复杂的脚本数据其实99%都没有卵用。
Audio tag 数据区
audio信息 | 1byte | 前四位bits表示音频格式:
下面两位bits表示samplerate:
下面一位bit表示每个采样的长度:
下面一位bit表示类型:
|
audio数据区 | 不定 |
if SoundFormat == 10 AACAUDIODATA else Sound data—varies by format
|
AACAUDIODATA
AACPacketType | 1byte |
0: AAC sequence header 1: AAC raw
|
AACAUDIODATA数据区 | 不定 |
iif AACPacketType == 0 AudioSpecificConfig else if AACPacketType == 1 Raw AAC frame data
|
2.1.2.2.1 音频数据
名称 | ⻓度 | 介绍 |
SoundFormat(音频格式) | UB[4] (4个bit) | 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 logarithmicPCM 9 = reserved 10 = AAC 11 = Speex 14 = MP3 8-Khz 15 = Device-specific sound |
SoundRate (音频波特率) | UB[2] | 0 = 5.5-kHz 1 = 11-kHz 2 = 22-kHz 3 = 44-kHz |
SoundSize(采样长度) | UB[1] | 0 = snd8Bit 1 = snd16Bit |
SoundType(音频类型) | UB[1] | 0 = sndMono 1 = sndStereo ACC来说总是1 |
音频数据 | |
video tag 数据区
video信息 | 1byte | 前四位bits表示类型:
后四位bits表示编码器id:
|
video数据区 | 不定 |
If CodecID == 2 H263VIDEOPACKET If CodecID == 3 SCREENVIDEOPACKET If CodecID == 4 VP6FLVVIDEOPACKET If CodecID == 5 VP6FLVALPHAVIDEOPACKET If CodecID == 6 SCREENV2VIDEOPACKET if CodecID == 7 AVCVIDEOPACKET
|
视频数据,类型信息如下表中的第一个字节
-
名称
⻓度
介绍
FrameType(帧类型)
UB[4] (4个bit)
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 logarithmicPCM
9= reserved
10= AAC
11= Speex
14= MP3 8-Khz
15= Device-specific sound1:keyframe (for AVC, a seekable
frame)
2:inter frame (for AVC, a non-
seekableframe)
3:disposable inter frame(H.263
only)
4:generated keyframe (reservedfor
serveruse only)
5:video info/command frame
CodecID(编码类型)
UB[4]
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
VideoData(视频数据)
IfCodecID == 2
orUI8
H263VIDEOPACKET
IfCodecID == 3
SCREENVIDEOPACKET
IfCodecID == 4
VP6FLVVIDEOPACKET
IfCodecID == 5
VP6FLVALPHAVIDEOPACKET
IfCodecID == 6
SCREENV2VIDEOPACKET
ifCodecID == 7
AVCVIDEOPACKET
类型信息如下表中的第一个字节
-
名称
⻓度
介绍
FrameType(帧类型)
UB[4] (4个bit)
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 logarithmicPCM
9= reserved
10= AAC
11= Speex
14= MP3 8-Khz
15= Device-specific sound1:keyframe (for AVC, a seekable
frame)
2:inter frame (for AVC, a non-
seekableframe)
3:disposable inter frame(H.263
only)
4:generated keyframe (reservedfor
serveruse only)
5:video info/command frame
CodecID(编码类型)
UB[4]
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
VideoData(视频数据)
IfCodecID == 2
orUI8
H263VIDEOPACKET
IfCodecID == 3
SCREENVIDEOPACKET
IfCodecID == 4
VP6FLVVIDEOPACKET
IfCodecID == 5
VP6FLVALPHAVIDEOPACKET
IfCodecID == 6
SCREENV2VIDEOPACKET
ifCodecID == 7
AVCVIDEOPACKET
类型信息如下表中的第一个字节
-
名称
⻓度
介绍
FrameType(帧类型)
UB[4] (4个bit)
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 logarithmicPCM
9= reserved
10= AAC
11= Speex
14= MP3 8-Khz
15= Device-specific sound1:keyframe (for AVC, a seekable
frame)
2:inter frame (for AVC, a non-
seekableframe)
3:disposable inter frame(H.263
only)
4:generated keyframe (reservedfor
serveruse only)
5:video info/command frame
CodecID(编码类型)
UB[4]
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
VideoData(视频数据)
IfCodecID == 2
orUI8
H263VIDEOPACKET
IfCodecID == 3
SCREENVIDEOPACKET
IfCodecID == 4
VP6FLVVIDEOPACKET
IfCodecID == 5
VP6FLVALPHAVIDEOPACKET
IfCodecID == 6
SCREENV2VIDEOPACKET
ifCodecID == 7
AVCVIDEOPACKET
H263VIDEOPACKET
H263VIDEOPACKET数据结构:
PictureStartCode UB[17] 和H.263 5.1.1相似
0000 0000 0000 0000 1
Version UB[5] 视频格式版本
Flash Player 6 supports 0 and 1
TemporalReference UB[8] 察看 H.263 5.1.2
PictureSize UB[3] 图像尺寸:
000: custom, 1 byte
001: custom, 2 bytes
010: CIF (352x288)
011: QCIF (176x144)
100: SQCIF (128x96)
101: 320x240
110: 160x120
111: 保留
CustomWidth If PictureSize = 000 UB[8]
If PictureSize = 001 UB[16]
否则不存在
注意:UB[16]不一样UI16,这里不是字节交换的
单位是像素
CustomHeight If PictureSize = 000 UB[8]
AVCVIDEOPACKET
AVCPacketType | 1byte |
0: AVC sequence header 1: AVC NALU 2: AVC end of sequence (lower level NALU sequence ender is not required or supported)
|
CompositionTime | 3byte |
if AVCPacketType == 1 Composition time offset else 0
|
Data | 不定 |
if AVCPacketType == 0 AVCDecoderConfigurationRecord else if AVCPacketType == 1 One or more NALUs (can be individual slices per FLV packets; that is, full frames are not strictly required) else if AVCPacketType == 2 Empty
|
script tag 数据区
来两张图片
学习的过程是漫长和痛苦的,学会了你就感觉神仙一样,一切神秘的flv,aac avc和复杂的音视频数据都是个屎