Bing-Copilot君たちの健闘

DirectShowを利用してマシンに接続されているカメラ(キャプチャーデバイス)の列挙

必要があって、上記のようなリストを得る必要が生じました。別記事のLibvlc wrapper関連です。なにげに検索して情報を得ようとして、以下のようなキーワードをEdgeに入れました。

directshow device enumerate with resolution c++

ちなみにenumerateは列挙するというような意味です。すると暫しの間があって、Copilot君が”口を挟んで”きたんです。それが、

コードをコピーして、dev-list.cppとかのファイル名でセーブして、このブログでも何度か使っているbcc32ないしbcc32cでコンパイルすると、なんと完動しました。こういうこともあるんですね。(筆者の経験ではノーエラーで動くというのは、確率として半分以下だと思います。)が、この率が結構上がってきているんですよね。最近特に。コードをまず提示しましょうか。読者が同様なキーワードで検索結果(Copilotのご託宣)が同じになるとは限りませんので。筆者が得たのは、

#include <dshow.h>
#include <iostream>
#include <vector>
#include <string>

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

void EnumerateDevicesAndResolutions() {
    HRESULT hr;
    ICreateDevEnum* pDevEnum = nullptr;
    IEnumMoniker* pEnum = nullptr;

    // Initialize COM
    CoInitialize(nullptr);

    // Create System Device Enumerator
    hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pDevEnum);
    if (FAILED(hr)) {
        std::cerr << "Failed to create device enumerator.\n";
        return;
    }

    // Enumerate video capture devices
    hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
    if (hr != S_OK) {
        std::cerr << "No video capture devices found.\n";
        pDevEnum->Release();
        return;
    }

    IMoniker* pMoniker = nullptr;
    while (pEnum->Next(1, &pMoniker, nullptr) == S_OK) {
        IPropertyBag* pPropBag;
        hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void**)&pPropBag);
        if (SUCCEEDED(hr)) {
            VARIANT varName;
            VariantInit(&varName);
            hr = pPropBag->Read(L"FriendlyName", &varName, nullptr);
            if (SUCCEEDED(hr)) {
                std::wcout << L"Device: " << varName.bstrVal << std::endl;
            }
            VariantClear(&varName);
            pPropBag->Release();
        }

        // Bind to the filter
        IBaseFilter* pFilter;
        hr = pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter, (void**)&pFilter);
        if (SUCCEEDED(hr)) {
            // Get IAMStreamConfig
            IEnumPins* pEnumPins;
            hr = pFilter->EnumPins(&pEnumPins);
            if (SUCCEEDED(hr)) {
                IPin* pPin;
                while (pEnumPins->Next(1, &pPin, nullptr) == S_OK) {
                    IAMStreamConfig* pStreamConfig;
                    hr = pPin->QueryInterface(IID_IAMStreamConfig, (void**)&pStreamConfig);
                    if (SUCCEEDED(hr)) {
                        int iCount = 0, iSize = 0;
                        hr = pStreamConfig->GetNumberOfCapabilities(&iCount, &iSize);
                        if (SUCCEEDED(hr)) {
                            for (int i = 0; i < iCount; i++) {
                                VIDEO_STREAM_CONFIG_CAPS scc;
                                AM_MEDIA_TYPE* pmt;
                                hr = pStreamConfig->GetStreamCaps(i, &pmt, (BYTE*)&scc);
                                if (SUCCEEDED(hr)) {
                                    if (pmt->formattype == FORMAT_VideoInfo) {
                                        VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*)pmt->pbFormat;
                                        std::cout << "  Resolution: " << pVih->bmiHeader.biWidth << "x" << pVih->bmiHeader.biHeight << std::endl;
                                    }
                                    CoTaskMemFree(pmt);
                                }
                            }
                        }
                        pStreamConfig->Release();
                    }
                    pPin->Release();
                }
                pEnumPins->Release();
            }
            pFilter->Release();
        }
        pMoniker->Release();
    }

    pEnum->Release();
    pDevEnum->Release();
    CoUninitialize();
}

int main() {
    EnumerateDevicesAndResolutions();
    return 0;
}

でした。bcc32ないしbcc32c(Clang系)でコンパイルしてみると、

$ bcc32 dev-list.cpp
Embarcadero C++ 7.70 for Win32 Copyright (c) 1993-2017 Embarcadero Technologies, Inc.
dev-list.cpp:
エラー E2451 dev-list.cpp 10: 未定義のシンボル nullptr (関数 EnumerateDevicesAndResolutions() )
*** 1 個のエラーがコンパイル中に発生しました ***

エラーが出ちゃいました。nullptrはご存じないようですから、より新しいClang系のコンパイラbcc32cでコンパイルしてみましょう。

$ bcc32c dev-list.cpp
Embarcadero C++ 7.70 for Win32 Copyright (c) 2012-2024 Embarcadero Technologies, Inc.
dev-list.cpp:
Turbo Incremental Link 6.99 Copyright (c) 1997-2024 Embarcadero Technologies, Inc.

今度はコンパイルできました。実行してみましょう。

