您的位置:首页 > 其它

流媒体-FLV格式详解及数据分析

2018-03-30 16:36 543 查看
一、FLV文件格式FLV包括文件头(File Header)和文件体(File Body)两部分。文件结构如图所示:


二、FLV文件头header部分记录了flv的类型、版本等信息,是flv的开头,一般都差不多,占9bytes。具体格式如下:1. 文件标识(3B):总是为”FLV”,0x46 0x4c 0x56
2. 版本(1B):目前为0x013. 流信息(1B):文件的标志位说明。前5位保留,必须为0;第6位为音频Tag:1表示有音频;第七位保留,为0; 第8位为视频Tag:1表示有视频4. Header长度(4B):整个header的长度,一般为9(版本为0x01时);大于9表示下面还有扩展信息。即0x00000009
 
.三、FLV文件体文件体由一系列的Tag组成。其中,每个Tag前面还包含了Previous Tag Size字段,表示前面一个Tag的大小。Tag的类型可以是视频、音频和Script,每个Tag只能包含以上三种类型的数据中的一种。

 . 
1、Tag每个Tag由也是由两部分组成的:Tag Header和Tag Data。Tag Header里存放的是当前Tag的类型、数据区(Tag Data)长度等信息,具体如下:
Tag类型(1):0x08:音频; 0x09:视频; 0x12:脚本; 其他:保留数据区长度(3):数据区的长度时间戳(3):整数,单位是毫秒。对于脚本型的tag总是0 (CTS)时间戳扩展(1):将时间戳扩展为4bytes,代表高8位。很少用到StreamsID(3):总是0数据区(由数据区长度决定):数据实体

1.1、Audio Tag Data结构(音频Tag). 
音频Tag开始的第1个字节包含了音频数据的参数信息,从第2个字节开始为音频流数据。如图为音频Tag结构:

第1个字节的前4位的数值表示了音频编码类型,第5-6位的数值表示音频采样率,第7位表示音频采样精度,第8位表示音频类型。具体格式如下:

从上图可以看出,FLV封装格式并不支持48KHz的采样率。
1.2、Video Tag Data结构(视频Tag)视频Tag也用开始的第1个字节包含视频数据的参数信息,从第2个字节为视频流数据。如图为视频Tag结构: 


第1个字节的前4位的数值表示帧类型,第1个字节的后4位的数值表示视频编码类型。具体格式如下:

VideoData为数据具体内容:如果CodecID=2,为H263VideoPacket; 
如果CodecID=3,为ScreenVideopacket; 
如果CodecID=4,为VP6FLVVideoPacket; 
如果CodecID=5,为VP6FLVAlphaVideoPacket; 
如果CodecID=6,为ScreenV2VideoPacket; 
如果CodecID=7,为AVCVideoPacket;AVCVideoPacket格式AVCVideoPacket同样包括Packet Header和Packet Body两部分: 
即AVCVideoPacket Format: 
| AVCPacketType(8)| CompostionTime(24) | Data | 
AVCPacketType为包的类型: 
如果AVCPacketType=0x00,为AVCSequence Header; 
如果AVCPacketType=0x01,为AVC NALU; 
如果AVCPacketType=0x02,为AVC end ofsequence 
CompositionTime为相对时间戳: 
如果AVCPacketType=0x01, 为相对时间戳; 
其它,均为0; 
Data为负载数据: 
如果AVCPacketType=0x00,为AVCDecorderConfigurationRecord; 
如果AVCPacketType=0x01,为NALUs; 
如果AVCPacketType=0x02,为空。 
AVCDecorderConfigurationRecord格式AVCDecorderConfigurationRecord包括文件的信息。 
具体格式如下: 
| cfgVersion(8) | avcProfile(8) | profileCompatibility(8) |avcLevel(8) | reserved(6) | lengthSizeMinusOne(2) | reserved(3) | numOfSPS(5) |spsLength(16) | sps(n) | numOfPPS(8) | ppsLength(16) | pps(n) |
1.3、Script Tag Data结构(控制帧)该类型Tag又通常被称为Metadata Tag,会放一些关于FLV视频和音频的元数据信息如:duration、width、height等。通常该类型Tag会跟在File Header后面作为第一个Tag出现,而且只有一个。结构如图所示:

