三、视频设备的枚举以及插拔检测

一、前言

        本章主要讲述,如何获取设备名称以及guid,采集设备的采集格式识别,设备的插拔

        设备列表以及属性的获取使用的directshow(后续的MediaFoundation无法获取OBS摄像头)

        设备的插拔使用的是QT 捕获系统消息,捕获到设备插拔后,重新获取下设备列表(这里并没有动态的添加或者删除,考虑的主要是维护UI显示时 设备顺序的一致性)

二、设备列表的获取

        ICreateDevEnum 接口,创建特定的类(如视频捕获设备,音频捕获设备,视频压缩等)的一个枚举器 ,可以使用CLSID_SystemDeviceEnum来得到该指针。

CreateDevEnum::CreateClassEnumerator(
REFCLSID clsidDeviceClass, //设备类别
IEnumMoniker **ppEnumMoniker, //输出参数,IEnumMoniker ××
DWORD dwFlags 
);

        IEnumMoniker 接口,  表示特定的设备枚举类

        IMoniker::Enum 方法获取指向 IEnumMoniker 实现的指针,该实现可以通过名字对象的组件向前或向后枚举。

        IRunningObjectTable::EnumRunning 方法返回一个指向 IEnumMoniker 实现的指针,该实现可以枚举在运行对象表中注册的名字对象。

        IEnumMoniker::Next  此方法检索枚举序列中下一个设备是否存在

struct CameraDevice
{int                  nIndex;                    // indexstd::string          uid;                       // 硬件层uniqueId mac中为BuiltInMicrophoneDevice  std::string          name;                      // 设备名称
};std::map<std::string, CameraDevice> VideoCoreDevice::getVideoDeviceList()
{if(!m_pDevEnum){::CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, IID_ICreateDevEnum, (void**)&m_pDevEnum);}std::map<std::string, CameraDevice> devices;if (!m_bCoUninitializeIsRequired){goto END;}HRESULT hr = m_pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &m_pMonikerDevEnum, 0);if (hr != NOERROR){std::cout << "CreateClassEnumerator failed" << std::endl;goto END;}m_pMonikerDevEnum->Reset();ULONG cFetched;IMoniker* pM;int index = 0;while (S_OK == m_pMonikerDevEnum->Next(1, &pM, &cFetched)) {IPropertyBag* pBag;hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag);if (S_OK == hr) {// Find the description or friendly name.VARIANT varName;VariantInit(&varName);hr = pBag->Read(L"Description", &varName, 0);if (FAILED(hr)) {hr = pBag->Read(L"FriendlyName", &varName, 0);}if (SUCCEEDED(hr)) {// ignore all VFW driversif ((wcsstr(varName.bstrVal, (L"(VFW)")) == NULL) &&(_wcsnicmp(varName.bstrVal, (L"Google Camera Adapter"), 21) != 0)) {// Found a valid device.{char device_name[256] = { 0 };char unique_name[256] = { 0 };char product_name[256] = { 0 };int convResult = WideCharToMultiByte(CP_UTF8, 0, varName.bstrVal, -1, (char*)device_name, sizeof(device_name), NULL, NULL);if (convResult == 0){std::cout << "WideCharToMultiByte failed" << std::endl;goto END;}hr = pBag->Read(L"DevicePath", &varName, 0);if (FAILED(hr)){strncpy_s((char*)unique_name, sizeof(unique_name),(char*)device_name, convResult);}else{convResult = WideCharToMultiByte(CP_UTF8, 0, varName.bstrVal, -1, (char*)unique_name, sizeof(unique_name), NULL, NULL);if (convResult == 0){std::cout << "WideCharToMultiByte failed" << std::endl;goto END;}}GetProductId(unique_name, product_name, sizeof(product_name));CameraDevice camera;camera.nIndex = index;camera.name = device_name;camera.uid = unique_name;devices.insert(std::make_pair(camera.uid, camera));}++index;  // increase the number of valid devices}}VariantClear(&varName);pBag->Release();pM->Release();}}END:return devices;
}// 不同获取方式得到的ID不一致,通过处理得到相同的ID
void GetProductId(const char* devicePath, char* productUniqueIdUTF8, uint32_t productUniqueIdUTF8Length)
{*productUniqueIdUTF8 = '\0';char* startPos = strstr((char*)devicePath, "\\\\?\\");if (!startPos) {strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);std::cout << "Failed to get the product Id" << std::endl;return;}startPos += 4;char* pos = strchr(startPos, '&');if (!pos || pos >= (char*)devicePath + strlen((char*)devicePath)) {strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);std::cout << "Failed to get the product Id" << std::endl;return;}// Find the second occurrence.pos = strchr(pos + 1, '&');uint32_t bytesToCopy = (uint32_t)(pos - startPos);if (pos && (bytesToCopy < productUniqueIdUTF8Length) &&bytesToCopy <= kVideoCaptureProductIdLength) {strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length,(char*)startPos, bytesToCopy);}else{strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);std::cout << "Failed to get the product Id" << std::endl;}
}

