Qt USB摄像头支持热插拔

7 篇文章 1 订阅
本文档展示了如何在Windows环境下使用C++注册USB摄像头的插拔事件,并在主窗口中通过nativeEvent函数处理这些事件。当USB摄像头插入或移除时,程序会捕获相应的设备变更通知,过滤掉重复的事件并识别摄像头设备路径,从而实现对USB摄像头的动态监控。同时,还提供了一个枚举USB摄像头的代码片段,用于获取摄像头的设备路径和名称。
摘要由CSDN通过智能技术生成

环境:windows

1. 注册USB摄像头插拔事件

bool util::registerDevNitification(QWidget *widget)
{
#if 1
    static const GUID GUID_DEVINTERFACE_LIST[] =
    {
        // GUID_DEVINTERFACE_CAMERA_DEVICE 
        { 0x65E8773D, 0x8F56, 0x11D0, { 0xA3, 0xB9, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96 } },
        // GUID_DEVINTERFACE_USB_DEVICE
        { 0xA5DCBF10, 0x6530, 0x11D2,{ 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } },
        // GUID_DEVINTERFACE_DISK
        { 0x53f56307, 0xb6bf, 0x11d0,{ 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } },
        // GUID_DEVINTERFACE_HID,
        { 0x4D1E55B2, 0xF16F, 0x11CF,{ 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } },
        // GUID_NDIS_LAN_CLASS
        { 0xad498944, 0x762f, 0x11d0,{ 0x8d, 0xcb, 0x00, 0xc0, 0x4f, 0xc3, 0x35, 0x8c } }
         GUID_DEVINTERFACE_COMPORT
        //{ 0x86e0d1e0, 0x8089, 0x11d0, { 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73 } },
         GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR
        //{ 0x4D36E978, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } },
         GUID_DEVINTERFACE_PARALLEL
        //{ 0x97F76EF0, 0xF883, 0x11D0, { 0xAF, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x84, 0x5C } },
         GUID_DEVINTERFACE_PARCLASS
        //{ 0x811FC6A5, 0xF728, 0x11D0, { 0xA5, 0x37, 0x00, 0x00, 0xF8, 0x75, 0x3E, 0xD1 } }
    };
    //注册插拔事件
    HDEVNOTIFY hDevNotify;
    DEV_BROADCAST_DEVICEINTERFACE NotifacationFiler;
    ZeroMemory(&NotifacationFiler, sizeof(DEV_BROADCAST_DEVICEINTERFACE));
    NotifacationFiler.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    NotifacationFiler.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;

    for (int i = 0; i < sizeof(GUID_DEVINTERFACE_LIST) / sizeof(GUID); i++)
    {
        NotifacationFiler.dbcc_classguid = GUID_DEVINTERFACE_LIST[i];

        hDevNotify = RegisterDeviceNotification((HANDLE)widget->winId(), &NotifacationFiler, DEVICE_NOTIFY_WINDOW_HANDLE);
        if (!hDevNotify)
        {
            qDebug() << "注册失败" << endl;
            return false;
        }
    }
    return true;
#endif
}

然后在主窗口调用:

util::registerDevNitification(this);

2. 在主窗口实现nativeEvent

注意插入和拔出都会触发两次通知,所以我过滤掉了第二次通知。

PDEV_BROADCAST_DEVICEINTERFACE::dbcc_name对应于枚举摄像头时获取到的DevicePath属性,通过这个设备路径我们就能知道具体是哪个摄像头。

