ビデオ入力デバイスを選択してから再生する

ここでは、ビデオカメラ(デスクトップカメラ、DVカメラなど)やTVチューナーデバイスの情報を取得するだけではなく、選択する方法を説明したいと思います。 (注意)ここの例では、サンプルを簡潔にするためにエラー処理を書いていません。

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

前述したビデオキャプチャデバイス情報サンプルは、情報の取得だけを行っていました。 実際には情報を取得するだけではなく、取得した情報を元に再生を行うデバイスを選択したくなると思います。 ここでは、取得した情報を元に再生するデバイスを選択するサンプルコードを示します。

このサンプルコードは他のサンプル同様にコンソールから利用してください。 コンソールにビデオデバイスのFriendly Nameが表示されて、その後に「select this device ? [y] or [n]」と表示されます。 そこで、コンソールに「y」と打ち込んでリターンキーを押すとそのビデオキャプチャデバイスが再生されます。 「y」以外のキーを押すと次のビデオデバイスが表示されます。


#include <stdio.h>

#include <dshow.h>

int
main()
{
 // 再生用
 IGraphBuilder *pGraphBuilder;
 ICaptureGraphBuilder2 *pCaptureGraphBuilder2;
 IMediaControl *pMediaControl;
 IBaseFilter *pDeviceFilter = NULL;

 // デバイス選択用
 ICreateDevEnum *pCreateDevEnum = NULL;
 IEnumMoniker *pEnumMoniker = NULL;
 IMoniker *pMoniker = NULL;
 ULONG nFetched = 0;

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

 //
 // デバイス選択
 //

 // デバイスを列挙するためのCreateDevEnumを生成
 CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, 
   IID_ICreateDevEnum, (PVOID *)&pCreateDevEnum);
    
 // VideoInputDeviceを列挙するためのEnumMonikerを生成 
 pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
   &pEnumMoniker, 0);
 if (pEnumMoniker == NULL) {
   // 接続された映像入力デバイスが一つも無い場合にはこのif文に入ります
   printf("no device\n");
   return 0;
 }

 // EnumMonikerをResetする
 // Resetすると、先頭から数えなおします
 pEnumMoniker->Reset();

 // 最初のMonikerを取得
 while (pEnumMoniker->Next(1, &pMoniker, &nFetched) == S_OK) {
   IPropertyBag *pPropertyBag;
   char devname[256];

   // IPropertyBagにbindする
   pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
     (void **)&pPropertyBag);

   VARIANT var;

   // FriendlyNameを取得
   var.vt = VT_BSTR;
   pPropertyBag->Read(L"FriendlyName", &var, 0);
   WideCharToMultiByte(CP_ACP, 0,
     var.bstrVal, -1, devname, sizeof(devname), 0, 0);
   VariantClear(&var);

   printf("%s\r\n", devname);
   printf("  select this device ? [y] or [n]\r\n");
   int ch = getchar();

   // コンソールで'y'を入力してリターンキーを押すと再生されます
   // その他のキーを押すと再生されません
   if (ch == 'y') {
     // MonkierをFilterにBindする
     pMoniker->BindToObject(0, 0, IID_IBaseFilter,
         (void**)&pDeviceFilter );
   }

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

   if (pDeviceFilter != NULL) {
     // getchar() で'y'が入力された場合、loopを抜けます
     break;
   }
 }

 if (pDeviceFilter != NULL) {
   //
   // 再生する
   //

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

   // CaptureGraphBuilder2というキャプチャ用GraphBuilderを生成する
   CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, 
     IID_ICaptureGraphBuilder2, 
     (LPVOID *)&pCaptureGraphBuilder2);

   // FilterGraphをセットする
   pCaptureGraphBuilder2->SetFiltergraph(pGraphBuilder);

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

   // FilterGraphにデバイスフィルタを追加する
   pGraphBuilder->AddFilter(pDeviceFilter, L"Device Filter");

   // Graphを生成する
   pCaptureGraphBuilder2->RenderStream(&PIN_CATEGORY_PREVIEW,
     NULL, pDeviceFilter, NULL, NULL);

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

   // 再生中にプログラムが終わってしまわないように
   MessageBox(NULL,
     "Block Execution",
     "Block",
     MB_OK);

   // 資源を解放
   pMediaControl->Release();
   pCaptureGraphBuilder2->Release();
   pGraphBuilder->Release();
 }

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

 // COM終了
 CoUninitialize();

 return 0;
}


上記サンプルが何かの役に立てば幸いです。 わかりにくかったり、間違っている部分を発見した場合にはご指摘頂ければ幸いです。