最近忙于处理摄像头测试的事,发现电脑连接多个同种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 ;
}
至此完美收关,能够随心所欲打开所要打开的视频设备了。