FLV Header(9Bytes) + FLV Body

         a比特: 1:有音频; 0:无音频
         v比特: 1:有视频; 0:无视频   如果音视频都有这个字节为0x05
‘F’|‘L’|‘V’|0x01|0b00000a0v| 0x00 00 00 09|
版本字节一般为0x01 头长度 固定为9

FLV Body由一系列的Tag组成,每个tag的结构如下: { 0x00000000| tag_header0| tag_data0 } | {tag_size0| tag_header1 | tag_data1 } | {tag_size1| tag_header2 | tag_data2 } |… tag_size(n) = tag_data_size(n-1) + 11 解析完一个tag后,读取下4个字节,可以比较下看是不是前帧完全解码完毕。 除了可以验证码流外,还有什么样的好处促使FLV格式采用这种方式呢?不太清楚。

0x00000000| { tag_header0| tag_data0 | tag_size0} | { tag_header1 | tag_data1 | tag_size1} |...

Tag body也可以这样看,编码时按照: 数据头 数据体 数据大小 三部分来划分,更适合一些。下面各个tag都是按照这个结构来写的。 Tag header 11Bytes

|1Byte Tag类型 | 只支持3类, 0x08音频; 0x09视频;0x12脚本 |3Byte data_size数据区长度 | 纯数据长度,不包括头信息的15字节 |3Byte timestamp 时间戳 | 最终的时间戳 = (timestamp_ex<< 24) | timestamp |1Byte timestamp_ex扩展时间戳 | 最终时间戳的高8位。不知道为什么定义成这样,可能是因为标准扩展 |3Byte StreamID | 总为0

Tag Data data_size字节 |Tag数据段,长度为data_size… | 音视频或者脚本数据。 |4Byte PreviousTagSize | 是前一个Tag数据长度,第一个Tag此数据段为0.

视频Tag 视频数据的第一个字节定义视频信息: 高4比特定义帧格式:1 关键帧; 2 内部帧(非IDR帧);3可丢弃内部帧(仅对H.263有用) 低4比特定义编码器:2:H.263, 4: VP6, 7:H264; … 对于264的视频FLV,这个字节一般为 0x17或者0x27.

音频tag 音频数据的第一个字节定义视频信息: 高4比特定义音频编码格式:0 Linear PCM, 1: ADPCM, 2: MP3, 10:AAC … 第4,3两比特定义采样率: 0 5.5KHz, 1 11KHz, 2 22KHz, 3 44KHz. 对于AAC该值总为3 第2比特定义采样大小: 0 8比特,1 16比特 第1比特定义声道数: 0 单声道,1 立体声

脚本tag 理论上不需要解析脚本Tag就可以解码FLV文件,它只是提供了一些信息记录。 脚本Tag里面的类型比较多,格式也不一样。 Number 类,记录一个8字节double数据。|0x00|8字节data|. Boolean类,记录一个1字节布尔型数据。|0x01|1字节data|. String类,记录一个变长字符串数据。 |0x02|2字节 字符串长度 N| N字节字符串内容|. 注意读取完字符串后加一个’\0’字符串结束符号。因此字符串申请的时候,长度为N+1. ECMA array type, 记录一些数据对。

|0x08| 4字节 数组长度 N|2字节 字符串长度 m1 | m1字节字符串内容|1字节 data1 type | X字节 data1 | ...

                                    |2字节 字符串长度 Nm| Nm字节字符串内容|1字节 dataN type| X字节 dataN|           
           有N个数据对                  元素名    (长度|数据)                               元素值(类型|数据)
     一般用这个记录一些音视频信息,例如:
                                                     0x0008                         "duration"                   0x00      8字节double
                                                     0x0005                            "width"                     0x00      8字节double
                                                     0x0006                           "stereo"                    0x01      1字节boolean型  
Strict array type类,记录一组数据。
|0x0A|4字节 数组长度 N| 1字节 data1 type| X字节 data1 | …
|1字节 dataN type| X字节 dataN|
有N个数据, 每个数据的类型 数据值
一般会用这个类记录关键帧的偏移地址和对应的pts值。类型都是 Number类,即X=8字节double型
Object type, 记录对象数据。一般用它来做keyframes的数据存储起始。

5.keyframes字段 FLV没有像mp4一样定义stbl,FLV文件如果做快进,seek等操作会比较麻烦。 业内通用做法是在脚本Tag里面增加keyfrmes object类。一般定义为:

|00 09| 9字节 “keyframes”|00 0D| 13字节 “filepositions”|0A| 4字节关键字数目 N|00|8字节 关键帧1偏移地址| … |00|8字节 关键帧N偏移地址| |00 05| 5字节 “times” |0A| 4字节关键字数目 N|00|8字节 关键帧1时间| … |00|8字节 关键帧N时间| 有了各个关键帧的偏移地址和时间,做Seek操作的时候就方便的多。

读取8字节double型,这个牵扯浮点型的存储结构问题,蛮罗嗦,没看明白,但找到下面一段代码可以实现转换。 uint64_t v = get_8bytes(); if(v+v > 0xFFEULL<<52) return 0.0/0.0; double x = ldexp(((v&((1LL<<52)-1)) + (1LL<<52)) * (v>>63|1), (v>>52&0x7FF)-1075);

FLV结构里面,数据长度有3字节和4字节限制,不会像Mp4一样出现8字节长度。 3字节最大表示16M数据,4字节最大是4G数据。 一个Video Tag里面的NALU长度不能超过16M字节。

分类:
后端
标签: