SampleGrabberを使って静止画を取得する
ここでは、SampleGrabberを使って動画フレームを静止画として取得する方法を説明したいと思います。 (注意)ここの例では、サンプルを簡潔にするためにエラー処理を書いていません。
SampleGrabber
動画から静止画を取得する方法にはIBasicVideo::GetCurrentImageを使う方法とSampleGrabberを使う方法の2つがあります。 ここでは、SampleGrabberを利用する方法を説明したいと思います。 IBasicVideo::GetCurrentImageを使った方法の方が単純で簡単ですが、制約も多く、利用出来ない場合もあります。 IBasicVideo::GetCurrentImageの制約ですが、まずPauseを行った状態でしか静止画が取得できません。 GraphがRun()している状態で静止画を取得できないのは結構つらいです。 また、ハードウェアやソフトウェアの環境によってはIBasicVideo::GetCurrentImageは使えなくなってしまいます。
SampleGrabberは、SampleGrabberフィルタをグラフに挿入して、そこからデータを直接取るという方式のため、IBasicVideo::GetCurrentImageより汎用性が高くなります。 SampleGrabberを使うには、DirectX8以上である必要があります。
サンプルコード
下記サンプルは、OKボタンを押すとフレームをビットマップとして保存します。 サンプルの後半部分はビットマップを作成して保存していますが、保存はサンプルをわかりやすくするために行っているだけであり、SampleGrabberの使い方と直接的な関係はありません。
#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();
// 再生中にプログラムが終わってしまわないように
MessageBox(NULL,
"Block Execution",
"Block",
MB_OK);
// OKが押されるとBITMAPを保存する
// バッファを用意
// 必要なバッファサイズを取得
long nBufferSize = 0;
pSampleGrabber->GetCurrentBuffer(&nBufferSize,
NULL);
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という名前でビットマップファイルを保存します。 このサンプルを実行後に出来上がったビットマップファイルを確認してください。 ビットマップファイルのフォーマットを知りたい方はこちらをご覧下さい。
ちょっとサンプルが長くなってしまったので、わかりにくければ質問してください。