DirectShowを使用してカメラ名をリストボックスに表示しカメラの選択結果を表示する

icon 項目のみ表示/展開表示の切り替え

概要

DirectShowを用い、接続されているカメラの名称を取得しリストボックスに表示します。複数ある場合はリストボックスで選択することができます。OKボタンをクリックするとリストボックスで選択されたカメラ番号が表示されます。
下記の図は、Windows 7でELECOM UCAM-C0220FとBUFFALO BSW32KM03が接続されている環境での実行結果です。
Windows XPで試したところ、USBビデオデバイスと表示されました。

COMは文字コードをUNICODEで表しますので、コンパイル時の文字セットはUNICODEとしてください。マルチバイトでコンパイルはできますが、実行するとデバイス名の1文字目しか表示されません。これは、UNICODEは2byteで1文字を表しており、英数字の場合は1byte目がANSIと同じコード、2バイト目が0なので、マルチバイトでは2バイト目がNULL文字となり文字の終了と判断されるからです。
マルチバイトでコンパイルしたい場合は、Readメンバ関数で取得したUNICODE文字列をマルチバイトに変換するコードを追加してください。

テスト環境

コンパイラ

Visual C++ 2008 Express(32bit)/Standard(32/64bit)
Visual C++ 2013 Express 32bit/64bit

実行環境

Windows 7 32/64bit

プログラムソースの概要

_tWinMain

DirectShowはCOMを使用しているので、CoInitializeでCOMの初期化を行います。
DialogBox APIによりカメラを選択するダイアログボックスを表示します。ダイアログボックスが終了するとグローバル変数cameraNumに選択されたカメラ番号が格納されています。
ダイアログボックスの終了アイコンがクリックされて終了したときは、カメラ番号を表示せずにプログラムを終了させます。
最後にCOMを終了させます。

DlgProc

WM_INITDIALOG

ダイアログの初期化時に呼び出されます。
cameraList関数によりカメラ名称を取得しリストボックスに表示します。
リストボックスのリソースファイルによる定義ではアイテムをソートしないように設定しておきます。
リストボックスにLB_SETCURSELメッセージを送信し最初のアイテムを選択した状態にします。

WM_COMMAND

IDOK
ダイアログボックスのOKボタンがクリックされたときに呼び出されます。
リストボックスの選択されているカーソル位置をLB_GETCURSELメッセージにより取得しグローバル変数に格納します。
IDCANCEL
ダイアログボックスの終了アイコンがクリックされたときに呼び出されます。
EndDialog APIを呼び出してダイアログボックスを終了させます。

cameraList

カメラの一覧を取得しリストボックスに設定します。
CoCreateInstanceによりデバイスの列挙子(オブジェクト)へのポインタを取得します。
取得したオブジェクトのCreateClassEnumeratorメンバ関数によりビデオ・キャプチャ・デバイスの列挙子(オブジェクト)へのポインタを取得します。
引数CLSID_VideoInputDeviceCategoryがビデオ・キャプチャ・デバイスを示しています。
CLSID_AudioInputDeviceCategoryを指定するとオーディオ・キャプチャ・デバイスが指定されます。
取得したオブジェクトのNextメンバ関数により順番にデバイスのモニカを取得します。
モニカのメンバ関数であるBindToStorageによりIPropertyBagインターフェースを取得します。
IPropertyBagインターフェースのReadメンバー関数に文字列FriendlyNameを与えて呼び出すとデバイス名が取得できます。
リストボックスにLB_ADDSTRINGメッセージを送りデバイス名をリストボックスに追加します。
VariantClearによりデイバス名が保存されているメモリを解放します。
Releaseによりモニカを解放します。
Nextメンバー関数がS_OK以外を返すまで繰り返します。
Nextメンバー関数がS_OK以外を返した場合、デバイスの列挙が終了したので、列挙子のメモリを開放します。

ソースコード

camera_sel1.cpp

//      DirectShow カメラ名をダイアログボックスに表示しカメラの選択結果を表示する
//      コンパイル時の文字セットはUNICODEとしてください。

#include <windows.h>
#include <tchar.h>
#include <dshow.h>
#include <stdio.h>
#include "resource.h"

#pragma comment(lib, "strmiids.lib")

//      ダイアログボックスプロシージャー
LRESULT CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);

//      カメラの一覧を取得しリストボックスに設定
int cameraList(HWND hList);

int cameraNum=0;        //      リストボックスで選択されたカメラ番号を保存
int cameraMax=0;        //      カメラ数

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, char* CmdLine, int nCmdShow){
        TCHAR buf[32];
        // COMの初期化
        if(FAILED(CoInitialize(NULL))){
                MessageBox(0,_TEXT("COMの初期化に失敗しました"),_TEXT("エラー"),MB_OK);
                return 1;
        }

        //      ダイアログボックスの表示
        if(DialogBox(hCurInst, TEXT("DLG1"), 0, (DLGPROC)DlgProc)==TRUE){
                _stprintf_s(buf , sizeof(buf)/sizeof(TCHAR) , _TEXT("カメラ%dが選択されました。") , ::cameraNum );
                MessageBox(0,buf,_TEXT("カメラ"),MB_OK);
        }
        CoUninitialize(); // COMを終了
        return 0;
}

//      ダイアログボックスプロシージャー

LRESULT CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
        static HWND hList;

        switch (msg) {
                case WM_INITDIALOG:
                        hList=GetDlgItem(hDlg,IDC_LISTBOX100);
                        ::cameraMax=cameraList(hList);
                        if(::cameraMax==0){
                                MessageBox(0,_TEXT("カメラが存在しません"),_TEXT("エラー"),MB_OK);
                                EndDialog(hDlg,FALSE);
                        }
                        SendMessage(hList, LB_SETCURSEL, 0, 0);
                        return TRUE;
                case WM_COMMAND:
                        switch (LOWORD(wParam)) {
                                case IDOK:
                                        ::cameraNum=(int)SendMessage(hList,LB_GETCURSEL,NULL,NULL);
                                        EndDialog(hDlg,TRUE);
                                        return TRUE;
                                case IDCANCEL:
                                        EndDialog(hDlg,FALSE);
                                        return TRUE;
                                default:
                                        return FALSE;
                        }
                default:
                        return FALSE;
        }
        return TRUE;
}

//      カメラの一覧を取得しリストボックスに設定

int cameraList(HWND hList){
        // システムデバイス列挙子の作成
        ICreateDevEnum *pDevEnum = NULL;
        CoCreateInstance(CLSID_SystemDeviceEnum, 
        NULL, CLSCTX_INPROC, IID_ICreateDevEnum, (void **)&pDevEnum);

        // ビデオ・キャプチャ・デバイス列挙子の取得
        IEnumMoniker *pClassEnum = NULL;
        pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
        if(pClassEnum == NULL){ // ビデオ・キャプチャ・デバイスが存在しない時
                pDevEnum->Release();
                return 0;
        }

        ULONG cFetched;
        IMoniker *pMoniker = NULL;
        IBaseFilter *pbf = NULL;
        int n=0;
        while(pClassEnum->Next(1, &pMoniker, &cFetched) == S_OK){
                IPropertyBag *pP = NULL;
                VARIANT var;

                pMoniker->BindToStorage(0,0,IID_IPropertyBag,(void**)&pP);
                var.vt=VT_BSTR;
                pP->Read( L"FriendlyName",&var,0);
                //      デバイス名をリストボックスに登録
                SendMessage(hList , LB_ADDSTRING , 0 , (LPARAM)var.bstrVal);

                VariantClear(&var);
                pMoniker->Release();
                ++n;
        }       
        pDevEnum->Release();
        pClassEnum->Release();
        return n;
}

resource.h

#define IDC_LISTBOX100          100

resource.rc

#include <windows.h>
#include "resource.h"


DLG1 DIALOG DISCARDABLE 0, 0, 238, 86
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_SETFONT
CAPTION "カメラの一覧"
FONT 9, "MS 明朝"
{
 CONTROL "カメラの選択", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 7, 78, 12
 CONTROL "ListBox100", IDC_LISTBOX100, "LISTBOX", WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | LBS_NOTIFY , 7, 19, 224,32
 CONTROL "OK", IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 92, 65, 54, 14
}

実行ファイルとソースファイルのダウンロード(camera_sel1.zip)