摘 要:AVI文件是Windows操作系統(tǒng)下最常用的流媒體文件格式之一,了解AVI文件格式是正確使用AVI文件的基礎(chǔ),以一個具體的AVI文件的二進(jìn)制碼為例,詳細(xì)解釋和研究AVI文件的各個字段的具體含義,并指出AVI-1文件與AVI-2文件格式的區(qū)別,在此基礎(chǔ)上,可以對AVI文件進(jìn)行各種編程操作。給出使用VFW(Video For Windows)用Visual C++編程語言從AVI-2文件中獲取視頻幀,轉(zhuǎn)換為BMP圖像,加以顯示并存儲為位圖文件的編程方法。
關(guān)鍵詞:流媒體文件;VC;AVI;VFW
中圖分類號:TP311.1 文獻(xiàn)標(biāo)識碼:A 文章編號:1004-373X(2008)02-119-04
Study on AVI File Format and Its Application
XU Dianwu
(Mechano-Electronic College,China University of Petroleum,Beijing,102249,China)
Abstract:Under Windows environment,AVI is a popular stream media format.To work with AVI file,it is the basic to know about AVI file format.In this paper,the example ofAVI file binary code is presented in binary code,to illustrate every chunk in the file.The format of AVI files,and especially the difference between AVI-1 and AVI-2 format is discussed.Programming method for pickup video frames from an AVI file and save it as BMP files in Visual C++ is presented.
Keywords:stream medium file;VC;AVI;VFW
1 引 言
自微軟公司推出AVI文件格式以來,AVI逐漸成為Windows 操作系統(tǒng)上最常用的流媒體文件格式之一[1]。AVI是視頻數(shù)據(jù)和音頻數(shù)據(jù)交叉編碼(Audio Video Interleaved)的一種RIFF文件,其多用于音視頻捕捉、編輯、播放等,其文件名后綴為.avi。應(yīng)用程序的開發(fā)者在編寫多媒體處理軟件時,除要對AVI文件進(jìn)行播放、編輯、制作外,也經(jīng)常需要從一個AVI文件中提取一視頻幀并存儲為BMP圖像文件,這些都需要對AVI文件格式有深入了解,本文在對AVI文件格式進(jìn)行解析的基礎(chǔ)上,給出了從AVI文件中獲取位圖的程序設(shè)計方法。這里介紹的AVI文件格式不包括OpenDML AVI M-JPEG文件格式分委員會制定的內(nèi)容以及nAVI(一種改進(jìn)的ASF格式)的內(nèi)容。
2 RIFF文件格式簡介
RIFF(Resource Interchange File Format)文件格式是一個信息交換框架[1],AVI文件格式是以RIFF為基礎(chǔ)的,所以在很多文獻(xiàn)中,AVI文件又被稱為AVI RIFF文件[2]。
RIFF文件組成的基本單元叫作“塊”(chunk),使用FOURCC碼(Four Character Code,4字符碼,Windows中的數(shù)據(jù)類型為FOURCC)來標(biāo)識文件中的每個塊。FOURCC是由4個ASCII碼組成的一個DWORD型數(shù)據(jù),高字節(jié)在后,如4字符abcd的FOURCC碼是0X64636261。RIFF就是一個FOURCC,表示這是一個RIFF塊。在一個RIFF文件中,除RIFF塊和LIST塊可以包含子塊(Sub Chunk)外,其他任何塊均不可以包含子塊。
塊的格式分為2種,可以包含子塊的塊(RIFF,LIST)的格式為:FOURCC、塊長度(4字節(jié),DWORD類型,長度不包括FOURCC和塊長度占用的8個字節(jié))、塊數(shù)據(jù)類型、塊數(shù)據(jù)。不可以包含子塊的塊的格式是:FOURCC、塊長度、塊數(shù)據(jù),沒有塊數(shù)據(jù)類型部分。
3 AVI文件格式
AVI文件是一種最復(fù)雜的RIFF文件?,F(xiàn)在常用的AVI文件有2種:AVI-1和AVI-2。在AVI-2文件中,通常包含2個流,一個視頻流和一個音頻流(被稱為標(biāo)準(zhǔn)AVI格式),但只有一個視頻流或音頻流也是合法的。在AVI-1文件中只包含一個DV(digital Video)數(shù)據(jù)流(視頻采集設(shè)備的輸出數(shù)據(jù),其中既有視頻信息也有音頻信息),在文件中以單個流的形式存在,其主要優(yōu)點是占用較少的存儲空間,對AVI-1的支持主要來自DirectShow中的DV Muxer和DV Splitter篩選器(Filter)[2]。雖然目前AVI-2仍然是最常使用的格式,但AVI-1由于其自身的優(yōu)點和受到微軟最新技術(shù)的支持,將來可能會比AVI-2更流行。雖然這2種AVI文件不兼容,但他們都以RIFF格式為基礎(chǔ),除了實際的流數(shù)據(jù)格式不同以外,差別主要存在于文件的strh塊和strf塊。
下面為AVI文件的格式,左邊小括號處省略了4字節(jié)的文件大小或列表大小或塊大小,凡FOURCC都用4個字母表示,除RIFF和LIST外,所有的4字符碼都用單引號括起來,方括號表示可選:
RIFF (′AVI◇′[JY]//RIFF文件頭,塊的數(shù)據(jù)類型是AVI,
[JY]◇表示空格。
LIST (′hdrl′[JY]//列表,列表中的數(shù)據(jù)類型是hdrl,
[JY]下面緊跟avih子塊。
′avih′(AVI文件的全局信息,本行構(gòu)成一個AVIMAINHEADER結(jié)構(gòu),長度為64個字節(jié))。
LIST (′strl′[JY]//strl列表,表示本列表是一個流的列表,
[JY]其后緊跟strh子塊。
′strh′([JY]//流的頭信息,本行構(gòu)成一個AVISTREAMHEADER
[JY]結(jié)構(gòu),長度為64字節(jié))
′strf′( [JY]//流的格式信息子塊,描述流中數(shù)據(jù)的格式。)
[ ′strd′([JY]//附加的流頭數(shù)據(jù),一般為
[JY]編解碼器驅(qū)動程序所定義) ]
[ ′strn′(以0結(jié)束的字符串,流的名字) ]
...
)
...
)[JY]//hdrl列表塊到此結(jié)束
LIST (′movi′[JY]//movi列表塊,包含了流的實際數(shù)據(jù),
[JY]數(shù)據(jù)可以是子塊,也可以將子塊組織成rec 列表,
[JY]一個rec列表中的數(shù)據(jù)應(yīng)該一次性地從磁盤讀出。
{SubChunk | LIST (′rec ′
SubChunk1
SubChunk2
...
)
...
}
...
)//movi列表塊到此結(jié)束
[′idx1′ (索引塊) ]
)//RIFF塊到此結(jié)束,這就是一個完整的AVI RIFF文件
從上面的格式可以看出,一個AVI RIFF文件由3大部分組成:RIFF文件頭;hdrl列表;movi列表。其中hdrl列表包含avih子塊和strl子列表,文件中有多少個流,hdrl列表中就有多少個strl子列表,strl子列表在hdrl中的次序就是流的序號(隱含)。
strl子列表由strh子塊、strf子塊、strd子塊(可選)、strn子塊(可選)構(gòu)成。movi列表中存儲的是流的實際數(shù)據(jù),movi列表中數(shù)據(jù)子塊的種類有:##db,##dc,##pc,##wb,其中##表示數(shù)據(jù)所屬的流的序號,如00db,01dc。db表示未壓縮的視頻幀;dc表示壓縮的視頻幀;wb表示音頻數(shù)據(jù);pc表示調(diào)色板變化,其后的數(shù)據(jù)是一個AVIPALCHANGE結(jié)構(gòu),此時strh中的dwFlages字段必須置為AVISF_VIDEO_PALCHANGES。
movi列表最后可以有一個可選的索引塊(idxl),他指出流的數(shù)據(jù)在文件中的位置,以便于對數(shù)據(jù)進(jìn)行隨機(jī)訪問。一個AVI RIFF文件如果有索引塊,則將AVIMAINHEADER結(jié)構(gòu)中的dwFlags字段置為AVIF_HASINDEX。AVI文件中的JUNK塊為數(shù)據(jù)對齊而存在,應(yīng)用程序應(yīng)該忽略JUNK塊的內(nèi)容。
AVI-1中只有一個流(DV data stream),其AVISTREAMHEADER(strh塊)中的fccType字段是4字符碼′iavs′,fccHandler字段是′dvsd′,′dvhd′,′dvsl′三者之一(分別對應(yīng)3種DV格式),strl子列表中的strf塊是DVINFO結(jié)構(gòu)(32字節(jié))。AVI-2中一般有2個流,視頻流的AVISTREAMHEADER(strh塊)中的fccType字段是4字符碼′vids′(也可以是′dvsd′,′dvhd′,′dvsl′三者之一)[2], 音頻流的4字符碼是′auds′,其視頻流的strl子列表中的strf塊是BITMAPINFOHEADER結(jié)構(gòu)(40字節(jié)),其后可以跟隨DVINFO結(jié)構(gòu)。AVI-2中音頻流的strf塊是WAVEFORMATEX結(jié)構(gòu)。
AVI RIFF文件格式只規(guī)定了文件的組成方式,即各種數(shù)據(jù)如何在文件中排列等,對于文件中的數(shù)據(jù)并沒有做出編碼格式的約束,如MPEG-1、DivX等都可以作為文件中數(shù)據(jù)的編碼格式。數(shù)據(jù)也可以未經(jīng)壓縮。AVI文件可以看成是一個數(shù)據(jù)容器,AVI格式只規(guī)定了將數(shù)據(jù)裝入這個容器的方法,但對數(shù)據(jù)本身的編碼格式并不涉及。要操作一個AVI文件(播放、抓圖、編輯),必須在系統(tǒng)中安裝與該AVI文件中的編碼器對應(yīng)的解碼器,才能進(jìn)行正確的工作。
4 AVI-2文件格式的例
下面以AVI文件bfh.avi(風(fēng)景介紹片)為例,將該AVI文件打開,逐字節(jié)解釋其中的數(shù)據(jù)(見圖1):
52494646是RIFF的ASCII碼;7E443400是文件長度,高位字節(jié)在后,(0034447E)16,(3425406)10;41564920是AVI◇的ASCII碼,◇表示空格;表示RIFF塊(文件)的數(shù)據(jù)類型是AVI。4C495354是LIST的ASCII碼,表示這是一個列表。10h開始的4字節(jié)表示本列表長度,(132)16,(306)10。接著6864726C是hdrl的ASCII碼,表明這是hdrl列表,61766968是avih的ASCII碼,表明這是hdrl列表中的avih塊,第二行最后4字節(jié)是16進(jìn)制的38,十進(jìn)制的56;表示avih塊的長度是56個字節(jié)(加上8正好是AVIMAINHEADER結(jié)構(gòu)的長度),接下來的56個字節(jié)就是avih的內(nèi)容,描述AVI文件的整體信息,AVIMAINHEADER結(jié)構(gòu)中各個字段的內(nèi)容不再分析,avih塊到057h結(jié)束。
從0058h開始的4C495354是LIST的ASCII碼,其后4字節(jié)(74)16,即(116)10是本列表的大小。60h開始的7374726C是strl的ASCII碼,表示這是strl(流)列表,從64h到a3h的64個字節(jié)是一個AVISTREAMHEADER結(jié)構(gòu);64h開始的73747268是strh的ASCII碼,表示這是strl列表中strh塊(流的頭信息);下面的4字節(jié)是strh塊的大小(38)16,十進(jìn)制的56,76696473是vids的ASCII碼,是流的類型,表示這是一個視頻流,從上面對AVI的2種格式的分析可知,這是一個AVI-2文件,如果是AVI-1文件,此處應(yīng)該是iavs的ASCII碼。70h開始的4字節(jié)是′MSVC′(已經(jīng)注冊的解碼器的4字符碼 )的ASCII碼4D535643,AVISTREAMHEADER結(jié)構(gòu)其他字段不再羅列,90h這一行最后4個字節(jié),表示顯示窗口左上角x,y,其值為0,0。a0h開始的2字節(jié)是窗口顯示的右下角x坐標(biāo),值為十進(jìn)制的128,以象素為單位,再2字節(jié)是右下角y坐標(biāo),十進(jìn)制的112,至此,AVISTREAMHEADER結(jié)束。
a4h開始的4字節(jié)是strf的ASCII碼73747266,后跟的4字節(jié)(28)16,(40)10是strf塊的長度,前面說過,在AVI-2中,如果是視頻流,strf就是一個BITMAPINFOHEADER結(jié)構(gòu),而該結(jié)構(gòu)的長度是40字節(jié),再后的4字節(jié)(28)16,就是BITMAPINFOHEADER的第一個字段,指明該結(jié)構(gòu)的長度,所以有2個(28)16相連。從boh開始,就是BITMAPINFOHEADER其余的內(nèi)容,(80)16是圖像的寬度,120象素。(70)16是圖像的高度,112象素,這些都與前面的信息相符合。0001兩字節(jié)是位平面數(shù),(0018)16是每象素比特數(shù)(24位)。接下來4字節(jié)44495633是′DIV3′(壓縮編碼格式的四字符碼)的ASCII碼,是BITMAPINFOHEADER的biCompression域的值,指壓縮方式,這是和普通的BMP文件不同的。后面的字段不再分析。值得指出的是,這里可以后跟一個DVINFO結(jié)構(gòu)(可選),如果是AVI-1文件,這里用DVINFO結(jié)構(gòu)取代BITMAPINFOHEADER結(jié)構(gòu)。接下來的4C495344是LIST的ASCII碼,表明下面又是一個列表,列表大小是6Ah,doh行最后4字節(jié)是strl的ASCII碼,表明這是一個“流”列表。后面的數(shù)據(jù)不再分析。
5 從AVI文件中提取位圖
Windows提供的VFW和DirectShow[3]都可以處理和操作AVI文件,考慮到VFW為大多數(shù)程序員所熟悉以及AVI-2仍然是最常使用的AVI文件格式,下面給出使用VFW從AVI-2文件中提取視頻幀加以顯示并存儲為BMP位圖文件的程序。
VFW 提供的AVIFile動態(tài)鏈接庫使用OLE技術(shù)實現(xiàn),在AVIFile中的API函數(shù)將AVI文件中的數(shù)據(jù)當(dāng)作“流”來處理,而不必逐一地尋找其中的塊(chunk)。使用該庫要首先調(diào)用函數(shù)AVIFileInit來初始化,使用完畢調(diào)用函數(shù)AVIFileExit后釋放。首先使用MFC AppWizard創(chuàng)建一個名為avi的單文檔項目,然后重載CDocument類的虛函數(shù)OnOpenDocument,在此函數(shù)中打開AVI文件,讀出其中的視頻幀,存儲為BMP文件并在屏幕上加以顯示(在VC++6.0中調(diào)試通過):
BOOL CAviDoc::OnOpenDocument(LPCTSTR lpszPathName)
{ if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE;
AVIFileInit(); [JY]//初始化
AVIFileOpen(pAviFile,lpszPathName,OF_SHARE_DENY_WRITE,0l);[JY]//打開AVI文件
AVIFileGetStream(pAviFile,pAviStream,streamtypeVIDEO,0L );[JY]//打開流
AVIStreamInfo(pAviStream,aviStrInfo,sizeof(AVISTREAMINFO));[JY]//獲取流的信息
long lStreamSize;[JY]//流格式長度
pBmpInfoH = new BITMAPINFOHEADER;[JY]//pBmpInfoH是文檔類的成員變量
AVIStreamFormatSize(pAviStream,0,lStreamSize);[JY]//獲取流的格式信息的長度
AVIStreamReadFormat(pAviStream,0L,pBmpInfoH,lStreamSize);
PGETFRAME pgf;
pgf = AVIStreamGetFrameOpen(pAviStream,NULL);[JY]//為解壓幀做準(zhǔn)備
[JY]//解壓讀出第0幀,緊縮DIB的數(shù)據(jù)以返回值pData為起始地址
pData = (LPBYTE)AVIStreamGetFrame(pgf,0L);[JY]//文檔類的成員變量,類型為LPBYTE
pBmpInfoH->biCompression = 0;
BITMAPFILEHEADER bmpFH;[JY]//位圖文件頭
bmpFH.bfType = 0x4d42;
bmpFH.bfSize = bmpInfoH.biWidth*bmpInfoH.biHeight*3 + 54;
bmpFH.bfReserved1 = 0;bmpFH.bfReserved2 = 0;
bmpFH.bfOffBits = 54;
CFileDialog dlg(FALSE);[JY]//獲取用戶要存儲成的位圖文件名和路徑
if(IDOK==dlg.DoModal ())str.Format (\"%s\",dlg.GetPathName() );
CFile bmp(str,CFile::modeWrite|CFile::modeCreate);
bmp.Write(bmpFH,sizeof(BITMAPFILEHEADER));[JY]//寫入位圖文件頭
bmp.WriteHuge(pData,bmpFH.bfSize-14);[JY]//寫入緊縮DIB
bmp.Close(); AVIFileExit();
UpdateAllViews(NULL);[JY]//更新視圖,在視圖類的OnDraw函數(shù)中顯示位圖
return TRUE;
}//程序中省略所有安全性檢查語句。位圖的顯示在視類的OnDraw函數(shù)(略)中。
圖2是程序執(zhí)行的結(jié)果:從bfh.avi中提取的第0幀轉(zhuǎn)換成的位圖的顯示。
上面程序中pAviFile,pAviStream,aviStrInfo都是文檔類的成員變量,類型分別為PAVIFILE,PAVISTREAM,AVISTREAMINFO。在文檔類的構(gòu)造函數(shù)中將所有指針初始化為NULL。還可以循環(huán)調(diào)用函數(shù)AVIStreamFindNextKey尋找所有的關(guān)鍵幀加以顯示并存儲為BMP文件。需要注意,函數(shù)AVIStreamGetFrame返回的是一個“緊縮DIB”的地址,即以一個位圖的BITMAPINFOHEADER開始的地址,且所需的緩沖區(qū)由該函數(shù)自己開辟,不需應(yīng)用程序動態(tài)申請。程序要加入#include
6 結(jié) 語
由于Windows是很流行的操作系統(tǒng),AVI是Microsoft公司大力支持的格式,因此在編寫基于Windows操作系統(tǒng)的流媒體圖像處理程序方面,AVI占有不可替代的位置。準(zhǔn)確理解AVI文件格式以及針對AVI文件的編程方法在工程實踐中具有重要的意義。AVI-1文件格式由于占用存儲空間小,受到DirectShow的支持,其將來會成為AVI文件中最流行的格式。
參 考 文 獻(xiàn)
[1]姜楠,王健.常用多媒體文件格式壓縮標(biāo)準(zhǔn)解析[M].北京:電子工業(yè)出版社,2005.
[2]DirectX Documentation for C++[EB/OL].Microsoft Corporation,2005.
[3]陸其明.DirectShow 開發(fā)指南[M].北京:清華大學(xué)出版社,2003.
注:本文中所涉及到的圖表、注解、公式等內(nèi)容請以PDF格式閱讀原文。