IBasicVideoを使って静止画を取得する

ここでは、動画フレームを静止画として取得する方法を説明したいと思います。 (注意)ここの例では、サンプルを簡潔にするためにエラー処理を書いていません。

とりあえず、コードを書いてみる

動画から静止画を取得する方法にはIBasicVideoを使う方法とSampleGrabberを使う方法の2つがあります。 ここでは、IBasicVideoを利用する方法を説明したいと思います。

下記サンプルは、OKボタンを押すとフレームをビットマップとして保存します。 サンプルの後半部分はビットマップを作成して保存していますが、保存はサンプルをわかりやすくするために行っているだけであり、IBasicVideoの使い方と直接的な関係はありません。


#include <stdio.h>
#include <dshow.h>

#define	FILENAME L"C:\\DXSDK\\Samples\\Media\\butterfly.mpg"

// 注意)このサンプルは機器や設定によっては失敗します
int
main()
{
 IGraphBuilder *pGraphBuilder;
 IMediaControl *pMediaControl;
 IBasicVideo *pBasicVideo;

 CoInitialize(NULL);

 CoCreateInstance(CLSID_FilterGraph,
	NULL,
	CLSCTX_INPROC,
	IID_IGraphBuilder,
	(LPVOID *)&pGraphBuilder);

 pGraphBuilder->QueryInterface(IID_IMediaControl,
	(LPVOID *)&pMediaControl);

 pMediaControl->RenderFile(FILENAME);

 pGraphBuilder->QueryInterface(IID_IBasicVideo,
	 (LPVOID *)&pBasicVideo);

 pMediaControl->Run();

 // OKを押した時の静止画を取得します
 MessageBox(NULL,
	"Grab Image",
	"Grab",
	MB_OK);

 // GetCurrentImageをする前にPauseをする必要があります
 pMediaControl->Pause();

 // 幅と高さを取得します
 long height, width;

 pBasicVideo->get_VideoHeight(&height);
 pBasicVideo->get_VideoWidth(&width);

 long bufSize;
 long *imgData;
 HRESULT hr;

 /*
 バッファサイズを取得するために第二引数をNULLで渡します。
 第二引数がNULLの場合、第一引数に必要なバッファサイズが
 格納されて返されます。
 */
 hr = pBasicVideo->GetCurrentImage(&bufSize, NULL);
 if (FAILED(hr)) {
	 printf("GetCurrentImage failed\n");
	 return 1;
 }

 if (bufSize < 1) {
	 printf("failed to get data size\n");
	 return 1;
 }
 imgData = (long *)malloc(bufSize);

 // 取得できるデータはDIB形式です。
 pBasicVideo->GetCurrentImage(&bufSize, imgData);

 // DIBファイルをBitmapに保存。
 // このサンプルでは、キャプチャ結果を見るために
 // Bitmapに保存しています。
 HANDLE fh;
 BITMAPFILEHEADER bmphdr;
 BITMAPINFOHEADER bmpinfo;
 DWORD nWritten;

 memset(&bmphdr, 0, sizeof(bmphdr));
 memset(&bmpinfo, 0, sizeof(bmpinfo));

 bmphdr.bfType = ('M' << 8) | 'B';
 bmphdr.bfSize = sizeof(bmphdr) + sizeof(bmpinfo) + bufSize;
 bmphdr.bfOffBits = sizeof(bmphdr) + sizeof(bmpinfo);

 bmpinfo.biSize = sizeof(bmpinfo);
 bmpinfo.biWidth = width;
 bmpinfo.biHeight = height;
 bmpinfo.biPlanes = 1;
 bmpinfo.biBitCount = 32;

 fh = CreateFile("result.bmp",
	 GENERIC_WRITE, 0, NULL,
	 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 WriteFile(fh, &bmphdr, sizeof(bmphdr), &nWritten, NULL);
 WriteFile(fh, &bmpinfo, sizeof(bmpinfo), &nWritten, NULL);
 WriteFile(fh, imgData, bufSize, &nWritten, NULL);
 CloseHandle(fh);

 free(imgData);

 // 資源を解放
 pBasicVideo->Release();

 pMediaControl->Release();
 pGraphBuilder->Release();

 CoUninitialize();

 return 0;
}

IBasicVideo::GetCurrentImageには様々な制限があります。 まず、Pauseを行った状態のGraphからしかGetCurrentImageは行えません。 また、ハードウェアやソフトウェアの環境によっては画像が取得できません。 (WindowsXPであれば恐らく大丈夫だと思います。)

このサンプルは、result.bmpという名前でビットマップファイルを保存します。 このサンプルを実行後に出来上がったビットマップファイルを確認してください。