DirectShow USBカメラの映像をAVIファイルへ出力
まずは、GraphEditで私のPCに手持ちのUSBカメラを接続して、今回のプログラムのグラフを書いてみると以下のようになります。左から、USBカメラ、マイク → ビデオコンプレッサ → AVI Mux → filewriterです。filewriterには、usbcam.aviというファイル名を設定しています。
映像のデータのみ圧縮しています。圧縮には、私の環境で標準で使えるようになっていたIndeoR video 5.10 Compression Filter を利用しています。このグラフではプレビューがありませんので、このまま実行すると画面上には映像は表示されません。映像は作成されたファイルで確認します。

ビデオキャプチャフィルタはGraphEditのメニュー → Insert Filters → DirectShow Filters → Video Capture Sources で追加出来ます。
オーディオキャプチャフィルタはGraphEditのメニュー → Insert Filters → DirectShow Filters → Audio Capture Sources で追加出来ます。
ビデオコンプレッサフィルタはGraphEditのメニュー → Insert Filters → DirectShow Filters → Video Compressors → IndeoR video 5.10 Compression Filter で追加出来ます。(※インストールされていればです。)
AVI MuxフィルタはGraphEditのメニュー → Insert Filters → DirectShow Filters → AVI Mux で追加出来ます。
File writerフィルタはGraphEditのメニュー → Insert Filters → DirectShow Filters → File writer で追加出来ます。
以下は、私のPCでのビデオコンプレッサフィルタを選択した画面です。