第一个AMF包:第1个字节表示AMF包类型,常见的数据类型如下:
0 = Number type
1 = Boolean type
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
1
2
3
4
5
6
7
8
9
10
11
12
FLV文件中,第一个字节一般总是0x02,表示字符串。第2-3个字节为UI16类型值,标识字符串的长度,一般总是0x000A(“onMetaData”长度)。后面字节为具体的字符串,一般总为“onMetaData”(6F,6E,4D,65,74,61,44,61,74,61)。第二个AMF包:第1个字节表示AMF包类型,一般总是0x08,表示数组。第2-5个字节为UI32类型值,表示数组元素的个数。后面即为各数组元素的封装,数组元素为元素名称和值组成的对。常见的数组元素如下表: 



四、FLV数据分析第一帧报文:

 1) 0x46 4c 56字符FLV头,固定字符2) 0x01:版本,目前为固定字符3)0x05:01表示有视频,04表示有音频,05表示既有视频又有音频。4)0x00 00 00 09:flv包头长度5)0x00 00 00 00 :这个是第1帧的PreviousTagSize0(前帧长度),因为是第一帧,所以肯定是0;6)0x08:帧开头第一字节:0x08表示音频,0x09表示视频,0x12表示脚本信息,放一些关于FLV视频和音频的参数信息,如duration、width、height等。7)0x00 00 04:帧payload长度:因为音频第一帧是ASC,所以只有4字节。8) 0x 00 00 00 00:timestamp,时间戳9) 0x 00 00 00:streamid,流ID10) 0x AF 00 13 90:音频payload: 0xaf00开头的后面是asc flag, 0xaf01开头的后面是真正的音频数据0x13 90,也就是0b0001 0011 1001 0000, ASC flag格式:xxxx xyyy yzzz z000x字符: aac type,类型2表示AAC-LC,5是SBR, 29是ps,5和29比较特殊asc flag的长度会变成4;y字符:  sample rate, 采样率, 7表示22050采样率z字符:  通道数,2是双通道11) 0x 00 00 00 0F这个还是PreviousTagSize1,上一帧长度15bytes12) 0x09 视频类型,新的一帧13)0x00 00 22视频帧payload长度14) 0x00 00 0a 00 时间戳:这个地方有个大坑,顺序是:a[3] a[0] a[1] a[2],最后一位是最高位。15) 0x00 00 00streamid, 流id。16) 0x 17 00 视频帧开头2字节:0x17 00: 表示内容是SPS和PPS0x17 01: 表示内容是I-FRAME0x27:      表示内容是P-FRAME17) 0000002bh: 17 00 00 00 00 01 42 C0 1F FF E1 00 0E 67 42 C0 ; ......B??.gB?
