之前在https://blog.csdn.net/fengbingchun/article/details/102806822中介紹過在Windows下獲取視頻設備列表的方法。其實那種實現方法是有缺陷的,當PC機上連接多個視頻設備,並且其中有設備處於啓動運行狀態時,再調用相關接口獲取視頻設備可能會崩潰,因爲爲了獲取視頻設備列表,它會先以索引0遍歷獲取視頻設備名,然後會執行啓動此設備操作。如果此設備已經被其它應用程序啓動過,則此時就會崩潰。
其實如果僅僅爲了獲取視頻設備列表,沒有必要執行啓動設備的操作。以下是測試代碼,無需啓動任何設備即可獲取到所有的視頻設備列表。
此測試代碼調用的是系統C接口,你也可以調整爲調用系統的C++接口實現。
#define CINTERFACE
#define COBJMACROS
#include <strmif.h>
#include <Setupapi.h>
#include <uuids.h>
#include <devguid.h>
#include <memory>
#include <algorithm>
namespace {
std::unique_ptr<char[]> dup_wchar_to_utf8(wchar_t *w, int& len)
{
len = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
std::unique_ptr<char[]> s(new char[len]);
WideCharToMultiByte(CP_UTF8, 0, w, -1, s.get(), len, 0, 0);
return s;
}
} // namespace
int test_get_windows_camera_list()
{
CoInitialize(nullptr);
ICreateDevEnum *devenum = nullptr;
int r = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, reinterpret_cast<void**>(&devenum));
if (r != S_OK) {
fprintf(stdout, "fail to CoCreateInstance: %d\n", r);
return -1;
}
IEnumMoniker *classenum = nullptr;
r = ICreateDevEnum_CreateClassEnumerator(devenum, CLSID_VideoInputDeviceCategory, (IEnumMoniker **)&classenum, 0);
if (r != S_OK) {
fprintf(stdout, "fail to ICreateDevEnum_CreateClassEnumerator: %d\n", r);
return -1;
}
IMoniker *m = nullptr;
typedef struct devices_info {
int index;
std::string name;
} devices_info;
std::vector<devices_info> lists;
int device_counter = 0;
while (IEnumMoniker_Next(classenum, 1, &m, nullptr) == S_OK) {
IPropertyBag *bag = nullptr;
VARIANT var;
r = IMoniker_BindToStorage(m, 0, 0, IID_IPropertyBag, (void **)&bag);
if (r != S_OK) {
fprintf(stdout, "fail to IMoniker_BindToStorage: %d\n", r);
return -1;
}
var.vt = VT_BSTR;
r = IPropertyBag_Read(bag, L"FriendlyName", &var, nullptr);
if (r != S_OK) {
fprintf(stdout, "fail to IPropertyBag_Read: %d\n", r);
return -1;
}
int length;
auto friendly_name = dup_wchar_to_utf8(var.bstrVal, length);
lists.push_back({ device_counter++, friendly_name.get() });
if (bag)
IPropertyBag_Release(bag);
IMoniker_Release(m);
}
IEnumMoniker_Release(classenum);
CoUninitialize();
fprintf(stdout, "device lists:\n");
std::for_each(lists.cbegin(), lists.cend(), [](const devices_info& info) {
fprintf(stdout, " index: %d, name: %s\n", info.index, info.name.c_str());
});
return 0;
}
執行結果如下:設備索引與設備名並不是固定不變的,再次拔下設備再插上,此時獲得的設備索引和設備名可能與之前不同。