簡介
FLV(Flash Video)是現在非常流行的流媒體格式,由於其視頻文件體積輕巧、封裝播放簡單等特點,使其很適合在網絡上進行應用,目前主流的視頻網站無一例外地使用了FLV格式。另外由於當前瀏覽器與Flash Player緊密的結合,使得網頁播放FLV視頻輕而易舉,也是FLV流行的原因之一。
FLV是流媒體封裝格式,我們可以將其數據看為二進制字節流。總體上看,FLV包括文件頭(File Header)和文件體(File Body)兩部分,其中文件體由一系列的Tag及Tag Size對組成。
FLV格式解析
先來一張圖,這是《東風破》——周傑倫(下載)的一個MV視頻。我使用的是Binary Viewer的二進制查看工具。
header
頭部分由一下幾部分組成
Signature(3 Byte)+Version(1 Byte)+Flags(1 Bypte)+DataOffset(4 Byte)
- signature 占3個字節
固定FLV三個字符作為標示。一般發現前三個字符為FLV時就認為他是flv文件。 - Version 占1個字節
標示FLV的版本號。這里我們看到是1 - Flags 占1個字節
內容標示。第0位和第2位,分別表示 video 與 audio 存在的情況.(1表示存在,0表示不存在)。截圖看到是0x05,也就是00000101,代表既有視頻,也有音頻。 - DataOffset 4個字節
表示FLV的header長度。這里可以看到固定是9
body
FLV的body部分是由一系列的back-pointers + tag構成
- back-pointers 固定4個字節,表示前一個tag的size。
- tag 分三種類型,video、audio、scripts。
tag組成
tag type+tag data size+Timestamp+TimestampExtended+stream id+ tag data
- type 1個字節。8為Audio,9為Video,18為scripts
- tag data size 3個字節。表示tag data的長度。從streamd id 后算起。
- Timestreamp 3個字節。時間戳
- TimestampExtended 1個字節。時間戳擴展字段
- stream id 3個字節。總是0
- tag data 數據部分
我們根據實例來分析:
看到第一個TAG
type=0x12=18。這里應該是一個scripts。
size=0x000125=293。長度為293。
timestreamp=0x000000。這里是scripts,所以為0
TimestampExtended =0x00。
stream id =0x000000
我們看一下TAG的data部分:
tag的划分
圖中紅色部分是我標出的兩個back-pointers,都是4個字節。而中間就是第一個TAG。那是怎么計算的呢?我們就以這個做個示例。
- 首先第一個back-pointers是
0x00000000,那是因為后面是第一個TAG。所以他為0。 - 然后根據我們我們前面格式獲取到size是
0x000125。也就是說從stream id后面再加上293個字節就到了第一個TAG的末尾,我們數一下一下。stream id以前總共有24個字節。那么到第一個TAG結束,總共有293+24=137=0x13D。 - 接下來我們找到
0x13D的地址,從工具上很容易找到,正好就是紅色下划線的前面。紅色部分是0x00000130=304,這代表的是上一個TAG的大小。 - 最后我們計算一下,上一個TAG數據部分是293個字節,前面type、stream id等字段占了11個字節。正好是匹配的。
上面我們已經知道了怎么取划分每個TAG。接下來我們就看TAG的具體內容
tag的內容
前面已經提到tag分3種。我們一個個看
script
腳本Tag一般只有一個,是flv的第一個Tag,用於存放flv的信息,比如duration、audiodatarate、creator、width等。
首先介紹下腳本的數據類型。所有數據都是以數據類型+(數據長度)+數據的格式出現的,數據類型占1byte,數據長度看數據類型是否存在,后面才是數據。
一般來說,該Tag Data結構包含兩個AMF包。AMF(Action Message Format)是Adobe設計的一種通用數據封裝格式,在Adobe的很多產品中應用,簡單來說,AMF將不同類型的數據用統一的格式來描述。第一個AMF包封裝字符串類型數據,用來裝入一個“onMetaData”標志,這個標志與Adobe的一些API調用有,在此不細述。第二個AMF包封裝一個數組類型,這個數組中包含了音視頻信息項的名稱和值。具體說明如下,大家可以參照圖片上的數據進行理解。
| 值 | 類型 | 說明 |
|---|---|---|
| 0 | Number type | 8 Bypte Double |
| 1 | Boolean type | 1 Bypte bool |
| 2 | String type | 后面2個字節為長度 |
| 3 | Object type | |
| 4 | MovieClip type | |
| 5 | Null type | |
| 6 | Undefined type | |
| 7 | Reference type | |
| 8 | ECMA array type | 數組,類似Map |
| 10 | Strict array type | |
| 11 | Date type | |
| 12 | Long string type | 后面4個字節為長度 |
上圖為第一個AMF包
- type=
0x02對應String - size=
0A=10 -
value=onMetaData 正好是10個字節。
5.png
上圖為第二個AMF
- type=
0x08對應ECMA array type。
表示數組,類似Map。后面4個字節為數組的個數。然后是鍵值對,第一個為鍵,2個字節為長度。后面跟具體的內容。接着3個字節表示值的類型,然后根據類型判斷長度。
上圖我們可以判斷,總共有13個鍵值對。
第一個長度為8個字節是duration。值類型是0x004073,第一個字節是00,所以是double,8個字節。
第二個長度5個字節是width。值也是double類型,8個字節。
依次解析下去...
到處,我們已經知道了如何解析FLV中Tag為script的數據。
video
type=
0x09=9。這里應該是一個video。
size=
0x000030=48。長度為48。
timestreamp=
0x000000。
TimestampExtended =
0x00。
stream id =
0x000000
我們看到數據部分:
視頻信息+數據
視頻信息,1個字節。
前4位為幀類型Frame Type
| 值 | 類型 |
|---|---|
| 1 | keyframe (for AVC, a seekable frame) 關鍵幀 |
| 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會多出4個字節的信息,AVCPacketType 和CompositionTime。
- AVCPacketType 占1個字節
| 值 | 類型 |
|---|---|
| 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解碼相關比較重要的sps和pps信息,再給AVC解碼器送數據流之前一定要把sps和pps信息送出,否則的話解碼器不能正常解碼。而且在解碼器stop之后再次start之前,如seek、快進快退狀態切換等,都需要重新送一遍sps和pps的信息.AVCDecoderConfigurationRecord在FLV文件中一般情況也是出現1次,也就是第一個video tag.
- CompositionTime 占3個字節
| 條件 | 值 |
|---|---|
| AVCPacketType ==1 | Composition time offset |
| AVCPacketType !=1 | 0 |
我們看第一個video tag,也就是前面那張圖。我們看到AVCPacketType =0。而后面三個字節也是0。說明這個tag記錄的是AVCDecoderConfigurationRecord。包含sps和pps數據。
再看到第二個video tag
我們看到 AVCPacketType =1,而后面三個字節為
000043。這是一個視頻幀數據。
解析到的數據完全符合上面的理論。
sps pps
前面我們提到第一個video 一般存放的是sps和pps。這里我們具體解析下sps和pps內容。先看下存儲的格式(圖6):
0x01+sps[1]+sps[2]+sps[3]+0xFF+0xE1+sps size+sps+01+pps size+pps
我們看到圖7 。
sps[1]=0x64
sps[2]=00
sps[3]=0D
sps size=0x001B=27
跳過27個字節后,是0x01
pps size=0x0005=118
跳過5個字節,就到了back-pointers。
視頻幀數據
解析出sps和pps tag后,后面的video tag就是真正的視頻數據內容了
這是第二個video tag其實和圖8一樣,只是我圈出來關鍵信息。先看下格式
frametype=
0x17=
00010111
AVCPacketType =1
Composition Time=
0x000043
后面就是NALU DATA
Audio
與視頻格式類似
前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位為采樣率
| 值 | 類型 |
|---|---|
| 0 | 5.5-kHz |
| 1 | 11-kHz |
| 2 | 22-kHz |
| 3 | 44-kHz |
對於AAC總是3
接着1位為采樣的長度
| 值 | 類型 |
|---|---|
| 0 | snd8Bit |
| 1 | snd16Bit |
壓縮過的音頻都是16bit
接着1位為音頻類型
| 值 | 類型 |
|---|---|
| 0 | sndMono |
| 1 | sndStereo |
對於AAC總是1
我們看到第三個TAG
這個留給大家自己來解析吧。
from:https://www.jianshu.com/p/7ffaec7b3be6