三、设备的插拔检测

        目前使用的是Qt::nativeEventFilter 过滤设备插拔信息,然后去响应设备列表

// 头文件
#pragma once
#include <QWidget>
#include <QUuid>
#include <QAbstractNativeEventFilter>
#include <Windows.h>
#include <QHash>class VideoNotificationClient : public QAbstractNativeEventFilter, public QWidget
{
public:class Listener{public:virtual void onDeviceAdded(const std::string& uid) = 0;virtual void onDeviceRemoved(const std::string& uid) = 0;};public:void initialized();void uninstallFilter();bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override;public:VideoNotificationClient(VideoNotificationClient::Listener* listener);~VideoNotificationClient();private:bool                                                     m_bInitialized;QHash<QUuid, HDEVNOTIFY>                                 m_hSevNotifys;VideoNotificationClient::Listener*                       m_pListener;
};
//源文件
#include "VideoNotificationClient.h"
#include <QDebug>
#include <iostream>
#include <Windows.h>
#include <Dbt.h>
#include <devguid.h>
//具体的设备guid如usbiodef需要initguid
#include <initguid.h>
//USB设备
//GUID_DEVINTERFACE_USB_DEVICE
#include <usbiodef.h>
//HID人机交互设备-鼠标键盘等
#include <hidclass.h>
//GUID_DEVINTERFACE_KEYBOARD
#include <ntddkbd.h>
//GUID_DEVINTERFACE_MOUSE
#include <ntddmou.h>
#include <QCoreApplication>
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "setupapi.lib")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 } }
};VideoNotificationClient::VideoNotificationClient(VideoNotificationClient::Listener *listener): QWidget(nullptr), m_bInitialized(false), m_pListener(listener)
{this->hide();qApp->installNativeEventFilter(this);
}VideoNotificationClient::~VideoNotificationClient()
{uninstallFilter();qApp->removeNativeEventFilter(this);
} void VideoNotificationClient::initialized()
{HANDLE winid = (HANDLE)this->winId();if (!winid){return;}//注册插拔事件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(winid, &NotifacationFiler, DEVICE_NOTIFY_WINDOW_HANDLE);if (!hDevNotify){qDebug() << "注册失败" << endl;m_bInitialized = false;return;}m_hSevNotifys.insert(QUuid(NotifacationFiler.dbcc_classguid), hDevNotify);}m_bInitialized = true;}void VideoNotificationClient::uninstallFilter()
{for (HDEVNOTIFY handle : qAsConst(m_hSevNotifys)){::UnregisterDeviceNotification(handle);}m_hSevNotifys.clear();
}bool VideoNotificationClient::nativeEventFilter(const QByteArray& eventType, void* message, long* result)
{Q_UNUSED(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){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]){return false;}devicePath = devicePath.toLower();m_pListener->onDeviceAdded(devicePath.toStdString());//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]){return false;}devicePath = devicePath.toLower();m_pListener->onDeviceRemoved(devicePath.toStdString());}}break;}}return false;
}

四、设备插拔库设计

项目完整代码,在后续的文章中给出

