Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwindowsvideodevices.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <private/qcameradevice_p.h>
7#include <private/qwindowsmfdefs_p.h>
8#include <private/qwindowsmultimediautils_p.h>
9#include <private/qcomptr_p.h>
10#include <private/qcomtaskresource_p.h>
11
12#include <dbt.h>
13
14#include <mfapi.h>
15#include <mfreadwrite.h>
16#include <mferror.h>
17
19
20LRESULT QT_WIN_CALLBACK deviceNotificationWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
21{
22 if (message == WM_DEVICECHANGE) {
23 auto b = (PDEV_BROADCAST_HDR)lParam;
24 if (b && b->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
25 auto wmd = reinterpret_cast<QWindowsVideoDevices *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
26 if (wmd) {
27 if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE) {
28 emit wmd->videoInputsChanged();
29 }
30 }
31 }
32 }
33
34 return 1;
35}
36
37static const auto windowClassName = TEXT("QWindowsMediaDevicesMessageWindow");
38
40{
41 WNDCLASSEX wx = {};
42 wx.cbSize = sizeof(WNDCLASSEX);
43 wx.lpfnWndProc = deviceNotificationWndProc;
44 wx.hInstance = GetModuleHandle(nullptr);
45 wx.lpszClassName = windowClassName;
46
47 if (!RegisterClassEx(&wx))
48 return nullptr;
49
50 auto hwnd = CreateWindowEx(0, windowClassName, TEXT("Message"),
51 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr);
52 if (!hwnd) {
53 UnregisterClass(windowClassName, GetModuleHandle(nullptr));
54 return nullptr;
55 }
56
57 return hwnd;
58}
59
61 : QPlatformVideoDevices(integration)
62{
63 CoInitialize(nullptr);
64
65 m_videoDeviceMsgWindow = createMessageOnlyWindow();
66 if (m_videoDeviceMsgWindow) {
67 SetWindowLongPtr(m_videoDeviceMsgWindow, GWLP_USERDATA, (LONG_PTR)this);
68
69 DEV_BROADCAST_DEVICEINTERFACE di = {};
70 di.dbcc_size = sizeof(di);
71 di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
72 di.dbcc_classguid = QMM_KSCATEGORY_VIDEO_CAMERA;
73
74 m_videoDeviceNotification =
75 RegisterDeviceNotification(m_videoDeviceMsgWindow, &di, DEVICE_NOTIFY_WINDOW_HANDLE);
76 if (!m_videoDeviceNotification) {
77 DestroyWindow(m_videoDeviceMsgWindow);
78 m_videoDeviceMsgWindow = nullptr;
79
80 UnregisterClass(windowClassName, GetModuleHandle(nullptr));
81 }
82 }
83
84 if (!m_videoDeviceNotification) {
85 qWarning() << "Video device change notification disabled";
86 }
87}
88
90{
91 if (m_videoDeviceNotification) {
92 UnregisterDeviceNotification(m_videoDeviceNotification);
93 }
94
95 if (m_videoDeviceMsgWindow) {
96 DestroyWindow(m_videoDeviceMsgWindow);
97 UnregisterClass(windowClassName, GetModuleHandle(nullptr));
98 }
99
100 CoUninitialize();
101}
102
103static std::optional<QCameraFormat> createCameraFormat(IMFMediaType *mediaFormat)
104{
105 GUID subtype = GUID_NULL;
106 if (FAILED(mediaFormat->GetGUID(MF_MT_SUBTYPE, &subtype)))
107 return {};
108
110 if (pixelFormat == QVideoFrameFormat::Format_Invalid)
111 return {};
112
113 UINT32 width = 0u;
114 UINT32 height = 0u;
115 if (FAILED(MFGetAttributeSize(mediaFormat, MF_MT_FRAME_SIZE, &width, &height)))
116 return {};
117 QSize resolution{ int(width), int(height) };
118
119 UINT32 num = 0u;
120 UINT32 den = 0u;
121 float minFr = 0.f;
122 float maxFr = 0.f;
123
124 if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MIN, &num, &den)))
125 minFr = float(num) / float(den);
126
127 if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MAX, &num, &den)))
128 maxFr = float(num) / float(den);
129
130 auto *f = new QCameraFormatPrivate{ QSharedData(), pixelFormat, resolution, minFr, maxFr };
131 return f->create();
132}
133
134static QString getString(IMFActivate *device, const IID &id)
135{
136 QComTaskResource<WCHAR> str;
137 UINT32 length = 0;
138 HRESULT hr = device->GetAllocatedString(id, str.address(), &length);
139 if (SUCCEEDED(hr)) {
140 return QString::fromWCharArray(str.get());
141 } else {
142 return {};
143 }
144}
145
146static std::optional<QCameraDevice> createCameraDevice(IMFActivate *device)
147{
148 auto info = std::make_unique<QCameraDevicePrivate>();
149 info->description = getString(device, MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME);
150 info->id = getString(device, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK).toUtf8();
151
152 IMFMediaSource *source = NULL;
153 HRESULT hr = device->ActivateObject(IID_PPV_ARGS(&source));
154 if (FAILED(hr))
155 return {};
156
157 ComPtr<IMFSourceReader> reader;
158 hr = MFCreateSourceReaderFromMediaSource(source, NULL, reader.GetAddressOf());
159 if (FAILED(hr))
160 return {};
161
162 QList<QSize> photoResolutions;
163 QList<QCameraFormat> videoFormats;
164 for (DWORD i = 0;; ++i) {
165 // Loop through the supported formats for the video device
166 ComPtr<IMFMediaType> mediaFormat;
167 hr = reader->GetNativeMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, i,
168 mediaFormat.GetAddressOf());
169 if (FAILED(hr))
170 break;
171
172 auto maybeCamera = createCameraFormat(mediaFormat.Get());
173 if (maybeCamera) {
174 videoFormats << *maybeCamera;
175 photoResolutions << maybeCamera->resolution();
176 }
177 }
178
179 info->videoFormats = videoFormats;
180 info->photoResolutions = photoResolutions;
181 return info.release()->create();
182}
183
184static QList<QCameraDevice> readCameraDevices(IMFAttributes *attr)
185{
186 QList<QCameraDevice> cameras;
187 UINT32 count = 0;
188 IMFActivate **devicesRaw = nullptr;
189 HRESULT hr = MFEnumDeviceSources(attr, &devicesRaw, &count);
190 if (SUCCEEDED(hr)) {
191 QComTaskResource<IMFActivate *[], QComDeleter> devices(devicesRaw, count);
192
193 for (UINT32 i = 0; i < count; i++) {
194 IMFActivate *device = devices[i];
195 if (device) {
196 auto maybeCamera = createCameraDevice(device);
197 if (maybeCamera)
198 cameras << *maybeCamera;
199 }
200 }
201 }
202 return cameras;
203}
204
205QList<QCameraDevice> QWindowsVideoDevices::videoDevices() const
206{
207 QList<QCameraDevice> cameras;
208
209 ComPtr<IMFAttributes> attr;
210 HRESULT hr = MFCreateAttributes(attr.GetAddressOf(), 2);
211 if (FAILED(hr))
212 return {};
213
214 hr = attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
215 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
216 if (SUCCEEDED(hr)) {
217 cameras << readCameraDevices(attr.Get());
218
219 hr = attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_CATEGORY,
221 if (SUCCEEDED(hr))
222 cameras << readCameraDevices(attr.Get());
223 }
224
225 return cameras;
226}
227
IOBluetoothDevice * device
\inmodule QtCore
Definition qshareddata.h:19
\inmodule QtCore
Definition qsize.h:25
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
QList< QCameraDevice > videoDevices() const override
QWindowsVideoDevices(QPlatformMediaIntegration *integration)
QString str
[2]
Combined button and popup list for selecting options.
Q_MULTIMEDIA_EXPORT QVideoFrameFormat::PixelFormat pixelFormatFromMediaSubtype(const GUID &subtype)
EGLDeviceEXT * devices
#define qWarning
Definition qlogging.h:166
GLboolean GLboolean GLboolean b
GLint GLsizei GLsizei height
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLint GLsizei width
GLuint GLsizei const GLchar * message
GLsizei GLsizei GLchar * source
GLuint num
#define emit
long HRESULT
const GUID QMM_KSCATEGORY_VIDEO_CAMERA
const GUID QMM_KSCATEGORY_SENSOR_CAMERA
static QString getString(IMFActivate *device, const IID &id)
QT_BEGIN_NAMESPACE LRESULT QT_WIN_CALLBACK deviceNotificationWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
static const auto windowClassName
static std::optional< QCameraFormat > createCameraFormat(IMFMediaType *mediaFormat)
static std::optional< QCameraDevice > createCameraDevice(IMFActivate *device)
static HWND createMessageOnlyWindow()
static QList< QCameraDevice > readCameraDevices(IMFAttributes *attr)
QHostInfo info
[0]