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.
2. 視頻Tag
視頻數據的第一個字節定義視頻信息:
高4比特定義幀格式:1 關鍵幀; 2 內部幀(非IDR幀);3可丟棄內部幀(僅對H.263有用)
低4比特定義編碼器:2:H.263, 4: VP6, 7:H264; ...
對於264的視頻FLV,這個字節一般爲 0x17或者0x27.
3. 音頻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 立體聲
4. 腳本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型
|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|8字節 關鍵幀N時間|
有了各個關鍵幀的偏移地址和時間,做Seek操作的時候就方便的多。
6. 讀取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);
7. FLV結構裏面,數據長度有3字節和4字節限制,不會像Mp4一樣出現8字節長度。
3字節最大表示16M數據,4字節最大是4G數據。
一個Video Tag裏面的NALU長度不能超過16M字節。