在如何看待嗶哩嗶哩的開源 HTML5 播放器內(nèi)核 flv.js?中,flv.js作者有這樣一段回復(fù):
一些人問(wèn)我為什么不直接采用 MP4 格式何陆,并表示對(duì) FLV 格式的厭惡甲献。這個(gè)問(wèn)題一方面是歷史遺留問(wèn)題晃洒,由于視頻網(wǎng)站前期完全依賴 Flash 播放而選擇 FLV 格式;另一方面吃引,如果仔細(xì)研究過(guò) FLV/MP4 封裝格式镊尺,你會(huì)發(fā)現(xiàn) FLV 格式非常簡(jiǎn)潔,而 MP4 內(nèi)部 box 種類繁雜弄砍,結(jié)構(gòu)復(fù)雜固實(shí)而又有太多冗余數(shù)據(jù)音婶。FLV 天生具備流式特征適合網(wǎng)絡(luò)流傳輸,而 MP4 這種使用最廣泛的存儲(chǔ)格式碴卧,設(shè)計(jì)卻并不一定優(yōu)雅螟深。
關(guān)于MP4格式,可以參考VillainHR 學(xué)好 MP4垢箕,讓直播更給力条获。本文來(lái)學(xué)習(xí)一下FLV格式。
參考
FLV文件格式詳解
flv格式詳解+實(shí)例剖析
FLV 實(shí)例分析
在集體挺進(jìn)HTML5的時(shí)代修档,來(lái)討論Adobe Flash相關(guān)的話題似乎有點(diǎn)過(guò)時(shí)吱窝,但現(xiàn)如今還是有很多的視頻網(wǎng)站采用的是Flash播放器,播放的文件也依然還有很多是FLV格式照激,而且僅從一個(gè)文件格式的角度去了解和分析FLV應(yīng)該也還說(shuō)的過(guò)去的实抡。FLV(Flash Video)是Adobe的一個(gè)免費(fèi)開放的音視頻格式吆寨,babala~~ 省略若干字的介紹,要看辣卒,到官網(wǎng)看吧想帅,這里不贅述港准,我們主要來(lái)討論下FLV文件格式的細(xì)節(jié),整體上衩椒,F(xiàn)LV分為Header
和Body
兩大塊毛萌。
Header: 記錄FLV的類型朝聋,版本,當(dāng)前文件類型等信息言蛇,這些信息可以讓我們對(duì)當(dāng)前FLV文件有個(gè)概括的了解腊尚。
Body: FLV的Body是Flv的數(shù)據(jù)區(qū)域,這些是FLV的具體內(nèi)容民宿,因?yàn)镕LV中的內(nèi)容有多種活鹰,并可同時(shí)存在着绷,因此,Body也不是一整塊的數(shù)據(jù)子漩,而是由更細(xì)分的塊來(lái)組成,這個(gè)細(xì)分的塊叫Tag讲衫。
先來(lái)一張圖,這是《東風(fēng)破》——周杰倫(下載)的一個(gè)MV視頻枷畏。我使用的是Binary Viewer的二進(jìn)制查看工具拥诡。
一、Header
頭部分由一下幾部分組成仇祭,Signature(3 Byte)+Version(1 Byte)+Flags(1 Bypte)+DataOffset(4 Byte)乌奇,共9字節(jié)。
1.signature
46 4C 56 正是FLV這三個(gè)字符的ASCII編碼困乒,這個(gè)是固定標(biāo)識(shí)迁霎,表示它是FLV文件考廉。
2.version
版本號(hào)0x01
3.Flags
0x05既绕,對(duì)應(yīng)二進(jìn)制00000101凄贩,前面一個(gè)1表示有音頻數(shù)據(jù)疲扎,后面一個(gè)1表示有視頻數(shù)據(jù)。
4.DataOffset
此4字節(jié)共同組成一個(gè)無(wú)符號(hào)32位整數(shù)(使用大頭序)救巷,表示文件從FLV Header開始到Flv Body的字節(jié)數(shù)棒假,當(dāng)前版本固定為9(0x00,0x00渤弛,0x00佳头,0x09)
二康嘉、Body
1.Previous Tag Size
這個(gè)比較好理解亭珍,就是前一個(gè)Tag的大小阻荒,這里同樣存的是無(wú)符號(hào)32位整型數(shù)值侨赡。因?yàn)榈谝粋€(gè)Previous Tag Size是緊接著FLV Header的,因此油猫,其值也是固定為0(0x00眨攘,0x00,0x00该肴,0x00)匀哄。
2.TAG
FLV中的TAG不止一種涎嚼,當(dāng)前版本共有3種類型組成:音頻(audio),視頻(video)立哑,腳本數(shù)據(jù)(script data),這三種類型會(huì)在Tag內(nèi)進(jìn)行標(biāo)志區(qū)分捂掰。其中:Audio Tag是音頻數(shù)據(jù)这嚣,Video Tag是視頻數(shù)據(jù)互广,Script Data存放的是關(guān)于FLV視頻和音頻的一些參數(shù)信息(亦稱為Metadata Tag),通常該Tag會(huì)在FLV File Header后面作為第一個(gè)Tag出現(xiàn)旅敷,并且一個(gè)文件僅有一個(gè)Script Data Tag。
為了在Tag內(nèi)存放不同的數(shù)據(jù)晴音,并且能夠方便區(qū)分锤躁,每個(gè)Tag被定義了Tag Header和Tag Data兩部分,他們的結(jié)構(gòu)如下:
-------------------------
| Tag Header |
-------------------------
| Tag Data |
-------------------------
-------------------------
| Tag Header |
-------------------------
/ \
--------------------------------------------------------
| 08 | 00 | 00 | 18 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
--------------------------------------------------------
Tag Header由11字節(jié)組成:
- 第1字節(jié)type:標(biāo)志當(dāng)前Tag的類型,音頻(0x08)梧乘,視頻(0x09)澎迎,Script Data(0x12),除此之外选调,其他值非法嗡善;
- 第2-4字節(jié)tag data size:表示一個(gè)無(wú)符號(hào)24位整型數(shù)值,表示當(dāng)前Tag Data的大醒纭罩引;
- 第5-7字節(jié)Timestreamp:無(wú)符號(hào)24位整型數(shù)值(UI24),當(dāng)前Tag的時(shí)間戳(單位為ms)枝笨,第一個(gè)Tag的時(shí)間戳總為0;
- 第8字節(jié)TimestampExtended:為時(shí)間戳的擴(kuò)展字節(jié)洒缀,當(dāng)前24位不夠用時(shí),該字節(jié)作為最高位瘫俊,將時(shí)間戳擴(kuò)展為32位無(wú)符號(hào)整數(shù)(UI32)
- 第9-11字節(jié)stream id:UI24類型川尖,表示Stream ID,總是0
看一下上述實(shí)例,第一個(gè)TAG:
type=0x12=18毛嫉,是一個(gè)Script Data仙粱。
tag data size=0x000125=293。長(zhǎng)度為293。
timestreamp=0x000000。這里是scripts楔脯,所以為0
TimestampExtended =0x00皆串。
stream id =0x000000
這里來(lái)找一下第一個(gè)TAG在哪里結(jié)束姥宝,Tag Header本身是11個(gè)字節(jié)漱凝,第2-4字節(jié)tag data size現(xiàn)在已經(jīng)知道是293字節(jié),合計(jì)第一個(gè)TAB長(zhǎng)度是11+293=304也就是16進(jìn)制的130快耿。那么下一個(gè)TAG的Previous Tag Size應(yīng)該就是0x 00 00 01 30吮便,見下圖紅線
可以在圖片上數(shù)一下,第一行的12 00 01是3個(gè),中間共18行,每行16字節(jié)宫蛆,最后劃紅線00 00 01那行有13字節(jié),合計(jì)是3+18*16+13=304,確認(rèn)無(wú)誤肴颊。
3.TAG DATA
Tag Data由Tag Header標(biāo)志后,就被分成音頻闯睹,視頻荣挨,Script Data三類
4.Script Data
腳本Tag一般只有一個(gè),是flv的第一個(gè)Tag,用于存放flv的信息,比如duration缚陷、audiodatarate非春、creator禽炬、width等。
首先介紹下腳本的數(shù)據(jù)類型。所有數(shù)據(jù)都是以數(shù)據(jù)類型+(數(shù)據(jù)長(zhǎng)度)+數(shù)據(jù)的格式出現(xiàn)的拒迅,數(shù)據(jù)類型占1byte蒙揣,數(shù)據(jù)長(zhǎng)度看數(shù)據(jù)類型是否存在,后面才是數(shù)據(jù)办龄。
一般來(lái)說(shuō),該Tag Data結(jié)構(gòu)包含兩個(gè)AMF包昼榛。AMF(Action Message Format)是Adobe設(shè)計(jì)的一種通用數(shù)據(jù)封裝格式境肾,在Adobe的很多產(chǎn)品中應(yīng)用,簡(jiǎn)單來(lái)說(shuō)胆屿,AMF將不同類型的數(shù)據(jù)用統(tǒng)一的格式來(lái)描述奥喻。第一個(gè)AMF包封裝字符串類型數(shù)據(jù),用來(lái)裝入一個(gè)“onMetaData”標(biāo)志非迹,這個(gè)標(biāo)志與Adobe的一些API調(diào)用有环鲤,在此不細(xì)述。第二個(gè)AMF包封裝一個(gè)數(shù)組類型憎兽,這個(gè)數(shù)組中包含了音視頻信息項(xiàng)的名稱和值冷离。具體說(shuō)明如下,大家可以參照?qǐng)D片上的數(shù)據(jù)進(jìn)行理解纯命。
(1)先看第一個(gè)AMF包西剥,從02 00 0A 6F往后讀
第一個(gè)域是Name。Name又是SCRIPTDATAVALUE類型
type = 0x 02 對(duì)照上表是SCRIPTDATASTRING
SCRIPTDATASTRING類型會(huì)先用uint16標(biāo)識(shí)出數(shù)據(jù)長(zhǎng)度
size = 0x 00 0A 亿汞,說(shuō)明長(zhǎng)度為10
value=onMeta Data= 0x 6F 6E 4D 65 74 61 44 61 74 61對(duì)應(yīng)的ASCII碼正是onMetaData瞭空,見下圖紅線。
(2)然后看第二個(gè)AMF
type = 0x 08 對(duì)照上表是數(shù)組疗我,后面Length UI32匙铡,即4個(gè)字節(jié)為數(shù)組的個(gè)數(shù)
size = 0x 00 00 00 0D = 13,說(shuō)明數(shù)組長(zhǎng)度為13碍粥,后面有13個(gè)SCRIPTDATAOBJECTPROPERTY鳖眼。
對(duì)照上圖,SCRIPTDATAOBJECTPROPERTY由PropertyName(SCRIPTDATASTRING類型)和PropertyData(SCRIPTDATAVAULE類型)組成
(3)第一個(gè)鍵值對(duì)
PropertyName 是SCRIPTDATASTRING類型
string length = 0x 00 08 說(shuō)明長(zhǎng)度為8
string data= 0x 64 75 72 61 74 69 6F 6E嚼摩,正是ASCII碼duration
PropertyData是一個(gè)SCRIPTDATAVAULE類型钦讳。用的是UI8標(biāo)識(shí)type
type = 0x 00 是個(gè)double數(shù)值(8字節(jié))
value = 0x 40 73 A7 85 1E B8 51 EC
(4)第二個(gè)鍵值對(duì)
PropertyName 是SCRIPTDATASTRING類型
string length = 0x 00 05 說(shuō)明長(zhǎng)度為5
string data = 0x 77 69 64 74 68矿瘦,正是ASCII碼width
PropertyData是一個(gè)SCRIPTDATAVAULE類型。用的是UI8標(biāo)識(shí)type
type = 0x 00 是個(gè)double數(shù)值(8字節(jié))
value = 0x 40 76 00 00 00 00 00 00
(5)第三個(gè)鍵值對(duì)
PropertyName
string length = 0x 00 06 長(zhǎng)度為6
string data = 68 65 69 64 74 68 即width
PropertyData
type = 0x 00
value = 0x 40 76 00 00 00 00 00 00
后面的屬性同理
5.video tag
type=0x09=9愿卒。這里應(yīng)該是一個(gè)video缚去。
size=0x000030=48。長(zhǎng)度為48琼开。
timestreamp=0x000000易结。
TimestampExtended =0x00。
stream id =0x000000
(1)接著StreamID字段之后的就是VideoTAagHeader
特殊情況
視頻的格式(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 |
(2)第一個(gè)字節(jié)是0x 17涣旨,即
FrameType= 0x01
CodecID = 0x07
因?yàn)閏odecID=7,所以后面有AVCPacketType1個(gè)字節(jié)=0,CompositionTime3個(gè)字節(jié)也是0股冗。
(3)我們看第一個(gè)video tag开泽,也就是前面那張圖。我們看到AVCPacketType =0魁瞪。而后面三個(gè)字節(jié)也是0穆律。說(shuō)明這個(gè)tag記錄的是AVCDecoderConfigurationRecord。包含sps和pps數(shù)據(jù)导俘。
下面為了復(fù)制截圖方便峦耘,引用了FLV 實(shí)例分析中的例子
configurationVersion = 0x01
AVCProfileIndication = 0x4D (77) Main Profile
profile_compatibiltity = 0x40
AVCLevelIndication = 0x1F (31)
第五六字節(jié)是0xFFE1 ,寫成二進(jìn)制格式 ‘1111 1111 1110 0001’b
對(duì)應(yīng)到AVCDecoderConfigurationRecord的語(yǔ)法定義
lengthSizeMinusOne = ‘11’b (3) 也就是NALUintLength字段會(huì)是4個(gè)字節(jié)
numOfSequenceParameterSets = ‘00001’b 有一個(gè)Sps結(jié)構(gòu)
接下來(lái)16bits 是 sequenceParameterSetLength = 0x0019 (25 bytes)
下圖選中部分就是Sps了
再往下:
numOfPictureParamterSets = 0x01
pictureParameterSetLength = 0x0004;
下圖選中的就是pps了
再后面四個(gè)字節(jié)是PreviousTagSize= 0x00 00 00 38 (56) 等于這個(gè)Tag的 DataSize + 11 == (45) + 11。
6.audio tag
再下來(lái)又是一個(gè)新的FLVTAG了旅薄。
11個(gè)字節(jié)的頭部先取出來(lái)
TagType = 8 (音頻)
DataSize =0x000009 ( 9bytes)
(1)AudioTagHeader結(jié)構(gòu) 0xAF
SoundFormat(4bits) = 0x0A (10 == AAC)
SoundRate(2bits) = ‘11’b (3 == 44kHz)
SoundSize(1bit) =’1’b (1 == 16-bit)
SoundType(1bit) = ‘1’b (1= Stereo)
注: 雖然這里SoundRate, SoundSize SoundType 都是 1 辅髓。但是這些都是定值,AAC格式的時(shí)候少梁,不看這里的值洛口,可以忽略掉。具體的真是值應(yīng)該從后面的數(shù)據(jù)從獲取
AACPacketType == 0x00 (AAC seqence header)
所以AACPacketType后面的就是AudioSpecificConfig了 0x13 90 56
AudioSpecificConfig.audioObjectType(5 bits) = 2 (AAC LC)
AudioSpecificConfig.samplingFrequencyIndex(4 bits) = 7
AudioSpecificConfig.channelConfiguration (4 bits)= 2
AudioSpecificConfig.GASpecificConfig.frameLengthFlag (1 bit) = 0
AudioSpecificConfig.GASpecificConfig.dependsOnCoreCoder : (1 bit) = 0
AudioSpecificConfig.GASpecificConfig.extensionFlag : (1 bit) = 1
剩下的四個(gè)字節(jié)就是extensionflag3的相關(guān)內(nèi)容凯沪,這塊還沒(méi)有研究過(guò)第焰。
到這一個(gè)audioTag結(jié)束。
后面的0x00000014是這個(gè)AudioTag的長(zhǎng)度 等于 20 = 9 + 11妨马。
7.再后面又是一個(gè)新的TAG
從這前11個(gè)字節(jié)知道的是這是一個(gè)視頻Tag. DataSize = 0x00099F (2463)timeStamp == 0挺举;
然后再往后看杀赢,是一個(gè)VideoTagHeader結(jié)構(gòu),可以得到的信息如下
FrameType = 1 (是一個(gè)Key Frame)
CodecID= 7 (AVC)
AVCPacketType = 1 湘纵;是一個(gè)普通的AVC NALUint
CompositionTime = 0x000043 (67)
那么這個(gè)Key Frame包含多少個(gè)NALUint呢脂崔,我們?cè)賮?lái)一步步看下去吧。記得前面我們分析的嗎梧喷?NALUint數(shù)據(jù)的開頭的NALUintLength字段砌左。由之前的分析可知。占四個(gè)字節(jié)铺敌,
NALUintLength = 0x00000222 (546 Bytes) 說(shuō)明第一NALUint的長(zhǎng)度是546字節(jié)
接著是第二個(gè)NALUintLength = 0x00 00 05 F3 (1523 bytes) 汇歹,圖片太長(zhǎng),詳見原文
接下來(lái)又是一個(gè)NALUintLength = 0x00 00 01 0B (267 bytes) 适刀,圖略
下來(lái)又是一個(gè)NALUintLenth = 0x00 00 00 32 (50 bytes) 秤朗,圖略
接下來(lái)還是一個(gè)NALUintLength = 0x00 00 00 34 (52 byte)煤蹭,圖略
好了笔喉,到這里我們第一個(gè)KeyFrame的所有NALUint都已經(jīng)取出來(lái)了 ,TagDataSize (2463 Bytes)= 1 (FrameType + CodecID) + 1 (AVCPacketType) + 3 (CompositionTime) + 4 + 546 +4 + 1523 + 4 + 267 + 4 + 50 + 4 + 52 等式成立硝皂。