$ ./dev-list
Device: Integrated Camera
  Resolution: 1280x720
  Resolution: 320x180
  Resolution: 320x240
  Resolution: 424x240
  Resolution: 640x480
  Resolution: 848x480
  Resolution: 960x540
  Resolution: 640x360
  Resolution: 640x360
  Resolution: 320x180
  Resolution: 320x240
  Resolution: 424x240
  Resolution: 640x480
Device: Hikvision
  Resolution: 2560x1440
  Resolution: 2560x1440
  Resolution: 1920x1080
  Resolution: 1920x1080
  Resolution: 1280x720
  Resolution: 1280x720
  Resolution: 704x480
  Resolution: 704x480
  Resolution: 352x240
  Resolution: 352x240

動いているようですね。このままでは使いにくいので、関数化まではしましょうかね。


#include <dshow.h>
#include <vector>
#include <string>
#include <iostream>

struct adevice {
	String dname;
	std::vector<String> resolutions;
};

extern adevice aentry;
extern std::vector<adevice> dlist;

// Helper function to release COM objects safely
template <typename T>
void SafeRelease(T** ppT) {
    if (*ppT) {
        (*ppT)->Release();
        *ppT = nullptr;
    }
}

// Function to enumerate video capture devices
void EnumerateCaptureDevices() {
    CoInitialize(nullptr); // Initialize COM

    ICreateDevEnum* pDevEnum = nullptr;
    IEnumMoniker* pEnum = nullptr;

    // Create the System Device Enumerator
    if (SUCCEEDED(CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pDevEnum))) {
        // Create an enumerator for video capture devices
        if (SUCCEEDED(pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0))) {
            IMoniker* pMoniker = nullptr;
            while (pEnum->Next(1, &pMoniker, nullptr) == S_OK) {
                IPropertyBag* pPropBag = nullptr;
                if (SUCCEEDED(pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void**)&pPropBag))) {
                    VARIANT varName;
                    VariantInit(&varName);

                    // Get the friendly name of the device
                    if (SUCCEEDED(pPropBag->Read(L"FriendlyName", &varName, nullptr))) {
						std::wcout << L"Device: " << varName.bstrVal << std::endl;
						aentry.dname = varName.bstrVal;

                        VariantClear(&varName);
                    }

                    SafeRelease(&pPropBag);
                }

                // Bind to the filter to enumerate supported resolutions and frame rates
                IBaseFilter* pFilter = nullptr;
                if (SUCCEEDED(pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter, (void**)&pFilter))) {
                    IAMStreamConfig* pStreamConfig = nullptr;
                    IEnumPins* pEnumPins = nullptr;

                    if (SUCCEEDED(pFilter->EnumPins(&pEnumPins))) {
                        IPin* pPin = nullptr;
                        while (pEnumPins->Next(1, &pPin, nullptr) == S_OK) {
                            if (SUCCEEDED(pPin->QueryInterface(IID_IAMStreamConfig, (void**)&pStreamConfig))) {
                                int iCount = 0, iSize = 0;
                                if (SUCCEEDED(pStreamConfig->GetNumberOfCapabilities(&iCount, &iSize))) {
                                    for (int i = 0; i < iCount; ++i) {
                                        VIDEO_STREAM_CONFIG_CAPS scc;
                                        AM_MEDIA_TYPE* pmt = nullptr;

                                        if (SUCCEEDED(pStreamConfig->GetStreamCaps(i, &pmt, (BYTE*)&scc))) {
                                            if (pmt->formattype == FORMAT_VideoInfo) {
                                                VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*)pmt->pbFormat;
												std::wcout << L"  Resolution: " << pVih->bmiHeader.biWidth << L"x" << pVih->bmiHeader.biHeight
														   << L", Frame Rate: " << 10000000.0 / pVih->AvgTimePerFrame << L" fps" << std::endl;
												//String astring = IntToStr((int)pVih->bmiHeader.biWidth) + "x" +  IntToStr((int)pVih->bmiHeader.biHeight) + " " + FloatToStrF(10000000.0 / pVih->AvgTimePerFrame, ffFixed, 8, 1);
												String astring = IntToStr((int)pVih->bmiHeader.biWidth) + "x" +  IntToStr((int)pVih->bmiHeader.biHeight);
												aentry.resolutions.push_back(astring);
											}
                                            CoTaskMemFree(pmt);
                                        }
                                    }
                                }
                                SafeRelease(&pStreamConfig);
                            }
                            SafeRelease(&pPin);
                        }
                        SafeRelease(&pEnumPins);
                    }
                    SafeRelease(&pFilter);
                }
				SafeRelease(&pMoniker);
				dlist.push_back(aentry);
                aentry.resolutions.clear();
			}

		}
		SafeRelease(&pEnum);

    }
	SafeRelease(&pDevEnum);

    CoUninitialize(); // Uninitialize COM
}

/*
int main() {
    EnumerateCaptureDevices();
    getchar();
    return 0;
}
*/

ほどんど一緒じゃないの?(笑)これをどう使うかは、続編で書く予定です。

コメント