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;
}
*/
ほどんど一緒じゃないの?(笑)これをどう使うかは、続編で書く予定です。
コメント