1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "qwinrtcameracontrol.h"
41 #include "qwinrtcameravideorenderercontrol.h"
42 #include "qwinrtvideodeviceselectorcontrol.h"
43 #include "qwinrtcameraimagecapturecontrol.h"
44 #include "qwinrtimageencodercontrol.h"
45 #include "qwinrtcameraflashcontrol.h"
46 #include "qwinrtcamerafocuscontrol.h"
47 #include "qwinrtcameralockscontrol.h"
48
49 #include <QtCore/qfunctions_winrt.h>
50 #include <QtCore/QMutex>
51 #include <QtCore/QPointer>
52 #include <QtGui/QGuiApplication>
53 #include <private/qeventdispatcher_winrt_p.h>
54
55 #include <functional>
56 #include <mfapi.h>
57 #include <mferror.h>
58 #include <mfidl.h>
59 #include <wrl.h>
60 #include <windows.devices.enumeration.h>
61 #include <windows.media.capture.h>
62 #include <windows.storage.streams.h>
63 #include <windows.media.devices.h>
64
65 #include <algorithm>
66
67 using namespace Microsoft::WRL;
68 using namespace Microsoft::WRL::Wrappers;
69 using namespace ABI::Windows::Devices::Enumeration;
70 using namespace ABI::Windows::Foundation;
71 using namespace ABI::Windows::Foundation::Collections;
72 using namespace ABI::Windows::Media;
73 using namespace ABI::Windows::Media::Capture;
74 using namespace ABI::Windows::Media::Devices;
75 using namespace ABI::Windows::Media::MediaProperties;
76 using namespace ABI::Windows::Storage::Streams;
77
78 QT_BEGIN_NAMESPACE
79
80 #define RETURN_VOID_AND_EMIT_ERROR(msg) \
81 if (FAILED(hr)) { \
82 emit error(QCamera::CameraError, qt_error_string(hr)); \
83 RETURN_VOID_IF_FAILED(msg); \
84 }
85
86 #define FOCUS_RECT_SIZE 0.01f
87 #define FOCUS_RECT_HALF_SIZE 0.005f // FOCUS_RECT_SIZE / 2
88 #define FOCUS_RECT_BOUNDARY 1.0f
89 #define FOCUS_RECT_POSITION_MIN 0.0f
90 #define FOCUS_RECT_POSITION_MAX 0.995f // FOCUS_RECT_BOUNDARY - FOCUS_RECT_HALF_SIZE
91 #define ASPECTRATIO_EPSILON 0.01f
92
93 Q_LOGGING_CATEGORY(lcMMCamera, "qt.mm.camera")
94
getMediaStreamResolutions(IMediaDeviceController * device,MediaStreamType type,IVectorView<IMediaEncodingProperties * > ** propertiesList,QVector<QSize> * resolutions)95 HRESULT getMediaStreamResolutions(IMediaDeviceController *device,
96 MediaStreamType type,
97 IVectorView<IMediaEncodingProperties *> **propertiesList,
98 QVector<QSize> *resolutions)
99 {
100 HRESULT hr;
101 hr = device->GetAvailableMediaStreamProperties(type, propertiesList);
102 Q_ASSERT_SUCCEEDED(hr);
103 quint32 listSize;
104 hr = (*propertiesList)->get_Size(&listSize);
105 Q_ASSERT_SUCCEEDED(hr);
106 resolutions->reserve(int(listSize));
107 for (quint32 index = 0; index < listSize; ++index) {
108 ComPtr<IMediaEncodingProperties> properties;
109 hr = (*propertiesList)->GetAt(index, &properties);
110 Q_ASSERT_SUCCEEDED(hr);
111 HString propertyType;
112 hr = properties->get_Type(propertyType.GetAddressOf());
113 Q_ASSERT_SUCCEEDED(hr);
114
115 const HStringReference videoRef = HString::MakeReference(L"Video");
116 const HStringReference imageRef = HString::MakeReference(L"Image");
117 if (propertyType == videoRef) {
118 ComPtr<IVideoEncodingProperties> videoProperties;
119 hr = properties.As(&videoProperties);
120 Q_ASSERT_SUCCEEDED(hr);
121 UINT32 width, height;
122 hr = videoProperties->get_Width(&width);
123 Q_ASSERT_SUCCEEDED(hr);
124 hr = videoProperties->get_Height(&height);
125 Q_ASSERT_SUCCEEDED(hr);
126 resolutions->append(QSize(int(width), int(height)));
127 } else if (propertyType == imageRef) {
128 ComPtr<IImageEncodingProperties> imageProperties;
129 hr = properties.As(&imageProperties);
130 Q_ASSERT_SUCCEEDED(hr);
131 UINT32 width, height;
132 hr = imageProperties->get_Width(&width);
133 Q_ASSERT_SUCCEEDED(hr);
134 hr = imageProperties->get_Height(&height);
135 Q_ASSERT_SUCCEEDED(hr);
136 resolutions->append(QSize(int(width), int(height)));
137 }
138 }
139 return resolutions->isEmpty() ? MF_E_INVALID_FORMAT : hr;
140 }
141
142 template<typename T, size_t typeSize> struct CustomPropertyValue;
143
propertyValueStatics()144 inline static ComPtr<IPropertyValueStatics> propertyValueStatics()
145 {
146 ComPtr<IPropertyValueStatics> valueStatics;
147 GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &valueStatics);
148 return valueStatics;
149 }
150
151 template <typename T>
152 struct CustomPropertyValue<T, 4>
153 {
createCustomPropertyValue154 static ComPtr<IReference<T>> create(T value)
155 {
156 ComPtr<IInspectable> propertyValueObject;
157 HRESULT hr = propertyValueStatics()->CreateUInt32(value, &propertyValueObject);
158 ComPtr<IReference<UINT32>> uint32Object;
159 Q_ASSERT_SUCCEEDED(hr);
160 hr = propertyValueObject.As(&uint32Object);
161 Q_ASSERT_SUCCEEDED(hr);
162 return reinterpret_cast<IReference<T> *>(uint32Object.Get());
163 }
164 };
165
166 template <typename T>
167 struct CustomPropertyValue<T, 8>
168 {
createCustomPropertyValue169 static ComPtr<IReference<T>> create(T value)
170 {
171 ComPtr<IInspectable> propertyValueObject;
172 HRESULT hr = propertyValueStatics()->CreateUInt64(value, &propertyValueObject);
173 ComPtr<IReference<UINT64>> uint64Object;
174 Q_ASSERT_SUCCEEDED(hr);
175 hr = propertyValueObject.As(&uint64Object);
176 Q_ASSERT_SUCCEEDED(hr);
177 return reinterpret_cast<IReference<T> *>(uint64Object.Get());
178 }
179 };
180
181 // Required camera point focus
182 class WindowsRegionOfInterestIterableIterator : public RuntimeClass<IIterator<RegionOfInterest *>>
183 {
184 public:
WindowsRegionOfInterestIterableIterator(const ComPtr<IRegionOfInterest> & item)185 explicit WindowsRegionOfInterestIterableIterator(const ComPtr<IRegionOfInterest> &item)
186 {
187 regionOfInterest = item;
188 }
get_Current(IRegionOfInterest ** current)189 HRESULT __stdcall get_Current(IRegionOfInterest **current)
190 {
191 *current = regionOfInterest.Detach();
192 return S_OK;
193 }
get_HasCurrent(boolean * hasCurrent)194 HRESULT __stdcall get_HasCurrent(boolean *hasCurrent)
195 {
196 *hasCurrent = true;
197 return S_OK;
198 }
MoveNext(boolean * hasCurrent)199 HRESULT __stdcall MoveNext(boolean *hasCurrent)
200 {
201 *hasCurrent = false;
202 return S_OK;
203 }
204 private:
205 ComPtr<IRegionOfInterest> regionOfInterest;
206 };
207
208 class WindowsRegionOfInterestIterable : public RuntimeClass<IIterable<RegionOfInterest *>>
209 {
210 public:
WindowsRegionOfInterestIterable(const ComPtr<IRegionOfInterest> & item)211 explicit WindowsRegionOfInterestIterable(const ComPtr<IRegionOfInterest> &item)
212 {
213 regionOfInterest = item;
214 }
First(IIterator<RegionOfInterest * > ** first)215 HRESULT __stdcall First(IIterator<RegionOfInterest *> **first)
216 {
217 ComPtr<WindowsRegionOfInterestIterableIterator> iterator = Make<WindowsRegionOfInterestIterableIterator>(regionOfInterest);
218 *first = iterator.Detach();
219 return S_OK;
220 }
221 private:
222 ComPtr<IRegionOfInterest> regionOfInterest;
223 };
224
225 class MediaStream : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, IMFStreamSink, IMFMediaEventGenerator, IMFMediaTypeHandler>
226 {
227 public:
MediaStream(IMFMediaType * type,IMFMediaSink * mediaSink,QWinRTCameraVideoRendererControl * videoRenderer)228 MediaStream(IMFMediaType *type, IMFMediaSink *mediaSink, QWinRTCameraVideoRendererControl *videoRenderer)
229 : m_type(type), m_sink(mediaSink), m_videoRenderer(videoRenderer)
230 {
231 Q_ASSERT(m_videoRenderer);
232
233 HRESULT hr;
234 hr = MFCreateEventQueue(&m_eventQueue);
235 Q_ASSERT_SUCCEEDED(hr);
236 hr = MFAllocateSerialWorkQueue(MFASYNC_CALLBACK_QUEUE_STANDARD, &m_workQueueId);
237 Q_ASSERT_SUCCEEDED(hr);
238 }
239
~MediaStream()240 ~MediaStream() override
241 {
242 QMutexLocker locker(&m_mutex);
243 m_eventQueue->Shutdown();
244 }
245
RequestSample()246 HRESULT RequestSample()
247 {
248 if (m_pendingSamples.load() < 3) {
249 m_pendingSamples.ref();
250 return QueueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, nullptr);
251 }
252 return S_OK;
253 }
254
GetEvent(DWORD flags,IMFMediaEvent ** event)255 HRESULT __stdcall GetEvent(DWORD flags, IMFMediaEvent **event) override
256 {
257 QMutexLocker locker(&m_mutex);
258 // Create an extra reference to avoid deadlock
259 ComPtr<IMFMediaEventQueue> eventQueue = m_eventQueue;
260 locker.unlock();
261
262 return eventQueue->GetEvent(flags, event);
263 }
264
BeginGetEvent(IMFAsyncCallback * callback,IUnknown * state)265 HRESULT __stdcall BeginGetEvent(IMFAsyncCallback *callback, IUnknown *state) override
266 {
267 QMutexLocker locker(&m_mutex);
268 HRESULT hr = m_eventQueue->BeginGetEvent(callback, state);
269 return hr;
270 }
271
EndGetEvent(IMFAsyncResult * result,IMFMediaEvent ** event)272 HRESULT __stdcall EndGetEvent(IMFAsyncResult *result, IMFMediaEvent **event) override
273 {
274 QMutexLocker locker(&m_mutex);
275 return m_eventQueue->EndGetEvent(result, event);
276 }
277
QueueEvent(MediaEventType eventType,const GUID & extendedType,HRESULT status,const PROPVARIANT * value)278 HRESULT __stdcall QueueEvent(MediaEventType eventType, const GUID &extendedType, HRESULT status, const PROPVARIANT *value) override
279 {
280 QMutexLocker locker(&m_mutex);
281 return m_eventQueue->QueueEventParamVar(eventType, extendedType, status, value);
282 }
283
GetMediaSink(IMFMediaSink ** mediaSink)284 HRESULT __stdcall GetMediaSink(IMFMediaSink **mediaSink) override
285 {
286 m_sink->AddRef();
287 *mediaSink = m_sink;
288 return S_OK;
289 }
290
GetIdentifier(DWORD * identifier)291 HRESULT __stdcall GetIdentifier(DWORD *identifier) override
292 {
293 *identifier = 0;
294 return S_OK;
295 }
296
GetMediaTypeHandler(IMFMediaTypeHandler ** handler)297 HRESULT __stdcall GetMediaTypeHandler(IMFMediaTypeHandler **handler) override
298 {
299 return QueryInterface(IID_PPV_ARGS(handler));
300 }
301
ProcessSample(IMFSample * sample)302 HRESULT __stdcall ProcessSample(IMFSample *sample) override
303 {
304 ComPtr<IMFMediaBuffer> buffer;
305 HRESULT hr = sample->GetBufferByIndex(0, &buffer);
306 RETURN_HR_IF_FAILED("Failed to get buffer from camera sample");
307 ComPtr<IMF2DBuffer> buffer2d;
308 hr = buffer.As(&buffer2d);
309 RETURN_HR_IF_FAILED("Failed to cast camera sample buffer to 2D buffer");
310
311 m_pendingSamples.deref();
312 m_videoRenderer->queueBuffer(buffer2d.Get());
313
314 return hr;
315 }
316
PlaceMarker(MFSTREAMSINK_MARKER_TYPE type,const PROPVARIANT * value,const PROPVARIANT * context)317 HRESULT __stdcall PlaceMarker(MFSTREAMSINK_MARKER_TYPE type, const PROPVARIANT *value, const PROPVARIANT *context) override
318 {
319 Q_UNUSED(type);
320 Q_UNUSED(value);
321 QueueEvent(MEStreamSinkMarker, GUID_NULL, S_OK, context);
322 return S_OK;
323 }
324
Flush()325 HRESULT __stdcall Flush() override
326 {
327 m_videoRenderer->discardBuffers();
328 m_pendingSamples.store(0);
329 return S_OK;
330 }
331
IsMediaTypeSupported(IMFMediaType * type,IMFMediaType **)332 HRESULT __stdcall IsMediaTypeSupported(IMFMediaType *type, IMFMediaType **) override
333 {
334 HRESULT hr;
335 GUID majorType;
336 hr = type->GetMajorType(&majorType);
337 Q_ASSERT_SUCCEEDED(hr);
338 if (!IsEqualGUID(majorType, MFMediaType_Video))
339 return MF_E_INVALIDMEDIATYPE;
340 return S_OK;
341 }
342
GetMediaTypeCount(DWORD * typeCount)343 HRESULT __stdcall GetMediaTypeCount(DWORD *typeCount) override
344 {
345 *typeCount = 1;
346 return S_OK;
347 }
348
GetMediaTypeByIndex(DWORD index,IMFMediaType ** type)349 HRESULT __stdcall GetMediaTypeByIndex(DWORD index, IMFMediaType **type) override
350 {
351 if (index == 0)
352 return m_type.CopyTo(type);
353 return E_BOUNDS;
354 }
355
SetCurrentMediaType(IMFMediaType * type)356 HRESULT __stdcall SetCurrentMediaType(IMFMediaType *type) override
357 {
358 if (FAILED(IsMediaTypeSupported(type, nullptr)))
359 return MF_E_INVALIDREQUEST;
360
361 m_type = type;
362 return S_OK;
363 }
364
GetCurrentMediaType(IMFMediaType ** type)365 HRESULT __stdcall GetCurrentMediaType(IMFMediaType **type) override
366 {
367 return m_type.CopyTo(type);
368 }
369
GetMajorType(GUID * majorType)370 HRESULT __stdcall GetMajorType(GUID *majorType) override
371 {
372 return m_type->GetMajorType(majorType);
373 }
374
375 private:
376 QMutex m_mutex;
377 ComPtr<IMFMediaType> m_type;
378 IMFMediaSink *m_sink;
379 ComPtr<IMFMediaEventQueue> m_eventQueue;
380 DWORD m_workQueueId;
381
382 QWinRTCameraVideoRendererControl *m_videoRenderer;
383 QAtomicInt m_pendingSamples;
384 };
385
386 class MediaSink : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, IMediaExtension, IMFMediaSink, IMFClockStateSink>
387 {
388 public:
MediaSink(IMediaEncodingProfile * encodingProfile,QWinRTCameraVideoRendererControl * videoRenderer)389 MediaSink(IMediaEncodingProfile *encodingProfile, QWinRTCameraVideoRendererControl *videoRenderer)
390 : m_videoRenderer(videoRenderer)
391 {
392 HRESULT hr;
393 ComPtr<IVideoEncodingProperties> videoProperties;
394 hr = encodingProfile->get_Video(&videoProperties);
395 RETURN_VOID_IF_FAILED("Failed to get video properties");
396 ComPtr<IMFMediaType> videoType;
397 hr = MFCreateMediaTypeFromProperties(videoProperties.Get(), &videoType);
398 RETURN_VOID_IF_FAILED("Failed to create video type");
399 m_stream = Make<MediaStream>(videoType.Get(), this, videoRenderer);
400 }
401
402 ~MediaSink() override = default;
403
RequestSample()404 HRESULT RequestSample()
405 {
406 return m_stream->RequestSample();
407 }
408
SetProperties(Collections::IPropertySet * configuration)409 HRESULT __stdcall SetProperties(Collections::IPropertySet *configuration) override
410 {
411 Q_UNUSED(configuration);
412 return E_NOTIMPL;
413 }
414
GetCharacteristics(DWORD * characteristics)415 HRESULT __stdcall GetCharacteristics(DWORD *characteristics) override
416 {
417 *characteristics = MEDIASINK_FIXED_STREAMS | MEDIASINK_RATELESS;
418 return S_OK;
419 }
420
AddStreamSink(DWORD streamSinkIdentifier,IMFMediaType * mediaType,IMFStreamSink ** streamSink)421 HRESULT __stdcall AddStreamSink(DWORD streamSinkIdentifier, IMFMediaType *mediaType, IMFStreamSink **streamSink) override
422 {
423 Q_UNUSED(streamSinkIdentifier);
424 Q_UNUSED(mediaType);
425 Q_UNUSED(streamSink);
426 return E_NOTIMPL;
427 }
428
RemoveStreamSink(DWORD streamSinkIdentifier)429 HRESULT __stdcall RemoveStreamSink(DWORD streamSinkIdentifier) override
430 {
431 Q_UNUSED(streamSinkIdentifier);
432 return E_NOTIMPL;
433 }
434
GetStreamSinkCount(DWORD * streamSinkCount)435 HRESULT __stdcall GetStreamSinkCount(DWORD *streamSinkCount) override
436 {
437 *streamSinkCount = 1;
438 return S_OK;
439 }
440
GetStreamSinkByIndex(DWORD index,IMFStreamSink ** streamSink)441 HRESULT __stdcall GetStreamSinkByIndex(DWORD index, IMFStreamSink **streamSink) override
442 {
443 if (index == 0)
444 return m_stream.CopyTo(streamSink);
445 return MF_E_INVALIDINDEX;
446 }
447
GetStreamSinkById(DWORD streamSinkIdentifier,IMFStreamSink ** streamSink)448 HRESULT __stdcall GetStreamSinkById(DWORD streamSinkIdentifier, IMFStreamSink **streamSink) override
449 {
450 // ID and index are always 0
451 HRESULT hr = GetStreamSinkByIndex(streamSinkIdentifier, streamSink);
452 return hr == MF_E_INVALIDINDEX ? MF_E_INVALIDSTREAMNUMBER : hr;
453 }
454
SetPresentationClock(IMFPresentationClock * presentationClock)455 HRESULT __stdcall SetPresentationClock(IMFPresentationClock *presentationClock) override
456 {
457 HRESULT hr = S_OK;
458 m_presentationClock = presentationClock;
459 if (m_presentationClock)
460 hr = m_presentationClock->AddClockStateSink(this);
461 return hr;
462 }
463
GetPresentationClock(IMFPresentationClock ** presentationClock)464 HRESULT __stdcall GetPresentationClock(IMFPresentationClock **presentationClock) override
465 {
466 return m_presentationClock.CopyTo(presentationClock);
467 }
468
Shutdown()469 HRESULT __stdcall Shutdown() override
470 {
471 m_stream->Flush();
472 scheduleSetActive(false);
473 return m_presentationClock ? m_presentationClock->Stop() : S_OK;
474 }
475
OnClockStart(MFTIME systemTime,LONGLONG clockStartOffset)476 HRESULT __stdcall OnClockStart(MFTIME systemTime, LONGLONG clockStartOffset) override
477 {
478 Q_UNUSED(systemTime);
479 Q_UNUSED(clockStartOffset);
480
481 scheduleSetActive(true);
482
483 return S_OK;
484 }
485
OnClockStop(MFTIME systemTime)486 HRESULT __stdcall OnClockStop(MFTIME systemTime) override
487 {
488 Q_UNUSED(systemTime);
489
490 scheduleSetActive(false);
491
492 return m_stream->QueueEvent(MEStreamSinkStopped, GUID_NULL, S_OK, nullptr);
493 }
494
OnClockPause(MFTIME systemTime)495 HRESULT __stdcall OnClockPause(MFTIME systemTime) override
496 {
497 Q_UNUSED(systemTime);
498
499 scheduleSetActive(false);
500
501 return m_stream->QueueEvent(MEStreamSinkPaused, GUID_NULL, S_OK, nullptr);
502 }
503
OnClockRestart(MFTIME systemTime)504 HRESULT __stdcall OnClockRestart(MFTIME systemTime) override
505 {
506 Q_UNUSED(systemTime);
507
508 scheduleSetActive(true);
509
510 return m_stream->QueueEvent(MEStreamSinkStarted, GUID_NULL, S_OK, nullptr);
511 }
512
OnClockSetRate(MFTIME systemTime,float rate)513 HRESULT __stdcall OnClockSetRate(MFTIME systemTime, float rate) override
514 {
515 Q_UNUSED(systemTime);
516 Q_UNUSED(rate);
517 return E_NOTIMPL;
518 }
519
520 private:
521
scheduleSetActive(bool active)522 inline void scheduleSetActive(bool active)
523 {
524 QMetaObject::invokeMethod(m_videoRenderer, "setActive", Qt::QueuedConnection, Q_ARG(bool, active));
525 }
526
527 ComPtr<MediaStream> m_stream;
528 ComPtr<IMFPresentationClock> m_presentationClock;
529
530 QWinRTCameraVideoRendererControl *m_videoRenderer;
531 };
532
533 class QWinRTCameraControlPrivate
534 {
535 public:
536 QCamera::State state;
537 QCamera::Status status;
538 QCamera::CaptureModes captureMode;
539
540 ComPtr<IMediaCapture> capture;
541 ComPtr<IMediaCaptureVideoPreview> capturePreview;
542 EventRegistrationToken captureFailedCookie;
543 EventRegistrationToken recordLimitationCookie;
544
545 ComPtr<IMediaEncodingProfileStatics> encodingProfileFactory;
546
547 ComPtr<IMediaEncodingProfile> encodingProfile;
548 ComPtr<MediaSink> mediaSink;
549 ComPtr<IFocusControl> focusControl;
550 ComPtr<IRegionsOfInterestControl> regionsOfInterestControl;
551 ComPtr<IAsyncAction> focusOperation;
552
553 QPointer<QWinRTCameraVideoRendererControl> videoRenderer;
554 QPointer<QWinRTVideoDeviceSelectorControl> videoDeviceSelector;
555 QPointer<QWinRTCameraImageCaptureControl> imageCaptureControl;
556 QPointer<QWinRTImageEncoderControl> imageEncoderControl;
557 QPointer<QWinRTCameraFlashControl> cameraFlashControl;
558 QPointer<QWinRTCameraFocusControl> cameraFocusControl;
559 QPointer<QWinRTCameraLocksControl> cameraLocksControl;
560 QAtomicInt framesMapped;
561 QEventLoop *delayClose;
562
563 bool initializing = false;
564 bool initialized = false;
565 HANDLE initializationCompleteEvent;
566 };
567
QWinRTCameraControl(QObject * parent)568 QWinRTCameraControl::QWinRTCameraControl(QObject *parent)
569 : QCameraControl(parent), d_ptr(new QWinRTCameraControlPrivate)
570 {
571 qCDebug(lcMMCamera) << __FUNCTION__ << parent;
572 Q_D(QWinRTCameraControl);
573
574 d->delayClose = nullptr;
575 d->state = QCamera::UnloadedState;
576 d->status = QCamera::UnloadedStatus;
577 d->captureMode = QCamera::CaptureStillImage;
578 d->captureFailedCookie.value = 0;
579 d->recordLimitationCookie.value = 0;
580 d->videoRenderer = new QWinRTCameraVideoRendererControl(QSize(), this);
581 connect(d->videoRenderer, &QWinRTCameraVideoRendererControl::bufferRequested,
582 this, &QWinRTCameraControl::onBufferRequested);
583 d->videoDeviceSelector = new QWinRTVideoDeviceSelectorControl(this);
584 connect(d->videoDeviceSelector, QOverload<int>::of(&QWinRTVideoDeviceSelectorControl::selectedDeviceChanged),
585 d->videoRenderer, &QWinRTCameraVideoRendererControl::resetSampleFormat);
586 d->imageCaptureControl = new QWinRTCameraImageCaptureControl(this);
587 d->imageEncoderControl = new QWinRTImageEncoderControl(this);
588 d->cameraFlashControl = new QWinRTCameraFlashControl(this);
589 d->cameraFocusControl = new QWinRTCameraFocusControl(this);
590 d->cameraLocksControl = new QWinRTCameraLocksControl(this);
591
592 d->initializationCompleteEvent = CreateEvent(nullptr, false, false, nullptr);
593
594 if (qGuiApp) {
595 connect(qGuiApp, &QGuiApplication::applicationStateChanged,
596 this, &QWinRTCameraControl::onApplicationStateChanged);
597 }
598 }
599
~QWinRTCameraControl()600 QWinRTCameraControl::~QWinRTCameraControl()
601 {
602 setState(QCamera::UnloadedState);
603 }
604
state() const605 QCamera::State QWinRTCameraControl::state() const
606 {
607 Q_D(const QWinRTCameraControl);
608 return d->state;
609 }
610
setState(QCamera::State state)611 void QWinRTCameraControl::setState(QCamera::State state)
612 {
613 qCDebug(lcMMCamera) << __FUNCTION__ << state;
614 Q_D(QWinRTCameraControl);
615
616 if (d->state == state)
617 return;
618
619 HRESULT hr;
620 switch (state) {
621 case QCamera::ActiveState: {
622 // Capture has not been created or initialized
623 if (d->state == QCamera::UnloadedState) {
624 if (!d->initialized) {
625 if (!d->initializing) {
626 hr = initialize();
627 RETURN_VOID_AND_EMIT_ERROR("Failed to initialize media capture");
628 }
629 DWORD waitResult = WaitForSingleObjectEx(d->initializationCompleteEvent, 30000, FALSE);
630 if (waitResult != WAIT_OBJECT_0) {
631 RETURN_VOID_AND_EMIT_ERROR("Failed to initialize camera control.");
632 return;
633 }
634 }
635
636 d->state = QCamera::LoadedState;
637 emit stateChanged(d->state);
638
639 d->status = QCamera::LoadedStatus;
640 emit statusChanged(d->status);
641 }
642 Q_ASSERT(d->state == QCamera::LoadedState);
643
644 ComPtr<IAsyncAction> op;
645 hr = QEventDispatcherWinRT::runOnXamlThread([d, &op]() {
646 d->mediaSink = Make<MediaSink>(d->encodingProfile.Get(), d->videoRenderer);
647 HRESULT hr = d->capturePreview->StartPreviewToCustomSinkAsync(d->encodingProfile.Get(), d->mediaSink.Get(), &op);
648 return hr;
649 });
650 RETURN_VOID_AND_EMIT_ERROR("Failed to initiate capture.");
651 if (d->status != QCamera::StartingStatus) {
652 d->status = QCamera::StartingStatus;
653 emit statusChanged(d->status);
654 }
655
656 hr = QEventDispatcherWinRT::runOnXamlThread([&op]() {
657 return QWinRTFunctions::await(op);
658 });
659 if (FAILED(hr)) {
660 emit error(QCamera::CameraError, qt_error_string(hr));
661 setState(QCamera::UnloadedState); // Unload everything, as initialize() will need be called again
662 return;
663 }
664
665 QCameraFocus::FocusModes focusMode = d->cameraFocusControl->focusMode();
666 if (focusMode != 0 && setFocus(focusMode) && focusMode == QCameraFocus::ContinuousFocus)
667 focus();
668
669 d->state = QCamera::ActiveState;
670 emit stateChanged(d->state);
671 d->status = QCamera::ActiveStatus;
672 emit statusChanged(d->status);
673 QEventDispatcherWinRT::runOnXamlThread([d]() { d->mediaSink->RequestSample(); return S_OK;});
674 break;
675 }
676 case QCamera::LoadedState: {
677 // If moving from unloaded, initialize the camera
678 if (d->state == QCamera::UnloadedState) {
679 if (!d->initialized) {
680 if (!d->initializing) {
681 hr = initialize();
682 RETURN_VOID_AND_EMIT_ERROR("Failed to initialize media capture");
683 }
684 DWORD waitResult = WaitForSingleObjectEx(d->initializationCompleteEvent, 30000, FALSE);
685 if (waitResult != WAIT_OBJECT_0) {
686 RETURN_VOID_AND_EMIT_ERROR("Failed to initialize camera control.");
687 return;
688 }
689 }
690
691 d->state = QCamera::LoadedState;
692 emit stateChanged(d->state);
693
694 d->status = QCamera::LoadedStatus;
695 emit statusChanged(d->status);
696 }
697 // fall through
698 }
699 case QCamera::UnloadedState: {
700 // Stop the camera if it is running (transition to LoadedState)
701 if (d->status == QCamera::ActiveStatus) {
702 HRESULT hr;
703 if (d->focusOperation) {
704 hr = QWinRTFunctions::await(d->focusOperation);
705 Q_ASSERT_SUCCEEDED(hr);
706 }
707 if (d->framesMapped > 0) {
708 qWarning("%d QVideoFrame(s) mapped when closing down camera. Camera will wait for unmap before closing down.",
709 d->framesMapped);
710 if (!d->delayClose)
711 d->delayClose = new QEventLoop(this);
712 d->delayClose->exec();
713 }
714
715 ComPtr<IAsyncAction> op;
716 hr = QEventDispatcherWinRT::runOnXamlThread([d, &op]() {
717 HRESULT hr = d->capturePreview->StopPreviewAsync(&op);
718 return hr;
719 });
720 RETURN_VOID_AND_EMIT_ERROR("Failed to stop camera preview");
721 if (d->status != QCamera::StoppingStatus) {
722 d->status = QCamera::StoppingStatus;
723 emit statusChanged(d->status);
724 }
725 Q_ASSERT_SUCCEEDED(hr);
726 hr = QEventDispatcherWinRT::runOnXamlThread([&op]() {
727 return QWinRTFunctions::await(op); // Synchronize unloading
728 });
729 if (FAILED(hr))
730 emit error(QCamera::InvalidRequestError, qt_error_string(hr));
731
732 if (d->mediaSink) {
733 hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
734 d->mediaSink->Shutdown();
735 d->mediaSink.Reset();
736 return S_OK;
737 });
738 }
739
740 d->state = QCamera::LoadedState;
741 emit stateChanged(d->state);
742
743 d->status = QCamera::LoadedStatus;
744 emit statusChanged(d->status);
745 }
746 // Completely unload if needed
747 if (state == QCamera::UnloadedState) {
748 if (!d->capture) // Already unloaded
749 break;
750
751 if (d->status != QCamera::UnloadingStatus) {
752 d->status = QCamera::UnloadingStatus;
753 emit statusChanged(d->status);
754 }
755
756 hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
757 HRESULT hr;
758 if (d->capture && d->captureFailedCookie.value) {
759 hr = d->capture->remove_Failed(d->captureFailedCookie);
760 Q_ASSERT_SUCCEEDED(hr);
761 d->captureFailedCookie.value = 0;
762 }
763 if (d->capture && d->recordLimitationCookie.value) {
764 d->capture->remove_RecordLimitationExceeded(d->recordLimitationCookie);
765 Q_ASSERT_SUCCEEDED(hr);
766 d->recordLimitationCookie.value = 0;
767 }
768 ComPtr<IClosable> capture;
769 hr = d->capture.As(&capture);
770 Q_ASSERT_SUCCEEDED(hr);
771 hr = capture->Close();
772 RETURN_HR_IF_FAILED("");
773 d->capture.Reset();
774 return hr;
775 });
776 RETURN_VOID_AND_EMIT_ERROR("Failed to close the capture manger");
777 if (d->state != QCamera::UnloadedState) {
778 d->state = QCamera::UnloadedState;
779 emit stateChanged(d->state);
780 }
781 if (d->status != QCamera::UnloadedStatus) {
782 d->status = QCamera::UnloadedStatus;
783 emit statusChanged(d->status);
784 }
785 d->initialized = false;
786 }
787 break;
788 }
789 default:
790 break;
791 }
792 }
793
status() const794 QCamera::Status QWinRTCameraControl::status() const
795 {
796 Q_D(const QWinRTCameraControl);
797 return d->status;
798 }
799
captureMode() const800 QCamera::CaptureModes QWinRTCameraControl::captureMode() const
801 {
802 Q_D(const QWinRTCameraControl);
803 return d->captureMode;
804 }
805
setCaptureMode(QCamera::CaptureModes mode)806 void QWinRTCameraControl::setCaptureMode(QCamera::CaptureModes mode)
807 {
808 qCDebug(lcMMCamera) << __FUNCTION__ << mode;
809 Q_D(QWinRTCameraControl);
810
811 if (d->captureMode == mode)
812 return;
813
814 if (!isCaptureModeSupported(mode)) {
815 qWarning("Unsupported capture mode: %d", mode);
816 return;
817 }
818
819 d->captureMode = mode;
820 emit captureModeChanged(d->captureMode);
821 }
822
isCaptureModeSupported(QCamera::CaptureModes mode) const823 bool QWinRTCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
824 {
825 return mode >= QCamera::CaptureViewfinder && mode <= QCamera::CaptureStillImage;
826 }
827
canChangeProperty(QCameraControl::PropertyChangeType changeType,QCamera::Status status) const828 bool QWinRTCameraControl::canChangeProperty(QCameraControl::PropertyChangeType changeType, QCamera::Status status) const
829 {
830 Q_UNUSED(changeType);
831
832 return status == QCamera::UnloadedStatus; // For now, assume shutdown is required for all property changes
833 }
834
videoRenderer() const835 QVideoRendererControl *QWinRTCameraControl::videoRenderer() const
836 {
837 Q_D(const QWinRTCameraControl);
838 return d->videoRenderer;
839 }
840
videoDeviceSelector() const841 QVideoDeviceSelectorControl *QWinRTCameraControl::videoDeviceSelector() const
842 {
843 Q_D(const QWinRTCameraControl);
844 return d->videoDeviceSelector;
845 }
846
imageCaptureControl() const847 QCameraImageCaptureControl *QWinRTCameraControl::imageCaptureControl() const
848 {
849 Q_D(const QWinRTCameraControl);
850 return d->imageCaptureControl;
851 }
852
imageEncoderControl() const853 QImageEncoderControl *QWinRTCameraControl::imageEncoderControl() const
854 {
855 Q_D(const QWinRTCameraControl);
856 return d->imageEncoderControl;
857 }
858
cameraFlashControl() const859 QCameraFlashControl *QWinRTCameraControl::cameraFlashControl() const
860 {
861 Q_D(const QWinRTCameraControl);
862 return d->cameraFlashControl;
863 }
864
cameraFocusControl() const865 QCameraFocusControl *QWinRTCameraControl::cameraFocusControl() const
866 {
867 Q_D(const QWinRTCameraControl);
868 return d->cameraFocusControl;
869 }
870
cameraLocksControl() const871 QCameraLocksControl *QWinRTCameraControl::cameraLocksControl() const
872 {
873 Q_D(const QWinRTCameraControl);
874 return d->cameraLocksControl;
875 }
876
handle() const877 Microsoft::WRL::ComPtr<ABI::Windows::Media::Capture::IMediaCapture> QWinRTCameraControl::handle() const
878 {
879 Q_D(const QWinRTCameraControl);
880 return d->capture;
881 }
882
onBufferRequested()883 void QWinRTCameraControl::onBufferRequested()
884 {
885 Q_D(QWinRTCameraControl);
886
887 if (d->mediaSink)
888 d->mediaSink->RequestSample();
889 }
890
onApplicationStateChanged(Qt::ApplicationState state)891 void QWinRTCameraControl::onApplicationStateChanged(Qt::ApplicationState state)
892 {
893 qCDebug(lcMMCamera) << __FUNCTION__ << state;
894 #ifdef _DEBUG
895 return;
896 #else // !_DEBUG
897 Q_D(QWinRTCameraControl);
898 static QCamera::State savedState = d->state;
899 switch (state) {
900 case Qt::ApplicationInactive:
901 if (d->state != QCamera::UnloadedState) {
902 savedState = d->state;
903 setState(QCamera::UnloadedState);
904 }
905 break;
906 case Qt::ApplicationActive:
907 setState(QCamera::State(savedState));
908 break;
909 default:
910 break;
911 }
912 #endif // _DEBUG
913 }
914
initialize()915 HRESULT QWinRTCameraControl::initialize()
916 {
917 qCDebug(lcMMCamera) << __FUNCTION__;
918 Q_D(QWinRTCameraControl);
919
920 if (d->status != QCamera::LoadingStatus) {
921 d->status = QCamera::LoadingStatus;
922 emit statusChanged(d->status);
923 }
924
925 HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() {
926 HRESULT hr;
927 ComPtr<IInspectable> capture;
928 hr = RoActivateInstance(Wrappers::HString::MakeReference(RuntimeClass_Windows_Media_Capture_MediaCapture).Get(),
929 &capture);
930 Q_ASSERT_SUCCEEDED(hr);
931 hr = capture.As(&d->capture);
932 Q_ASSERT_SUCCEEDED(hr);
933 hr = d->capture.As(&d->capturePreview);
934 Q_ASSERT_SUCCEEDED(hr);
935 hr = d->capture->add_Failed(Callback<IMediaCaptureFailedEventHandler>(this, &QWinRTCameraControl::onCaptureFailed).Get(),
936 &d->captureFailedCookie);
937 Q_ASSERT_SUCCEEDED(hr);
938 hr = d->capture->add_RecordLimitationExceeded(Callback<IRecordLimitationExceededEventHandler>(this, &QWinRTCameraControl::onRecordLimitationExceeded).Get(),
939 &d->recordLimitationCookie);
940 Q_ASSERT_SUCCEEDED(hr);
941 hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(),
942 IID_PPV_ARGS(&d->encodingProfileFactory));
943 Q_ASSERT_SUCCEEDED(hr);
944
945 int deviceIndex = d->videoDeviceSelector->selectedDevice();
946 if (deviceIndex < 0)
947 deviceIndex = d->videoDeviceSelector->defaultDevice();
948
949 const QString deviceName = d->videoDeviceSelector->deviceName(deviceIndex);
950 if (deviceName.isEmpty()) {
951 qWarning("No video device available or selected.");
952 return E_FAIL;
953 }
954
955 d->videoRenderer->setScanLineDirection(QVideoSurfaceFormat::TopToBottom);
956 ComPtr<IMediaCaptureInitializationSettings> settings;
957 hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_Capture_MediaCaptureInitializationSettings).Get(),
958 &settings);
959 Q_ASSERT_SUCCEEDED(hr);
960 HStringReference deviceId(reinterpret_cast<LPCWSTR>(deviceName.utf16()), uint(deviceName.length()));
961 hr = settings->put_VideoDeviceId(deviceId.Get());
962 Q_ASSERT_SUCCEEDED(hr);
963
964 hr = settings->put_StreamingCaptureMode(StreamingCaptureMode_Video);
965 Q_ASSERT_SUCCEEDED(hr);
966
967 hr = settings->put_PhotoCaptureSource(PhotoCaptureSource_Auto);
968 Q_ASSERT_SUCCEEDED(hr);
969
970 ComPtr<IAsyncAction> op;
971 hr = d->capture->InitializeWithSettingsAsync(settings.Get(), &op);
972 RETURN_HR_IF_FAILED("Failed to begin initialization of media capture manager");
973 hr = op.Get()->put_Completed(Callback<IAsyncActionCompletedHandler>(
974 this, &QWinRTCameraControl::onInitializationCompleted).Get());
975 RETURN_HR_IF_FAILED("Failed to register initialization callback");
976 return S_OK;
977 });
978 return hr;
979 }
980
981 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
982
initializeFocus()983 HRESULT QWinRTCameraControl::initializeFocus()
984 {
985 Q_D(QWinRTCameraControl);
986 ComPtr<IFocusControl2> focusControl2;
987 HRESULT hr = d->focusControl.As(&focusControl2);
988 Q_ASSERT_SUCCEEDED(hr);
989 ComPtr<IVectorView<enum FocusMode>> focusModes;
990 hr = focusControl2->get_SupportedFocusModes(&focusModes);
991 if (FAILED(hr)) {
992 d->cameraFocusControl->setSupportedFocusMode(nullptr);
993 d->cameraFocusControl->setSupportedFocusPointMode(QSet<QCameraFocus::FocusPointMode>());
994 qErrnoWarning(hr, "Failed to get camera supported focus mode list");
995 return hr;
996 }
997 quint32 size;
998 hr = focusModes->get_Size(&size);
999 Q_ASSERT_SUCCEEDED(hr);
1000 QCameraFocus::FocusModes supportedModeFlag = nullptr;
1001 for (quint32 i = 0; i < size; ++i) {
1002 FocusMode mode;
1003 hr = focusModes->GetAt(i, &mode);
1004 Q_ASSERT_SUCCEEDED(hr);
1005 switch (mode) {
1006 case FocusMode_Continuous:
1007 supportedModeFlag |= QCameraFocus::ContinuousFocus;
1008 break;
1009 case FocusMode_Single:
1010 supportedModeFlag |= QCameraFocus::AutoFocus;
1011 break;
1012 default:
1013 break;
1014 }
1015 }
1016
1017 ComPtr<IVectorView<enum AutoFocusRange>> focusRange;
1018 hr = focusControl2->get_SupportedFocusRanges(&focusRange);
1019 if (FAILED(hr)) {
1020 qErrnoWarning(hr, "Failed to get camera supported focus range list");
1021 } else {
1022 hr = focusRange->get_Size(&size);
1023 Q_ASSERT_SUCCEEDED(hr);
1024 for (quint32 i = 0; i < size; ++i) {
1025 AutoFocusRange range;
1026 hr = focusRange->GetAt(i, &range);
1027 Q_ASSERT_SUCCEEDED(hr);
1028 switch (range) {
1029 case AutoFocusRange_Macro:
1030 supportedModeFlag |= QCameraFocus::MacroFocus;
1031 break;
1032 case AutoFocusRange_FullRange:
1033 supportedModeFlag |= QCameraFocus::InfinityFocus;
1034 break;
1035 default:
1036 break;
1037 }
1038 }
1039 }
1040 d->cameraFocusControl->setSupportedFocusMode(supportedModeFlag);
1041 if (!d->regionsOfInterestControl) {
1042 d->cameraFocusControl->setSupportedFocusPointMode(QSet<QCameraFocus::FocusPointMode>());
1043 return S_OK;
1044 }
1045 boolean isRegionsfocusSupported = false;
1046 hr = d->regionsOfInterestControl->get_AutoFocusSupported(&isRegionsfocusSupported);
1047 Q_ASSERT_SUCCEEDED(hr);
1048 UINT32 maxRegions;
1049 hr = d->regionsOfInterestControl->get_MaxRegions(&maxRegions);
1050 Q_ASSERT_SUCCEEDED(hr);
1051 if (!isRegionsfocusSupported || maxRegions == 0) {
1052 d->cameraFocusControl->setSupportedFocusPointMode(QSet<QCameraFocus::FocusPointMode>());
1053 return S_OK;
1054 }
1055 QSet<QCameraFocus::FocusPointMode> supportedFocusPointModes;
1056 supportedFocusPointModes << QCameraFocus::FocusPointCustom
1057 << QCameraFocus::FocusPointCenter
1058 << QCameraFocus::FocusPointAuto;
1059 d->cameraFocusControl->setSupportedFocusPointMode(supportedFocusPointModes);
1060 return S_OK;
1061 }
1062
setFocus(QCameraFocus::FocusModes modes)1063 bool QWinRTCameraControl::setFocus(QCameraFocus::FocusModes modes)
1064 {
1065 Q_D(QWinRTCameraControl);
1066 if (d->status == QCamera::UnloadedStatus)
1067 return false;
1068
1069 bool result = false;
1070 HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([modes, &result, d, this]() {
1071 ComPtr<IFocusSettings> focusSettings;
1072 ComPtr<IInspectable> focusSettingsObject;
1073 HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_Devices_FocusSettings).Get(), &focusSettingsObject);
1074 Q_ASSERT_SUCCEEDED(hr);
1075 hr = focusSettingsObject.As(&focusSettings);
1076 Q_ASSERT_SUCCEEDED(hr);
1077 FocusMode mode;
1078 if (modes.testFlag(QCameraFocus::ContinuousFocus)) {
1079 mode = FocusMode_Continuous;
1080 } else if (modes.testFlag(QCameraFocus::AutoFocus)
1081 || modes.testFlag(QCameraFocus::MacroFocus)
1082 || modes.testFlag(QCameraFocus::InfinityFocus)) {
1083 // The Macro and infinity focus modes are only supported in auto focus mode on WinRT.
1084 // QML camera focus doesn't support combined focus flags settings. In the case of macro
1085 // and infinity Focus modes, the auto focus setting is applied.
1086 mode = FocusMode_Single;
1087 } else {
1088 emit error(QCamera::NotSupportedFeatureError, QStringLiteral("Unsupported camera focus modes."));
1089 result = false;
1090 return S_OK;
1091 }
1092 hr = focusSettings->put_Mode(mode);
1093 Q_ASSERT_SUCCEEDED(hr);
1094 AutoFocusRange range = AutoFocusRange_Normal;
1095 if (modes.testFlag(QCameraFocus::MacroFocus))
1096 range = AutoFocusRange_Macro;
1097 else if (modes.testFlag(QCameraFocus::InfinityFocus))
1098 range = AutoFocusRange_FullRange;
1099 hr = focusSettings->put_AutoFocusRange(range);
1100 Q_ASSERT_SUCCEEDED(hr);
1101 hr = focusSettings->put_WaitForFocus(true);
1102 Q_ASSERT_SUCCEEDED(hr);
1103 hr = focusSettings->put_DisableDriverFallback(false);
1104 Q_ASSERT_SUCCEEDED(hr);
1105
1106 ComPtr<IFocusControl2> focusControl2;
1107 hr = d->focusControl.As(&focusControl2);
1108 Q_ASSERT_SUCCEEDED(hr);
1109 hr = focusControl2->Configure(focusSettings.Get());
1110 result = SUCCEEDED(hr);
1111 RETURN_OK_IF_FAILED("Failed to configure camera focus control");
1112 return S_OK;
1113 });
1114 Q_ASSERT_SUCCEEDED(hr);
1115 Q_UNUSED(hr); // Silence release build
1116 return result;
1117 }
1118
setFocusPoint(const QPointF & focusPoint)1119 bool QWinRTCameraControl::setFocusPoint(const QPointF &focusPoint)
1120 {
1121 Q_D(QWinRTCameraControl);
1122 if (focusPoint.x() < double(FOCUS_RECT_POSITION_MIN)
1123 || focusPoint.x() > double(FOCUS_RECT_BOUNDARY)) {
1124 emit error(QCamera::CameraError, QStringLiteral("Focus horizontal location should be between 0.0 and 1.0."));
1125 return false;
1126 }
1127
1128 if (focusPoint.y() < double(FOCUS_RECT_POSITION_MIN)
1129 || focusPoint.y() > double(FOCUS_RECT_BOUNDARY)) {
1130 emit error(QCamera::CameraError, QStringLiteral("Focus vertical location should be between 0.0 and 1.0."));
1131 return false;
1132 }
1133
1134 ABI::Windows::Foundation::Rect rect;
1135 rect.X = qBound<float>(FOCUS_RECT_POSITION_MIN, float(focusPoint.x() - double(FOCUS_RECT_HALF_SIZE)), FOCUS_RECT_POSITION_MAX);
1136 rect.Y = qBound<float>(FOCUS_RECT_POSITION_MIN, float(focusPoint.y() - double(FOCUS_RECT_HALF_SIZE)), FOCUS_RECT_POSITION_MAX);
1137 rect.Width = (rect.X + FOCUS_RECT_SIZE) < FOCUS_RECT_BOUNDARY ? FOCUS_RECT_SIZE : FOCUS_RECT_BOUNDARY - rect.X;
1138 rect.Height = (rect.Y + FOCUS_RECT_SIZE) < FOCUS_RECT_BOUNDARY ? FOCUS_RECT_SIZE : FOCUS_RECT_BOUNDARY - rect.Y;
1139
1140 ComPtr<IRegionOfInterest> regionOfInterest;
1141 ComPtr<IInspectable> regionOfInterestObject;
1142 HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_Devices_RegionOfInterest).Get(), ®ionOfInterestObject);
1143 Q_ASSERT_SUCCEEDED(hr);
1144 hr = regionOfInterestObject.As(®ionOfInterest);
1145 Q_ASSERT_SUCCEEDED(hr);
1146 ComPtr<IRegionOfInterest2> regionOfInterest2;
1147 hr = regionOfInterestObject.As(®ionOfInterest2);
1148 Q_ASSERT_SUCCEEDED(hr);
1149 hr = regionOfInterest2->put_BoundsNormalized(true);
1150 Q_ASSERT_SUCCEEDED(hr);
1151 hr = regionOfInterest2->put_Weight(1);
1152 Q_ASSERT_SUCCEEDED(hr);
1153 hr = regionOfInterest2->put_Type(RegionOfInterestType_Unknown);
1154 Q_ASSERT_SUCCEEDED(hr);
1155 hr = regionOfInterest->put_AutoFocusEnabled(true);
1156 Q_ASSERT_SUCCEEDED(hr);
1157 hr = regionOfInterest->put_Bounds(rect);
1158 Q_ASSERT_SUCCEEDED(hr);
1159
1160 ComPtr<WindowsRegionOfInterestIterable> regionOfInterestIterable = Make<WindowsRegionOfInterestIterable>(regionOfInterest);
1161 ComPtr<IAsyncAction> op;
1162 hr = d->regionsOfInterestControl->SetRegionsAsync(regionOfInterestIterable.Get(), &op);
1163 Q_ASSERT_SUCCEEDED(hr);
1164 return QWinRTFunctions::await(op) == S_OK;
1165 }
1166
focus()1167 bool QWinRTCameraControl::focus()
1168 {
1169 Q_D(QWinRTCameraControl);
1170 HRESULT hr;
1171 if (!d->focusControl)
1172 return false;
1173
1174 QEventDispatcherWinRT::runOnXamlThread([&d, &hr]() {
1175 if (d->focusOperation) {
1176 ComPtr<IAsyncInfo> info;
1177 hr = d->focusOperation.As(&info);
1178 Q_ASSERT_SUCCEEDED(hr);
1179
1180 AsyncStatus status = AsyncStatus::Completed;
1181 hr = info->get_Status(&status);
1182 Q_ASSERT_SUCCEEDED(hr);
1183 if (status == AsyncStatus::Started)
1184 return E_ASYNC_OPERATION_NOT_STARTED;
1185 }
1186
1187 hr = d->focusControl->FocusAsync(&d->focusOperation);
1188 Q_ASSERT_SUCCEEDED(hr);
1189
1190 const long errorCode = HRESULT_CODE(hr);
1191 if (errorCode == ERROR_OPERATION_IN_PROGRESS
1192 || errorCode == ERROR_WRITE_PROTECT) {
1193 return E_ASYNC_OPERATION_NOT_STARTED;
1194 }
1195 Q_ASSERT_SUCCEEDED(hr);
1196 return S_OK;
1197 });
1198
1199 return hr == S_OK;
1200 }
1201
clearFocusPoint()1202 void QWinRTCameraControl::clearFocusPoint()
1203 {
1204 Q_D(QWinRTCameraControl);
1205 if (!d->focusControl)
1206 return;
1207 ComPtr<IAsyncAction> op;
1208 HRESULT hr = d->regionsOfInterestControl->ClearRegionsAsync(&op);
1209 Q_ASSERT_SUCCEEDED(hr);
1210 hr = QWinRTFunctions::await(op);
1211 Q_ASSERT_SUCCEEDED(hr);
1212 }
1213
lockFocus()1214 bool QWinRTCameraControl::lockFocus()
1215 {
1216 Q_D(QWinRTCameraControl);
1217 if (!d->focusControl)
1218 return false;
1219
1220 bool result = false;
1221 ComPtr<IAsyncAction> op;
1222 HRESULT hr;
1223 hr = QEventDispatcherWinRT::runOnXamlThread([d, &result, &op]() {
1224 ComPtr<IFocusControl2> focusControl2;
1225 HRESULT hr = d->focusControl.As(&focusControl2);
1226 Q_ASSERT_SUCCEEDED(hr);
1227 hr = focusControl2->LockAsync(&op);
1228 if (HRESULT_CODE(hr) == ERROR_WRITE_PROTECT)
1229 return S_OK;
1230 Q_ASSERT_SUCCEEDED(hr);
1231 result = true;
1232 return hr;
1233 });
1234 return result ? (QWinRTFunctions::await(op) == S_OK) : false;
1235 }
1236
unlockFocus()1237 bool QWinRTCameraControl::unlockFocus()
1238 {
1239 Q_D(QWinRTCameraControl);
1240 if (!d->focusControl)
1241 return false;
1242
1243 bool result = false;
1244 ComPtr<IAsyncAction> op;
1245 HRESULT hr;
1246 hr = QEventDispatcherWinRT::runOnXamlThread([d, &result, &op]() {
1247 ComPtr<IFocusControl2> focusControl2;
1248 HRESULT hr = d->focusControl.As(&focusControl2);
1249 Q_ASSERT_SUCCEEDED(hr);
1250 hr = focusControl2->UnlockAsync(&op);
1251 if (HRESULT_CODE(hr) == ERROR_WRITE_PROTECT)
1252 return S_OK;
1253 Q_ASSERT_SUCCEEDED(hr);
1254 result = true;
1255 return hr;
1256 });
1257 return result ? (QWinRTFunctions::await(op) == S_OK) : false;
1258 }
1259
1260 #else // !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
1261
initializeFocus()1262 HRESULT QWinRTCameraControl::initializeFocus()
1263 {
1264 Q_D(QWinRTCameraControl);
1265 d->cameraFocusControl->setSupportedFocusMode(0);
1266 d->cameraFocusControl->setSupportedFocusPointMode(QSet<QCameraFocus::FocusPointMode>());
1267 return S_OK;
1268 }
1269
setFocus(QCameraFocus::FocusModes modes)1270 bool QWinRTCameraControl::setFocus(QCameraFocus::FocusModes modes)
1271 {
1272 Q_UNUSED(modes)
1273 return false;
1274 }
1275
setFocusPoint(const QPointF & focusPoint)1276 bool QWinRTCameraControl::setFocusPoint(const QPointF &focusPoint)
1277 {
1278 Q_UNUSED(focusPoint)
1279 return false;
1280 }
1281
focus()1282 bool QWinRTCameraControl::focus()
1283 {
1284 return false;
1285 }
1286
clearFocusPoint()1287 void QWinRTCameraControl::clearFocusPoint()
1288 {
1289 }
1290
lockFocus()1291 bool QWinRTCameraControl::lockFocus()
1292 {
1293 return false;
1294 }
1295
unlockFocus()1296 bool QWinRTCameraControl::unlockFocus()
1297 {
1298 return false;
1299 }
1300
1301 #endif // !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
1302
frameMapped()1303 void QWinRTCameraControl::frameMapped()
1304 {
1305 Q_D(QWinRTCameraControl);
1306 ++d->framesMapped;
1307 }
1308
frameUnmapped()1309 void QWinRTCameraControl::frameUnmapped()
1310 {
1311 Q_D(QWinRTCameraControl);
1312 --d->framesMapped;
1313 Q_ASSERT(d->framesMapped >= 0);
1314 if (!d->framesMapped && d->delayClose && d->delayClose->isRunning())
1315 d->delayClose->exit();
1316 }
1317
onCaptureFailed(IMediaCapture *,IMediaCaptureFailedEventArgs * args)1318 HRESULT QWinRTCameraControl::onCaptureFailed(IMediaCapture *, IMediaCaptureFailedEventArgs *args)
1319 {
1320 qCDebug(lcMMCamera) << __FUNCTION__ << args;
1321 HRESULT hr;
1322 UINT32 code;
1323 hr = args->get_Code(&code);
1324 RETURN_HR_IF_FAILED("Failed to get error code");
1325 HString message;
1326 args->get_Message(message.GetAddressOf());
1327 RETURN_HR_IF_FAILED("Failed to get error message");
1328 quint32 messageLength;
1329 const wchar_t *messageBuffer = message.GetRawBuffer(&messageLength);
1330 emit error(QCamera::CameraError, QString::fromWCharArray(messageBuffer, int(messageLength)));
1331 setState(QCamera::LoadedState);
1332 return S_OK;
1333 }
1334
onRecordLimitationExceeded(IMediaCapture *)1335 HRESULT QWinRTCameraControl::onRecordLimitationExceeded(IMediaCapture *)
1336 {
1337 qCDebug(lcMMCamera) << __FUNCTION__;
1338 emit error(QCamera::CameraError, QStringLiteral("Recording limit exceeded."));
1339 setState(QCamera::LoadedState);
1340 return S_OK;
1341 }
1342
onInitializationCompleted(IAsyncAction *,AsyncStatus status)1343 HRESULT QWinRTCameraControl::onInitializationCompleted(IAsyncAction *, AsyncStatus status)
1344 {
1345 qCDebug(lcMMCamera) << __FUNCTION__;
1346 Q_D(QWinRTCameraControl);
1347
1348 if (status != Completed) {
1349 d->initializing = false;
1350 d->initialized = false;
1351 return S_OK;
1352 }
1353
1354 ComPtr<IVideoDeviceController> videoDeviceController;
1355 HRESULT hr = d->capture->get_VideoDeviceController(&videoDeviceController);
1356 ComPtr<IAdvancedVideoCaptureDeviceController2> advancedVideoDeviceController;
1357 hr = videoDeviceController.As(&advancedVideoDeviceController);
1358 Q_ASSERT_SUCCEEDED(hr);
1359 hr = advancedVideoDeviceController->get_FocusControl(&d->focusControl);
1360 Q_ASSERT_SUCCEEDED(hr);
1361
1362 d->cameraFlashControl->initialize(advancedVideoDeviceController);
1363
1364 boolean isFocusSupported;
1365 hr = d->focusControl->get_Supported(&isFocusSupported);
1366 Q_ASSERT_SUCCEEDED(hr);
1367 if (isFocusSupported) {
1368 hr = advancedVideoDeviceController->get_RegionsOfInterestControl(&d->regionsOfInterestControl);
1369 if (FAILED(hr))
1370 qCDebug(lcMMCamera) << "Focus supported, but no control for regions of interest available";
1371 hr = initializeFocus();
1372 Q_ASSERT_SUCCEEDED(hr);
1373 }
1374
1375 Q_ASSERT_SUCCEEDED(hr);
1376 ComPtr<IMediaDeviceController> deviceController;
1377 hr = videoDeviceController.As(&deviceController);
1378 Q_ASSERT_SUCCEEDED(hr);
1379
1380 // Get preview stream properties.
1381 ComPtr<IVectorView<IMediaEncodingProperties *>> previewPropertiesList;
1382 QVector<QSize> previewResolutions;
1383 hr = getMediaStreamResolutions(deviceController.Get(),
1384 MediaStreamType_VideoPreview,
1385 &previewPropertiesList,
1386 &previewResolutions);
1387 RETURN_HR_IF_FAILED("Failed to find a suitable video format");
1388
1389 MediaStreamType mediaStreamType =
1390 d->captureMode == QCamera::CaptureVideo ? MediaStreamType_VideoRecord : MediaStreamType_Photo;
1391
1392 // Get capture stream properties.
1393 ComPtr<IVectorView<IMediaEncodingProperties *>> capturePropertiesList;
1394 QVector<QSize> captureResolutions;
1395 hr = getMediaStreamResolutions(deviceController.Get(),
1396 mediaStreamType,
1397 &capturePropertiesList,
1398 &captureResolutions);
1399 RETURN_HR_IF_FAILED("Failed to find a suitable video format");
1400
1401 std::sort(captureResolutions.begin(), captureResolutions.end(), [](QSize size1, QSize size2) {
1402 return size1.width() * size1.height() < size2.width() * size2.height();
1403 });
1404
1405 // Set capture resolutions.
1406 d->imageEncoderControl->setSupportedResolutionsList(captureResolutions.toList());
1407 const QSize captureResolution = d->imageEncoderControl->imageSettings().resolution();
1408 const quint32 captureResolutionIndex = quint32(captureResolutions.indexOf(captureResolution));
1409 ComPtr<IMediaEncodingProperties> captureProperties;
1410 hr = capturePropertiesList->GetAt(captureResolutionIndex, &captureProperties);
1411 Q_ASSERT_SUCCEEDED(hr);
1412 ComPtr<IAsyncAction> op;
1413 hr = deviceController->SetMediaStreamPropertiesAsync(mediaStreamType, captureProperties.Get(), &op);
1414 Q_ASSERT_SUCCEEDED(hr);
1415 hr = QWinRTFunctions::await(op);
1416 Q_ASSERT_SUCCEEDED(hr);
1417
1418 // Set preview resolution.
1419 QSize maxSize;
1420 const float captureAspectRatio = float(captureResolution.width()) / captureResolution.height();
1421 for (const QSize &resolution : qAsConst(previewResolutions)) {
1422 const float aspectRatio = float(resolution.width()) / resolution.height();
1423 if ((qAbs(aspectRatio - captureAspectRatio) <= ASPECTRATIO_EPSILON)
1424 && (maxSize.width() * maxSize.height() < resolution.width() * resolution.height())) {
1425 maxSize = resolution;
1426 }
1427 }
1428
1429 const QSize &viewfinderResolution = maxSize;
1430 const quint32 viewfinderResolutionIndex = quint32(previewResolutions.indexOf(viewfinderResolution));
1431 hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(),
1432 &d->encodingProfile);
1433 Q_ASSERT_SUCCEEDED(hr);
1434 ComPtr<IMediaEncodingProperties> previewProperties;
1435 hr = previewPropertiesList->GetAt(viewfinderResolutionIndex, &previewProperties);
1436 Q_ASSERT_SUCCEEDED(hr);
1437 hr = deviceController->SetMediaStreamPropertiesAsync(MediaStreamType_VideoPreview, previewProperties.Get(), &op);
1438 Q_ASSERT_SUCCEEDED(hr);
1439 hr = QWinRTFunctions::await(op);
1440 Q_ASSERT_SUCCEEDED(hr);
1441 ComPtr<IVideoEncodingProperties> videoPreviewProperties;
1442 hr = previewProperties.As(&videoPreviewProperties);
1443 Q_ASSERT_SUCCEEDED(hr);
1444 hr = d->encodingProfile->put_Video(videoPreviewProperties.Get());
1445 Q_ASSERT_SUCCEEDED(hr);
1446
1447 if (d->videoRenderer)
1448 d->videoRenderer->setSize(viewfinderResolution);
1449
1450 if (!isFocusSupported) {
1451 d->cameraFocusControl->setSupportedFocusMode(0);
1452 d->cameraFocusControl->setSupportedFocusPointMode(QSet<QCameraFocus::FocusPointMode>());
1453 }
1454 d->cameraLocksControl->initialize();
1455
1456 d->initializing = false;
1457 d->initialized = true;
1458 SetEvent(d->initializationCompleteEvent);
1459 return S_OK;
1460 }
1461
emitError(int errorCode,const QString & errorString)1462 void QWinRTCameraControl::emitError(int errorCode, const QString &errorString)
1463 {
1464 qCDebug(lcMMCamera) << __FUNCTION__ << errorString << errorCode;
1465 emit error(errorCode, errorString);
1466 }
1467
1468 QT_END_NAMESPACE
1469