SampleGrabberを使って動画の最初のフレームを取得する

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

サンプルコード

前述したサンプルはMessageBoxを使って静止画を保存するタイミングを指定していましたが、今回は最初のフレームを取得できるまで自動的に待つサンプルを作ってみました。 参考になれば幸いです。



#include <stdio.h>

#include <dshow.h>
#include <qedit.h> // SampleGrabber用

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

int
main()
{
 IGraphBuilder *pGraphBuilder;
 IMediaControl *pMediaControl;

 IBaseFilter *pSampleGrabberFilter;
 ISampleGrabber *pSampleGrabber;
 AM_MEDIA_TYPE am_media_type;

 // COMを初期化
 CoInitialize(NULL);

 // FilterGraphを生成
 CoCreateInstance(CLSID_FilterGraph,
	NULL,
	CLSCTX_INPROC,
	IID_IGraphBuilder,
	(LPVOID *)&pGraphBuilder);

 // SampleGrabber(Filter)を生成
 CoCreateInstance(CLSID_SampleGrabber,
	 NULL,
	 CLSCTX_INPROC,
	 IID_IBaseFilter,
	 (LPVOID *)&pSampleGrabberFilter);

 // FilterからISampleGrabberインターフェースを取得します
 pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber,
	 (LPVOID *)&pSampleGrabber);

 // SampleGrabberを接続するフォーマットを指定。
 // ここがポイントです。
 // ここの指定の仕方によりSampleGrabberの挿入箇所を
 // 決定できます。このサンプルのような指定をすると
 // 画面出力の寸前でサンプルを取得できます。
 ZeroMemory(&am_media_type, sizeof(am_media_type));
 am_media_type.majortype = MEDIATYPE_Video;
 am_media_type.subtype = MEDIASUBTYPE_RGB24;
 am_media_type.formattype = FORMAT_VideoInfo;
 pSampleGrabber->SetMediaType(&am_media_type);

 // GraphにSampleGrabber Filterを追加
 pGraphBuilder->AddFilter(pSampleGrabberFilter,
	 L"Sample Grabber");

 // MediaControlインターフェース取得
 pGraphBuilder->QueryInterface(IID_IMediaControl,
	(LPVOID *)&pMediaControl);

 // Graphを生成。
 // ここでSampleGrabberを含んだGraphが自動的に作成されます。
 pMediaControl->RenderFile(FILENAME);

 // 接続情報取得。
 // この処理はRenderFileによりGraphが構成された後に
 // 行う必要があります。
 pSampleGrabber->GetConnectedMediaType(&am_media_type);
 VIDEOINFOHEADER *pVideoInfoHeader =
     (VIDEOINFOHEADER *)am_media_type.pbFormat;

 // 画像(映像)の幅と高さを表示
 // サンプルをわかりやすくするために表示しているだけなので、
 // 必ず必要というわけではありません。
 printf("size = %dx%d\n",
	 pVideoInfoHeader->bmiHeader.biWidth,
	 pVideoInfoHeader->bmiHeader.biHeight);

 // データサイズを表示
 // これも説明のために表示しています。
 printf("sample size = %d\n",
	 am_media_type.lSampleSize);

 // Grabを行う事を設定
 // SetBufferSamplesを行わないとバッファから
 // データを取得できません。
 // 不必要に負荷をかけたくない場合にはFALSEにしておいて、
 // データを取得したくなったら、TRUEに変える
 // という方法もできます。
 pSampleGrabber->SetBufferSamples(TRUE);

 //
 // SetOneShotをTRUEにすると画像を1枚だけ取得して
 // graphをstop状態にできます。
 //
 pSampleGrabber->SetOneShot(TRUE);

 // 再生開始
 pMediaControl->Run();

 //
 // graphが終了するのを待ちます
 //
 IMediaEvent *pEvent;
 pGraphBuilder->QueryInterface(IID_IMediaEvent,
     (LPVOID *)&pEvent);

 long evCode;
 pEvent->WaitForCompletion(INFINITE, &evCode);

 //
 // ここから先はBITMAP保存処理です。
 //

 // バッファを用意
 long nBufferSize = am_media_type.lSampleSize;
 long *pBuffer = (long *)malloc(nBufferSize);

 // 現在表示されている映像を静止画として取得
 pSampleGrabber->GetCurrentBuffer(&nBufferSize,
     pBuffer);

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

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

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

 fh = CreateFile("result.bmp",
	 GENERIC_WRITE, 0, NULL,
	 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 WriteFile(fh, &bmphdr, sizeof(bmphdr), &nWritten, NULL);
 WriteFile(fh,
     &pVideoInfoHeader->bmiHeader,
     sizeof(BITMAPINFOHEADER), &nWritten, NULL);
 WriteFile(fh, pBuffer, nBufferSize, &nWritten, NULL);
 CloseHandle(fh);

 free(pBuffer);

 // 資源を解放
 pSampleGrabber->Release();
 pSampleGrabberFilter->Release();
 pMediaControl->Release();
 pGraphBuilder->Release();

 // COM終了
 CoUninitialize();

 return 0;
}


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

今回のサンプルはSetOneShotを使って「一つ目のフレームが来たらGraphをストップしてしまって下さい」と設定しています。 Graphがストップ状態になるのを待つにはWaitForCompletionを使っています。 WaitForCompletionに関しては「映像再生が終わるまで待つ」をご覧下さい。

ちょっとサンプルが長くなってしまったので、わかりにくければ質問してください。