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(), &regionOfInterestObject);
1143     Q_ASSERT_SUCCEEDED(hr);
1144     hr = regionOfInterestObject.As(&regionOfInterest);
1145     Q_ASSERT_SUCCEEDED(hr);
1146     ComPtr<IRegionOfInterest2> regionOfInterest2;
1147     hr = regionOfInterestObject.As(&regionOfInterest2);
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