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.
#include "stdafx.h"
// 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 = pMediaType->put_Type (bstr);
SysFreeString(bstr);
if(FAILED(hr))
goto error;
// set subtype to MEDIASUBTYPE_RGB24
bstr = SysAllocString(L"{e436eb7d-524f-11ce-9f53-0020af0ba770}");
hr = pMediaType->put_Subtype (bstr);
SysFreeString(bstr);
if(FAILED(hr))
goto error;
// set format to FORMAT_VideoInfo
bstr = SysAllocString(L"{05589f80-c356-11ce-bf01-00aa0055595a}");
hr = pMediaType->put_FormatType (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 = pMediaType->SetFormatData (sizeof(vih), var);
if(FAILED(hr))
goto error;
// fixed image size samples
hr = pMediaType->put_FixedSizeSamples (VARIANT_TRUE);
if(FAILED(hr))
goto error;
hr = pMediaType->put_SampleSize (vih.bmiHeader.biSizeImage);
if(FAILED(hr))
goto error;
// set the source media type
hr = pSampleSource->SetMediaType (pMediaType);
if(FAILED(hr))
goto error;
// set the convert object source
hr = pConvert->put_SourceObject (pSampleSource);
if(FAILED(hr))
goto error;
#ifdef _DEBUG
{
IUnknown* punk;
pConvert->get_SourceObject(&punk);
assert(punk != NULL);
if(punk)
punk->Release();
}
#endif
// set the convert output file name
bstr = SysAllocString(pszAviFile);
hr = pConvert->put_TargetFile (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 = pConvert->StartConvert ();
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 = pSampleSource->GetSampleBuffer (1000, &pMediaSample);
if(FAILED(hr))
goto converterror;
// set the time in 100 nanoseconds (based on frame rate)
stoptime.QuadPart = starttime.QuadPart + vih.AvgTimePerFrame;
hr = pMediaSample->SetTime (starttime.HighPart, starttime.LowPart, stoptime.HighPart, stoptime.LowPart);
if(FAILED(hr))
{
pMediaSample->Release();
goto converterror;
}
// media time is equal to the frame number
mstoptime.QuadPart = mstarttime.QuadPart + 1;
hr = pMediaSample->SetMediaTime (mstarttime.HighPart, mstarttime.LowPart, mstoptime.HighPart, mstoptime.LowPart);
if(FAILED(hr))
{
pMediaSample->Release();
goto converterror;
}
// this is a sync point
hr = pMediaSample->put_SyncPoint (VARIANT_TRUE);
if(FAILED(hr))
{
pMediaSample->Release();
goto converterror;
}
// set the sample data
hr = pMediaSample->SetData (vih.bmiHeader.biSizeImage, var);
if(FAILED(hr))
{
pMediaSample->Release();
goto converterror;
}
// send the sample downstream
hr = pSampleSource->DeliverSample (1000, pMediaSample);
if(FAILED(hr))
{
pMediaSample->Release();
goto converterror;
}
// adjust the next time values
starttime = stoptime;
mstarttime = mstoptime;
// release the sample buffer
pMediaSample->Release();
}
// all done, inform the downstream filters
hr = pSampleSource->DeliverEndOfStream(1000);
if(FAILED(hr))
goto converterror;
// stop
hr = pConvert->StopConvert ();
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)
pConvert->Release();
if(pSampleSource)
pSampleSource->Release();
if(pMediaType)
pMediaType->Release();
CoUninitialize();
return hr;
}
int main(int argc, char* argv[])
{
CreateCountingAvi(L"c:\\count.avi", 10);
return 0;
}