AVI格式詳細解析

來源:互聯網
上載者:User

摘要:本文詳細的解析了AVI檔案的儲存結構,介紹了微軟提供的用來操作AVI檔案的一組API使用方法,並通過例子代碼,示範了如何將一組靜態Bmp圖片合成一個avi視頻檔案以及如何將一個avi視頻檔案解析儲存為一系列的bmp影像檔。 AVI是音訊視訊交錯格式(Audio Video Interleaved)的英文縮寫,它是Microsoft公司開發的一種符合RIFF檔案規格的數字音頻與視頻檔案格式,原先用於Microsoft Video for Windows (簡稱VFW)環境,現在已被Windows 95/98、OS/2等多數作業系統直接支援。AVI格式允許視頻和音頻交錯在一起同步播放,支援256色和RLE壓縮,但AVI檔案並未限定壓縮標準,因此,AVI檔案格式只是作為控制介面上的標準,不具有相容性,用不同壓縮演算法產生的AVI檔案,必須使用相應的解壓縮演算法才能播放出來。常用的AVI播放驅動程式,主要是Microsoft
Video for Windows或Windows 95/98中的Video 1,以及Intel公司的Indeo Video。  在介紹AVI檔案前,我們要先來看看RIFF檔案結構。AVI檔案採用的是RIFF檔案結構方式,RIFF(Resource Interchange File Format,資源互換檔案格式)是微軟公司定義的一種用於管理windows環境中多媒體資料的檔案格式,波形音頻wave,MIDI和數位視訊AVI都採用這種格式儲存。構造RIFF檔案的基本單元叫做資料區塊(Chunk),每個資料區塊包含3個部分,1 4位元組的資料區塊標記(或者叫做資料區塊的ID)2 資料區塊的大小3 資料整個RIFF檔案可以看成一個資料區塊,其資料區塊ID為RIFF,稱為RIFF塊。一個RIFF檔案中只允許存在一個RIFF塊。RIFF塊中包含一系列的子塊,其中有一種字塊的ID為“LIST”,稱為LIST,LIST塊中可以再包含一系列的子塊,但除了LIST塊外的其他所有的子塊都不能再包含子塊。   RIFF和LIST塊分別比普通的資料區塊多一個被稱為形式類型(Form Type)和清單類型(List Type)的資料域,其組成如下:1 4位元組的資料區塊標記(Chunk ID)2 資料區塊的大小3 4位元組的形式類型或者清單類型4 資料 下面我們看看AVI檔案的結構。AVI檔案是目前使用的最複雜的RIFF檔案,它能同時儲存同步表現的音頻視頻資料。AVI的RIFF塊的形式類型是AVI,它包含3個子塊,如下所述:1資訊塊,一個ID為”hdrl”的LIST塊,定義AVI檔案的資料格式。2資料區塊,一個ID為 “movi”的LIST塊,包含AVI的音視頻序列資料3索引塊,ID為 “idxl”的子塊,定義 “movi”LIST塊的索引資料,是可選塊。AVI檔案的結構如所示,下面將具體介紹AVI檔案的各子塊構造。1資訊塊,資訊塊包含兩個子塊,即一個ID為 avih 的子塊和一個ID 為 strl 的LIST塊。“avih”子塊的內容可由如下的結構定義: typedef struct{ DWORD dwMicroSecPerFrame ; //顯示每楨所需的時間ns,定義avi的顯示速率 DWORD dwMaxBytesPerSec; //      最大的資料轉送率 DWORD dwPaddingGranularity;    //記錄塊的長度需為此值的倍數,通常是2048 DWORD dwFlages;                 //AVI檔案的特殊屬性,如是否包含索引塊,音視頻資料是否交叉儲存 DWORD dwTotalFrame;              //檔案中的總楨數 DWORD dwInitialFrames;           //說明在開始播放前需要多少楨 DWORD dwStreams;                 //檔案中包含的資料流種類 DWORD dwSuggestedBufferSize; //建議使用的緩衝區的大小,                              //通常為儲存一楨映像以及同步聲音所需要的資料之和 DWORD dwWidth;                   //映像寬 DWORD dwHeight;                   //映像高 DWORD dwReserved[4];               //保留值}MainAVIHeader; “strl” LIST塊用於記錄AVI資料流,每一種資料流都在該LIST塊中佔有3個子塊,他們的ID分別是”strh”,”strf”, “strd”;“strh”子塊由如下結構定義,typedef struct{       FOURCC   fccType;      //4位元組,表示資料流的種類 vids 表示視頻資料流                              //auds 音頻資料流       FOURCC   fccHandler;//4位元組 ,表示資料流解壓縮的驅動程式代號       DWORD    dwFlags;      //資料流屬性   WORD     wPriority;    //此資料流的播放優先順序       WORD     wLanguage;    //音訊語言代號       DWORD    dwInitalFrames;//說明在開始播放前需要多少楨       DWORD    dwScale;       //資料量,視頻每楨的大小或者音訊採樣大小       DWORD    dwRate;        //dwScale /dwRate = 每秒的採樣數       DWORD    dwStart;       //資料流開始播放的位置,以dwScale為單位       DWORD    dwLength;      //資料流的資料量,以dwScale為單位       DWORD    dwSuggestedBufferSize; //建議緩衝區的大小       DWORD    dwQuality;      //解壓縮品質參數,值越大,品質越好       DWORD    dwSampleSize;   //音訊採樣大小       RECT     rcFrame;        //視頻映像所佔的矩形}AVIStreamHeader;“strf”子塊緊跟在”strh”子塊之後,其結構視”strh”子塊的類型而定,如下所述;如果 strh子塊是視頻資料流,則 strf子塊的內容是一個與windows裝置無關位元影像的BIMAPINFO結構,如下typedef struct tagBITMAPINFO{       BITMAPINFOHEADER     bmiHeader;       RGBQUAD              bmiColors[1]; //顏色表}BITMAPINFO; typedef struct tagBITMAPINFOHEADER{       DWORD biSize;       LONG biWidth;       LONG biHeight;       WORD biPlanes;       WORD biBitCount;       DWORD biCompression;       DWORD biSizeImage;       LONG biXPelsPerMeter;       LONG biYPelsPerMeter;       DWORD biClrUsed;       DWORD biClrImportant;}BITMAPINFOHEADER;如果 strh子塊是音頻資料流,則strf子塊的內容是一個WAVEFORMAT結構,如下typedef struct{        WORD   wFormatTag;         WORD   nChannels;   //聲道數        DWORD nSamplesPerSec; //採樣率        DWORD nAvgBytesPerSec; //WAVE聲音中每秒的資料量        WORD   nBlockAlign;     //資料區塊的對齊標誌        WORD   biSize;          //此結構的大小}WAVEFORMAT“strd”子塊緊跟在strf子塊後,儲存供壓縮驅動程式使用的參數,不一定存在,也沒有固定的結構。“strl” LIST塊定義的AVI資料流依次將 “hdrl “ LIST 塊中的資料流頭結構與”movi” LIST塊中的資料聯絡在一起,第一個資料流頭結構用於資料流0,第二個用於資料流1,依次類推。資料區塊中儲存視頻和音頻資料流,資料可直接存於 “movi” LIST塊中。資料區塊中音視頻資料按不同的字塊存放,其結構如下所述,音頻字塊 “##wb” Wave 資料流視頻子塊中儲存DIB資料,又分為壓縮或者未壓縮DIB, “##db” RGB資料流  “##dc” 壓縮的映像資料流 看到了吧,avi檔案的映像資料可以是壓縮的,和非壓縮格式的。對於壓縮格式來說,也可採用不同的編碼,也許你曾經遇到有些avi沒法識別,就是因為編碼方式不一樣,如果沒有相應的解碼,你就沒法識別視頻資料。AVI的編碼方式有很多種,比較常見的有 mpeg2,mpeg4, divx等。索引塊,索引快包含資料區塊在檔案中的位置索引,能提高avi檔案的讀寫速度,其中存放著一組AVIINDEXENTRY結構資料。如下,這個塊並不是必需的,也許不存在。typedef struct {       DWORD ckid;          //記錄資料區塊中子塊的標記       DWORD dwFlags;        //表示chid所指子塊的屬性       DWORD dwChunkOffset;   //子塊的相對位置       DWORD dwChunkLength; //子塊長度};Ok,現在我相信你肯定會對AVI的檔案結構已經很清楚了,在介紹完了AVI檔案結構後,我們就來看看如何對avi檔案進行讀寫了,為了對avi進行讀寫,微軟提供了一套API,總共50個函數,他們的用途主要有兩類,一個是avi檔案的操作,一類是資料流streams的操作。1 開啟和關閉檔案  AVIFileOpen ,AVIFileAddRef, AVIFileRelease2從檔案中讀取檔案資訊通過AVIFileInfo可以擷取avi檔案的一些資訊,這個函數返回一個AVIFILEINFO結構通過AVIFileReadData可以用來擷取AVIFileInfo函數得不到的資訊。這些資訊也許不包含在檔案的頭部,比如擁有file的公司和個人的名稱。3寫入檔案資訊可以通過AVIFileWriteData函數來寫入檔案的一些額外資訊。4開啟和關閉一個流  開啟一個資料流就跟開啟檔案一樣,你可以通過 AVIFileGetStream函數來開啟一個資料流,這個函數建立了一個流的介面,然後在該介面中儲存了一個控制代碼。 如果你想操作檔案的某一個單獨的流,你可以採用AVIStreamOpenFromFile函數,這個函數綜合了AVIFileOpen和AVIFileGetStream函數。 如果你想操作檔案中的多個資料流,你就要首先AVIFileOpen,然後AVIFileGetStream。 可以通過AVIStreamAddRef來增加stream介面的引用。通過AVIStreamRelease函數來關閉資料流。這個函數用來減少streams的引用計數,當計數減少為0時,刪除。5從流中讀取資料和資訊AVIStreamInfo函數可以擷取資料的一些資訊,該函數返回一個AVISTREAMINFO結構,該結構包含了資料的類型壓縮方法,建議的buffersize,回放的rate,以及一些description。   如果資料流還有一些其它的額外的資訊,你可以通過AVIStreamReadData函數來擷取。應用程式分配一個記憶體,傳遞給這個函數,然後這個函數會通過這個記憶體返回資料流的資訊,額外的資訊可能包括資料流的壓縮和解壓縮的方法,你可以通過AVIStreamDataSize宏來回去需要申請記憶體塊的大小。   可以通過AVIStreamReadFormat函數擷取資料流的格式資訊。這個函數通過指定的記憶體返回資料流的格式資訊,比如對於視頻流,這個buffer包含了一個BIMAPINFO結構,對於音頻流,記憶體塊包含了WAVEFORMATEX或者PCMAVEFORMAT結構。你可以通過給AVIStreamReadFormat傳遞一個空buffer就可以擷取buffer的大小。也可以通過AVIStreamFormatSize宏。  可以通過AVIStreamRead函數來返回多媒體的資料。這個函數將資料複製到應用程式提供的記憶體中,對於視頻流,這個函數返回映像禎,對於音頻流,這個函數返迴音頻的sample資料。可以通過給AVIStreamRead傳遞一個NULL的buffer來擷取需要的buffer的大小。也可以通過AVIStreamSampleSize宏來擷取buffer的大小。  有些AVI資料流控制代碼可能需要在啟動資料流的前要做一下準備工作,此時,我們可以調用AVIStreamBeginStreaming函數來告知AVI資料流handle來申請分配它需要的一些資源。在完畢後,調用AVIStreamEndStreamming函數來釋放資源。6操作壓縮的視頻資料  如果你要示範一禎或者幾禎壓縮視頻映像時,你可以調用AVIStreamRead函數,將擷取的資料傳遞給DrawDib函數來顯示映像。這些函數可以顯示壓縮和未壓縮的映像。 AVIFile也提供了一個函數AVIStreamGetFrameOpen,來擷取未壓縮的視頻禎,這個函數建立了記憶體來擷取未壓縮的資料。也可以通過AVIStreamGetFrame函數來解壓縮一個單獨的視頻禎。這個函數可以解壓縮某一禎映像,然後將資料以一個BIMAPINFOHEADER結構返回。當你調用完AVIStreamGetFrame函數後,要調用AVIStreamGetFrameClose函數釋放上一個函數申請的資源。7根據已存在的資料流建立檔案   建立一個包含多個資料流的檔案的方法就是整合多個資料流,將其寫入一個新檔案。這些資料流可以是記憶體中的資料,也可以是存在於另一個檔案中。  我們可以用AVISave這個函數來build一個檔案。這個函數可以建立一個檔案,並且將指定的多個資料流按照指定的順序寫入檔案,你也可以通過AVISaveV函數來建立一個新的檔案,這個函數的功能和AVISave的功能一樣,主要區別是AVISaveV採用的資料流數組,而AVISave是單個的資料流,多次儲存。  我們可以調用AVISaveOptions函數來顯示一個對話方塊,可以讓使用者來選擇壓縮方式。  我們可以在調用AVISave和AVISaveV函數時指定一個回呼函數,用來顯示avi檔案的產生進度,可以讓使用者隨時地取消產生avi檔案。  我們可以調用GetSaveFileNamePreview函數來顯示儲存的對話方塊讓使用者選擇儲存的檔案名稱。  通過AVIMakeFileFromStreams函數我們可以建立一個虛擬檔案控制代碼,其他的avi函數可以通過這個虛擬檔案控制代碼來操作檔案中的資料流,操作完畢要記得調用AVIFileRelease釋放。8向檔案寫入一個資料流  我們可以通過AVIFileCreateStream函數來在一個新檔案或者已經存在的檔案中建立一個資料流。這個函數根據AVISTREAMINFO結構定義了新的資料流,並為新的資料流建立一個介面,返回介面的指標。  在寫入新的資料前,一定要指定流的格式資訊,通過AVIStreamSetFormat函數,當設定一個視頻流的時候,一定要使用BIMAPINFO結構來設定,音頻就用WAVEFORMAT。  然後我們就可以通過AVIStreamWrite函數將我們的多媒體資料寫入資料流了。這個函數將應用程式提供的記憶體資料複製到指定的流。預設的avi handler將資料寫入流的最後。  如果你有其他額外的資訊需要寫入流,你可以調用AVIFileWriteData或者AVIStreamWriteData, 最後記得在完成資料寫入後,要調用AVIStreamRelease。9資料流中的禎的位置 尋找起始禎:  可以通過AVIStreamStart函數來擷取第一禎包含的sample number。也可以通過AVIStreamInfo函數來擷取這個資訊,這個函數的AVISTREAMINFO結構中包含了dwStart,可以通過AVIStreamStartTime宏來擷取第一個sample。  可以通過AVIStreamLength函數來擷取流的長度。這個函數返迴流中的sample的數目。也可以通過AVIStreamInfo函數來擷取這些資訊,可以通過AVIStreamLengthTime宏來擷取流的長度,毫秒。  在視頻流中,一個sample對應著一禎映像,所以,有時這些sample中沒有視頻資料,如果你調用AVIStreamRead函數來資料,可能返回NULL,也可以通過AVIStreamFindSample通過指定FIND_ANY標誌來尋找指定的sample。尋找關鍵禎通過AVIStreamFindSample函數尋找符合要尋找的sample,然後可以通過下面的宏判斷是否關鍵禎。在time和sample間互相切換 AVIStreamSampleToTime這個函數可以將smaple轉換成毫秒。對於視頻,這個值代表的是這個禎開始播放的時間。 在瞭解了上面的知識後,我們對avi的檔案結構以及如何操作avi檔案心裡就明白了,下面我們可以開始我們的編程了。我們要做兩件事情,1如何將一組靜態bmp位元影像合成一個avi的視頻檔案,2 如何將一個未壓縮的avi檔案解析成一幅幅位元影像。 樣本程式介面如下: 下面的函數示範了如何將一個檔案夾下面的所有bmp檔案都儲存為一個avi檔案,函數的第一個參數是要產生的AVI的檔案名稱,第二個參數是存放bmp檔案的檔案夾名,這個函數會枚舉該檔案夾下的所有bmp檔案,合成一個AVI檔案。 void Cbmp2aviDlg::AVItoBmp(CString strAVIFileName, CString   strBmpDir){       // TODO: 在此添加控制項通知處理常式代碼       AVIFileInit();       PAVIFILE avi;       int res=AVIFileOpen(&avi, strAVIFileName, OF_READ, NULL);    int n = GetLastError();       if (res!=AVIERR_OK)       {              //an error occures              if (avi!=NULL)                     AVIFileRelease(avi);              return ;       }           AVIFILEINFO avi_info;       AVIFileInfo(avi, &avi_info, sizeof(AVIFILEINFO));       PAVISTREAM pStream;       res=AVIFileGetStream(avi, &pStream, streamtypeVIDEO /*video stream*/,              0 /*first stream*/);       if (res!=AVIERR_OK)       {              if (pStream!=NULL)                     AVIStreamRelease(pStream);              AVIFileExit();              return ;       }        //do some task with the stream       int iNumFrames;       int iFirstFrame;       iFirstFrame=AVIStreamStart(pStream);       if (iFirstFrame==-1)       {              //Error getteing the frame inside the stream              if (pStream!=NULL)                     AVIStreamRelease(pStream);              AVIFileExit();              return ;       }       iNumFrames=AVIStreamLength(pStream);       if (iNumFrames==-1)       {              //Error getteing the number of frames inside the stream              if (pStream!=NULL)                     AVIStreamRelease(pStream);              AVIFileExit();              return ;       }        //getting bitmap from frame       BITMAPINFOHEADER bih;       ZeroMemory(&bih, sizeof(BITMAPINFOHEADER));        bih.biBitCount=24;    //24 bit per pixel       bih.biClrImportant=0;       bih.biClrUsed = 0;       bih.biCompression = BI_RGB;       bih.biPlanes = 1;       bih.biSize = 40;       bih.biXPelsPerMeter = 0;       bih.biYPelsPerMeter = 0;       //calculate total size of RGBQUAD scanlines (DWORD aligned)       bih.biSizeImage = (((bih.biWidth * 3) + 3) & 0xFFFC) * bih.biHeight ;        PGETFRAME pFrame;       pFrame=AVIStreamGetFrameOpen(pStream, NULL );        AVISTREAMINFO streaminfo;       AVIStreamInfo(pStream,&streaminfo,sizeof(AVISTREAMINFO));        //Get the first frame       BITMAPINFOHEADER bih2;       long lsize = sizeof(bih2);       int index=0;       for (int i=iFirstFrame; i<iNumFrames; i++)       {              index= i-iFirstFrame;               BYTE* pDIB = (BYTE*) AVIStreamGetFrame(pFrame, index);                   //        AVIStreamReadFormat(pStream,index,&bih2,&lsize);              BITMAPFILEHEADER stFileHdr;                BYTE* Bits=new BYTE[bih2.biSizeImage];        AVIStreamRead(pStream,index,1,Bits,bih2.biSizeImage,NULL,NULL);              //RtlMoveMemory(Bits, pDIB + sizeof(BITMAPINFOHEADER), bih2.biSizeImage);         bih2.biClrUsed =0;              stFileHdr.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);              stFileHdr.bfSize=sizeof(BITMAPFILEHEADER);              stFileHdr.bfType=0x4d42;               CString FileName;              FileName.Format("Frame-%05d.bmp", index);        CString strtemp = strBmpDir;        strtemp += "//";        strtemp += FileName;              FILE* fp=_tfopen(strtemp ,_T("wb"));              fwrite(&stFileHdr,1,sizeof(BITMAPFILEHEADER),fp);              fwrite(&bih2,1,sizeof(BITMAPINFOHEADER),fp);              int ff = fwrite(Bits,1,bih2.biSizeImage,fp);              int e = GetLastError();              fclose(fp);              /////              delete Bits;              //CreateFromPackedDIBPointer(pDIB, index);       }        AVIStreamGetFrameClose(pFrame);        //close the stream after finishing the task       if (pStream!=NULL)              AVIStreamRelease(pStream);        AVIFileExit();}下面的這個函數示範了如何將AVI檔案中的每一楨映像單獨取出來,儲存為bmp檔案。函數的頭一個參數是avi檔案名稱,第二個參數是存放bmp檔案的檔案夾。//產生avivoid Cbmp2aviDlg::BMPtoAVI(CString szAVIName, CString strBmpDir){       CFileFind finder;       strBmpDir += _T("//*.*");       AVIFileInit();         AVISTREAMINFO strhdr;       PAVIFILE pfile;       PAVISTREAM ps;       int nFrames =0;       HRESULT hr;        BOOL bFind = finder.FindFile(strBmpDir);       while(bFind)       {              bFind = finder.FindNextFile();              if(!finder.IsDots() && !finder.IsDirectory())              {                     CString str = finder.GetFilePath();                     FILE *fp = fopen(str,"rb");                     BITMAPFILEHEADER bmpFileHdr;                     BITMAPINFOHEADER bmpInfoHdr;                     fseek( fp,0,SEEK_SET);                     fread(&bmpFileHdr,sizeof(BITMAPFILEHEADER),1, fp);                     fread(&bmpInfoHdr,sizeof(BITMAPINFOHEADER),1, fp);                      BYTE *tmp_buf = NULL;                     if(nFrames ==0 )                     {                           AVIFileOpen(&pfile,szAviName,OF_WRITE | OF_CREATE,NULL);                            _fmemset(&strhdr, 0, sizeof(strhdr));                            strhdr.fccType                = streamtypeVIDEO;// stream type                            strhdr.fccHandler             = 0;                            strhdr.dwScale                = 1;                            strhdr.dwRate                 = 15;                 // 15 fps                            strhdr.dwSuggestedBufferSize = bmpInfoHdr.biSizeImage ;                            SetRect(&strhdr.rcFrame, 0, 0, bmpInfoHdr.biWidth, bmpInfoHdr.biHeight);                             // And create the stream;                            hr = AVIFileCreateStream(pfile,&ps,&strhdr);                       // hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));                    }                     tmp_buf = new BYTE[bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3];                   fread(tmp_buf, 1, bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3, fp);                     hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));                     hr = AVIStreamWrite(ps,       // stream pointer                              nFrames ,                          // time of this frame                                   1,                         // number to write                                   (LPBYTE) tmp_buf,                                   bmpInfoHdr.biSizeImage , // size of this frame                                   AVIIF_KEYFRAME,                    // flags....                                   NULL,                                   NULL);                      nFrames ++;                     fclose(fp);               }       }        AVIStreamClose(ps);       if(pfile != NULL)      AVIFileRelease(pfile);         AVIFileExit();} 結束語:以上代碼在 vc 6.0 和windows xp平台調試通過。這兩個函數你可以直接在你的程式中使用,更詳細的代碼可以參見隨著本文附上的樣本源碼。這裡我要指出的是,這個AVI檔案和bmp互相轉換過程中,avi中的視頻資料都是存放的是沒有壓縮的資料,如果你要分解AVI檔案是經過壓縮編碼,比如,DVSD,MPEG4編碼,首先你要採用相應的解碼器對視頻資料解碼,然後將解碼過的資料儲存為bmp檔案。好了,關於avi檔案的介紹就到這裡結束了轉自:http://blog.csdn.net/aoosang/article/details/596474

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.