1. 程式人生 > 其它 >一臺電腦連線多個同種USB攝像頭區分測試

一臺電腦連線多個同種USB攝像頭區分測試

技術標籤:vc++visual studiovisual c++經驗分享usb

最近忙於處理攝像頭測試的事,發現電腦連線多個同種USB視訊裝置時裝置的名字都是一樣的,GUID也是一樣的,不能再像以前使用opencv通過名字直接開啟攝像頭採集影象,擺在眼前的是同臺電腦如何同時開啟多個攝像頭同時進行測試呢?在網上搜了很多資料,也沒找到相關的資料,沒有相關的經驗可以借鑑,或許有沒有分享出來,只能摸著石頭過河,硬著頭皮探索。

通過比對計算機的裝置管理器發現對應的裝置屬性之間有關位置資訊存在差異(port,hub),思考是否可以通過這個差異區分出不同的視屏裝置呢?

因此 朝著這個思路去尋找方法,通過opencv 寫的demo開啟攝像頭是通過VideoCapture capture= VideoCapture(camID); 當連線多個視訊裝置時,會枚舉出所有視屏裝置,程式碼如下(Vedioinput,directshow 都有介紹。):

int EnumDevices(CStringArray& listCamera)
{
	//列舉視訊裝置
	ICreateDevEnum *pCreateDevEnum;
	HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pCreateDevEnum);
	if (hr != NOERROR)return -1;

	CComPtr<IEnumMoniker> pEm;
	hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEm, 0);
	if (hr != NOERROR)return -1;

	pEm->Reset();

	int id = 0;
	ULONG cFetched;
	IMoniker *pM;
	while (hr = pEm->Next(1, &pM, &cFetched), hr == S_OK)
	{
		IPropertyBag *pBag;
		hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag);
		if (SUCCEEDED(hr))
		{
			VARIANT var;
			CString tempstr;
			var.vt = VT_BSTR;
			hr = pBag->Read(L"FriendlyName", &var, NULL);
			if (hr == NOERROR)
			{
				id++;
				LPWSTR* str = var.pbstrVal;
				listCamera.Add(var.bstrVal);
				SysFreeString(var.bstrVal);
			}
			pBag->Release();
		}
		pM->Release();
	}
	return id;
}

嘗試更新VideoCapture capture= VideoCapture(camID);中的camID, 發現camID 跟枚舉出來的視屏裝置的順序一致,開啟的也是對應順序的視訊裝置,因此猜想對應的裝置ID就是對應枚舉出來的順序號(當然還可以根據對應的port 來控制不同的視訊裝置,不過後面沒有嘗試),後來也證明了這個猜想是對的。

解決了視訊裝置號的問題,可以這個只能打開了攝像頭才知道開啟的是對應哪個USB port 口的裝置,如何想要開啟連線哪個USB Port 口的就開啟哪個port 口的裝置呢?這就回到了開篇講的裝置位置資訊。通過裝置名稱和裝置位置資訊繫結就知道到打開了哪個USB port口的對應視訊裝置, 上面的方法已經不適用了沒有找到對應的屬性,因此重新研究,參考文章如下:

https://stackoverflow.com/questions/3438366/setupdigetdeviceproperty-usage-example

void EnumDevices(vector<CString> &listCamera)
{
	CONST GUID *pClassGuid = NULL;
	CString strDevice_Friendly_Name, strDevice_Location_Info;
	unsigned i, j;
	DWORD dwSize, dwPropertyRegDataType;
	DEVPROPTYPE ulPropertyType;
	CONFIGRET status;
	HDEVINFO hDevInfo;
	SP_DEVINFO_DATA DeviceInfoData;
	const static LPCTSTR arPrefix[3] = { TEXT("VID_"), TEXT("PID_"), TEXT("MI_") };
	TCHAR szDeviceInstanceID[MAX_DEVICE_ID_LEN];
	TCHAR szDesc[1024], szHardwareIDs[4096];
	WCHAR szBuffer[4096] = { 0 };
	LPTSTR pszToken, pszNextToken;
	TCHAR szVid[MAX_DEVICE_ID_LEN], szPid[MAX_DEVICE_ID_LEN], szMi[MAX_DEVICE_ID_LEN];
	
	USES_CONVERSION;
	Sleep(1000); //防止更新模組時,USB camera 沒有識別
	// List all connected USB devices  
	hDevInfo = SetupDiGetClassDevs(pClassGuid, _T("USB"), NULL,
		pClassGuid != NULL ? DIGCF_PRESENT : DIGCF_ALLCLASSES | DIGCF_PRESENT);
	if (hDevInfo == INVALID_HANDLE_VALUE)
		return;

	// Find the ones that are driverless  
	for (i = 0; ; i++)
	{
		DeviceInfoData.cbSize = sizeof(DeviceInfoData);
		if (!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData))
			break;

		status = CM_Get_Device_ID(DeviceInfoData.DevInst, szDeviceInstanceID, MAX_PATH, 0);
		if (status != CR_SUCCESS)
			continue;

		if (SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc,
			&ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0))
		{
			memset(szBuffer, 0, sizeof(WCHAR) * 4096);
			if (SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_FriendlyName,
				&ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0))
			{
				strDevice_Friendly_Name = szBuffer;
			}

			memset(szBuffer, 0, sizeof(WCHAR) * 4096);
			if (SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_LocationInfo,
				&ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0))
			{
				strDevice_Location_Info = szBuffer;
			}

			if (strDevice_Location_Info.Find(_T("0000.0014"), 0) != -1)
			{
				CString TempStr;
				TempStr.Format(_T("%s-%s"), strDevice_Friendly_Name, strDevice_Location_Info);
				listCamera.push_back(TempStr);
			}
		}

	}

	return ;
}

至此完美收關,能夠隨心所欲開啟所要開啟的視訊裝置了。