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

一、前言

        本章主要讲述,如何获取设备名称以及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;
}

四、设备插拔库设计

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

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

相关文章

day6_网络编程

网络聊天室 客户端 main.c #include "include/errorAndHead.h"void hander(int sig) {while (waitpid(-1, NULL, WNOHANG) > 0); }int main(int argc, const char *argv[]) {/* 捕获 SIGCHLD 信号 hander 处理*/if (signal(SIGCHLD, hander) SIG_ERR){ERR_MSG…

webpack5 (三)

webpack 高级配置 其实就是对 webpack 进行优化&#xff0c;让代码在编译/运行时性能更好 1. 提升开发体验 2. 提升打包构建速度 3. 减少代码体积 4. 优化代码运行性能 一、提升开发体验 sourcemap 在编译打包后所有的 css 和 js 都合并为了一个文件&#xff0c;并多了很多…

APS系统设计经验分享(时间推导II - 2023.09)

在前一篇关于APS系统设计分享文章(《APS系统设计经验分享(时间推导 - 2023.03)》)中&#xff0c;我们提到将会分享使用OptaPlanner作为规划引擎开发APS系统过程中&#xff0c;遇到的一些时间相关的设计建议与异常情况分析。后来一直忙于项目工作&#xff0c;直到现在才想起仍欠…

CC-TAIX01 HONEYWELL 霍尼韦尔连接工厂热智商远程监测系统

CC-TAIX01 HONEYWELL 霍尼韦尔连接工厂热智商远程监测系统 -霍尼韦尔宣布霍尼韦尔连接工厂热智商,一种基于云的远程监测系统,旨在监测和管理关键的热过程数据。这是霍尼韦尔资产绩效管理(APM)投资组合的一部分。 热智商是工业和商业热应用的远程监测解决方案。它将燃烧设备连…

Flink基础实操-计算单词出现次数

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

【云原生】容器编排工具Kubernetes

目录 一、 K8S介绍 官网地址&#xff1a; 1.1docker编排与k8s编排相比 1.2特性 1.3功能 二、K8S重要组件 2.1核心组件 &#xff08;1&#xff09;Kube-apiserver &#xff08;2&#xff09;Kube-controller-manager &#xff08;3&#xff09;Kube-scheduler &#x…

DevOps管理软件生命周期

整体的软件开发流程 PLAN&#xff1a;开发团队根据客户的目标制定开发计划 CODE&#xff1a;根据PLAN开始编码过程&#xff0c;需要将不同版本的代码存储在一个库中。GIT,SVN BUILD&#xff1a;编码完成后&#xff0c;需要将代码构建并且运行。MAVEN TEST&#xff1a;成功构建…

【性能优化】聊聊性能优化那些事

针对于互联网应用来说&#xff0c;性能优化其实就是一直需要做的事情&#xff0c;因为系统响应慢&#xff0c;是非常影响用户的体验&#xff0c;可能回造成用户流失。所以对于性能非常重要。最近正好接到一个性能优化的需求&#xff0c;需要对所负责的系统进行性能提升。目前接…

Windows环境下RabbitMQ下载安装

一、准备安装文件 1、下载Erlang 登录网站Downloads - Erlang/OTP&#xff0c;选择“Download Windows installer”&#xff0c;如下图所示&#xff1a; 弹出框中&#xff0c;选在下载保存地址&#xff0c;保存文件&#xff0c;如下图所示&#xff1a; 2、下载RabbitMQ 登录…

ElasticSearch简介

一、基本概念 1、Index&#xff08;索引&#xff09; 动词&#xff0c;相当于 MySQL 中的 insert&#xff1b; 名词&#xff0c;相当于 MySQL 中的 Database 2、Type&#xff08;类型&#xff09; 在 Index&#xff08;索引&#xff09;中&#xff0c;可以定义一个或多个类…

无需公网IP,在家SSH远程连接公司内网服务器「cpolar内网穿透」

文章目录 1. Linux CentOS安装cpolar2. 创建TCP隧道3. 随机地址公网远程连接4. 固定TCP地址5. 使用固定公网TCP地址SSH远程 本次教程我们来实现如何在外公网环境下&#xff0c;SSH远程连接家里/公司的Linux CentOS服务器&#xff0c;无需公网IP&#xff0c;也不需要设置路由器。…

unity tolua热更新框架教程(2)

Lua启动流程 增加脚本luamain&#xff0c;继承luaclient 建立第一个场景GameMain&#xff0c;在对象GameMain挂载脚本LuaMain&#xff0c;启动场景 看到打印&#xff0c;lua被成功加载 lua入口及调用堆栈 这里会执行main.lua文件的main函数 C#接口导出 在此处配置C#导出的代码 …

算法leetcode|75. 颜色分类(rust重拳出击)

文章目录 75. 颜色分类&#xff1a;样例 1&#xff1a;样例 2&#xff1a;提示&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 75. 颜色分类&#xff1a; 给定一个包含红色、白色和蓝色、共 n…

js集成zTree实现树形结构菜单

官方文档:https://www.treejs.cn/v3/api.php 一、简单创建 zTree 演示 $.fn.zTree.init($("#tree"), setting, zTreeNodes) 的用法$.fn.zTree.init 概述[ 依赖 jquery.ztree.core 核心 js ] zTree 初始化方法,创建 zTree 必须使用此方法 1、页面需要进行 W3C 申明…

【Python】爬虫练习-爬取豆瓣网电影评论用户的观影习惯数据

目录 前言 一、配置环境 1.1、 安装Python 1.2、 安装Requests库和BeautifulSoup库 1.3.、安装Matplotlib 二、登录豆瓣网&#xff08;重点&#xff09; 2.1、获取代理 2.2、测试代理ip是否可用 2.3、设置大量请求头随机使用 2.4、登录豆瓣网 三、爬取某一部热门电影…

SQLserver基础入门理论(超基础)二

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…

leetcode第361场周赛补题

7020. 统计对称整数的数目 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;转化为字符串后枚举 class Solution { public:int countSymmetricIntegers(int low, int high) {int res 0;for(int i low; i < high; i ){string s to_string(i);if(s.size() % 2) …

【51单片机实验笔记】声学篇(一) 蜂鸣器基本控制

目录 前言硬件介绍PWM基础蜂鸣器简介 原理图分析蜂鸣器驱动电路 软件实现蜂鸣器短鸣蜂鸣器功能封装 总结 前言 蜂鸣器在生活中的应用实则相当广泛。通过本章你将学会制造噪声 &#xff08;笑~&#xff09;你将学会驱动它们&#xff0c;并发出响声。 硬件介绍 PWM基础 占空比…

shell bash中设置命令set

1 Preface/Foreword set命令用于shell脚本在执行命令时候&#xff0c;遇到异常的处理机制。 2 Usage 2.1 set -e 当执行命令过程中遇到异常&#xff0c;那么就退出脚本&#xff0c;不会往下执行其它命令。 #!/bin/bash #set -eroot GIT_TAG${CI_BUILD_TAG-NOTAG} GIT_REV…

设计模式之建造者模式与原型模式

目录 建造者模式 简介 使用场景 优缺点 模式结构 实现 原型模式 简介 应用场景 优缺点 模式结构 实现 建造者模式 简介 将复杂对象的构建与表示进行分离&#xff0c;使得同样的构建过程可以创建不同的表示。是一个将复杂的对象分解为多个简单的对象&#xff0c;然…