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 <private/qcameradevice_p.h>
7#include <private/qwindowsmultimediautils_p.h>
8#include <QtCore/private/qcomptr_p.h>
9#include <private/qcomtaskresource_p.h>
10
11#include <dbt.h>
12
13#include <initguid.h>
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
29LRESULT QT_WIN_CALLBACK deviceNotificationWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
30{
31 if (message == WM_DEVICECHANGE) {
32 auto b = (PDEV_BROADCAST_HDR)lParam;
33 if (b && b->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
34 auto wmd = reinterpret_cast<QWindowsVideoDevices *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
35 if (wmd) {
36 if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE) {
37 wmd->onVideoInputsChanged();
38 }
39 }
40 }
41 }
42
43 return 1;
45
46static const auto windowClassName = TEXT("QWindowsMediaDevicesMessageWindow");
47
49{
50 WNDCLASSEX wx = {};
51 wx.cbSize = sizeof(WNDCLASSEX);
52 wx.lpfnWndProc = deviceNotificationWndProc;
53 wx.hInstance = GetModuleHandle(nullptr);
54 wx.lpszClassName = windowClassName;
55
56 if (!RegisterClassEx(&wx))
57 return nullptr;
58
59 auto hwnd = CreateWindowEx(0, windowClassName, TEXT("Message"),
60 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr);
61 if (!hwnd) {
62 UnregisterClass(windowClassName, GetModuleHandle(nullptr));
63 return nullptr;
64 }
65
66 return hwnd;
67}
68
69QWindowsVideoDevices::QWindowsVideoDevices(QPlatformMediaIntegration *integration)
70 : QPlatformVideoDevices(integration)
71{
72 m_videoDeviceMsgWindow = createMessageOnlyWindow();
73 if (m_videoDeviceMsgWindow) {
74 SetWindowLongPtr(m_videoDeviceMsgWindow, GWLP_USERDATA, (LONG_PTR)this);
75
76 DEV_BROADCAST_DEVICEINTERFACE di = {};
77 di.dbcc_size = sizeof(di);
78 di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
79 di.dbcc_classguid = KSCATEGORY_VIDEO_CAMERA;
80
81 m_videoDeviceNotification =
82 RegisterDeviceNotification(m_videoDeviceMsgWindow, &di, DEVICE_NOTIFY_WINDOW_HANDLE);
83 if (!m_videoDeviceNotification) {
84 DestroyWindow(m_videoDeviceMsgWindow);
85 m_videoDeviceMsgWindow = nullptr;
86
87 UnregisterClass(windowClassName, GetModuleHandle(nullptr));
88 }
89 }
90
91 if (!m_videoDeviceNotification) {
92 qWarning() << "Video device change notification disabled";
93 }
94}
95
97{
98 if (m_videoDeviceNotification) {
99 UnregisterDeviceNotification(m_videoDeviceNotification);
100 }
101
102 if (m_videoDeviceMsgWindow) {
103 DestroyWindow(m_videoDeviceMsgWindow);
104 UnregisterClass(windowClassName, GetModuleHandle(nullptr));
105 }
106}
107
108static std::optional<QCameraFormat> createCameraFormat(IMFMediaType *mediaFormat)
109{
110 GUID subtype = GUID_NULL;
111 if (FAILED(mediaFormat->GetGUID(MF_MT_SUBTYPE, &subtype)))
112 return {};
113
114 auto pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype);
115 if (pixelFormat == QVideoFrameFormat::Format_Invalid)
116 return {};
117
118 UINT32 width = 0u;
119 UINT32 height = 0u;
120 if (FAILED(MFGetAttributeSize(mediaFormat, MF_MT_FRAME_SIZE, &width, &height)))
121 return {};
122 QSize resolution{ int(width), int(height) };
123
124 UINT32 num = 0u;
125 UINT32 den = 0u;
126 float minFr = 0.f;
127 float maxFr = 0.f;
128
129 if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MIN, &num, &den)))
130 minFr = float(num) / float(den);
131
132 if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MAX, &num, &den)))
133 maxFr = float(num) / float(den);
134
135 auto *f = new QCameraFormatPrivate{ QSharedData(), pixelFormat, resolution, minFr, maxFr };
136 return f->create();
137}
138
139static QString getString(IMFActivate *device, const IID &id)
140{
141 QComTaskResource<WCHAR> str;
142 UINT32 length = 0;
143 HRESULT hr = device->GetAllocatedString(id, str.address(), &length);
144 if (SUCCEEDED(hr)) {
145 return QString::fromWCharArray(str.get());
146 } else {
147 return {};
148 }
149}
150
151static std::optional<QCameraDevice> createCameraDevice(const QWindowsMediaFoundation &wmf,
152 IMFActivate *device)
153{
154 auto info = std::make_unique<QCameraDevicePrivate>();
155 info->description = getString(device, MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME);
156 info->id = getString(device, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK).toUtf8();
157
158 IMFMediaSource *source = NULL;
159 HRESULT hr = device->ActivateObject(IID_PPV_ARGS(&source));
160 if (FAILED(hr))
161 return {};
162
163 ComPtr<IMFSourceReader> reader;
164 hr = wmf.mfCreateSourceReaderFromMediaSource(source, NULL, reader.GetAddressOf());
165 if (FAILED(hr))
166 return {};
167
168 QList<QSize> photoResolutions;
169 QList<QCameraFormat> videoFormats;
170 for (DWORD i = 0;; ++i) {
171 // Loop through the supported formats for the video device
172 ComPtr<IMFMediaType> mediaFormat;
173 hr = reader->GetNativeMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, i,
174 mediaFormat.GetAddressOf());
175 if (FAILED(hr))
176 break;
177
178 auto maybeCamera = createCameraFormat(mediaFormat.Get());
179 if (maybeCamera) {
180 videoFormats << *maybeCamera;
181 photoResolutions << maybeCamera->resolution();
182 }
183 }
184
185 info->videoFormats = videoFormats;
186 info->photoResolutions = photoResolutions;
187 return info.release()->create();
188}
189
190static QList<QCameraDevice> readCameraDevices(const QWindowsMediaFoundation &wmf,
191 IMFAttributes *attr)
192{
193 QList<QCameraDevice> cameras;
194 UINT32 count = 0;
195 IMFActivate **devicesRaw = nullptr;
196 HRESULT hr = wmf.mfEnumDeviceSources(attr, &devicesRaw, &count);
197 if (SUCCEEDED(hr)) {
198 QComTaskResource<IMFActivate *[], QComDeleter> devices(devicesRaw, count);
199
200 for (UINT32 i = 0; i < count; i++) {
201 IMFActivate *device = devices[i];
202 if (device) {
203 auto maybeCamera = createCameraDevice(wmf, device);
204 if (maybeCamera)
205 cameras << *maybeCamera;
206 }
207 }
208 }
209 return cameras;
210}
211
213{
214 if (!m_wmf)
215 return {};
216
217 QList<QCameraDevice> cameras;
218
219 ComPtr<IMFAttributes> attr;
220 HRESULT hr = m_wmf->mfCreateAttributes(attr.GetAddressOf(), 2);
221 if (FAILED(hr))
222 return {};
223
224 hr = attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
225 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
226 if (SUCCEEDED(hr)) {
227 cameras << readCameraDevices(*m_wmf, attr.Get());
228
229 hr = attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_CATEGORY,
230 KSCATEGORY_SENSOR_CAMERA);
231 if (SUCCEEDED(hr))
232 cameras << readCameraDevices(*m_wmf, attr.Get());
233 }
234
235 return cameras;
236}
237
238QT_END_NAMESPACE
Q_MULTIMEDIA_EXPORT ~QWindowsVideoDevices()
QList< QCameraDevice > findVideoInputs() const override
static QString getString(IMFActivate *device, const IID &id)
static const auto windowClassName
static std::optional< QCameraFormat > createCameraFormat(IMFMediaType *mediaFormat)
static QList< QCameraDevice > readCameraDevices(const QWindowsMediaFoundation &wmf, IMFAttributes *attr)
static HWND createMessageOnlyWindow()
static std::optional< QCameraDevice > createCameraDevice(const QWindowsMediaFoundation &wmf, IMFActivate *device)