Window下USB相机使用及同批次相机区分

搞了下Win的USB相机今天来写写
1.记录日常
2.抛砖引玉献丑了,还有一些问题没有解决

Win 下 Usb相机视频流播放及查看属性

  • 相机视频流播放
    开始菜单->相机
    就可以获取实时视频流,上边有个转换按钮可以转换不同的摄像头

  • 相机属性查看
    连接上usb设备后,我的电脑-> 管理->设备管理器 ->照相机 ->对应的相机
    左键双击或者右键属性->详细信息就可以查看属性包括:
    {设备名称,硬件ID,设备实例路径,哈希ID,序列号(有的可能没有),…}
    Window下USB相机使用及同批次相机区分_第1张图片
    属性含义

VID - 供应商标识
PID - 产品表识 
REV - 修订版本
IserialNumber - 序列号
设备路径 - Windows操作系统依赖一个唯一的设备路径来唯一地识别连接到系统的每个USB设备/接口。设备路径字符串用于与USB设备通信,被传递给Win32 API函数CreateFile()
容器ID - 系统提供的容器 ID 通过多种方式生成。 此决定基于设备中包含的信息。 此信息是从 ACPI 设置、USB 总线驱动程序和 USB 集线器检索的
GUID - 标志你安装的设备是属于一个什么类

理论上可以通过VID 和 PID 来唯一标识一个相机,但是在实际使用过程中同时使用同一厂家的同一型号相机而 绝大多数“山寨”【没用过贵的所以不知道情况】会出现 设备名称,VID/PID一致的情况而导致无法进行区分设备;如下图某宝的俩台一样的设备:名称VID/PID完成一样
Window下USB相机使用及同批次相机区分_第2张图片

获取相机视频流

通过 Opencv 获取获取还是比较简单方便,难点是不知道 index 和 usb相机的对应关系

#include 
#include 
 
using namespace std;
using namespace cv;
 
int main()
{
    VideoCapture camera(0) // 从0开始有几个相机就到自然数几;调用可以依次打开不同的相机
 
    if(!camera.isOpened())
    { 
       std::cout<<"The camera is not turned on!!!"<<std::endl;
       return 1;
    }   
    
    while (true)
    {
        Mat frame;
        camera >> frame;
 
	    if(frame.empty())
            break
        else
        {
            imshow("test",frame);
         }
        return 0;
 }

区分相同 Usb相机设备

先说结论:获取到设备实例路径 或 容器ID 来区分
1.设备实例路径不同Window下USB相机使用及同批次相机区分_第3张图片
打开相机采用的opencv库之前有大佬看过opencv的源码定位到获取usb设备的代码:是采用directshow;代码如下

.h文件

#pragma once
#include
#include
#include
#include
#include

#pragma   comment(lib,"Strmiids.lib")   
using namespace std;

class ListDivice
{
public:
	void Test(string usbpath);
	
	int listDevicesTuple(vector<tuple<string, string>>& list); // 这里获取了名称和设备路径ID,可以自己选择增删
};

.cpp文件

#include "ListDivice.h"

void ListDivice::Test(string usbpath)
{
    vector<tuple<string, string>> deviceName;
    int deviceNum = listDevicesTuple(deviceName);
    for (int i = 0; i < deviceNum; i++)
    {
        string tmp = get<0>(deviceName[i]);
        int a = tmp.find("usb");
        int b = tmp.find("{");
        tmp = tmp.substr(a, b - a - 1);

        while (tmp.find("#") != -1)
        {
            int postion = tmp.find("#");
            tmp.replace(postion, 1, "\\");
        }

        transform(tmp.begin(), tmp.end(), tmp.begin(), ::toupper);

        int shenbo = tmp.compare(usbpath);
        if (!shenbo)
        {
            cout << "Find i want camera, it's id is " << i << endl;
        }
        cout << i << " [" << a << ", " << b << "] " << tmp << endl;;
        cout << get<0>(deviceName[i]) << "---" << get<1>(deviceName[i]) << endl;
    }
    cout << endl;
    cout << "************************************************我是YD的分割线*********************************************" << endl;
    cout << endl;
}

int ListDivice::listDevicesTuple(vector<tuple<string, string>>& 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))
    {
        string usbCameraName = "";
        string usbCameraPath = "";

        hr = pDevEnum->CreateClassEnumerator(
            CLSID_VideoInputDeviceCategory,
            &pEnum, 0);

        if (hr == S_OK) {
            IMoniker* pMoniker = NULL;

            while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
                IPropertyBag* pPropBag;
                hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
                    (void**)(&pPropBag));

                IBaseFilter* pFilter;
                hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter,
                    (void**)(&pFilter));

                if (FAILED(hr)) {
                    pMoniker->Release();
                    continue;
                }

                VARIANT varName;
                VariantInit(&varName);
                hr = pPropBag->Read(L"Description", &varName, 0);

                if (FAILED(hr)) hr = pPropBag->Read(L"FriendlyName", &varName, 0);
                if (SUCCEEDED(hr)) 
                {
                    hr = pPropBag->Read(L"FriendlyName", &varName, 0);

                    int count = 0;
                    char tmp[255] = { 0 };

                    while (varName.bstrVal[count] != 0x00 && count < 255) {
                        tmp[count] = (char)varName.bstrVal[count];
                        count++;
                    }
                    usbCameraName = tmp;
                }

                hr = pPropBag->Read(L"ContainerId", &varName, 0);
                hr = pPropBag->Read(L"DevicePath", &varName, 0);
                if (SUCCEEDED(hr)) 
                {
                    hr = pPropBag->Read(L"DevicePath", &varName, 0);

                    int count = 0;
                    char tmp[255] = { 0 };

                    while (varName.bstrVal[count] != 0x00 && count < 255) {
                        tmp[count] = (char)varName.bstrVal[count];
                        count++;
                    }
                    usbCameraPath = tmp;
                }
                auto tuple = make_tuple(usbCameraPath, usbCameraName);
                list.push_back(tuple);
                
                pPropBag->Release();
                pPropBag = NULL;
                pMoniker->Release();
                pMoniker = NULL;
                deviceCounter++;
            }
            pDevEnum->Release();
            pDevEnum = NULL;
            
            pEnum->Release();
            pEnum = NULL;
        }
    }
    return deviceCounter;
}

