——如果要學習一個新的知識點,官方手冊可能是最快的途徑。查看網上其他人的總結也許入門更快,但是要準確,深入,完整,還是要看官方手冊。
以下內容來自對官方文檔Video File Format Specification Version 10的分析總結。過程中借助ffmpeg實際轉換了一個flv文件用例研究。
一個FLV文件,每種類型的tag都屬於一個流,也就是一個flv文件最多只有一個音頻流,一個視頻流,不存在多個獨立的音視頻流在一個文件的情況。(mp4好像是可以的)
另外,FLV文件格式所用的是大端序。
註:下面的數據type中,UI表示無符號整形,後面跟的數字表示其長度是多少位。比如UI8,表示無法整形,長度一個字節。UI24是三個字節。UB表示位域,UB5表示一個字節的5位。可以參考c中的位域結構體。
FLV頭
Field
|
type
|
Comment
|
簽名
|
UI8
|
』F'(0X46)
|
簽名
|
UI8
|
『L'(0X4C)
|
簽名
|
UI8
|
『V'(0x56)
|
版本
|
UI8
|
FLV的版本。0x01表示FLV 版本是1
|
保留字段
|
UB5
|
前五位必須是0
|
是否有音頻流
|
UB1
|
音頻流是否存在標誌
|
保留字段
|
UB1
|
必須是0
|
是否有視頻流
|
UB1
|
視頻流是否存在標誌
|
文件頭大小
|
UI32
|
FLV版本1時填寫9,表明的是FLV頭的大小,為後期的FLV版本擴展使用。包括這四個字節。
數據的起始位置就是從文件開頭偏移這麼多的大小。
|
FLV文件體
body部分由一個個Tag組成,每個Tag的下面有一塊4bytes的空間,用來記錄這個tag的長度,這個後置用於逆向讀取處理,他們的關係如下圖:
注意:頭下面四個自己就是PreviousTagSize,因為前一個沒有Tag,所以,值填寫0。
FLV tags 結構
Field
|
type
|
Comment
|
TAG類型 |
UI8
|
8: audio
9: video
18: script data——這裡是一些描述信息。
all others: reserved其他所有值未使用。
|
數據大小
|
UI24
|
數據區的大小,不包括包頭。包頭總大小是11個字節。
|
時戳
|
UI24
|
當前幀時戳,單位是毫秒。相對於FLV文件的第一個TAG時戳。第一個tag的時戳總是0。——不是時戳增量,rtmp中是時戳增量。
|
時戳擴展字段
|
UI8
|
如果時戳大於0xFFFFFF,將會使用這個字節。這個字節是時戳的高8位,上面的三個字節是低24位。
|
流ID
|
U24
|
總是0
|
數據區
|
UI8[n]
|
音頻數據
Field
|
type
|
Comment
|
音頻格式
|
UB4
|
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 logarithmic PCM 9 = reserved
10 = AAC
11 = Speex
14 = MP3 8-Khz
15 = Device-specific sound
7, 8, 14, and 15:內部保留使用。
flv是不支持g711a的,如果要用,可能要用線性音頻。
|
採樣率
|
UB2
|
For AAC: always 3
0 = 5.5-kHz
1 = 11-kHz
2 = 22-kHz
3 = 44-kHz
|
採樣大小
|
UB1
|
0 = snd8Bit
1 = snd16Bit
|
聲道
|
UB1
|
0=單聲道
1=立體聲,雙聲道。AAC永遠是1
|
聲音數據
| UI8[N] |
如果是PCM線性數據,存儲的時候每個16bit小端存儲,有符號。
如果音頻格式是AAC,則存儲的數據是AAC AUDIO DATA,否則為線性數組。
|
AAC AUDIO DATA
視頻數據
Field
|
type
|
Comment
|
幀類型
|
UB4
|
1: keyframe (for AVC, a seekable frame)——h264的IDR,關鍵幀,可重入幀。
2: inter frame (for AVC, a non- seekable frame)——h264的普通幀
3: disposable inter frame (H.263 only)
4: generated keyframe (reserved for server use only)
5: video info/command frame
|
編碼ID
|
UB4
|
使用哪種編碼類型:
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
|
視頻數據
|
UI[N]
|
如果是avc,則參考下面的介紹:
AVCVIDEOPACKET
|
AVCVIDEOPACKET
Field
|
type
|
Comment
|
AVC packet類型
|
UI8
|
0:AVC序列頭
1:AVC NALU單元
2:AVC序列結束。低級別avc不需要。
|
CTS
|
SI24
|
如果AVC packet類型是1,則為cts偏移(見下面的解釋),為0則為0
|
數據
|
UI8[n]
|
如果AVC packet類型是0,則是解碼器配置,sps,pps。
如果是1,則是nalu單元,可以是多個,具體格式:將下面
|
關於CTS:這是一個比較難以理解的概念,需要和pts,dts配合一起理解。
首先,pts(presentation time stamps),dts(decoder timestamps),cts(CompositionTime)的概念:
pts:顯示時間,也就是接收方在顯示器顯示這幀的時間。單位為1/90000 秒。
dts:解碼時間,也就是rtp包中傳輸的時間戳,表明解碼的順序。單位單位為1/90000 秒。——根據後面的理解,pts就是標準中的CompositionTime
cts偏移:cts = (pts - dts) / 90 。cts的單位是毫秒。
pts和dts的時間不一樣,應該只出現在含有B幀的情況下,也就是profile main以上。baseline是沒有這個問題的,baseline的pts和dts一直想吐,所以cts一直為0。
在flv tag中的時戳就是DTS。
研究 一下文檔, ISO/IEC 14496-12:2005(E) 8.15 Time to Sample Boxes,發現CompositionTime就是presentation time stamps,只是叫法不同。——需要再進一步確認。
在上圖中,cp就是pts,顯示時間。DT是解碼時間,rtp的時戳。
I1是第一個幀,B2是第二個,後面的序號就是攝像頭輸出的順序。決定了顯示的順序。
DT,是編碼的順序,特別是在有B幀的情況,P4要在第二個解,因為B2和B3依賴於P4,但是P4的顯示要在B3之後,因為他的順序靠後。這樣就存在顯示時間CT(PTS)和解碼時間DT的差,就有了CT偏移。
P4解碼時間是10,但是顯示時間是40,
AVCVIDEOPACKET中data格式:
Field
|
type
|
Comment
|
長度
|
UI32
|
nalu單元的長度,不包括長度字段。
|
nalu數據
|
UI8[N]
|
NALU數據,沒有四個字節的nalu單元頭,直接從h264頭開始,比如:65 ** ** **,41 ** ** **
|
長度
|
UI32
|
nalu單元的長度,不包括長度字段。
|
nalu數據
|
UI8[N]
|
NALU數據,沒有四個字節的nalu單元頭,直接從h264頭開始,比如:65 ** ** **,41 ** ** **
|
...
|
...
|
...
|
Data tags
主要是onMeta信息需要關注。
AVCDecoderConfigurationRecord
AVCVIDEOPACKET的數據格式,保存控制信息。
記錄sps,pps信息。一般出現在第二個tag中,緊跟在onMeta之後。
一個典型的序列:
0000190: 0900 0033 0000 0000 0000 0017 0000 0000 ...3............
00001a0: 0164 002a ffe1 001e 6764 002a acd9 4078 .d.*....gd.*..@x
00001b0: 0227 e5ff c389 4388 0400 0003 0028 0000 .'....C......(..
00001c0: 0978 3c60 c658 0100 0568 ebec b22c 0000 .x<`.X...h...,..
17:表示h264IDR data
00:表示是AVC序列頭
00 00 00 :cts為0
//從此往下就是AVCDecoderConfigurationRecord
01 :版本號
64 00 2a:profile level id,sps的三個字節,64表示是h264 high profile,2a表示level。
FF:NALU長度,為3?不知道這個長度用在哪裡。
E1:表示下面緊跟SPS有一個。
//sps[N]:sps數組。
00 1e: 前面是兩個字節的sps長度,表示後面的sps的長度是1e大小。
6764 002a acd9 4078 0227 e5ff c389 4388 0400 0003 0028 0000 0978 3c60 c658:sps的數據。
//因為只有一個sps,跳過這些長度,然後就是pps的個數信息:
01 :pps個數,1
//pps[n] pps 的個數
00 05:表示pps的大小是5個字節。
68 eb ec b2 2c:pps的數據
00 00 …….這是下一個tag 的內容了
沒有留言:
張貼留言