為了對avi進行讀寫,微軟提供了一套API,總共50個函數,他們的用途主要有兩類,一個是avi文件的操作,一類是數據流streams的操作。
1、打開和關閉文件
AVIFileOpen ,AVIFileAddRef, AVIFileRelease
2、從文件中讀取文件信息
通過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文件的文件夾。
//生成avi
void 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文件的介紹就到這里結束了。
posted on 2007-10-24 10:30
石正 閱讀(7539)
評論(16) 編輯 收藏