2.设备容器ID不同
Window下USB相机使用及同批次相机区分_第4张图片
获取容器ID使用WIN api

.h

#pragma once
#include
#include // W2A USES_CONVERSION
#include // HDEVINFO
#include
#include
#include

#define ARRAY_SIZE(arr)     (sizeof(arr)/sizeof(arr[0]))
#define GUID_CAMERA_STRING L"{65e8773d-8f56-11d0-a3b9-00a0c9223196}"
DEFINE_DEVPROPKEY(DEVPKEY_Device_ContainerId, 0x8c7ed206, 0x3f8a, 0x4827, 0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c, 2);

using namespace std;

class GetContainerID
{
public:
	void Test();

	void GetSerialNum(vector<std::tuple<string,string>>&);
	std::string WcharToString(LPCWSTR pwszsrc);
};

.cpp

#include "GetContainerID.h"

void GetContainerID::Test()
{
    vector<tuple<string, string>> containerList;
    GetSerialNum(containerList);

    for (auto member : containerList)
    {
        cout << get<0>(member) << " ," << get<1>(member) << endl;
    }

    cout << "************************************************我是YD的分割线*********************************************" << endl;
    cout << endl;
}

typedef BOOL(WINAPI* FN_SetupDiGetDevicePropertyW)(
    __in       HDEVINFO DeviceInfoSet,
    __in       PSP_DEVINFO_DATA DeviceInfoData,
    __in       const DEVPROPKEY* PropertyKey,
    __out      DEVPROPTYPE* PropertyType,
    __out_opt  PBYTE PropertyBuffer,
    __in       DWORD PropertyBufferSize,
    __out_opt  PDWORD RequiredSize,
    __in       DWORD Flags
    );

