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
qwindowscamera.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
4#include "qwindowscamera_p.h"
5#include "qsemaphore.h"
6#include "qmutex.h"
7
8#include <private/qmemoryvideobuffer_p.h>
9#include <private/qwindowsmfdefs_p.h>
10#include <private/qwindowsmultimediautils_p.h>
11#include <private/qcomobject_p.h>
12
13#include <mfapi.h>
14#include <mfidl.h>
15#include <mferror.h>
16#include <mfreadwrite.h>
17
18#include <system_error>
19
21
22using namespace QWindowsMultimediaUtils;
23
24class CameraReaderCallback : public QComObject<IMFSourceReaderCallback>
25{
26public:
27 //from IMFSourceReaderCallback
28 STDMETHODIMP OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample) override;
29 STDMETHODIMP OnFlush(DWORD) override;
30 STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) override { return S_OK; }
31
32 void setActiveCamera(ActiveCamera *activeCamera)
33 {
34 QMutexLocker locker(&m_mutex);
35 m_activeCamera = activeCamera;
36 }
37private:
38 // Destructor is not public. Caller should call Release.
39 ~CameraReaderCallback() override = default;
40
41 ActiveCamera *m_activeCamera = nullptr;
42 QMutex m_mutex;
43};
44
45static ComPtr<IMFSourceReader> createCameraReader(IMFMediaSource *mediaSource,
46 const ComPtr<CameraReaderCallback> &callback)
47{
48 ComPtr<IMFSourceReader> sourceReader;
49 ComPtr<IMFAttributes> readerAttributes;
50
51 HRESULT hr = MFCreateAttributes(readerAttributes.GetAddressOf(), 1);
52 if (SUCCEEDED(hr)) {
53 hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback.Get());
54 if (SUCCEEDED(hr)) {
55 hr = MFCreateSourceReaderFromMediaSource(mediaSource, readerAttributes.Get(), sourceReader.GetAddressOf());
56 if (SUCCEEDED(hr))
57 return sourceReader;
58 }
59 }
60
61 qWarning() << "Failed to create camera IMFSourceReader" << hr;
62 return sourceReader;
63}
64
65static ComPtr<IMFMediaSource> createCameraSource(const QString &deviceId)
66{
67 ComPtr<IMFMediaSource> mediaSource;
68 ComPtr<IMFAttributes> sourceAttributes;
69 HRESULT hr = MFCreateAttributes(sourceAttributes.GetAddressOf(), 2);
70 if (SUCCEEDED(hr)) {
71 hr = sourceAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, QMM_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
72 if (SUCCEEDED(hr)) {
73 hr = sourceAttributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
74 reinterpret_cast<LPCWSTR>(deviceId.utf16()));
75 if (SUCCEEDED(hr)) {
76 hr = MFCreateDeviceSource(sourceAttributes.Get(), mediaSource.GetAddressOf());
77 if (SUCCEEDED(hr))
78 return mediaSource;
79 }
80 }
81 }
82 qWarning() << "Failed to create camera IMFMediaSource" << hr;
83 return mediaSource;
84}
85
86static int calculateVideoFrameStride(IMFMediaType *videoType, int width)
87{
88 Q_ASSERT(videoType);
89
90 GUID subtype = GUID_NULL;
91 HRESULT hr = videoType->GetGUID(MF_MT_SUBTYPE, &subtype);
92 if (SUCCEEDED(hr)) {
93 LONG stride = 0;
94 hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &stride);
95 if (SUCCEEDED(hr))
96 return int(qAbs(stride));
97 }
98
99 qWarning() << "Failed to calculate video stride" << errorString(hr);
100 return 0;
101}
102
103static bool setCameraReaderFormat(IMFSourceReader *sourceReader, IMFMediaType *videoType)
104{
105 Q_ASSERT(sourceReader);
106 Q_ASSERT(videoType);
107
108 HRESULT hr = sourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, nullptr,
109 videoType);
110 if (FAILED(hr))
111 qWarning() << "Failed to set video format" << errorString(hr);
112
113 return SUCCEEDED(hr);
114}
115
116static ComPtr<IMFMediaType> findVideoType(IMFSourceReader *reader,
117 const QCameraFormat &format)
118{
119 for (DWORD i = 0;; ++i) {
120 ComPtr<IMFMediaType> candidate;
121 HRESULT hr = reader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, i,
122 candidate.GetAddressOf());
123 if (FAILED(hr))
124 break;
125
126 GUID subtype = GUID_NULL;
127 if (FAILED(candidate->GetGUID(MF_MT_SUBTYPE, &subtype)))
128 continue;
129
130 if (format.pixelFormat() != pixelFormatFromMediaSubtype(subtype))
131 continue;
132
133 UINT32 width = 0u;
134 UINT32 height = 0u;
135 if (FAILED(MFGetAttributeSize(candidate.Get(), MF_MT_FRAME_SIZE, &width, &height)))
136 continue;
137
138 if (format.resolution() != QSize{ int(width), int(height) })
139 continue;
140
141 return candidate;
142 }
143 return {};
144}
145
147public:
148 static std::unique_ptr<ActiveCamera> create(QWindowsCamera &wc, const QCameraDevice &device, const QCameraFormat &format)
149 {
150 auto ac = std::unique_ptr<ActiveCamera>(new ActiveCamera(wc));
151 ac->m_source = createCameraSource(device.id());
152 if (!ac->m_source)
153 return {};
154
155 ac->m_readerCallback = makeComObject<CameraReaderCallback>();
156 ac->m_readerCallback->setActiveCamera(ac.get());
157 ac->m_reader = createCameraReader(ac->m_source.Get(), ac->m_readerCallback);
158 if (!ac->m_reader)
159 return {};
160
161 if (!ac->setFormat(format))
162 return {};
163
164 return ac;
165 }
166
168 {
169 flush();
170
171 auto videoType = findVideoType(m_reader.Get(), format);
172 if (videoType) {
173 if (setCameraReaderFormat(m_reader.Get(), videoType.Get())) {
174 m_frameFormat = { format.resolution(), format.pixelFormat() };
175 m_videoFrameStride =
176 calculateVideoFrameStride(videoType.Get(), format.resolution().width());
177 }
178 }
179
180 m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, nullptr, nullptr, nullptr,
181 nullptr);
182 return true;
183 }
184
185 void onReadSample(HRESULT status, LONGLONG timestamp, IMFSample *sample)
186 {
187 if (FAILED(status)) {
188 const std::string msg{ std::system_category().message(status) };
190 return;
191 }
192
193 if (sample) {
194 ComPtr<IMFMediaBuffer> mediaBuffer;
195 if (SUCCEEDED(sample->ConvertToContiguousBuffer(mediaBuffer.GetAddressOf()))) {
196
197 DWORD bufLen = 0;
198 BYTE *buffer = nullptr;
199 if (SUCCEEDED(mediaBuffer->Lock(&buffer, nullptr, &bufLen))) {
200 QByteArray bytes(reinterpret_cast<char*>(buffer), qsizetype(bufLen));
201 QVideoFrame frame(new QMemoryVideoBuffer(bytes, m_videoFrameStride), m_frameFormat);
202
203 // WMF uses 100-nanosecond units, Qt uses microseconds
204 frame.setStartTime(timestamp / 10);
205
206 LONGLONG duration = -1;
207 if (SUCCEEDED(sample->GetSampleDuration(&duration)))
208 frame.setEndTime((timestamp + duration) / 10);
209
210 emit m_windowsCamera.newVideoFrame(frame);
211 mediaBuffer->Unlock();
212 }
213 }
214 }
215
216 m_reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, nullptr,
217 nullptr, nullptr, nullptr);
218 }
219
220 void onFlush()
221 {
222 m_flushWait.release();
223 }
224
226 {
227 flush();
228 m_readerCallback->setActiveCamera(nullptr);
229 }
230
231private:
232 explicit ActiveCamera(QWindowsCamera &wc) : m_windowsCamera(wc), m_flushWait(0) {};
233
234 void flush()
235 {
236 if (SUCCEEDED(m_reader->Flush(MF_SOURCE_READER_FIRST_VIDEO_STREAM))) {
237 m_flushWait.acquire();
238 }
239 }
240
241 QWindowsCamera &m_windowsCamera;
242
243 QSemaphore m_flushWait;
244
245 ComPtr<IMFMediaSource> m_source;
246 ComPtr<IMFSourceReader> m_reader;
247 ComPtr<CameraReaderCallback> m_readerCallback;
248
249 QVideoFrameFormat m_frameFormat;
250 int m_videoFrameStride = 0;
251};
252
253STDMETHODIMP CameraReaderCallback::OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample)
254{
255 QMutexLocker locker(&m_mutex);
256 if (m_activeCamera)
257 m_activeCamera->onReadSample(status, timestamp, sample);
258
259 return status;
260}
261
263{
264 QMutexLocker locker(&m_mutex);
265 if (m_activeCamera)
266 m_activeCamera->onFlush();
267 return S_OK;
268}
269
275
280
282{
283 if (bool(m_active) == active)
284 return;
285
286 if (active) {
287 if (m_cameraDevice.isNull())
288 return;
289
290 if (m_cameraFormat.isNull())
291 m_cameraFormat = findBestCameraFormat(m_cameraDevice);
292
293 m_active = ActiveCamera::create(*this, m_cameraDevice, m_cameraFormat);
294 if (m_active)
295 activeChanged(true);
296
297 } else {
298 m_active.reset();
299 emit activeChanged(false);
300 }
301}
302
304{
305 bool active = bool(m_active);
306 if (active)
307 setActive(false);
308 m_cameraDevice = camera;
309 m_cameraFormat = {};
310 if (active)
311 setActive(true);
312}
313
315{
316 if (format.isNull())
317 return false;
318
319 bool ok = m_active ? m_active->setFormat(format) : true;
320 if (ok)
321 m_cameraFormat = format;
322
323 return ok;
324}
325
IOBluetoothDevice * device
bool setFormat(const QCameraFormat &format)
static std::unique_ptr< ActiveCamera > create(QWindowsCamera &wc, const QCameraDevice &device, const QCameraFormat &format)
void onReadSample(HRESULT status, LONGLONG timestamp, IMFSample *sample)
STDMETHODIMP OnReadSample(HRESULT status, DWORD, DWORD, LONGLONG timestamp, IMFSample *sample) override
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) override
STDMETHODIMP OnFlush(DWORD) override
void setActiveCamera(ActiveCamera *activeCamera)
~CameraReaderCallback() override=default
\inmodule QtCore
Definition qbytearray.h:57
The QCameraDevice class provides general information about camera devices.
bool isNull() const
Returns true if this QCameraDevice is null or invalid.
The QCameraFormat class describes a video format supported by a camera device. \inmodule QtMultimedia...
bool isNull() const noexcept
Returns true if this is a default constructed QCameraFormat.
The QCamera class provides interface for system camera devices.
Definition qcamera.h:28
QCameraDevice cameraDevice
\qmlproperty cameraDevice QtMultimedia::Camera::cameraDevice
Definition qcamera.h:32
@ CameraError
Definition qcamera.h:63
The QMemoryVideoBuffer class provides a system memory allocated video data buffer.
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
QCameraFormat findBestCameraFormat(const QCameraDevice &camera) const
void updateError(QCamera::Error error, const QString &errorString)
void newVideoFrame(const QVideoFrame &)
void activeChanged(bool)
\inmodule QtCore
Definition qsemaphore.h:18
void acquire(int n=1)
Tries to acquire n resources guarded by the semaphore.
void release(int n=1)
Releases n resources guarded by the semaphore.
\inmodule QtCore
Definition qsize.h:25
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6995
static QString fromStdString(const std::string &s)
Definition qstring.h:1447
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
QVideoFrameFormat::PixelFormat pixelFormat() const
Returns the pixel format of frames in a video stream.
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:27
bool setCameraFormat(const QCameraFormat &) override
void setCamera(const QCameraDevice &camera) override
void setActive(bool active) override
QWindowsCamera(QCamera *parent)
~QWindowsCamera() override
QCamera * camera
Definition camera.cpp:19
static int calculateVideoFrameStride(IMFMediaType *videoType, int width)
static bool setCameraReaderFormat(IMFSourceReader *sourceReader, IMFMediaType *videoType)
static ComPtr< IMFSourceReader > createCameraReader(IMFMediaSource *mediaSource, const ComPtr< CameraReaderCallback > &callback)
static ComPtr< IMFMediaType > findVideoType(IMFSourceReader *reader, const QCameraFormat &format)
static ComPtr< IMFMediaSource > createCameraSource(const QString &deviceId)
Combined button and popup list for selecting options.
Q_MULTIMEDIA_EXPORT QString errorString(HRESULT hr)
Q_MULTIMEDIA_EXPORT QVideoFrameFormat::PixelFormat pixelFormatFromMediaSubtype(const GUID &subtype)
#define qWarning
Definition qlogging.h:166
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLint GLsizei GLsizei height
const void GLsizei GLsizei stride
GLenum GLuint buffer
GLint GLsizei width
GLint GLsizei GLsizei GLenum format
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
ptrdiff_t qsizetype
Definition qtypes.h:165
long HRESULT
const GUID QMM_MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
HRESULT WINAPI MFCreateDeviceSource(IMFAttributes *pAttributes, IMFMediaSource **ppSource)
QFrame frame
[0]