0000003bh: 1F 8C 8D 40 F0 28 90 0F 08 84 6A 01 00 04 68 CE ; .實@??.刯...h?
0000004bh: 3C 80 ; <€第12, 13字节: 0x00 0E是spslen,也就是14字节长度跳过14字节后,0x01是pps开始的标识,跳过它。0x00 04是ppslen,也就是4个字节,最后0x68 ce 3c 80就是pps。
2.1 Tag 解析每一个tag也是由两部分组成:tag头和tag数据区。好了至此FLV格式分析结束,嘿嘿(裤子都脱了你就给看这个)。其实只是想强调下每个tag都是由 头+数据 组成。如下表所示,很明显tag头为11个字节。
2.1.1 tag头tag头为下表中的前11个字节,剩下的为tag数据区(具体分析见2.1.2)
名称⻓度介绍
TagType(tag类型)UI88: audio(音频)9: video(视频)18: script data(脚本数据)all others: reserved(其他保留)
DataSize(tag数据区大小)UI24tag的数据区大小以字节为单位,注意字节序
Timestamp(时间戳)本tag相对于FLV文件第一个tag的时间,以毫秒为单位,当然第一个tag的时间戳肯定为0(很重要)
TimestampExtended(时间戳扩展)UI8将时间戳扩展至32位,该字节代表高8位(很重要)
StreamID(信息流ID)UI24恒为0
Data(tag数据区)If TagType == 8AUDIODATAIf TagType == 9VIDEODATAIf TagType == 18SCRIPTDATAOBJECT由第一个字节的tag类型决定:为8时代表音频为9时代表视频为18时代表脚本数据


2.1.2 tag数据tag数据分为:脚本数据,音频数据以及视频数据,因为脚本数据比较复杂先详细分析脚本数据。
2.1.2.1脚本数据
一般来说脚本数据都是第一帧,所以先分析脚本数据吧,这个是FLV文件中最复杂的部分(音频和视频数据比较简单),脚本数据包含了一个FLV文件的信息数据如:分辨率,视频长度,码率,采样率等信息...
脚本数据的数据类型有:0 = Number type (double型)
1 = Boolean type(bool型)2 = String type(字符串型)3 = Object type4 = MovieClip type5 = Null type6 = Undefined type7 = Reference type8 = ECMA array type10 = Strict array type11 = Date type(时间类型)12 = Long string type(长字符串)
所有的数据可由公式来表达,  公式 =  数据类型 + 数据长度(根据类型决定) + 数据  三部分组成
注意:公式中可以确定长度的数据长度就会省略(bool , double),有些类型确定的类型就省略(下面介绍)看来真是抠门到家了,为了弄清这点我拿了一个16禁止编辑器(非常推荐QtCreator或者Vc 编辑器,可以直接打开)一个字节一个字节对比才得出这点。连官方文档都没有细说,如果本人理解有误请在留言中指出!!!
上面的数据类型,我分为两类:
1.简单类型包括:double类型 (数据长度为8个字节,省略)bool类型(数据长度为1个字节,省略)string 类型(数据长度为2个字节,然后是字符串数据)long string类型(数据长度为4个字节,然后是字符串数据)
2.复杂类型:复杂类型为简单类型的组合Strict array type(严格数组,翻译的不准,嘿嘿):数组的长度一定,根据数组长度可以知道该数组有多少项(如a[10]表示有10个数据项),项数为数据类型后的4个字节,数组中的每一项也是用上面的公式表示
Object type(object类型,靠词穷了):为一个数据字典(Python的说法):key + value其中key一定为string类型(所以代表string类型的2省略),value为随意类型的数据,见公式,需要说明的是可以该项可以嵌套,必须以00 00 09结尾
ECMA array type(Ecma数组):为一个数据字典(Python的说法):key + value其中key一定为string类型(所以代表string类型的2省略),value为随意类型的数据的数组。该数组也装模作样的用4个字节来表示该数组的大小,但是然并卵,你并不能通过这个值来做什么,该项以00 00 09结尾,该项也可以嵌套下面贴出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%都没有卵用。
解析的时候发现有两个数据项需要格外注意,而且至关重要的并不在脚本tag数据区中,让我们在复习一下tag数据= tag头+tag数据区
第一个需要注意的数据项为tag 头中的 时间戳 和 时间戳扩展 必须解析对,否则合并后的视频在播放完第一个之后会出问题,原因为在播放时播放器只根据这个值来进行音频和视频的同步第二个需要注意的是tag数据区中的duration 这个给出了该段视频的长度,如果不解析的话快进会出问题,但是依然可以播放。
2.1.2.2 音频数据和视频
音频和视频数据跟脚本数据比起来就是个渣渣,当然我只是说很少的东西需要我们分析,而是把数据直接扔给播放器就行。数据 公式 = 类型信息(1个字节)+ 音频/视频数据需要注意的是上面提到的时间戳的解析,比如视频合并时第二个视频的时间戳需要在第一个视频的最后一个时间戳的的基础上进行累加,否则同步会出问题
2.1.2.2.1 音频数据

