Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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 <QtCore/quuid.h>
7#include <QtCore/private/qcomptr_p.h>
8#include <QtCore/private/qfunctions_win_p.h>
9#include <QtMultimedia/private/qcameradevice_p.h>
10#include <QtMultimedia/private/qcomtaskresource_p.h>
11#include <QtMultimedia/private/qwindowsmultimediautils_p.h>
12
13#include <dbt.h>
14
15#include <mfapi.h>
16#include <mfidl.h>
17#include <mfreadwrite.h>
18#include <mferror.h>
19#include <ks.h>
20
21// MinGW-13.1 workaround
22#ifndef KSCATEGORY_SENSOR_CAMERA
23static constexpr GUID KSCATEGORY_SENSOR_CAMERA = {
24 0x24e552d7, 0x6523, 0x47f7, { 0xa6, 0x47, 0xd3, 0x46, 0x5b, 0xf1, 0xf5, 0xca }
25};
26#endif
27
28QT_BEGIN_NAMESPACE
29
30namespace {
31
32LRESULT QT_WIN_CALLBACK deviceNotificationWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
33{
34 if (message == WM_DEVICECHANGE) {
35 auto b = (PDEV_BROADCAST_HDR)lParam;
36 if (b && b->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
37 auto wmd = reinterpret_cast<QWindowsVideoDevices *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
38 if (wmd) {
39 if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE) {
40 wmd->onVideoInputsChanged();
41 }
42 }
43 }
44 }
45
46 return 1;
47}
48
50{
51 // each Qt instance should have its own window class name to prevent name clashes when multiple
52 // qt instances are used in the same process.
53 // appending a uuid is good enough to ensure that.
54 static const std::wstring singleton =
55 QString(QStringLiteral("QWindowsMediaDevicesMessageWindow_")
56 + QUuid::createUuid().toString())
57 .toStdWString();
58 Q_ASSERT(singleton.size() < 256 && "The maximum length for lpszClassName is 256");
59 return singleton.c_str();
60}
61
63{
64 WNDCLASSEX wx = {};
65 wx.cbSize = sizeof(WNDCLASSEX);
66 wx.lpfnWndProc = deviceNotificationWndProc;
67 wx.hInstance = GetModuleHandle(nullptr);
68 wx.lpszClassName = getWindowsClassName();
69
70 if (!RegisterClassEx(&wx))
71 return nullptr;
72
73 auto hwnd = CreateWindowEx(0, getWindowsClassName(), TEXT("Message"), 0, 0, 0, 0, 0,
74 HWND_MESSAGE, nullptr, nullptr, nullptr);
75 if (!hwnd) {
76 UnregisterClass(getWindowsClassName(), GetModuleHandle(nullptr));
77 return nullptr;
78 }
79
80 return hwnd;
81}
82
83} // namespace
84
85QWindowsVideoDevices::QWindowsVideoDevices(QPlatformMediaIntegration *integration)
86 : QPlatformVideoDevices(integration)
87{
88 qt_win_ensureComInitializedOnThisThread();
89
90 m_videoDeviceMsgWindow = createMessageOnlyWindow();
91 if (m_videoDeviceMsgWindow) {
92 SetWindowLongPtr(m_videoDeviceMsgWindow, GWLP_USERDATA, (LONG_PTR)this);
93
94 DEV_BROADCAST_DEVICEINTERFACE di = {};
95 di.dbcc_size = sizeof(di);
96 di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
97 di.dbcc_classguid = KSCATEGORY_VIDEO_CAMERA;
98
99 m_videoDeviceNotification =
100 RegisterDeviceNotification(m_videoDeviceMsgWindow, &di, DEVICE_NOTIFY_WINDOW_HANDLE);
101 if (!m_videoDeviceNotification) {
102 DestroyWindow(m_videoDeviceMsgWindow);
103 m_videoDeviceMsgWindow = nullptr;
104
105 UnregisterClass(getWindowsClassName(), GetModuleHandle(nullptr));
106 }
107 }
108
109 if (!m_videoDeviceNotification) {
110 qWarning() << "Video device change notification disabled";
111 }
112}
113
115{
116 if (m_videoDeviceNotification) {
117 UnregisterDeviceNotification(m_videoDeviceNotification);
118 }
119
120 if (m_videoDeviceMsgWindow) {
121 DestroyWindow(m_videoDeviceMsgWindow);
122 UnregisterClass(getWindowsClassName(), GetModuleHandle(nullptr));
123 }
124}
125
126static std::optional<QCameraFormat> createCameraFormat(IMFMediaType *mediaFormat)
127{
128 GUID subtype = GUID_NULL;
129 if (FAILED(mediaFormat->GetGUID(MF_MT_SUBTYPE, &subtype)))
130 return {};
131
132 auto pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype);
133 if (pixelFormat == QVideoFrameFormat::Format_Invalid)
134 return {};
135
136 UINT32 nominalRange = 0;
137 auto colorRange = QVideoFrameFormat::ColorRange_Unknown;
138
139 if (SUCCEEDED(mediaFormat->GetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, &nominalRange)))
140 colorRange = QWindowsMultimediaUtils::colorRangeFromNominalRange(nominalRange);
141
142 UINT32 yuvMatrix = 0;
143 auto colorSpace = QVideoFrameFormat::ColorSpace_Undefined;
144
145 if (SUCCEEDED(mediaFormat->GetUINT32(MF_MT_YUV_MATRIX , &yuvMatrix)))
146 colorSpace = QWindowsMultimediaUtils::colorSpaceFromMatrix(yuvMatrix);
147
148 UINT32 width = 0u;
149 UINT32 height = 0u;
150 if (FAILED(MFGetAttributeSize(mediaFormat, MF_MT_FRAME_SIZE, &width, &height)))
151 return {};
152 QSize resolution{ int(width), int(height) };
153
154 UINT32 num = 0u;
155 UINT32 den = 0u;
156 float minFr = 0.f;
157 float maxFr = 0.f;
158
159 if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MIN, &num, &den)))
160 minFr = float(num) / float(den);
161
162 if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MAX, &num, &den)))
163 maxFr = float(num) / float(den);
164
165 auto *f = new QCameraFormatPrivate{ QSharedData(), pixelFormat, resolution, minFr, maxFr , colorRange, colorSpace };
166 return f->create();
167}
168
169static QString getString(IMFActivate *device, const IID &id)
170{
171 QComTaskResource<WCHAR> str;
172 UINT32 length = 0;
173 HRESULT hr = device->GetAllocatedString(id, str.address(), &length);
174 if (SUCCEEDED(hr)) {
175 return QString::fromWCharArray(str.get());
176 } else {
177 return {};
178 }
179}
180
181static std::optional<QCameraDevice> createCameraDevice(const QWindowsMediaFoundation &wmf,
182 IMFActivate *device)
183{
184 auto info = std::make_unique<QCameraDevicePrivate>();
185 info->description = getString(device, MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME);
186 info->id = getString(device, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK).toUtf8();
187
188 IMFMediaSource *source = NULL;
189 HRESULT hr = device->ActivateObject(IID_PPV_ARGS(&source));
190 if (FAILED(hr))
191 return {};
192
193 ComPtr<IMFSourceReader> reader;
194 hr = wmf.mfCreateSourceReaderFromMediaSource(source, NULL, reader.GetAddressOf());
195 if (FAILED(hr))
196 return {};
197
198 QList<QSize> photoResolutions;
199 QList<QCameraFormat> videoFormats;
200 for (DWORD i = 0;; ++i) {
201 // Loop through the supported formats for the video device
202 ComPtr<IMFMediaType> mediaFormat;
203 hr = reader->GetNativeMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, i,
204 mediaFormat.GetAddressOf());
205 if (FAILED(hr))
206 break;
207
208 auto maybeCamera = createCameraFormat(mediaFormat.Get());
209 if (maybeCamera) {
210 videoFormats << *maybeCamera;
211 photoResolutions << maybeCamera->resolution();
212 }
213 }
214
215 info->videoFormats = videoFormats;
216 info->photoResolutions = photoResolutions;
217 return info.release()->create();
218}
219
220static QList<QCameraDevice> readCameraDevices(const QWindowsMediaFoundation &wmf,
221 IMFAttributes *attr)
222{
223 QList<QCameraDevice> cameras;
224 UINT32 count = 0;
225 IMFActivate **devicesRaw = nullptr;
226 HRESULT hr = wmf.mfEnumDeviceSources(attr, &devicesRaw, &count);
227 if (SUCCEEDED(hr)) {
228 QComTaskResource<IMFActivate *[], QComDeleter> devices(devicesRaw, count);
229
230 for (UINT32 i = 0; i < count; i++) {
231 IMFActivate *device = devices[i];
232 if (device) {
233 auto maybeCamera = createCameraDevice(wmf, device);
234 if (maybeCamera)
235 cameras << *maybeCamera;
236 }
237 }
238 }
239 return cameras;
240}
241
243{
244 if (!m_wmf)
245 return {};
246
247 QList<QCameraDevice> cameras;
248
249 ComPtr<IMFAttributes> attr;
250 HRESULT hr = m_wmf->mfCreateAttributes(attr.GetAddressOf(), 2);
251 if (FAILED(hr))
252 return {};
253
254 hr = attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
255 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
256 if (SUCCEEDED(hr)) {
257 cameras << readCameraDevices(*m_wmf, attr.Get());
258
259 hr = attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_CATEGORY,
260 KSCATEGORY_SENSOR_CAMERA);
261 if (SUCCEEDED(hr))
262 cameras << readCameraDevices(*m_wmf, attr.Get());
263 }
264
265 return cameras;
266}
267
268QT_END_NAMESPACE
Q_MULTIMEDIA_EXPORT ~QWindowsVideoDevices()
QList< QCameraDevice > findVideoInputs() const override
static QString getString(IMFActivate *device, const IID &id)
static std::optional< QCameraFormat > createCameraFormat(IMFMediaType *mediaFormat)
static QList< QCameraDevice > readCameraDevices(const QWindowsMediaFoundation &wmf, IMFAttributes *attr)
static std::optional< QCameraDevice > createCameraDevice(const QWindowsMediaFoundation &wmf, IMFActivate *device)