Introduction
In the last project that I worked on, the project required to extract frames of an AVI movie and save them as bitmaps files. Here I want to share my experiences in this project with you.
AVIFile Functions
The best way to obtain information about AVI format and learn more about the internal behavior of the file, is going directly to MSDN as I did. The AVI related functions are inside vfw.h header file. This means you must include this header and add vfw32.lib library file to your project.
Here is the list of functions that I used in the project and a brief information about them (taken from MSDN):
void AVIFileInit(void); |
The AVIFileInit function initializes the AVIFile library. |
void AVIFileOpen(PAVIFILE* ppfile, LPCTSTR szFile, UINT mode, CLSID pclsidHandler); |
The AVIFileOpen function opens an AVI file and returns the address of a file interface used to access it. |
void AVIFileInfo(PAVIFILE pfile, AVIFILEINFO* pfi, LONG lSize); |
The AVIFileInfo function obtains information about an AVI file. |
void AVIFileGetStream(PAVIFILE pfile, PAVISTREAM* ppavi, DWORD fccType, LONG lParam); |
The AVIFileGetStream function returns the address of a stream interface that is associated with a specified AVI file. |
LONG AVIStreamStart(PAVISTREAM pavi); |
The AVIStreamStart function returns the starting sample number for the stream. |
LONG AVIStreamLength(PAVISTREAM pavi); |
The AVIStreamLength function returns the length of the stream. |
PGETFRAME AVIStreamGetFrameOpen(PAVISTREAM pavi, LPBITMAPINFOHEADER lpbiWanted); |
The AVIStreamGetFrameOpen function prepares to decompress video frames from the specified video stream. |
LPVOID AVIStreamGetFrame(PGETFRAME pgf, LONG lPos); |
The AVIStreamGetFrame function returns the address of a decompressed video frame. |
void AVIStreamGetFrameClose(PGETFRAME pget); |
The AVIStreamGetFrameClose function releases resources used to decompress video frames. |
LONG AVIStreamRelease(PAVISTREAM pavi); |
The AVIStreamRelease function decrements the reference count of an AVI stream interface handle, and closes the stream if the count reaches zero. |
void AVIFileExit(void); |
The AVIFileExit function exits the AVIFile library and decrements the reference count for the library. |
Now it's time to write some code:
BOOL ExtractAVIFrames(CString szFileName)
{
AVIFileInit();
PAVIFILE avi;
int res=AVIFileOpen(&avi, szFileName, OF_READ, NULL);
if (res!=AVIERR_OK)
{
if (avi!=NULL)
AVIFileRelease(avi);
return FALSE;
}
AVIFILEINFO avi_info;
AVIFileInfo(avi, &avi_info, sizeof(AVIFILEINFO));
CString szFileInfo;
szFileInfo.Format("Dimention: %dx%d\n"
"Length: %d frames\n"
"Max bytes per second: %d\n"
"Samples per second: %d\n"
"Streams: %d\n"
"File Type: %d", avi_info.dwWidth,
avi_info.dwHeight,
avi_info.dwLength,
avi_info.dwMaxBytesPerSec,
(DWORD) (avi_info.dwRate / avi_info.dwScale),
avi_info.dwStreams,
avi_info.szFileType);
AfxMessageBox(szFileInfo, MB_ICONINFORMATION | MB_OK);
PAVISTREAM pStream;
res=AVIFileGetStream(avi, &pStream, streamtypeVIDEO ,
0 );
if (res!=AVIERR_OK)
{
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return FALSE;
}
int iNumFrames;
int iFirstFrame;
iFirstFrame=AVIStreamStart(pStream);
if (iFirstFrame==-1)
{
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return FALSE;
}
iNumFrames=AVIStreamLength(pStream);
if (iNumFrames==-1)
{
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return FALSE;
}
BITMAPINFOHEADER bih;
ZeroMemory(&bih, sizeof(BITMAPINFOHEADER));
bih.biBitCount=24;
bih.biClrImportant=0;
bih.biClrUsed = 0;
bih.biCompression = BI_RGB;
bih.biPlanes = 1;
bih.biSize = 40;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biSizeImage = (((bih.biWidth * 3) + 3) & 0xFFFC) * bih.biHeight ;
PGETFRAME pFrame;
pFrame=AVIStreamGetFrameOpen(pStream,
NULL );
int index=0;
for (int i=iFirstFrame; i<iNumFrames; i++)
{
index= i-iFirstFrame;
BYTE* pDIB = (BYTE*) AVIStreamGetFrame(pFrame, index);
CreateFromPackedDIBPointer(pDIB, index);
}
AVIStreamGetFrameClose(pFrame);
if (pStream!=NULL)
AVIStreamRelease(pStream);
AVIFileExit();
return TRUE;
}
The only one function that I must describe more about is: CreateFromPackedDIBPointer()
. This function takes a pointer returned from AVIStreamGetFrame()
function and creates a bitmap from it. As you know, the AVIStreamGetFrame()
returns a pointer to DIB information about the frame. We take this pointer and create a bitmap from it.
BOOL CreateFromPackedDIBPointer(LPBYTE pDIB, int iFrame)
{
ASSERT(pDIB!=NULL);
BITMAPINFOHEADER bih;
RtlMoveMemory(&bih.biSize, pDIB, sizeof(BITMAPINFOHEADER));
if (bih.biSizeImage < 1)
{
return FALSE;
}
BYTE* Bits=new BYTE;
RtlMoveMemory(Bits, pDIB + sizeof(BITMAPINFOHEADER), bih.biSizeImage);
BYTE memBitmapInfo[40];
RtlMoveMemory(memBitmapInfo, &bih, sizeof(bih));
BITMAPFILEHEADER bfh;
bfh.bfType=19778;
bfh.bfSize=55 + bih.biSizeImage;
bfh.bfReserved1=0;
bfh.bfReserved2=0;
bfh.bfOffBits=sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
CString FileName;
FileName.Format("Frame-%05d.bmp", iFrame);
FILE* fp=fopen(FileName, "wb");
if (fp!=NULL)
{
fwrite(&bfh, sizeof(bfh), 1, fp);
fwrite(&memBitmapInfo, sizeof(memBitmapInfo), 1, fp);
fwrite(Bits, bih.biSizeImage, 1, fp);
fclose(fp);
}
else
{
TRACE0(_T("Error writing the bitmap file"));
return FALSE;
}
delete [] Bits;
return TRUE;
}
Enjoy!