以上の機能でプレビューも出来るプログラムを作成します。単純なコンソールアプリケーションで、動作的にはプログラムを実行すると、USBカメラの映像とマイクが接続されている場合は音声をc:\usbcam.aviに保存します。メッセージボックスでプログラムが中断しますので、これをクリックするとプログラムが終了します。
プログラムの流れは以下のようになります。大きな流れとしては、以前書いたオーディオキャプチャのプログラムと同様に、上のグラフの機能をプログラムで実装しています。
COMの初期化
↓
フィルタグラフマネージャの生成
↓
ビデオ入力とオーディオ入力の追加
↓
ビデオコンプレッサフィルタの追加
↓
キャプチャグラフビルダの作成と設定
↓
録画開始
↓
メッセージボックスで終了待ち
↓
終了処理
プログラムは以下になります。
// usbcam1.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//
#include "stdafx.h"
//DirectShowのインクルード
#include <dshow.h>
//
// DirectShowサンプルプログラム
// USBカメラの映像とマイクの音声(接続されている場合)をaviファイルへ保存する
// (出力ファイルは、以下の定義で固定→プログラムの130行目あたりに記述)
//
#define AVI_FILE_NAME L"C:\\usbcam.avi"
int _tmain(int argc, _TCHAR* argv[])
{
// COMの初期化
CoInitialize(NULL);
// フィルタグラフ作成
IGraphBuilder *pGraph = NULL;
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,IID_IGraphBuilder, (void **)&pGraph);
// キャプチャデバイスをグラフに追加する一連の処理
ICreateDevEnum *pDevEnum = NULL;
IEnumMoniker *pClassEnum = NULL;
ULONG cFetched;
IMoniker *pMoniker = NULL;
IBaseFilter *pVideoInput = NULL;
HRESULT hr;
// ビデオ入力から
// ビデオ入力を列挙して最初のデバイスをグラフに追加する
CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,IID_ICreateDevEnum, (void **)&pDevEnum);
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
if(hr == S_FALSE){
printf("ビデオ入力が接続されていません。\n");
pDevEnum->Release();
return 0;
}
// 最初のモニカを取得して(これがUSBカメラと想定)フィルタオブジェクトにバインドする
hr = pClassEnum->Next(1, &pMoniker, &cFetched);
if(hr == S_OK){
pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void **)&pVideoInput);
pMoniker->Release();
}
pClassEnum->Release();
// ビデオ入力のフィルタをグラフに追加する
pGraph->AddFilter(pVideoInput, L"Video Capture");
// 次にオーディオ入力
// オーディオ入力を列挙して最初のデバイスをグラフに追加する
bool bAudio = true;
IBaseFilter *pAudioInput = NULL;
hr = pDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory,&pClassEnum, 0);
if(hr == S_FALSE){
// 接続されたオーディオ入力デバイスが一つも無い場合
printf("オーディオ入力デバイスは接続されていません。\n");
bAudio = false;
}else{
// 最初のモニカを取得して(これがマイクと想定)フィルタオブジェクトにバインドする
hr = pClassEnum->Next(1, &pMoniker, &cFetched);
if(hr == S_OK){
pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void **)&pAudioInput);
pMoniker->Release();
pGraph->AddFilter(pAudioInput, L"Audio Capture");
}
}
pClassEnum->Release();
// キャプチャデバイスをグラフに追加する処理はここまで
// ビデオ圧縮フィルタを追加する処理
// Compressorを列挙するためのCreateDevEnumを生成
VARIANT varName;
IBaseFilter *pVideoComp = NULL;
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pClassEnum, 0);
bool bFound = false;
// Compressorを列挙して"IndeoR video 5.10 Compression Filter"を探す
while(pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK)
{
IPropertyBag *pProp=NULL;
pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pProp);
VariantInit(&varName);
pProp->Read(L"FriendlyName", &varName, 0);
BSTR bstr = varName.bstrVal;
//"IndeoR video 5.10 Compression Filter"が見つかった場合
if(wcscmp(L"IndeoR video 5.10 Compression Filter", varName.bstrVal) == 0)
{
//グラフビルダーにCompressorを追加
pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pVideoComp);
pGraph->AddFilter(pVideoComp, L"Compress Filtter");
bFound = true;
}
VariantClear(&varName);
pMoniker->Release();
if(pProp){
pProp->Release();
}
if(bFound){
break;
}
}
pClassEnum->Release();
pDevEnum->Release();
// ビデオ圧縮フィルタを追加する処理はここまで
// キャプチャグラフビルダの作成
ICaptureGraphBuilder2 *pBuilder = NULL;
CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC,IID_ICaptureGraphBuilder2, (void **)&pBuilder);
pBuilder->SetFiltergraph(pGraph);
// ファイルライタフィルタの設定
IBaseFilter *pMux = NULL;
// aviファイルへ出力
pBuilder->SetOutputFileName(&MEDIASUBTYPE_Avi, AVI_FILE_NAME, &pMux, NULL);
pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pVideoInput, pVideoComp, pMux);
pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, pAudioInput, NULL, pMux);
// ディスプレイへ(必要な場合はスピーカにも)
pBuilder->RenderStream( &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pVideoInput, NULL, NULL );
//pBuilder->RenderStream( &PIN_CATEGORY_PREVIEW, &MEDIATYPE_Audio, pAudioInput, NULL, NULL );
//キャプチャ開始から終了まで
IMediaControl *pMediaControl;
pGraph->QueryInterface(IID_IMediaControl, (void **)&pMediaControl);
pMediaControl->Run();
MessageBox(NULL, (LPCSTR)"Click to stop.", (LPCSTR)"DirectShow", MB_OK);
pMediaControl->Stop();
//終了処理
pVideoInput->Release();
if(pAudioInput) pAudioInput->Release();
if(pVideoComp) pVideoComp->Release();
pMux->Release();
pMediaControl->Release();
pBuilder->Release();
pGraph->Release();
CoUninitialize();
return 0;
}
プログラミングのポイントとしては、キャプチャグラフビルダを利用しているところでしょうか。キャプチャグラフビルダはキャプチャグラフの作成を簡単にするために提供されているDirectShowのコンポーネントです。このキャプチャグラフビルダを使用すると、キャプチャアプリのプログラミングが簡単になるということです。詳細は参考リンク等を参照していただければと思います。
以前書いたオーディオキャプチャのプログラムではキャプチャグラフビルダを使用していませんので、今回のプログラムに比べてフィルタの接続部分等で細かいプログラミングになっています。
ソースファイル一式
プロジェクトファイル一式(Visual C++ 2008 Express Editionで作成)
参考リンク
キャプチャ アプリケーションの書き方(※Microsoft DirectX 8.0のドキュメントです)
AVI ファイルの再圧縮(Microsoft DirectX 9.0)
Windowsプログラミング関連記事
Posted by nishida at 22:03:37


