一台电脑同时连接多个相同的USB摄像头并根据USB口进行区分
百度搜了下,
https://blog.csdn.net/kingston110/article/details/112060113?spm=1001.2014.3001.5506
这位老哥给的方案最靠谱,但是这老哥貌似有点保留,根据他的代码是没办法对相同摄像头by不同的USB口进行区分。
最后小研究了下找到解决方案。
1.OPENCV中打开摄像头都是打开摄像头的Index,这个Index是通过COM来遍历“CLSID_VideoInputDeviceCategory”,这里可以查询到不同Index的“DevicePath”。
比如:
Index: 0->"\\?\usb#vid_xxxx&pid_xxxx&mi_00#6&53e1f6&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
。
。
。
这里用“#”作为字符串的分割点,取出“6&53e1f6&0&0000”作为后面USB端口比较的字符串
代码:
int listDevices(std::map<std::string,int>& list)
{
ICreateDevEnum *pDevEnum = NULL;
IEnumMoniker *pEnum = NULL;
int deviceCounter = 0;
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
reinterpret_cast<void**>(&pDevEnum));
if (SUCCEEDED(hr))
{
// Create an enumerator for the video capture category.
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEnum, 0);
if (hr == S_OK)
{
IMoniker *pMoniker = NULL;
int iCamID = 0;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
{
IPropertyBag *pPropBag;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,(void**)(&pPropBag));
if (FAILED(hr))
{
pMoniker->Release();
continue; // Skip this one, maybe the next one will work.
}
// Find the description or friendly name.
VARIANT varName;
VariantInit(&varName);
hr = pPropBag->Read(L"DevicePath", &varName, 0);
if (SUCCEEDED(hr))
{
_bstr_t bstr(varName);
std::string strtmp = bstr;
std::vector<std::string> listtmp;
tool_StringA::split(strtmp, listtmp, '#');
strtmp = listtmp[2];
std::transform(strtmp.begin(), strtmp.end(), strtmp.begin(), ::tolower);
list.emplace(strtmp,iCamID++);
printf("ID:%d - %s\n", iCamID - 1, strtmp.c_str());
}
pPropBag->Release();
pPropBag = NULL;
pMoniker->Release();
pMoniker = NULL;
deviceCounter++;
}
pDevEnum->Release();
pDevEnum = NULL;
pEnum->Release();
pEnum = NULL;
}
//if (!silent)printf("SETUP: %i Device(s) found\n\n", deviceCounter);
}
//comUnInit();
return deviceCounter;
}
2. 根据USB口找到上面对应的信息:
这个位置可以通过SetupDiGetDevicePropertyW这个API 传入DEVPKEY_Device_LocationInfo得到,
对应的信息如下:
“USB\VID_xxxx&PID_xxxx&MI_00\6&53e1f6&0&0000”->"0000.0014.0000.005.000.000.000.000.000"
“USB\VID_xxxx&PID_xxxx&MI_00\6&53e1f6&0&0000”分割‘\\’,得到"6&53e1f6&0&0000",和上面取到的信息一样,当然大小写不一样需要转换下。
这段代码如下:
void EnumDevices(std::map<std::string, std::string> &listCamera)
{
CONST GUID *pClassGuid = NULL;
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];
// List all connected USB devices
hDevInfo = SetupDiGetClassDevs(pClassGuid, ("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++)
{
std::wstring strDevice_Friendly_Name, strDevice_Location_Info;
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;
}
std::string Location, FriendlyName;
//Location = Cto_string(strDevice_Location_Info);
//FriendlyName = Cto_string(strDevice_Friendly_Name);
Unicode2ANSI(strDevice_Location_Info, Location);
Unicode2ANSI(strDevice_Friendly_Name, FriendlyName);
std::string::size_type pos = Location.find("0000.0014");
if (pos != std::string::npos)
{
pos = FriendlyName.find("Camera");
if (pos != std::string::npos)
{
std::string strtmp = szDeviceInstanceID;
std::vector<std::string> listtmp;
tool_StringA::split(strtmp, listtmp, '\\');
strtmp = listtmp[2];
std::transform(strtmp.begin(), strtmp.end(), strtmp.begin(), ::tolower);
listCamera.emplace(Location, strtmp);
printf("%s - %s\n", strtmp.c_str(), Location.c_str());
}
}
}
}
}
这样基本就可以了,可以把需要区分的usb端口号“0000.0014.0000.006.000.000.000.000”写入你的配置文件中即可~
std::map<std::string, int> camIDlist;
listDevices(camIDlist);
inih::INIReader r = inih::INIReader{ "./setting.ini" };
const auto& v3 = r.Get<std::string>("Setting", "CamUSBPortLocation");
std::map<std::string, std::string> camList;
EnumDevices(camList);
auto pos = camList.find(v3);
decltype (pos->second) strDeviceID;
if (pos != camList.end())
{
bFind = true;
strDeviceID = pos->second;
}
else
{
printf("没有找到Setting.ini对应的USB摄像头!\n");
return 0;
}
auto itFind = camIDlist.find(strDeviceID);
if (itFind!=camIDlist.end())
{
iCamID = itFind->second;
}
else
{
printf("没有找到对应的USB摄像头!\n");
return 0;
}