簡介
FLV(Flash Video)是現(xiàn)在非常流行的流媒體格式,由于其視頻文件體積輕巧、封裝播放簡單等特點(diǎn),使其很適合在網(wǎng)絡(luò)上進(jìn)行應(yīng)用,目前主流的視頻網(wǎng)站無一例外地使用了FLV格式。另外由于當(dāng)前瀏覽器與Flash Player緊密的結(jié)合,使得網(wǎng)頁播放FLV視頻輕而易舉,也是FLV流行的原因之一。
FLV是流媒體封裝格式,我們可以將其數(shù)據(jù)看為二進(jìn)制字節(jié)流。總體上看,F(xiàn)LV包括文件頭(File Header)和文件體(File Body)兩部分,其中文件體由一系列的Tag及Tag Size對組成。
FLV格式解析
先來一張圖,這是《科比退役演講》下載)的一個(gè)FLV視頻。我使用的是UltraEdit的二進(jìn)制查看工具。
header
頭部分由一下幾部分組成
Signature(3 Byte)+Version(1 Byte)+Flags(1 Bypte)+DataOffset(4 Byte)
- signature 占3個(gè)字節(jié) 固定FLV三個(gè)字符作為標(biāo)示。一般發(fā)現(xiàn)前三個(gè)字符為FLV時(shí)就認(rèn)為他是flv文件。圖中0x46 0x4C 0x56,代表FLV
- Version 占1個(gè)字節(jié) 標(biāo)示FLV的版本號。這里我們看到是1
- Flags 占1個(gè)字節(jié) 內(nèi)容標(biāo)示。第0位和第2位,分別表示 video 與 audio 存在的情況.(1表示存在,0表示不存在)。截圖看到是0x05,也就是00000101,代表既有視頻,也有音頻。
- DataOffset 4個(gè)字節(jié) 表示FLV的header長度。這里可以看到固定是9
Body
FLV的body部分是一系列的back-pointers+tag構(gòu)成的
- back-pointers固定4個(gè)字節(jié),表示前一個(gè)tag的size
- tag分三種類型:video,audio,scripts.
tag組成
tag type+tag data size+Timestamp+TimestampExtended+stream id+ tag data
- type 1個(gè)字節(jié)。8為Audio,9為Video,18為scripts
- tag data size 3個(gè)字節(jié)。表示tag data的長度。從streamd id 后算起。
- Timestreamp 3個(gè)字節(jié)。時(shí)間戳
- TimestampExtended 1個(gè)字節(jié)。時(shí)間戳擴(kuò)展字段
- stream id 3個(gè)字節(jié)??偸?
- tag data 數(shù)據(jù)部分
圖上第一個(gè)tag:
- type=0x12=18,表示是一個(gè)scripts,FLV中,header后的第一個(gè)tag是script tag,script tag內(nèi)容是amf格式數(shù)據(jù),包含兩個(gè)amf.
- size=0x00 0x01 0x74 = 372
- timpestreamp = 0x00 0x00 0x00
- TimestampExtended=0x00
- streamid=0x00 0x00 0x00
-
tag data部分:
FLV-TAG
tag的劃分
圖中紅色部分是我標(biāo)出"("與")"前后的的兩個(gè)back-pointers,都是4個(gè)字節(jié)。而括號中間就是第一個(gè)TAG。那是怎么計(jì)算的呢?我們就以這個(gè)做個(gè)示例。
- 首先第一個(gè)back-pointers是0x00000000,那是因?yàn)楹竺媸堑谝粋€(gè)TAG。所以他為0。
- 然后根據(jù)我們我們前面格式獲取到size是0x00 0x01 0x74 = 372。也就是說從stream id后面再加上372個(gè)字節(jié)就到了第一個(gè)TAG的末尾,我們數(shù)一下。tag header有11個(gè)字節(jié)。那么到第一個(gè)TAG,總共有372+11=383=0x17f。
- 接下來我們找到0x17f的地址,從工具上很容易找到,正好就是后括號")"的前面。紅0x00 0x00 0x01 0x7F=372,這代表的是上一個(gè)TAG的大小。
- 最后我們計(jì)算一下,上一個(gè)TAG數(shù)據(jù)部分是372個(gè)字節(jié),前面type、stream id等字段占了11個(gè)字節(jié)。正好是匹配的。
上面我們已經(jīng)知道了怎么取劃分每個(gè)TAG。接下來我們就看TAG的具體內(nèi)容:
tag的內(nèi)容
前面已經(jīng)提到tag分3種。我們一個(gè)個(gè)看
script
腳本Tag一般只有一個(gè),是flv的第一個(gè)Tag,用于存放flv的信息,比如duration、audiodatarate、creator、width等。
首先介紹下腳本的數(shù)據(jù)類型。所有數(shù)據(jù)都是以數(shù)據(jù)類型+(數(shù)據(jù)長度)+數(shù)據(jù)的格式出現(xiàn)的,數(shù)據(jù)類型占1byte,數(shù)據(jù)長度看數(shù)據(jù)類型是否存在,后面才是數(shù)據(jù)。
一般來說,該Tag Data結(jié)構(gòu)包含兩個(gè)AMF包。AMF(Action Message Format)是Adobe設(shè)計(jì)的一種通用數(shù)據(jù)封裝格式,在Adobe的很多產(chǎn)品中應(yīng)用,簡單來說,AMF將不同類型的數(shù)據(jù)用統(tǒng)一的格式來描述。第一個(gè)AMF包封裝字符串類型數(shù)據(jù),用來裝入一個(gè)“onMetaData”標(biāo)志,這個(gè)標(biāo)志與Adobe的一些API調(diào)用有,在此不細(xì)述。第二個(gè)AMF包封裝一個(gè)數(shù)組類型(srs返回為object類型),這個(gè)數(shù)組中包含了音視頻信息項(xiàng)的名稱和值。具體說明如下
| 值 | 類型 | 說明 |
|---|---|---|
| 0 | Number type | 8 Bypte Double |
| 1 | Boolean type | 1 Bypte bool |
| 2 | String type | 后面2個(gè)字節(jié)為長度 |
| 3 | Object type | |
| 4 | MovieClip type | |
| 5 | Null type | |
| 6 | Undefined type | |
| 7 | Reference type | |
| 8 | ECMA array type | 數(shù)組,類似Map |
| 10 | Strict array type | |
| 11 | Date type | |
| 12 | Long string type | 后面4個(gè)字節(jié)為長度 |
上圖為第一個(gè)AMF包
- type=0x02對應(yīng)String
- size=0A=10
-
value=onMetaData 正好是10個(gè)字節(jié)。
FLV_script-2.png
上圖為第二個(gè)AMF
- type=0x08 對應(yīng)ECMA array type。
表示數(shù)組,類似Map。后面4個(gè)字節(jié)為數(shù)組的個(gè)數(shù)。然后是鍵值對,第一個(gè)為鍵,2個(gè)字節(jié)為長度。后面跟具體的內(nèi)容。接著3個(gè)字節(jié)表示值的類型,然后根據(jù)類型判斷長度。
上圖我們可以判斷,總共有13個(gè)鍵值對。
第一個(gè)長度為8個(gè)字節(jié)是duration。值類型是0x004073,第一個(gè)字節(jié)是00,所以是double,8個(gè)字節(jié)。
第二個(gè)長度5個(gè)字節(jié)是width。值也是double類型,8個(gè)字節(jié)。
依次解析下去...
Audio
視頻中第二個(gè)tag為音頻tag
stream-id之后:
- 前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 |
| 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 |
- 接著2位為采樣率(對于AAC總是3)
| 值 | 類型 |
|---|---|
| 0 | 5.5-kHz |
| 1 | 11-kHz |
| 2 | 22-kHz |
| 3 | 44-kHz |
- 接著1位為采樣的長度(壓縮過的音視頻都是16bit)
| 值 | 類型 |
|---|---|
| 0 | snd8Bit |
| 1 | snd16Bit |
- 接著1位為音頻類型(對于AAC總是1)
| 值 | 類型 |
|---|---|
| 0 | sndMono |
| 1 | sndStereo |
video
由于kobe視頻音頻編碼是pcm,查找視頻tag太難,使用<<東風(fēng)破>> mv視頻
- type=0x09=9。這里應(yīng)該是一個(gè)video。
- size=0x000030=48。長度為48。
- timestreamp=0x000000。
- TimestampExtended =0x00。
- stream id =0x000000
我們看到數(shù)據(jù)部分:
視頻信息+數(shù)據(jù)
視頻信息,1個(gè)字節(jié)。
StreamId之后的數(shù)據(jù)就表示是VideoTagHeader,如果是avc,VideoTagHeader會(huì)多出4個(gè)字節(jié)的信息就是AVCPacketType和CompositionTime
- 前4位為幀類型Frame Type
| 值 | 類型 |
|---|---|
| 1 | keyframe (for AVC, a seekable frame) 關(guān)鍵幀 |
| 2 | inter frame (for AVC, a non-seekable frame) |
| 3 | disposable inter frame (H.263 only) |
| 4 | generated keyframe (reserved for server use only) |
| 5 | video info/command frame |
- 后4位為編碼ID (CodecID)
| 值 | 類型 |
|---|---|
| 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 |
特殊情況
視頻的格式(CodecID)是AVC(H.264)的話,VideoTagHeader會(huì)多出4個(gè)字節(jié)的信息,AVCPacketType 和CompositionTime。
- AVCPacketType 占1個(gè)字節(jié)
| 值 | 類型 |
|---|---|
| 0 | AVCDecoderConfigurationRecord(AVC sequence header) |
| 1 | AVC NALU |
| 2 | AVC end of sequence (lower level NALU sequence ender is not required or supported) |
AVCDecoderConfigurationRecord.包含著是H.264解碼相關(guān)比較重要的sps和pps信息,再給AVC解碼器送數(shù)據(jù)流之前一定要把sps和pps信息送出,否則的話解碼器不能正常解碼。而且在解碼器stop之后再次start之前,如seek、快進(jìn)快退狀態(tài)切換等,都需要重新送一遍sps和pps的信息.AVCDecoderConfigurationRecord在FLV文件中一般情況也是出現(xiàn)1次,也就是第一個(gè)video tag.
- CompositionTime 占3個(gè)字節(jié)
| 條件 | 值 |
|---|---|
| AVCPacketType ==1 | Composition time offset |
| AVCPacketType !=1 | 0 |
再看到第二個(gè)video tag
我們看到 AVCPacketType =1,而后面三個(gè)字節(jié)為000043。這是一個(gè)視頻幀數(shù)據(jù)。
解析到的數(shù)據(jù)完全符合上面的理論。
sps pps
前面我們提到第一個(gè)video 一般存放的是sps和pps。這里我們具體解析下sps和pps內(nèi)容。先看下存儲(chǔ)的格):
0x01+sps[1]+sps[2]+sps[3]+0xFF+0xE1+sps size+sps+01+pps size+pps
sps[1]=0x64
sps[2]=00
sps[3]=0D
sps size=0x001B=27(占兩個(gè)字節(jié))
跳過27個(gè)字節(jié)后,是0x01
pps size=0x0005=118(占兩個(gè)字節(jié))
跳過5個(gè)字節(jié),就到了back-pointers。
視頻幀數(shù)據(jù)
解析出sps和pps tag后,后面的video tag就是真正的視頻數(shù)據(jù)內(nèi)容了
這是第二個(gè)video tag其實(shí)和之前圖一樣,只是我圈出來關(guān)鍵信息。先看下格式
frametype=0x17=00010111
AVCPacketType =1
Composition Time=0x000043
后面就是NALU DATA
引用:
(原)從mp4,flv文件中解析出h264和aac,送解碼器解碼失敗:,avc1與H264區(qū)別在這里其實(shí)有人遇到了和我一樣的問題:http://stackoverflow.com/questions/11330764/ffmpeg-cant-decode-h264-stream-frame-data