bool CMainWindow::nativeEvent(const QByteArray & eventType, void * message, long * result)
{
    MSG* msg = reinterpret_cast<MSG*>(message);
    int msgType = msg->message;
    if (msgType == WM_DEVICECHANGE)
    {
        PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)msg->lParam;
        switch (msg->wParam) {
        case DBT_DEVICEARRIVAL:
            if (lpdb->dbch_devicetype = DBT_DEVTYP_DEVICEINTERFACE)
            {
                PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
                GUID cameraGuid = { 0x65E8773D, 0x8F56, 0x11D0, { 0xA3, 0xB9, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96 } };
                if (cameraGuid == pDevInf->dbcc_classguid)
                {
                    // USB插入需要6到10秒才能触发该回调
                    QString devicePath = QString::fromWCharArray(pDevInf->dbcc_name);
                    QStringList parts = devicePath.split('#');
                    if (parts.length() != 4)
                    {
                        qDebug() << "camera logic error";
                        return false;
                    }
                    QString usbPortStr = parts[2];
                    QStringList usbPortParts = usbPortStr.split('&');
                    if (usbPortParts.length() != 4)
                    {
                        qDebug() << "camera logic error";
                        return false;
                    }
                    if ("0000" != usbPortParts[3])
                    {
                        // "0002"不发送信号
                        return false;
                    }
                    qDebug() << qstr("USB摄像头插入");
                    devicePath = devicePath.toLower();
                    emit cameraPlugged(true, devicePath);
                }
            }
            break;
        case DBT_DEVICEREMOVECOMPLETE:
            if (lpdb->dbch_devicetype = DBT_DEVTYP_DEVICEINTERFACE)
            {
                PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
                GUID cameraGuid = { 0x65E8773D, 0x8F56, 0x11D0, { 0xA3, 0xB9, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96 } };
                if (cameraGuid == pDevInf->dbcc_classguid)
                {
                    // USB拔出马上触发
                    QString devicePath = QString::fromWCharArray(pDevInf->dbcc_name);
                    QStringList parts = devicePath.split('#');
                    if (parts.length() != 4)
                    {
                        qDebug() << "camera logic error";
                        return false;
                    }
                    QString usbPortStr = parts[2];
                    QStringList usbPortParts = usbPortStr.split('&');
                    if (usbPortParts.length() != 4)
                    {
                        qDebug() << "camera logic error";
                        return false;
                    }
                    if ("0000" != usbPortParts[3])
                    {
                        // "0002"不发送信号
                        return false;
                    }
                    qDebug() << qstr("USB摄像头拔出");
                    devicePath = devicePath.toLower();
                    emit cameraPlugged(false, devicePath);
                }
            }
            break;
        }
    }
    return false;
}

枚举USB摄像头代码:

bool DeviceEnumerator::enumDevices()
{
	HRESULT hr;
	// enumerate all video capture devices
	ICreateDevEnum *pCreateDevEnum = NULL;
	IEnumMoniker *pEm = NULL;
	IMoniker *pM = NULL;
	IPropertyBag *pBag = NULL;

	//releaseMoniker();

	hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
		IID_ICreateDevEnum, (void**)&pCreateDevEnum);
	if (hr != NOERROR)
	{
		qCritical() << "Error Creating Device Enumerator";
		goto exit;
	}

	hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEm, 0);
	if (hr != NOERROR)
	{
		qWarning() << "Sorry, you have no video capture hardware";
		goto exit;
	}

	while (pEm->Next(1, &pM, NULL) == S_OK)
	{
		hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
        if (FAILED(hr))
        {
            pM->Release();
            continue;
        }

        VARIANT var;
        VARIANT var2;
        VariantInit(&var);
        VariantInit(&var2);
        hr = pBag->Read(L"FriendlyName", &var, NULL);
        HRESULT hr2 = pBag->Read(L"DevicePath", &var2, NULL);
        if (hr == NOERROR && hr2 == NOERROR)
        {
            QString name = QString::fromUtf16(reinterpret_cast<ushort*>(var.bstrVal));
            if ("screen-capture-recorder" != name)
            {
                QString devicePath = QString::fromUtf16(reinterpret_cast<ushort*>(var2.bstrVal));
                qDebug() << devicePath;

                QVariantMap devInfo;
                devInfo.insert("FriendlyName", name);
                devInfo.insert("DevicePath", devicePath);
                devInfo.insert("IMoniker*", reinterpret_cast<int>(pM));
                m_devInfoList.append(devInfo);

                pM->AddRef();
            }
            SysFreeString(var.bstrVal);
        }

        SAFE_RELEASE(pBag);
        SAFE_RELEASE(pM);

        if (m_devInfoList.length() >= 2)
        {
            break;
        }
	}

exit:
	SAFE_RELEASE(pEm);
	SAFE_RELEASE(pCreateDevEnum);

	return true;
}

dshow根据枚举到的IMoniker打开摄像头,然后拿到h264流,然后就可以播放或推流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值