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


    一、前言

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

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

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

    二、设备列表的获取

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

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

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

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

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

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

    1. struct CameraDevice
    2. {
    3. int nIndex; // index
    4. std::string uid; // 硬件层uniqueId mac中为BuiltInMicrophoneDevice
    5. std::string name; // 设备名称
    6. };
    7. std::map VideoCoreDevice::getVideoDeviceList()
    8. {
    9. if(!m_pDevEnum)
    10. {
    11. ::CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, IID_ICreateDevEnum, (void**)&m_pDevEnum);
    12. }
    13. std::map devices;
    14. if (!m_bCoUninitializeIsRequired)
    15. {
    16. goto END;
    17. }
    18. HRESULT hr = m_pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &m_pMonikerDevEnum, 0);
    19. if (hr != NOERROR)
    20. {
    21. std::cout << "CreateClassEnumerator failed" << std::endl;
    22. goto END;
    23. }
    24. m_pMonikerDevEnum->Reset();
    25. ULONG cFetched;
    26. IMoniker* pM;
    27. int index = 0;
    28. while (S_OK == m_pMonikerDevEnum->Next(1, &pM, &cFetched))
    29. {
    30. IPropertyBag* pBag;
    31. hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag);
    32. if (S_OK == hr)
    33. {
    34. // Find the description or friendly name.
    35. VARIANT varName;
    36. VariantInit(&varName);
    37. hr = pBag->Read(L"Description", &varName, 0);
    38. if (FAILED(hr))
    39. {
    40. hr = pBag->Read(L"FriendlyName", &varName, 0);
    41. }
    42. if (SUCCEEDED(hr))
    43. {
    44. // ignore all VFW drivers
    45. if ((wcsstr(varName.bstrVal, (L"(VFW)")) == NULL) &&
    46. (_wcsnicmp(varName.bstrVal, (L"Google Camera Adapter"), 21) != 0))
    47. {
    48. // Found a valid device.
    49. {
    50. char device_name[256] = { 0 };
    51. char unique_name[256] = { 0 };
    52. char product_name[256] = { 0 };
    53. int convResult = WideCharToMultiByte(CP_UTF8, 0, varName.bstrVal, -1, (char*)device_name, sizeof(device_name), NULL, NULL);
    54. if (convResult == 0)
    55. {
    56. std::cout << "WideCharToMultiByte failed" << std::endl;
    57. goto END;
    58. }
    59. hr = pBag->Read(L"DevicePath", &varName, 0);
    60. if (FAILED(hr))
    61. {
    62. strncpy_s((char*)unique_name, sizeof(unique_name),
    63. (char*)device_name, convResult);
    64. }
    65. else
    66. {
    67. convResult = WideCharToMultiByte(CP_UTF8, 0, varName.bstrVal, -1, (char*)unique_name, sizeof(unique_name), NULL, NULL);
    68. if (convResult == 0)
    69. {
    70. std::cout << "WideCharToMultiByte failed" << std::endl;
    71. goto END;
    72. }
    73. }
    74. GetProductId(unique_name, product_name, sizeof(product_name));
    75. CameraDevice camera;
    76. camera.nIndex = index;
    77. camera.name = device_name;
    78. camera.uid = unique_name;
    79. devices.insert(std::make_pair(camera.uid, camera));
    80. }
    81. ++index; // increase the number of valid devices
    82. }
    83. }
    84. VariantClear(&varName);
    85. pBag->Release();
    86. pM->Release();
    87. }
    88. }
    89. END:
    90. return devices;
    91. }
    92. // 不同获取方式得到的ID不一致,通过处理得到相同的ID
    93. void GetProductId(const char* devicePath, char* productUniqueIdUTF8, uint32_t productUniqueIdUTF8Length)
    94. {
    95. *productUniqueIdUTF8 = '\0';
    96. char* startPos = strstr((char*)devicePath, "\\\\?\\");
    97. if (!startPos)
    98. {
    99. strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);
    100. std::cout << "Failed to get the product Id" << std::endl;
    101. return;
    102. }
    103. startPos += 4;
    104. char* pos = strchr(startPos, '&');
    105. if (!pos || pos >= (char*)devicePath + strlen((char*)devicePath))
    106. {
    107. strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);
    108. std::cout << "Failed to get the product Id" << std::endl;
    109. return;
    110. }
    111. // Find the second occurrence.
    112. pos = strchr(pos + 1, '&');
    113. uint32_t bytesToCopy = (uint32_t)(pos - startPos);
    114. if (pos && (bytesToCopy < productUniqueIdUTF8Length) &&
    115. bytesToCopy <= kVideoCaptureProductIdLength)
    116. {
    117. strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length,
    118. (char*)startPos, bytesToCopy);
    119. }
    120. else
    121. {
    122. strncpy_s((char*)productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1);
    123. std::cout << "Failed to get the product Id" << std::endl;
    124. }
    125. }

    三、设备的插拔检测

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

    1. // 头文件
    2. #pragma once
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. class VideoNotificationClient : public QAbstractNativeEventFilter, public QWidget
    9. {
    10. public:
    11. class Listener
    12. {
    13. public:
    14. virtual void onDeviceAdded(const std::string& uid) = 0;
    15. virtual void onDeviceRemoved(const std::string& uid) = 0;
    16. };
    17. public:
    18. void initialized();
    19. void uninstallFilter();
    20. bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override;
    21. public:
    22. VideoNotificationClient(VideoNotificationClient::Listener* listener);
    23. ~VideoNotificationClient();
    24. private:
    25. bool m_bInitialized;
    26. QHash m_hSevNotifys;
    27. VideoNotificationClient::Listener* m_pListener;
    28. };
    1. //源文件
    2. #include "VideoNotificationClient.h"
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. //具体的设备guid如usbiodef需要initguid
    9. #include
    10. //USB设备
    11. //GUID_DEVINTERFACE_USB_DEVICE
    12. #include
    13. //HID人机交互设备-鼠标键盘等
    14. #include
    15. //GUID_DEVINTERFACE_KEYBOARD
    16. #include
    17. //GUID_DEVINTERFACE_MOUSE
    18. #include
    19. #include
    20. #pragma comment(lib, "user32.lib")
    21. #pragma comment(lib, "setupapi.lib")
    22. static const GUID GUID_DEVINTERFACE_LIST[] =
    23. {
    24. // GUID_DEVINTERFACE_CAMERA_DEVICE
    25. { 0x65E8773D, 0x8F56, 0x11D0, { 0xA3, 0xB9, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96 } },
    26. // GUID_DEVINTERFACE_USB_DEVICE
    27. { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } },
    28. // GUID_DEVINTERFACE_DISK
    29. { 0x53f56307, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } },
    30. // GUID_DEVINTERFACE_HID,
    31. { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } },
    32. // GUID_NDIS_LAN_CLASS
    33. { 0xad498944, 0x762f, 0x11d0, { 0x8d, 0xcb, 0x00, 0xc0, 0x4f, 0xc3, 0x35, 0x8c } },
    34. // GUID_DEVINTERFACE_COMPORT
    35. { 0x86e0d1e0, 0x8089, 0x11d0, { 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73 } },
    36. // GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR
    37. { 0x4D36E978, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } },
    38. // GUID_DEVINTERFACE_PARALLEL
    39. { 0x97F76EF0, 0xF883, 0x11D0, { 0xAF, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x84, 0x5C } },
    40. // GUID_DEVINTERFACE_PARCLASS
    41. { 0x811FC6A5, 0xF728, 0x11D0, { 0xA5, 0x37, 0x00, 0x00, 0xF8, 0x75, 0x3E, 0xD1 } }
    42. };
    43. VideoNotificationClient::VideoNotificationClient(VideoNotificationClient::Listener *listener)
    44. : QWidget(nullptr)
    45. , m_bInitialized(false)
    46. , m_pListener(listener)
    47. {
    48. this->hide();
    49. qApp->installNativeEventFilter(this);
    50. }
    51. VideoNotificationClient::~VideoNotificationClient()
    52. {
    53. uninstallFilter();
    54. qApp->removeNativeEventFilter(this);
    55. }
    56. void VideoNotificationClient::initialized()
    57. {
    58. HANDLE winid = (HANDLE)this->winId();
    59. if (!winid)
    60. {
    61. return;
    62. }
    63. //注册插拔事件
    64. HDEVNOTIFY hDevNotify;
    65. DEV_BROADCAST_DEVICEINTERFACE NotifacationFiler;
    66. ZeroMemory(&NotifacationFiler, sizeof(DEV_BROADCAST_DEVICEINTERFACE));
    67. NotifacationFiler.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    68. NotifacationFiler.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    69. for (int i = 0; i < sizeof(GUID_DEVINTERFACE_LIST) / sizeof(GUID); i++)
    70. {
    71. NotifacationFiler.dbcc_classguid = GUID_DEVINTERFACE_LIST[i];
    72. hDevNotify = RegisterDeviceNotification(winid, &NotifacationFiler, DEVICE_NOTIFY_WINDOW_HANDLE);
    73. if (!hDevNotify)
    74. {
    75. qDebug() << "注册失败" << endl;
    76. m_bInitialized = false;
    77. return;
    78. }
    79. m_hSevNotifys.insert(QUuid(NotifacationFiler.dbcc_classguid), hDevNotify);
    80. }
    81. m_bInitialized = true;
    82. }
    83. void VideoNotificationClient::uninstallFilter()
    84. {
    85. for (HDEVNOTIFY handle : qAsConst(m_hSevNotifys))
    86. {
    87. ::UnregisterDeviceNotification(handle);
    88. }
    89. m_hSevNotifys.clear();
    90. }
    91. bool VideoNotificationClient::nativeEventFilter(const QByteArray& eventType, void* message, long* result)
    92. {
    93. Q_UNUSED(result);
    94. MSG* msg = reinterpret_cast(message);
    95. int msgType = msg->message;
    96. if (msgType == WM_DEVICECHANGE)
    97. {
    98. PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)msg->lParam;
    99. switch (msg->wParam) {
    100. case DBT_DEVICEARRIVAL:
    101. if (lpdb->dbch_devicetype = DBT_DEVTYP_DEVICEINTERFACE)
    102. {
    103. PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
    104. GUID cameraGuid = { 0x65E8773D, 0x8F56, 0x11D0, { 0xA3, 0xB9, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96 } };
    105. if (cameraGuid == pDevInf->dbcc_classguid)
    106. {
    107. QString devicePath = QString::fromWCharArray(pDevInf->dbcc_name);
    108. QStringList parts = devicePath.split('#');
    109. if (parts.length() != 4)
    110. {
    111. qDebug() << "camera logic error";
    112. return false;
    113. }
    114. QString usbPortStr = parts[2];
    115. QStringList usbPortParts = usbPortStr.split('&');
    116. if (usbPortParts.length() != 4)
    117. {
    118. qDebug() << "camera logic error";
    119. return false;
    120. }
    121. if ("0000" != usbPortParts[3])
    122. {
    123. return false;
    124. }
    125. devicePath = devicePath.toLower();
    126. m_pListener->onDeviceAdded(devicePath.toStdString());
    127. //emit cameraPlugged(true, devicePath);
    128. }
    129. }
    130. break;
    131. case DBT_DEVICEREMOVECOMPLETE:
    132. if (lpdb->dbch_devicetype = DBT_DEVTYP_DEVICEINTERFACE)
    133. {
    134. PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
    135. GUID cameraGuid = { 0x65E8773D, 0x8F56, 0x11D0, { 0xA3, 0xB9, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96 } };
    136. if (cameraGuid == pDevInf->dbcc_classguid)
    137. {
    138. // USB拔出马上触发
    139. QString devicePath = QString::fromWCharArray(pDevInf->dbcc_name);
    140. QStringList parts = devicePath.split('#');
    141. if (parts.length() != 4)
    142. {
    143. qDebug() << "camera logic error";
    144. return false;
    145. }
    146. QString usbPortStr = parts[2];
    147. QStringList usbPortParts = usbPortStr.split('&');
    148. if (usbPortParts.length() != 4)
    149. {
    150. qDebug() << "camera logic error";
    151. return false;
    152. }
    153. if ("0000" != usbPortParts[3])
    154. {
    155. return false;
    156. }
    157. devicePath = devicePath.toLower();
    158. m_pListener->onDeviceRemoved(devicePath.toStdString());
    159. }
    160. }
    161. break;
    162. }
    163. }
    164. return false;
    165. }

    四、设备插拔库设计

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

    1. // camra
    2. struct CameraDevice
    3. {
    4. int nIndex = 0; // index
    5. std::string uid = ""; // 硬件层uniqueId mac中为BuiltInMicrophoneDevice
    6. std::string name = ""; // 设备名称
    7. };
    8. // IVideoCore
    9. class VIDEODEVICE_SHARED_EXPORT IVideoCore
    10. {
    11. public:
    12. static IVideoCore* getInstance();
    13. virtual void addListener(IVideoDeviceListner* listner) = 0;
    14. virtual void removeListener(IVideoDeviceListner* listner) = 0;
    15. virtual std::map getVideoDevicesList() = 0;
    16. };
    17. // IVideoDevice
    18. class VIDEODEVICE_SHARED_EXPORT IVideoDevice
    19. {
    20. public:
    21. virtual bool initlized() = 0;
    22. virtual std::map getVideoDevicesList() = 0;
    23. virtual void addListener(IVideoDeviceListner* listener) = 0;
    24. virtual void removeListener(IVideoDeviceListner* listener) = 0;
    25. };
    26. // IVideoDeviceListner
    27. class VIDEODEVICE_SHARED_EXPORT IVideoDeviceListner
    28. {
    29. public:
    30. virtual void onDeviceAdded(CameraDevice device) = 0;
    31. virtual void onDeviceRemoved(CameraDevice device) = 0;
    32. virtual void onDeviceListUpdate(std::map cameraList) = 0;
    33. };
    34. class VideoNotificationClient
    35. {
    36. public:
    37. class Listener
    38. {
    39. public:
    40. virtual void onDeviceAdded(const std::string& uid) = 0;
    41. virtual void onDeviceRemoved(const std::string& uid) = 0;
    42. };
    43. }

  • 相关阅读:
    MATLAB中polyval函数用法
    【PAT甲级】1073 Scientific Notation
    JVM概述和内存管理(未完待续)
    2.mysql--备份恢复
    Ubuntu MongoDB账户密码设置
    Linux命令系列之top——里面藏着很多鲜为人知的宝藏知识
    MySQL8小时连接超时断开问题
    基于R语言结构方程模型
    测试开发-celery框架详解
    【Django】网上蛋糕项目商城-商品分类
  • 原文地址:https://blog.csdn.net/qq_15821883/article/details/132671310