Windows下獲取視頻設備的一種改進實現

之前在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;
}

執行結果如下:設備索引與設備名並不是固定不變的,再次拔下設備再插上,此時獲得的設備索引和設備名可能與之前不同。

GitHubhttps://github.com//fengbingchun/OpenCV_Test

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章