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