SampleGrabberを使って複数の静止画を取得する

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

サンプルコード

前述したサンプルはビットマップファイルを一つ作成して終わりでしたが、今回は3つ作成するサンプルを作ってみました。 一つだけではなく、複数のフレームを扱う方法の手がかりとなれば幸いです。


#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);

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

 int i;
 for (i = 0; i < 3; i++) {
   // 再生中にプログラムが終わってしまわないように
   MessageBox(NULL,
	"Block Execution",
	"Block",
	MB_OK);
   // OKが押されると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);

   char filename[128];
   _snprintf(filename, sizeof(filename),
             "result%d.bmp", i);
   fh = CreateFile(filename,
                   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);
 } // for文最後

 pMediaControl->Stop();

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

 // COM終了
 CoUninitialize();

 return 0;
}

このサンプルは、result0.bmp、result1.bmp、result2.bmpという名前で3つのビットマップファイルを保存します。 ビットマップファイルはMessageBoxのOKを押した瞬間に保存されます。 このサンプルを実行後に出来上がったビットマップファイルを確認してください。

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

IPv6基礎検定

YouTubeチャンネルやってます!