Creating an AVI File from Bitmaps Using ltmmSampleSource for C
The following code utilizes ltmmSampleSource and ltmmConvert to generate an AVI file from generated 24-bit device independent bitmaps.
// define helper macros for using interfaces under C
#define COBJMACROS
// include the LEAD Multimedia TOOLKIT header
#include "ltmm.h"
// include amvideo.h for VIDEOINFOHEADER, available in the Visual Studio 6.0 or the DirectX SDK
#include <amvideo.h>
// includes for string handling
#include <tchar.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
/////////////////////////////////////////////////////////////////
// CreateCountingAvi
// create an AVI file composed of frames of counting numbers
// pszAviFile - output file name
// nFrames - number of frames to generate
//
HRESULT CreateCountingAvi(LPCWSTR pszAviFile, int nFrames)
{
HRESULT hr;
IltmmConvert* pConvert = NULL;
IltmmSampleSource* pSampleSource = NULL;
IltmmMediaTypeDisp* pMediaType = NULL;
IltmmMediaSampleDisp* pMediaSample;
BSTR bstr;
VIDEOINFOHEADER vih;
SAFEARRAY sa;
VARIANT var;
int n;
HBITMAP hbmFrame;
HBITMAP hbmSave;
HDC hDC;
RECT rc;
TCHAR sz[16];
LARGE_INTEGER starttime;
LARGE_INTEGER stoptime;
LARGE_INTEGER mstarttime;
LARGE_INTEGER mstoptime;
LOGFONT logfont;
HFONT hfntSave;
HFONT hfntFrame;
int len;
SIZE size;
// initialize COM library
hr = CoInitialize(NULL);
if(FAILED(hr))
goto error;
// create the convert object
hr = CoCreateInstance(&CLSID_ltmmConvert, NULL, CLSCTX_INPROC_SERVER, &IID_IltmmConvert, (void**) &pConvert);
if(FAILED(hr))
goto error;
// create the source object
hr = CoCreateInstance(&CLSID_ltmmSampleSource, NULL, CLSCTX_INPROC_SERVER, &IID_IltmmSampleSource, (void**) &pSampleSource);
if(FAILED(hr))
goto error;
// create the media type object
hr = CoCreateInstance(&CLSID_ltmmMediaType, NULL, CLSCTX_INPROC_SERVER, &IID_IltmmMediaTypeDisp, (void**) &pMediaType);
if(FAILED(hr))
goto error;
// set type to MEDIATYPE_Video
bstr = SysAllocString(L"{73646976-0000-0010-8000-00AA00389B71}");
hr = IltmmMediaTypeDisp_put_Type (pMediaType, bstr);
SysFreeString(bstr);
if(FAILED(hr))
goto error;
// set subtype to MEDIASUBTYPE_RGB24
bstr = SysAllocString(L"{e436eb7d-524f-11ce-9f53-0020af0ba770}");
hr = IltmmMediaTypeDisp_put_Subtype (pMediaType, bstr);
SysFreeString(bstr);
if(FAILED(hr))
goto error;
// set format to FORMAT_VideoInfo
bstr = SysAllocString(L"{05589f80-c356-11ce-bf01-00aa0055595a}");
hr = IltmmMediaTypeDisp_put_FormatType (pMediaType, bstr);
SysFreeString(bstr);
if(FAILED(hr))
goto error;
// set VIDEOINFOHEADER to 320x240 24-bit RGB image at 15 frames per second
memset(&vih, 0, sizeof(vih));
vih.bmiHeader.biCompression = BI_RGB;
vih.bmiHeader.biBitCount = 24;
vih.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
vih.bmiHeader.biWidth = 320;
vih.bmiHeader.biHeight = 240;
vih.bmiHeader.biPlanes = 1;
vih.bmiHeader.biSizeImage = (((vih.bmiHeader.biWidth * 3) + 3) & ~3) * vih.bmiHeader.biHeight;
vih.bmiHeader.biClrImportant = 0;
vih.AvgTimePerFrame = ((__int64) 10000000 / 15);
vih.dwBitRate = vih.bmiHeader.biSizeImage * 8 * 15;
// pass VIDEOINFOHEADER to ltmmMediaType via SAFEARRAY
memset(&sa, 0, sizeof(sa));
sa.cbElements = sizeof(unsigned char);
sa.cDims = 1;
sa.fFeatures = (FADF_AUTO | FADF_FIXEDSIZE);
sa.pvData = &vih;
sa.rgsabound[0].cElements = sizeof(vih);
VariantInit(&var);
V_VT(&var) = (VT_ARRAY | VT_UI1);
V_ARRAY(&var) = &sa;
hr = IltmmMediaTypeDisp_SetFormatData (pMediaType, sizeof(vih), var);
if(FAILED(hr))
goto error;
// fixed image size samples
hr = IltmmMediaTypeDisp_put_FixedSizeSamples(pMediaType, VARIANT_TRUE);
if(FAILED(hr))
goto error;
hr = IltmmMediaTypeDisp_put_SampleSize (pMediaType, vih.bmiHeader.biSizeImage);
if(FAILED(hr))
goto error;
// set the source media type
hr = IltmmSampleSource_SetMediaType (pSampleSource, pMediaType);
if(FAILED(hr))
goto error;
// set the convert object source
hr = IltmmConvert_put_SourceObject (pConvert, (IUnknown*) pSampleSource);
if(FAILED(hr))
goto error;
#ifdef _DEBUG
{
IUnknown* punk;
IltmmConvert_get_SourceObject(pConvert, &punk);
assert(punk != NULL);
if(punk)
IUnknown_Release(punk);
}
#endif
// set the convert output file name
bstr = SysAllocString(pszAviFile);
hr = IltmmConvert_put_TargetFile (pConvert, bstr);
SysFreeString(bstr);
if(FAILED(hr))
goto error;
// need a dc to draw to
hDC = CreateCompatibleDC(NULL);
if(!hDC)
{
hr = E_OUTOFMEMORY;
goto error;
}
// create a SAFEARRAY that points to a DIB sections bits
memset(&sa, 0, sizeof(sa));
sa.cbElements = sizeof(unsigned char);
sa.cDims = 1;
sa.fFeatures = (FADF_AUTO | FADF_FIXEDSIZE);
sa.pvData = &vih;
sa.rgsabound[0].cElements = vih.bmiHeader.biSizeImage;
V_VT(&var) = (VT_ARRAY | VT_UI1);
V_ARRAY(&var) = &sa;
hbmFrame = CreateDIBSection(NULL, (BITMAPINFO*) &vih.bmiHeader, DIB_RGB_COLORS, &sa.pvData, NULL, 0);
if(!hbmFrame)
{
DeleteDC(hDC);
hr = E_OUTOFMEMORY;
goto error;
}
// create a font big enough to fit the height of each frame
memset(&logfont, 0, sizeof(logfont));
_tcscpy(logfont.lfFaceName, _T("Arial"));
logfont.lfHeight = vih.bmiHeader.biHeight;
hfntFrame = CreateFontIndirect(&logfont);
if(!hfntFrame)
{
DeleteDC(hDC);
DeleteObject(hbmFrame);
hr = E_OUTOFMEMORY;
goto error;
}
// select the font and the bitmap into the device context
hbmSave = (HBITMAP) SelectObject(hDC, hbmFrame);
hfntSave = (HFONT) SelectObject(hDC, hfntFrame);
// need a rect for the ExtTextOut
SetRect(&rc, 0, 0, vih.bmiHeader.biWidth, vih.bmiHeader.biHeight);
// start the conversion
hr = IltmmConvert_StartConvert(pConvert);
if(FAILED(hr))
goto converterror;
starttime.QuadPart = 0;
mstarttime.QuadPart = 0;
for(n = 0; n < nFrames; n++)
{
// convert the frame number to text and draw it to the bitmap
len = _stprintf(sz, _T("%u"), n + 1);
GetTextExtentPoint32(hDC, sz, len, &size);
ExtTextOut(hDC, (rc.right - rc.left - size.cx) / 2, (rc.bottom - rc.top - size.cy) / 2, ETO_CLIPPED | ETO_OPAQUE, &rc, sz, len, NULL);
GdiFlush();
// get a free sample buffer
hr = IltmmSampleSource_GetSampleBuffer (pSampleSource, 1000, &pMediaSample);
if(FAILED(hr))
goto converterror;
// set the time in 100 nanoseconds (based on frame rate)
stoptime.QuadPart = starttime.QuadPart + vih.AvgTimePerFrame;
hr = IltmmMediaSampleDisp_SetTime (pMediaSample, starttime.HighPart, starttime.LowPart, stoptime.HighPart, stoptime.LowPart);
if(FAILED(hr))
{
IUnknown_Release(pMediaSample);
goto converterror;
}
// media time is equal to the frame number
mstoptime.QuadPart = mstarttime.QuadPart + 1;
hr = IltmmMediaSampleDisp_SetMediaTime (pMediaSample, mstarttime.HighPart, mstarttime.LowPart, mstoptime.HighPart, mstoptime.LowPart);
if(FAILED(hr))
{
IUnknown_Release(pMediaSample);
goto converterror;
}
// this is a sync point
hr = IltmmMediaSampleDisp_put_SyncPoint (pMediaSample, VARIANT_TRUE);
if(FAILED(hr))
{
IUnknown_Release(pMediaSample);
goto converterror;
}
// set the sample data
hr = IltmmMediaSampleDisp_SetData (pMediaSample, vih.bmiHeader.biSizeImage, var);
if(FAILED(hr))
{
IUnknown_Release(pMediaSample);
goto converterror;
}
// send the sample downstream
hr = IltmmSampleSource_DeliverSample (pSampleSource, 1000, pMediaSample);
if(FAILED(hr))
{
IUnknown_Release(pMediaSample);
goto converterror;
}
// adjust the next time values
starttime = stoptime;
mstarttime = mstoptime;
// release the sample buffer
IUnknown_Release(pMediaSample);
}
// all done, inform the downstream filters
hr = IltmmSampleSource_DeliverEndOfStream (pSampleSource, 1000);
if(FAILED(hr))
goto converterror;
// stop
hr = IltmmConvert_StopConvert(pConvert);
if(FAILED(hr))
goto converterror;
// cleanup and exit
hr = S_OK;
converterror:
SelectObject(hDC, hfntSave);
DeleteObject(hfntFrame);
SelectObject(hDC, hbmSave);
DeleteObject(hbmFrame);
DeleteDC(hDC);
error:
if(pConvert)
IUnknown_Release(pConvert);
if(pSampleSource)
IUnknown_Release(pSampleSource);
if(pMediaType)
IUnknown_Release(pMediaType);
CoUninitialize();
return hr;
}
int main(int argc, char* argv[])
{
CreateCountingAvi(L"c:\\count.avi", 10);
return 0;
}
The following code utilizes ltmmSampleSource and ltmmConvert to generate an AVI file from generated 24-bit device independent bitmaps.
// define helper macros for using interfaces under C #ifndef COBJMACROS #define COBJMACROS #endif // include the LEAD Multimedia TOOLKIT header #include "ltmm.h" // include amvideo.h for VIDEOINFOHEADER, available in the Visual Studio 6.0 or the DirectX SDK #include <amvideo.h> // includes for string handling #include <tchar.h> #include <string.h> #include <stdio.h> #include <assert.h> ///////////////////////////////////////////////////////////////// // CreateCountingAvi // create an AVI file composed of frames of counting numbers // pszAviFile - output file name // nFrames - number of frames to generate // L_MULTIMEDIATEX_API HRESULT CreateCountingAviExample (LPCWSTR pszAviFile, int nFrames) { HRESULT hr; IltmmConvert* pConvert = NULL; IltmmSampleSource* pSampleSource = NULL; IltmmMediaTypeDisp* pMediaType = NULL; IltmmMediaSampleDisp* pMediaSample; BSTR bstr; VIDEOINFOHEADER vih; SAFEARRAY sa; VARIANT var; int n; HBITMAP hbmFrame; HBITMAP hbmSave; HDC hDC; RECT rc; TCHAR sz[16]; LARGE_INTEGER starttime; LARGE_INTEGER stoptime; LARGE_INTEGER mstarttime; LARGE_INTEGER mstoptime; LOGFONT logfont; HFONT hfntSave; HFONT hfntFrame; int len; SIZE size; // initialize COM library hr = CoInitialize(NULL); if(FAILED(hr)) goto error; // create the convert object hr = CoCreateInstance(&CLSID_ltmmConvert, NULL, CLSCTX_INPROC_SERVER, &IID_IltmmConvert, (void**) &pConvert); if(FAILED(hr)) goto error; // create the source object hr = CoCreateInstance(&CLSID_ltmmSampleSource, NULL, CLSCTX_INPROC_SERVER, &IID_IltmmSampleSource, (void**) &pSampleSource); if(FAILED(hr)) goto error; // create the media type object hr = CoCreateInstance(&CLSID_ltmmMediaType, NULL, CLSCTX_INPROC_SERVER, &IID_IltmmMediaTypeDisp, (void**) &pMediaType); if(FAILED(hr)) goto error; // set type to MEDIATYPE_Video bstr = SysAllocString(L"{73646976-0000-0010-8000-00AA00389B71}"); hr = IltmmMediaTypeDisp_put_Type (pMediaType, bstr); SysFreeString(bstr); if(FAILED(hr)) goto error; // set subtype to MEDIASUBTYPE_RGB24 bstr = SysAllocString(L"{e436eb7d-524f-11ce-9f53-0020af0ba770}"); hr = IltmmMediaTypeDisp_put_Subtype (pMediaType, bstr); SysFreeString(bstr); if(FAILED(hr)) goto error; // set format to FORMAT_VideoInfo bstr = SysAllocString(L"{05589f80-c356-11ce-bf01-00aa0055595a}"); hr = IltmmMediaTypeDisp_put_FormatType (pMediaType, bstr); SysFreeString(bstr); if(FAILED(hr)) goto error; // set VIDEOINFOHEADER to 320x240 24-bit RGB image at 15 frames per second memset(&vih, 0, sizeof(vih)); vih.bmiHeader.biCompression = BI_RGB; vih.bmiHeader.biBitCount = 24; vih.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); vih.bmiHeader.biWidth = 320; vih.bmiHeader.biHeight = 240; vih.bmiHeader.biPlanes = 1; vih.bmiHeader.biSizeImage = (((vih.bmiHeader.biWidth * 3) + 3) & ~3) * vih.bmiHeader.biHeight; vih.bmiHeader.biClrImportant = 0; vih.AvgTimePerFrame = ((__int64) 10000000 / 15); vih.dwBitRate = vih.bmiHeader.biSizeImage * 8 * 15; // pass VIDEOINFOHEADER to ltmmMediaType via SAFEARRAY memset(&sa, 0, sizeof(sa)); sa.cbElements = sizeof(unsigned char); sa.cDims = 1; sa.fFeatures = (FADF_AUTO | FADF_FIXEDSIZE); sa.pvData = &vih; sa.rgsabound[0].cElements = sizeof(vih); VariantInit(&var); V_VT(&var) = (VT_ARRAY | VT_UI1); V_ARRAY(&var) = &sa; hr = IltmmMediaTypeDisp_SetFormatData (pMediaType, sizeof(vih), var); if(FAILED(hr)) goto error; // fixed image size samples hr = IltmmMediaTypeDisp_put_FixedSizeSamples(pMediaType, VARIANT_TRUE); if(FAILED(hr)) goto error; hr = IltmmMediaTypeDisp_put_SampleSize (pMediaType, vih.bmiHeader.biSizeImage); if(FAILED(hr)) goto error; // set the source media type hr = IltmmSampleSource_SetMediaType (pSampleSource, pMediaType); if(FAILED(hr)) goto error; // set the convert object source hr = IltmmConvert_put_SourceObject (pConvert, (IUnknown*) pSampleSource); if(FAILED(hr)) goto error; #ifdef _DEBUG { IUnknown* punk; IltmmConvert_get_SourceObject(pConvert, &punk); assert(punk != NULL); if(punk) IUnknown_Release(punk); } #endif // set the convert output file name bstr = SysAllocString(pszAviFile); hr = IltmmConvert_put_TargetFile (pConvert, bstr); SysFreeString(bstr); if(FAILED(hr)) goto error; // need a dc to draw to hDC = CreateCompatibleDC(NULL); if(!hDC) { hr = E_OUTOFMEMORY; goto error; } // create a SAFEARRAY that points to a DIB sections bits memset(&sa, 0, sizeof(sa)); sa.cbElements = sizeof(unsigned char); sa.cDims = 1; sa.fFeatures = (FADF_AUTO | FADF_FIXEDSIZE); sa.pvData = &vih; sa.rgsabound[0].cElements = vih.bmiHeader.biSizeImage; V_VT(&var) = (VT_ARRAY | VT_UI1); V_ARRAY(&var) = &sa; hbmFrame = CreateDIBSection(NULL, (BITMAPINFO*) &vih.bmiHeader, DIB_RGB_COLORS, &sa.pvData, NULL, 0); if(!hbmFrame) { DeleteDC(hDC); hr = E_OUTOFMEMORY; goto error; } // create a font big enough to fit the height of each frame memset(&logfont, 0, sizeof(logfont)); _tcscpy(logfont.lfFaceName, _T("Arial")); logfont.lfHeight = vih.bmiHeader.biHeight; hfntFrame = CreateFontIndirect(&logfont); if(!hfntFrame) { DeleteDC(hDC); DeleteObject(hbmFrame); hr = E_OUTOFMEMORY; goto error; } // select the font and the bitmap into the device context hbmSave = (HBITMAP) SelectObject(hDC, hbmFrame); hfntSave = (HFONT) SelectObject(hDC, hfntFrame); // need a rect for the ExtTextOut SetRect(&rc, 0, 0, vih.bmiHeader.biWidth, vih.bmiHeader.biHeight); // start the conversion hr = IltmmConvert_StartConvert(pConvert); if(FAILED(hr)) goto converterror; starttime.QuadPart = 0; mstarttime.QuadPart = 0; for(n = 0; n < nFrames; n++) { // convert the frame number to text and draw it to the bitmap len = _stprintf(sz, _T("%u"), n + 1); GetTextExtentPoint32(hDC, sz, len, &size); ExtTextOut(hDC, (rc.right - rc.left - size.cx) / 2, (rc.bottom - rc.top - size.cy) / 2, ETO_CLIPPED | ETO_OPAQUE, &rc, sz, len, NULL); GdiFlush(); // get a free sample buffer hr = IltmmSampleSource_GetSampleBuffer (pSampleSource, 1000, &pMediaSample); if(FAILED(hr)) goto converterror; // set the time in 100 nanoseconds (based on frame rate) stoptime.QuadPart = starttime.QuadPart + vih.AvgTimePerFrame; hr = IltmmMediaSampleDisp_SetTime (pMediaSample, starttime.HighPart, starttime.LowPart, stoptime.HighPart, stoptime.LowPart); if(FAILED(hr)) { IUnknown_Release(pMediaSample); goto converterror; } // media time is equal to the frame number mstoptime.QuadPart = mstarttime.QuadPart + 1; hr = IltmmMediaSampleDisp_SetMediaTime (pMediaSample, mstarttime.HighPart, mstarttime.LowPart, mstoptime.HighPart, mstoptime.LowPart); if(FAILED(hr)) { IUnknown_Release(pMediaSample); goto converterror; } // this is a sync point hr = IltmmMediaSampleDisp_put_SyncPoint (pMediaSample, VARIANT_TRUE); if(FAILED(hr)) { IUnknown_Release(pMediaSample); goto converterror; } // set the sample data hr = IltmmMediaSampleDisp_SetData (pMediaSample, vih.bmiHeader.biSizeImage, var); if(FAILED(hr)) { IUnknown_Release(pMediaSample); goto converterror; } // send the sample downstream hr = IltmmSampleSource_DeliverSample (pSampleSource, 1000, pMediaSample); if(FAILED(hr)) { IUnknown_Release(pMediaSample); goto converterror; } // adjust the next time values starttime = stoptime; mstarttime = mstoptime; // release the sample buffer IUnknown_Release(pMediaSample); } // all done, inform the downstream filters hr = IltmmSampleSource_DeliverEndOfStream (pSampleSource, 1000); if(FAILED(hr)) goto converterror; // stop hr = IltmmConvert_StopConvert(pConvert); if(FAILED(hr)) goto converterror; // cleanup and exit hr = S_OK; converterror: SelectObject(hDC, hfntSave); DeleteObject(hfntFrame); SelectObject(hDC, hbmSave); DeleteObject(hbmFrame); DeleteDC(hDC); error: if(pConvert) IUnknown_Release(pConvert); if(pSampleSource) IUnknown_Release(pSampleSource); if(pMediaType) IUnknown_Release(pMediaType); CoUninitialize(); return hr; } int main() { CreateCountingAviExample (L"c:\\count.avi", 10); return 0; }