void GetContainerID::GetSerialNum(vector<std::tuple<string, string>>& list)
{
    FN_SetupDiGetDevicePropertyW fn_SetupDiGetDevicePropertyW = (FN_SetupDiGetDevicePropertyW)
        GetProcAddress(GetModuleHandle(TEXT("Setupapi.dll")), "SetupDiGetDevicePropertyW");

    USES_CONVERSION;
    GUID guid;
    CLSIDFromString(GUID_CAMERA_STRING, &guid);

    HDEVINFO devInfo = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    WCHAR szDeviceInstanceId[200];

    if (devInfo != INVALID_HANDLE_VALUE)
    {
        DWORD devIndex = 0;
        SP_DEVINFO_DATA devInfoData;
        devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
        
        // 获取设备容器ID
        DWORD dwSize;
        DEVPROPTYPE ulPropertyType;
        TCHAR szDesc[1024];
        WCHAR szBuffer[2048];
        string usbCameraPath, usbCameraContainerID;

        while (SetupDiEnumDeviceInfo(devInfo, devIndex, &devInfoData))
        {
            memset(szDeviceInstanceId, 0, 200);
            SetupDiGetDeviceInstanceIdW(devInfo, &devInfoData, szDeviceInstanceId, MAX_PATH, 0);

            usbCameraPath = WcharToString(szDeviceInstanceId);
            std::cout << WcharToString(szDeviceInstanceId) << endl;

            if (fn_SetupDiGetDevicePropertyW(devInfo, &devInfoData, &DEVPKEY_Device_ContainerId,
                &ulPropertyType, (BYTE*)szDesc, sizeof(szDesc), &dwSize, 0)) 
            {
                StringFromGUID2((REFGUID)szDesc, szBuffer, ARRAY_SIZE(szBuffer));
                usbCameraContainerID = WcharToString(szBuffer);
                std::cout <<" ContainerId: "<< WcharToString(szBuffer) << endl;
            }

            auto tuple = make_tuple(usbCameraPath, usbCameraContainerID);
            list.push_back(tuple);

            devIndex++;
        }
    }
}

std::string GetContainerID::WcharToString(LPCWSTR pwszsrc)
{
    int nLen = WideCharToMultiByte(CP_ACP, 0, pwszsrc, -1, NULL, 0, NULL, NULL);
    if (nLen <= 0)
        return std::string("");

    char* pszDst = new char[nLen];
    if (NULL == pszDst)
        return string("");

    WideCharToMultiByte(CP_ACP, 0, pwszsrc, -1, pszDst, nLen, NULL, NULL);
    pszDst[nLen - 1] = 0;

    std::string strTmp(pszDst);
    delete[] pszDst;

    return strTmp;
}

测试实例:tb俩个相同牌子的摄像头利用上编代码获取如下

手动查询到想要指定摄像头的设备实例路径修改格式与获取到的设备路径比较相同,在 vector的下标号就是相机的index

在这里插入图片描述

因为我用的xml对于 ‘&’ 支持不友好所以才有了容器ID这个方式,手动查询到想要指定摄像头的容器ID,获取所有的设备的【实例路径,容器ID】 与上边的作比较;若相同就把容器ID的值添加到 tuple中用来作表示;然后比较容器ID一样就获取index调用opencv打开

Window下USB相机使用及同批次相机区分_第5张图片

参考文档如下(感谢各位前辈):
https://blog.csdn.net/mywmy/article/details/79311432
https://blog.csdn.net/mars_xiaolei/article/details/100556476
https://blog.csdn.net/chrovery/article/details/52402350 // 获取usb相机属性包括 容器ID
https://docs.microsoft.com/zh-cn/windows-hardware/drivers/install/container-ids // 微软关于容器ID和usb设备的一些介绍

一些疑问

1.看到一些文章说通过序列号来区分,但是tb买的俩个序列号都是一样的?

2.可以通过修改注册表来修改directshow获取到的设备名称,函数查出来的信息已经改变但是设备管理器显示的还是未改变。是否可以写程序来修改usb相机的名称做区分这样比较简单?【有空我去试一下】

3.有没有大哥有在Linux上使用usb相机的经验,遇到个问题就是:相机都挂载上了但是使用cheese或者camorama 只能打开某一个 剩下的一直报错打不开?

你可能感兴趣的:(外设联动,usb,c++,opencv,directx,winapi)