// camra
struct CameraDevice
{int                  nIndex = 0;                     // indexstd::string          uid = "";                       // 硬件层uniqueId mac中为BuiltInMicrophoneDevice  std::string          name = "";                      // 设备名称
};// IVideoCore
class VIDEODEVICE_SHARED_EXPORT IVideoCore
{
public:static IVideoCore* getInstance();virtual void addListener(IVideoDeviceListner* listner) = 0;virtual void removeListener(IVideoDeviceListner* listner) = 0;virtual std::map<std::string, CameraDevice> getVideoDevicesList() = 0;
};// IVideoDevice
class VIDEODEVICE_SHARED_EXPORT IVideoDevice
{
public:virtual bool initlized() = 0;virtual std::map<std::string, CameraDevice> getVideoDevicesList() = 0;virtual void addListener(IVideoDeviceListner* listener) = 0;virtual void removeListener(IVideoDeviceListner* listener) = 0;};// IVideoDeviceListner
class VIDEODEVICE_SHARED_EXPORT IVideoDeviceListner
{
public:virtual void onDeviceAdded(CameraDevice device) = 0;virtual void onDeviceRemoved(CameraDevice device) = 0;virtual void onDeviceListUpdate(std::map<std::string, CameraDevice> cameraList) = 0;
};class VideoNotificationClient 
{
public:class Listener{public:virtual void onDeviceAdded(const std::string& uid) = 0;virtual void onDeviceRemoved(const std::string& uid) = 0;};
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/106896.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《基于区块链的数据资产评估实施指南》技术研讨会成功召开

2023年9月1日&#xff0c;《基于区块链的数据资产评估实施指南》&#xff08;以下简称《指南》&#xff09;技术研讨会在深圳召开&#xff0c;竹云科技作为主要参编单位出席此次研讨会。 中国科协决策咨询首席专家王春晖&#xff0c;中国社会科学院博士于小丽&#xff0c;中国…

多版本CUDA安装切换

系统中默认的安装CUDA为12.0&#xff0c;现在需要在个人用户下安装CUDA11.7。 CUDA 下载 CUDA官网下载 安装 Log file not open.Segmentation fault (core dumped)错误 将/tmp/cuda-installer.log删除即可。重新安装&#xff0c;去掉驱动的安装&#xff0c;设置Toolkit的安装…

LeetCode刷题笔记【26】:贪心算法专题-4(柠檬水找零、根据身高重建队列、用最少数量的箭引爆气球)

文章目录 前置知识860.柠檬水找零题目描述解题思路代码 406.根据身高重建队列题目描述解题思路代码 452. 用最少数量的箭引爆气球题目描述踩坑-进行模拟正确思路的贪心 总结 前置知识 参考前文 参考文章&#xff1a; LeetCode刷题笔记【23】&#xff1a;贪心算法专题-1&#x…

【vue2】data中数据赋值失败找不到、data数据不声明的影响

&#x1f609;博主&#xff1a;初映CY的前说(前端领域) ,&#x1f4d2;本文核心&#xff1a;vue2data作用 前言&#xff1a;当你看到这篇文章相比你已经对vue有了一定的了解&#xff0c;对data的有了一个基本的认识&#xff1a;data是存放我们当前页面数据地方。是的&#xff0…

vue 使用canvas 详细教程

Vue.js 中使用 Canvas Vue.js 是一个流行的 JavaScript 框架&#xff0c;用于构建用户界面。它提供了一种简洁的方式来管理和渲染数据&#xff0c;同时也支持与其他库和工具的集成。要在 Vue.js 中使用 Canvas&#xff0c;您可以按照以下步骤进行操作&#xff1a; 在 Vue.js …

[移动通讯]【Carrier Aggregation-4】【LTE-3】

前言: 这里面重点讲解一下跟CA 相关的 UL UECapabilityInformation 参考&#xff1a; LTE附着流程详解-UECapabilityInformation_Amao_come_on的博客-CSDN博客 支持载波聚合的终端(UE) 4G | ShareTechnote CSDN CSDN LTE category New_is lte category supported in th…

华为云云耀云服务器L实例评测|centos系统搭建git私服

搭建git私服 前言一、华为云云耀云服务器L实例租用二、华为云云耀云服务器L实例安装git三、华为云云耀云服务器L实例git配置1.创建文件用于存放公钥2.设置文件权限3.配置本地公钥 四、华为云云耀云服务器L实例部署git仓库四、git仓库到本地总结 前言 之前一直想搭建一个属于自…

微服务-gateway跨域配置

文章目录 一、前言二、gateway跨域配置1、问题描述1.1、什么是跨域请求&#xff1f;1.1.1、同源策略1.1.2. 安全性考虑1.1.3. 跨域攻击 1.2、问题产生原因 2、解决方法2.1、修改配置文件2.2、配置类统一配置2.3、全局跨域拦截器 三、总结 一、前言 在SpringCloud项目中&#x…

OpenCV(三十):图像膨胀

1.图像膨胀原理 图像膨胀的原理是将一个结构元素&#xff08;也称为核或模板&#xff09;在图像上滑动&#xff0c;并将其与图像中对应位置的像素进行比较。如果结构元素的所有像素与图像中对应位置的像素都匹配&#xff0c;那么该位置的像素值保持不变。如果结构元素的任何一个…

【直接运行TS文件的三种方法】

直接运行TS文件的三种方法 文章目录 直接运行TS文件的三种方法法一&#xff1a;将 ts 编译成 js&#xff0c;然后运行 js 文件法二&#xff1a;用 ts-node 直接运行 ts法三&#xff1a;webstorm中直接运行ts(TypeScript) 法一&#xff1a;将 ts 编译成 js&#xff0c;然后运行 …

keep-alive缓存三级及三级以上路由

需求需要缓存这个出入记录&#xff0c;当tab切换时不重新加载&#xff0c;当刷新页面时&#xff0c;或把这个关闭在重新打开时重新加载如图&#xff1a; &#xff08;我这里用的是芋道源码的前端框架) keep-alive 1、include 包含页面组件name的这些组件页面&#xff0c;会被…

Wireshark TS | 网络路径不一致传输丢包问题

问题背景 网络路径不一致&#xff0c;或者说是网络路径来回不一致&#xff0c;再专业点可以说是网络路径不对称&#xff0c;以上种种说法&#xff0c;做网络方向的工程师肯定会更清楚些&#xff0c;用简单的描述就是&#xff1a; A 与 B 通讯场景&#xff0c;C 和 D 代表中间…