名称⻓度介绍
SoundFormat(音频格式)UB[4] (4个bit)0 = Linear PCM, platform endian1 = ADPCM2 = MP33 = Linear PCM, little endian4 = Nellymoser 16-kHz mono5 = Nellymoser 8-kHz mono6 = Nellymoser7 = G.711 A-law logarithmic PCM8 = G.711 mu-law logarithmicPCM9 = reserved10 = AAC11 = Speex14 = MP3 8-Khz15 = Device-specific sound
SoundRate (音频波特率)
UB[2]0 = 5.5-kHz1 = 11-kHz2 = 22-kHz3 = 44-kHz
SoundSize(采样长度)UB[1]0 = snd8Bit1 = snd16Bit
SoundType(音频类型)UB[1]0 = sndMono1 = sndStereoACC来说总是1
音频数据
[cpp] view plain copy// notice the bits order is reverse  
// my pc is intel' CPU(little endding i guess)  
typedef struct  _AUDIO_INFO  
{  
    byte audio_type:1;  
  
    byte sampling_len:1;  
  
    byte sampling_rate:2;   // 0 = 5.5-kHz  
                            // 1 = 11-kHz  
                            // 2 bits  
                            // 2 = 22-kHz  
                            // 3 = 44-kHz  
                            // 对于 AAC 总是 3  
      
    byte audio_format:4;    // 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  
                            // 4 bits  
                            // 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  
  
}AUDIO_INFO;  

2.1.2.2.1视频数据类型信息如下表中的第一个字节
名称⻓度介绍
FrameType(帧类型)UB[4] (4个bit)0= Linear PCM, platform endian1= ADPCM2= MP33= Linear PCM, little endian4= Nellymoser 16-kHz mono5= Nellymoser 8-kHz mono6= Nellymoser7= G.711 A-law logarithmic PCM8= G.711 mu-law logarithmicPCM9= reserved10= AAC11= Speex14= MP3 8-Khz15= Device-specific sound1:keyframe (for AVC, a seekableframe)2:inter frame (for AVC, a non-seekableframe)3:disposable inter frame(H.263only)4:generated keyframe (reservedforserveruse only)5:video info/command frame
CodecID(编码类型)
UB[4]1:JPEG (currently unused)2:Sorenson H.2633:Screen video4:On2 VP65:On2 VP6 with alpha channel6:Screen video version 27:AVC
VideoData(视频数据)IfCodecID == 2orUI8H263VIDEOPACKETIfCodecID == 3SCREENVIDEOPACKETIfCodecID == 4VP6FLVVIDEOPACKETIfCodecID == 5VP6FLVALPHAVIDEOPACKETIfCodecID == 6SCREENV2VIDEOPACKETifCodecID == 7AVCVIDEOPACKET
[cpp] view plain copytypedef struct  _VEDIO_INFO  
{  
    byte encode:4;      // 1: JPEG,  
                        // 2: Sorenson H.263  
                        // 3: Screen video  
                        // 4 bits  
                        // 4: On2 VP6  
                        // 5: On2 VP6 with alpha channel  
                        // 6: Screen video version 2  
                        // 7: AVC  
  
    byte vedio_type:4;  // 1: keyframe (for AVC, a seekable frame)  
                        // 2: inter frame (for AVC, a non-seekable frame)  
                        // 4 bits  
                        // 3: disposable inter frame (H.263 only)  
                        // 4: generated keyframe (reserved for server use only)  
                        // 5: video info/command frame  
  
}VEDIO_INFO;  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: