DirectShowを使用してカメラ名と解像度の一覧を取得する

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

概要

DirectShowを用い、接続されているカメラの名称と解像度の一覧を取得し、camera.txtファイルに保存します。
下記は、Windows 7でELECOM UCAM-C0220FとBUFFALO BSW32KM03が接続されている環境での実行後のcamera.txtの内容です。
UCAM-C0220F
  640 *   480  30.0fps
  800 *   600  20.0fps
 1280 *   720   9.0fps
 1280 *  1024   8.0fps
 1600 *  1200   4.0fps
  352 *   288  30.0fps
  320 *   240  30.0fps
  176 *   144  30.0fps
  160 *   120  30.0fps
  640 *   480  30.0fps
BUFFALO BSW32KM03 USB PC Camera
  640 *   480  30.0fps
 1280 *   720  20.0fps
 1280 *   800  20.0fps
 1920 *  1080  20.0fps
  352 *   288  30.0fps
  320 *   240  30.0fps
  176 *   144  30.0fps
  160 *   120  30.0fps
 2016 *  1512  15.0fps
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の初期化を行います。
cameraList関数によりカメラの種類と解像度の一覧を取得します。

cameraList

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

resList

CoCreateInstance関数によりキャプチャグラフを作成します。
FindInterface関数によりビデオフォーマットを示すポインタを取得します。
GetNumberOfCapabilitiesによりサポートしているピン数と構造体のサイズを取得しVIDEO_STREAM_CONFIG_CAPS構造体であれば、解像度が取得できます。
GetStreamCaps関数によりフォーマット機能のセットを取得し有効なものであれば、res_fput関数を呼び出し解像度をファイルに保存します。

res_fput

解像度をファイルに保存します。

ソースコード

// Direct Show カメラの一覧と解像度を表示

#include <windows.h>
#include <tchar.h>
#include <dshow.h>
#include <stdio.h>
#include <conio.h>

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

int cameraMax=0;        //      カメラ数

//      カメラ一覧を取得
int cameraList(FILE* fp);

//      サポートしている解像度・フレームレートを取得する
void resList(IBaseFilter *pbf , FILE* fp);

//      解像度の情報をファイルに出力
void res_fput(FILE* fp,VIDEOINFOHEADER* video);


int WINAPI _tWinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, TCHAR* CmdLine, int nCmdShow){
        // COMの初期化
        if(FAILED(CoInitialize(NULL))){
                MessageBox(0,_TEXT("COMの初期化に失敗しました"),_TEXT("エラー"),MB_OK);
                return 1;
        }
        FILE* fp;
        if (_tfopen_s(&fp, _TEXT("camera.txt"), _TEXT("w"))){
                MessageBox(0, _TEXT("ログファイルが作成できませんでした"), _TEXT("エラー"), MB_OK);
                return 1;
        }
        ::cameraMax=cameraList(fp);
        if(::cameraMax==0){
                _ftprintf(fp,_TEXT("カメラは見つかりませんでした。\n"));
        }
        CoUninitialize(); // COMを終了
        return 0;
}

//      カメラ一覧を取得

int cameraList(FILE* fp){

        // システムデバイス列挙子の作成
        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(_TEXT("FriendlyName"),&var,0);
                //      デバイス名をファイルへ書き込み
                _ftprintf(fp,_TEXT("%s\n"),var.bstrVal);
                VariantClear(&var);

                pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pbf); // モニカをフィルタオブジェクトにバインド
                resList(pbf,fp);

                pMoniker->Release();
                ++n;
        }       
        pDevEnum->Release(); // 以後に不要なメモリーをリリース
        pClassEnum->Release();
        return n;
}

//      サポートしている解像度・フレームレートを取得する

void resList(IBaseFilter *pbf , FILE* fp){
        HRESULT hr;

        // キャプチャグラフ作成
        ICaptureGraphBuilder2 *pCapture = NULL;
        CoCreateInstance(CLSID_CaptureGraphBuilder2, 
        NULL, CLSCTX_INPROC, IID_ICaptureGraphBuilder2, (void **) &pCapture);

        // ビデオフォーマットの取得
        IAMStreamConfig *pConfig = NULL;
        hr = pCapture->FindInterface(&PIN_CATEGORY_CAPTURE, 
        0, pbf, IID_IAMStreamConfig, (void**)&pConfig); // インターフェイスのポインタを取得

        int iCount=0;
        int     iSize=0;
        hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
        if(iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS)){// VIDEO_STREAM_CONFIG_CAPS構造体かサイズを確認
                for(int iFormat=0; iFormat<iCount; iFormat++){
                        VIDEO_STREAM_CONFIG_CAPS scc;
                        AM_MEDIA_TYPE *pmtConfig;
                        hr = pConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)&scc);
                        VIDEOINFOHEADER *pVih2;
                        if(SUCCEEDED(hr)){
                                if((pmtConfig->majortype == MEDIATYPE_Video)
                                        && (pmtConfig->formattype == FORMAT_VideoInfo)
                                        && (pmtConfig->cbFormat >= sizeof(VIDEOINFOHEADER))
                                        && (pmtConfig->pbFormat != NULL)){

                                        pVih2 = (VIDEOINFOHEADER*)pmtConfig->pbFormat;
                                        res_fput(fp,pVih2);
                                }
                        }
                }
        }
        pConfig->Release();
        pCapture->Release();
}

//      解像度の情報をファイルに出力

void res_fput(FILE* fp,VIDEOINFOHEADER* video){
        double ns=100*1.0e-9;
        double frame=1/(double(video->AvgTimePerFrame)*ns);
        _ftprintf(fp,_TEXT("%5d * %5d %5.1ffps\n"), video->bmiHeader.biWidth , video->bmiHeader.biHeight, frame );
}

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