summaryrefslogtreecommitdiffstats
path: root/src/plugins/winrt/qwinrtcameracontrol.cpp
diff options
context:
space:
mode:
authorAndrew Knight <andrew.knight@digia.com>2014-08-29 16:59:09 +0300
committerAndrew Knight <andrew.knight@digia.com>2014-08-30 08:24:07 +0200
commit0c3438c9a12fbc607eada8f938cf0ad8fdea374d (patch)
treea7656d4bcaa8a9625955061ae2f3e7147867885a /src/plugins/winrt/qwinrtcameracontrol.cpp
parent80ba1d635dc37963b4c90d776c46774b0349c4f8 (diff)
winrt: Add camera service
This adds a basic camera service with viewfinder (video renderer based), still image capture, and device selection support. Runtime apps must set the "webcam" and "microphone" device capabilities in order to access the hardware. This can be done by adding the following to the .pro file: WINRT_MANIFEST.capabilites_device += webcam microphone [ChangeLog] Enabled basic camera support in the winrt backend. Change-Id: If4f963ef645d93c757ae23aec9a9c8aae122324f Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
Diffstat (limited to 'src/plugins/winrt/qwinrtcameracontrol.cpp')
-rw-r--r--src/plugins/winrt/qwinrtcameracontrol.cpp783
1 files changed, 783 insertions, 0 deletions
diff --git a/src/plugins/winrt/qwinrtcameracontrol.cpp b/src/plugins/winrt/qwinrtcameracontrol.cpp
new file mode 100644
index 000000000..619e97315
--- /dev/null
+++ b/src/plugins/winrt/qwinrtcameracontrol.cpp
@@ -0,0 +1,783 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwinrtcameracontrol.h"
+#include "qwinrtcameravideorenderercontrol.h"
+#include "qwinrtvideodeviceselectorcontrol.h"
+#include "qwinrtcameraimagecapturecontrol.h"
+
+#include <QtCore/qfunctions_winrt.h>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QPointer>
+
+#include <mfapi.h>
+#include <mferror.h>
+#include <mfidl.h>
+#include <wrl.h>
+#include <windows.devices.enumeration.h>
+#include <windows.media.capture.h>
+#include <windows.storage.streams.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Devices::Enumeration;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Foundation::Collections;
+using namespace ABI::Windows::Media;
+using namespace ABI::Windows::Media::Capture;
+using namespace ABI::Windows::Media::Devices;
+using namespace ABI::Windows::Media::MediaProperties;
+using namespace ABI::Windows::Storage::Streams;
+
+QT_USE_NAMESPACE
+
+#define RETURN_VOID_AND_EMIT_ERROR(msg) \
+ if (FAILED(hr)) { \
+ emit error(QCamera::CameraError, qt_error_string(hr)); \
+ RETURN_VOID_IF_FAILED(msg); \
+ }
+
+class CriticalSectionLocker
+{
+public:
+ CriticalSectionLocker(CRITICAL_SECTION *section)
+ : m_section(section)
+ {
+ EnterCriticalSection(m_section);
+ }
+ ~CriticalSectionLocker()
+ {
+ LeaveCriticalSection(m_section);
+ }
+private:
+ CRITICAL_SECTION *m_section;
+};
+
+class MediaStream : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, IMFStreamSink, IMFMediaEventGenerator, IMFMediaTypeHandler>
+{
+public:
+ MediaStream(IMFMediaType *type, IMFMediaSink *mediaSink, QWinRTCameraVideoRendererControl *videoRenderer)
+ : m_type(type), m_sink(mediaSink), m_videoRenderer(videoRenderer)
+ {
+ Q_ASSERT(m_videoRenderer);
+
+ InitializeCriticalSectionEx(&m_mutex, 0, 0);
+
+ HRESULT hr;
+ hr = MFCreateEventQueue(&m_eventQueue);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = MFAllocateSerialWorkQueue(MFASYNC_CALLBACK_QUEUE_STANDARD, &m_workQueueId);
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+
+ ~MediaStream()
+ {
+ CriticalSectionLocker locker(&m_mutex);
+ m_eventQueue->Shutdown();
+ DeleteCriticalSection(&m_mutex);
+ }
+
+ HRESULT RequestSample()
+ {
+ if (m_pendingSamples.load() < 3) {
+ m_pendingSamples.ref();
+ return QueueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, Q_NULLPTR);
+ }
+ return S_OK;
+ }
+
+ HRESULT __stdcall GetEvent(DWORD flags, IMFMediaEvent **event) Q_DECL_OVERRIDE
+ {
+ EnterCriticalSection(&m_mutex);
+ // Create an extra reference to avoid deadlock
+ ComPtr<IMFMediaEventQueue> eventQueue = m_eventQueue;
+ LeaveCriticalSection(&m_mutex);
+
+ return eventQueue->GetEvent(flags, event);
+ }
+
+ HRESULT __stdcall BeginGetEvent(IMFAsyncCallback *callback, IUnknown *state) Q_DECL_OVERRIDE
+ {
+ CriticalSectionLocker locker(&m_mutex);
+ HRESULT hr = m_eventQueue->BeginGetEvent(callback, state);
+ return hr;
+ }
+
+ HRESULT __stdcall EndGetEvent(IMFAsyncResult *result, IMFMediaEvent **event) Q_DECL_OVERRIDE
+ {
+ CriticalSectionLocker locker(&m_mutex);
+ return m_eventQueue->EndGetEvent(result, event);
+ }
+
+ HRESULT __stdcall QueueEvent(MediaEventType eventType, const GUID &extendedType, HRESULT status, const PROPVARIANT *value) Q_DECL_OVERRIDE
+ {
+ CriticalSectionLocker locker(&m_mutex);
+ return m_eventQueue->QueueEventParamVar(eventType, extendedType, status, value);
+ }
+
+ HRESULT __stdcall GetMediaSink(IMFMediaSink **mediaSink) Q_DECL_OVERRIDE
+ {
+ *mediaSink = m_sink;
+ return S_OK;
+ }
+
+ HRESULT __stdcall GetIdentifier(DWORD *identifier) Q_DECL_OVERRIDE
+ {
+ *identifier = 0;
+ return S_OK;
+ }
+
+ HRESULT __stdcall GetMediaTypeHandler(IMFMediaTypeHandler **handler) Q_DECL_OVERRIDE
+ {
+ return QueryInterface(IID_PPV_ARGS(handler));
+ }
+
+ HRESULT __stdcall ProcessSample(IMFSample *sample) Q_DECL_OVERRIDE
+ {
+ ComPtr<IMFMediaBuffer> buffer;
+ HRESULT hr = sample->GetBufferByIndex(0, &buffer);
+ RETURN_HR_IF_FAILED("Failed to get buffer from camera sample");
+ ComPtr<IMF2DBuffer> buffer2d;
+ hr = buffer.As(&buffer2d);
+ RETURN_HR_IF_FAILED("Failed to cast camera sample buffer to 2D buffer");
+
+ m_pendingSamples.deref();
+ m_videoRenderer->queueBuffer(buffer2d.Get());
+
+ return hr;
+ }
+
+ HRESULT __stdcall PlaceMarker(MFSTREAMSINK_MARKER_TYPE type, const PROPVARIANT *value, const PROPVARIANT *context) Q_DECL_OVERRIDE
+ {
+ Q_UNUSED(type);
+ Q_UNUSED(value);
+ QueueEvent(MEStreamSinkMarker, GUID_NULL, S_OK, context);
+ return S_OK;
+ }
+
+ HRESULT __stdcall Flush() Q_DECL_OVERRIDE
+ {
+ m_videoRenderer->discardBuffers();
+ m_pendingSamples.store(0);
+ return S_OK;
+ }
+
+ HRESULT __stdcall IsMediaTypeSupported(IMFMediaType *type, IMFMediaType **) Q_DECL_OVERRIDE
+ {
+ HRESULT hr;
+ GUID majorType;
+ hr = type->GetMajorType(&majorType);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (!IsEqualGUID(majorType, MFMediaType_Video))
+ return MF_E_INVALIDMEDIATYPE;
+ return S_OK;
+ }
+
+ HRESULT __stdcall GetMediaTypeCount(DWORD *typeCount) Q_DECL_OVERRIDE
+ {
+ *typeCount = 1;
+ return S_OK;
+ }
+
+ HRESULT __stdcall GetMediaTypeByIndex(DWORD index, IMFMediaType **type) Q_DECL_OVERRIDE
+ {
+ if (index == 0)
+ return m_type.CopyTo(type);
+ return E_BOUNDS;
+ }
+
+ HRESULT __stdcall SetCurrentMediaType(IMFMediaType *type) Q_DECL_OVERRIDE
+ {
+ if (FAILED(IsMediaTypeSupported(type, Q_NULLPTR)))
+ return MF_E_INVALIDREQUEST;
+
+ m_type = type;
+ return S_OK;
+ }
+
+ HRESULT __stdcall GetCurrentMediaType(IMFMediaType **type) Q_DECL_OVERRIDE
+ {
+ return m_type.CopyTo(type);
+ }
+
+ HRESULT __stdcall GetMajorType(GUID *majorType) Q_DECL_OVERRIDE
+ {
+ return m_type->GetMajorType(majorType);
+ }
+
+private:
+ CRITICAL_SECTION m_mutex;
+ ComPtr<IMFMediaType> m_type;
+ IMFMediaSink *m_sink;
+ ComPtr<IMFMediaEventQueue> m_eventQueue;
+ DWORD m_workQueueId;
+
+ QWinRTCameraVideoRendererControl *m_videoRenderer;
+ QAtomicInt m_pendingSamples;
+};
+
+class MediaSink : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, IMediaExtension, IMFMediaSink, IMFClockStateSink>
+{
+public:
+ MediaSink(IMediaEncodingProfile *encodingProfile, QWinRTCameraVideoRendererControl *videoRenderer)
+ : m_videoRenderer(videoRenderer)
+ {
+ HRESULT hr;
+ ComPtr<IVideoEncodingProperties> videoProperties;
+ hr = encodingProfile->get_Video(&videoProperties);
+ RETURN_VOID_IF_FAILED("Failed to get video properties");
+ ComPtr<IMFMediaType> videoType;
+ hr = MFCreateMediaTypeFromProperties(videoProperties.Get(), &videoType);
+ RETURN_VOID_IF_FAILED("Failed to create video type");
+ m_stream = Make<MediaStream>(videoType.Get(), this, videoRenderer);
+ }
+
+ ~MediaSink()
+ {
+ }
+
+ HRESULT RequestSample()
+ {
+ return m_stream->RequestSample();
+ }
+
+ HRESULT __stdcall SetProperties(Collections::IPropertySet *configuration) Q_DECL_OVERRIDE
+ {
+ Q_UNUSED(configuration);
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall GetCharacteristics(DWORD *characteristics) Q_DECL_OVERRIDE
+ {
+ *characteristics = MEDIASINK_FIXED_STREAMS | MEDIASINK_RATELESS;
+ return S_OK;
+ }
+
+ HRESULT __stdcall AddStreamSink(DWORD streamSinkIdentifier, IMFMediaType *mediaType, IMFStreamSink **streamSink) Q_DECL_OVERRIDE
+ {
+ Q_UNUSED(streamSinkIdentifier);
+ Q_UNUSED(mediaType);
+ Q_UNUSED(streamSink);
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall RemoveStreamSink(DWORD streamSinkIdentifier) Q_DECL_OVERRIDE
+ {
+ Q_UNUSED(streamSinkIdentifier);
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall GetStreamSinkCount(DWORD *streamSinkCount) Q_DECL_OVERRIDE
+ {
+ *streamSinkCount = 1;
+ return S_OK;
+ }
+
+ HRESULT __stdcall GetStreamSinkByIndex(DWORD index, IMFStreamSink **streamSink) Q_DECL_OVERRIDE
+ {
+ if (index == 0)
+ return m_stream.CopyTo(streamSink);
+ return MF_E_INVALIDINDEX;
+ }
+
+ HRESULT __stdcall GetStreamSinkById(DWORD streamSinkIdentifier, IMFStreamSink **streamSink) Q_DECL_OVERRIDE
+ {
+ // ID and index are always 0
+ HRESULT hr = GetStreamSinkByIndex(streamSinkIdentifier, streamSink);
+ return hr == MF_E_INVALIDINDEX ? MF_E_INVALIDSTREAMNUMBER : hr;
+ }
+
+ HRESULT __stdcall SetPresentationClock(IMFPresentationClock *presentationClock) Q_DECL_OVERRIDE
+ {
+ HRESULT hr = S_OK;
+ m_presentationClock = presentationClock;
+ if (m_presentationClock)
+ hr = m_presentationClock->AddClockStateSink(this);
+ return hr;
+ }
+
+ HRESULT __stdcall GetPresentationClock(IMFPresentationClock **presentationClock) Q_DECL_OVERRIDE
+ {
+ return m_presentationClock.CopyTo(presentationClock);
+ }
+
+ HRESULT __stdcall Shutdown() Q_DECL_OVERRIDE
+ {
+ m_stream->Flush();
+ m_videoRenderer->setActive(false);
+ return m_presentationClock->Stop();
+ }
+
+ HRESULT __stdcall OnClockStart(MFTIME systemTime, LONGLONG clockStartOffset) Q_DECL_OVERRIDE
+ {
+ Q_UNUSED(systemTime);
+ Q_UNUSED(clockStartOffset);
+
+ m_videoRenderer->setActive(true);
+
+ return S_OK;
+ }
+
+ HRESULT __stdcall OnClockStop(MFTIME systemTime) Q_DECL_OVERRIDE
+ {
+ Q_UNUSED(systemTime);
+
+ m_videoRenderer->setActive(false);
+
+ return m_stream->QueueEvent(MEStreamSinkStopped, GUID_NULL, S_OK, Q_NULLPTR);
+ }
+
+ HRESULT __stdcall OnClockPause(MFTIME systemTime) Q_DECL_OVERRIDE
+ {
+ Q_UNUSED(systemTime);
+
+ m_videoRenderer->setActive(false);
+
+ return m_stream->QueueEvent(MEStreamSinkPaused, GUID_NULL, S_OK, Q_NULLPTR);
+ }
+
+ HRESULT __stdcall OnClockRestart(MFTIME systemTime) Q_DECL_OVERRIDE
+ {
+ Q_UNUSED(systemTime);
+
+ m_videoRenderer->setActive(true);
+
+ return m_stream->QueueEvent(MEStreamSinkStarted, GUID_NULL, S_OK, Q_NULLPTR);
+ }
+
+ HRESULT __stdcall OnClockSetRate(MFTIME systemTime, float rate) Q_DECL_OVERRIDE
+ {
+ Q_UNUSED(systemTime);
+ Q_UNUSED(rate);
+ return E_NOTIMPL;
+ }
+
+private:
+ ComPtr<MediaStream> m_stream;
+ ComPtr<IMFPresentationClock> m_presentationClock;
+
+ QWinRTCameraVideoRendererControl *m_videoRenderer;
+};
+
+class QWinRTCameraControlPrivate
+{
+public:
+ QCamera::State state;
+ QCamera::Status status;
+ QCamera::CaptureModes captureMode;
+
+ ComPtr<IMediaCapture> capture;
+ ComPtr<IMediaCaptureVideoPreview> capturePreview;
+ EventRegistrationToken captureFailedCookie;
+ EventRegistrationToken recordLimitationCookie;
+
+ ComPtr<IMediaEncodingProfileStatics> encodingProfileFactory;
+
+ ComPtr<IMediaEncodingProfile> encodingProfile;
+ ComPtr<MediaSink> mediaSink;
+
+ QSize size;
+ QPointer<QWinRTCameraVideoRendererControl> videoRenderer;
+ QPointer<QWinRTVideoDeviceSelectorControl> videoDeviceSelector;
+ QPointer<QWinRTCameraImageCaptureControl> imageCaptureControl;
+};
+
+QWinRTCameraControl::QWinRTCameraControl(QObject *parent)
+ : QCameraControl(parent), d_ptr(new QWinRTCameraControlPrivate)
+{
+ Q_D(QWinRTCameraControl);
+
+ d->state = QCamera::UnloadedState;
+ d->status = QCamera::UnloadedStatus;
+ d->captureMode = QCamera::CaptureStillImage;
+ d->captureFailedCookie.value = 0;
+ d->recordLimitationCookie.value = 0;
+ d->videoRenderer = new QWinRTCameraVideoRendererControl(d->size, this);
+ connect(d->videoRenderer, &QWinRTCameraVideoRendererControl::bufferRequested,
+ this, &QWinRTCameraControl::onBufferRequested);
+ d->videoDeviceSelector = new QWinRTVideoDeviceSelectorControl(this);
+ d->imageCaptureControl = new QWinRTCameraImageCaptureControl(this);
+}
+
+QWinRTCameraControl::~QWinRTCameraControl()
+{
+ setState(QCamera::UnloadedState);
+}
+
+QCamera::State QWinRTCameraControl::state() const
+{
+ Q_D(const QWinRTCameraControl);
+ return d->state;
+}
+
+void QWinRTCameraControl::setState(QCamera::State state)
+{
+ Q_D(QWinRTCameraControl);
+
+ if (d->state == state)
+ return;
+
+ HRESULT hr;
+ switch (state) {
+ case QCamera::ActiveState: {
+ // Capture has not been created or initialized
+ if (d->state == QCamera::UnloadedState) {
+ hr = initialize();
+ RETURN_VOID_AND_EMIT_ERROR("Failed to initialize media capture");
+ }
+ Q_ASSERT(d->state == QCamera::LoadedState);
+
+ d->mediaSink = Make<MediaSink>(d->encodingProfile.Get(), d->videoRenderer);
+ ComPtr<IAsyncAction> op;
+ hr = d->capturePreview->StartPreviewToCustomSinkAsync(d->encodingProfile.Get(), d->mediaSink.Get(), &op);
+ RETURN_VOID_AND_EMIT_ERROR("Failed to initiate capture");
+ if (d->status != QCamera::StartingStatus) {
+ d->status = QCamera::StartingStatus;
+ emit statusChanged(d->status);
+ }
+
+ hr = QWinRTFunctions::await(op);
+ if (FAILED(hr)) {
+ emit error(QCamera::CameraError, qt_error_string(hr));
+ setState(QCamera::UnloadedState); // Unload everything, as initialize() will need be called again
+ return;
+ }
+
+ d->state = QCamera::ActiveState;
+ emit stateChanged(d->state);
+ d->status = QCamera::ActiveStatus;
+ emit statusChanged(d->status);
+ break;
+ }
+ case QCamera::LoadedState: {
+ // If moving from unloaded, initialize the camera
+ if (d->state == QCamera::UnloadedState) {
+ hr = initialize();
+ RETURN_VOID_AND_EMIT_ERROR("Failed to initialize media capture");
+ }
+ // fall through
+ }
+ case QCamera::UnloadedState: {
+ // Stop the camera if it is running (transition to LoadedState)
+ if (d->status == QCamera::ActiveStatus) {
+ ComPtr<IAsyncAction> op;
+ hr = d->capturePreview->StopPreviewAsync(&op);
+ RETURN_VOID_AND_EMIT_ERROR("Failed to stop camera preview");
+ if (d->status != QCamera::StoppingStatus) {
+ d->status = QCamera::StoppingStatus;
+ emit statusChanged(d->status);
+ }
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = QWinRTFunctions::await(op); // Synchronize unloading
+ if (FAILED(hr))
+ emit error(QCamera::InvalidRequestError, qt_error_string(hr));
+
+ d->mediaSink->Shutdown();
+ d->mediaSink.Reset();
+
+ d->state = QCamera::LoadedState;
+ emit stateChanged(d->state);
+
+ d->status = QCamera::LoadedStatus;
+ emit statusChanged(d->status);
+ }
+ // Completely unload if needed
+ if (state == QCamera::UnloadedState) {
+ if (!d->capture) // Already unloaded
+ break;
+
+ if (d->status != QCamera::UnloadingStatus) {
+ d->status = QCamera::UnloadingStatus;
+ emit statusChanged(d->status);
+ }
+
+ if (d->capture && d->captureFailedCookie.value) {
+ hr = d->capture->remove_Failed(d->captureFailedCookie);
+ Q_ASSERT_SUCCEEDED(hr);
+ d->captureFailedCookie.value = 0;
+ }
+ if (d->capture && d->recordLimitationCookie.value) {
+ d->capture->remove_RecordLimitationExceeded(d->recordLimitationCookie);
+ Q_ASSERT_SUCCEEDED(hr);
+ d->recordLimitationCookie.value = 0;
+ }
+ ComPtr<IClosable> capture;
+ hr = d->capture.As(&capture);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = capture->Close();
+ RETURN_VOID_AND_EMIT_ERROR("Failed to close the capture manger");
+ d->capture.Reset();
+ if (d->state != QCamera::UnloadedState) {
+ d->state = QCamera::UnloadedState;
+ emit stateChanged(d->state);
+ }
+ if (d->status != QCamera::UnloadedStatus) {
+ d->status = QCamera::UnloadedStatus;
+ emit statusChanged(d->status);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+QCamera::Status QWinRTCameraControl::status() const
+{
+ Q_D(const QWinRTCameraControl);
+ return d->status;
+}
+
+QCamera::CaptureModes QWinRTCameraControl::captureMode() const
+{
+ Q_D(const QWinRTCameraControl);
+ return d->captureMode;
+}
+
+void QWinRTCameraControl::setCaptureMode(QCamera::CaptureModes mode)
+{
+ Q_D(QWinRTCameraControl);
+
+ if (d->captureMode == mode)
+ return;
+
+ if (!isCaptureModeSupported(mode)) {
+ qWarning("Unsupported capture mode: %d", mode);
+ return;
+ }
+
+ d->captureMode = mode;
+ emit captureModeChanged(d->captureMode);
+}
+
+bool QWinRTCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
+{
+ return mode >= QCamera::CaptureViewfinder && mode <= QCamera::CaptureStillImage;
+}
+
+bool QWinRTCameraControl::canChangeProperty(QCameraControl::PropertyChangeType changeType, QCamera::Status status) const
+{
+ Q_UNUSED(changeType);
+
+ return status == QCamera::UnloadedStatus; // For now, assume shutdown is required for all property changes
+}
+
+QVideoRendererControl *QWinRTCameraControl::videoRenderer() const
+{
+ Q_D(const QWinRTCameraControl);
+ return d->videoRenderer;
+}
+
+QVideoDeviceSelectorControl *QWinRTCameraControl::videoDeviceSelector() const
+{
+ Q_D(const QWinRTCameraControl);
+ return d->videoDeviceSelector;
+}
+
+QCameraImageCaptureControl *QWinRTCameraControl::imageCaptureControl() const
+{
+ Q_D(const QWinRTCameraControl);
+ return d->imageCaptureControl;
+}
+
+IMediaCapture *QWinRTCameraControl::handle() const
+{
+ Q_D(const QWinRTCameraControl);
+ return d->capture.Get();
+}
+
+QSize QWinRTCameraControl::imageSize() const
+{
+ Q_D(const QWinRTCameraControl);
+ return d->size;
+}
+
+void QWinRTCameraControl::onBufferRequested()
+{
+ Q_D(QWinRTCameraControl);
+
+ if (d->mediaSink)
+ d->mediaSink->RequestSample();
+}
+
+HRESULT QWinRTCameraControl::initialize()
+{
+ Q_D(QWinRTCameraControl);
+
+ if (d->status != QCamera::LoadingStatus) {
+ d->status = QCamera::LoadingStatus;
+ emit statusChanged(d->status);
+ }
+
+ HRESULT hr;
+ ComPtr<IInspectable> capture;
+ hr = RoActivateInstance(Wrappers::HString::MakeReference(RuntimeClass_Windows_Media_Capture_MediaCapture).Get(),
+ &capture);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = capture.As(&d->capture);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = d->capture.As(&d->capturePreview);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = d->capture->add_Failed(Callback<IMediaCaptureFailedEventHandler>(this, &QWinRTCameraControl::onCaptureFailed).Get(),
+ &d->captureFailedCookie);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = d->capture->add_RecordLimitationExceeded(Callback<IRecordLimitationExceededEventHandler>(this, &QWinRTCameraControl::onRecordLimitationExceeded).Get(),
+ &d->recordLimitationCookie);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(),
+ IID_PPV_ARGS(&d->encodingProfileFactory));
+ Q_ASSERT_SUCCEEDED(hr);
+
+ int deviceIndex = d->videoDeviceSelector->selectedDevice();
+ if (deviceIndex < 0)
+ deviceIndex = d->videoDeviceSelector->defaultDevice();
+
+ const QString deviceName = d->videoDeviceSelector->deviceName(deviceIndex);
+ if (deviceName.isEmpty()) {
+ qWarning("No video device available or selected.");
+ return E_FAIL;
+ }
+
+ ComPtr<IMediaCaptureInitializationSettings> settings;
+ hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_Capture_MediaCaptureInitializationSettings).Get(),
+ &settings);
+ Q_ASSERT_SUCCEEDED(hr);
+ HStringReference deviceId(reinterpret_cast<LPCWSTR>(deviceName.utf16()), deviceName.length());
+ hr = settings->put_VideoDeviceId(deviceId.Get());
+ Q_ASSERT_SUCCEEDED(hr);
+
+ hr = settings->put_StreamingCaptureMode(StreamingCaptureMode_Video);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ hr = settings->put_PhotoCaptureSource(PhotoCaptureSource_Auto);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<IAsyncAction> op;
+ hr = d->capture->InitializeWithSettingsAsync(settings.Get(), &op);
+ RETURN_HR_IF_FAILED("Failed to begin initialization of media capture manager");
+ hr = QWinRTFunctions::await(op, QWinRTFunctions::ProcessThreadEvents);
+ if (hr == E_ACCESSDENIED) {
+ qWarning("Access denied when initializing the media capture manager. "
+ "Check your manifest settings for microphone and webcam access.");
+ }
+ RETURN_HR_IF_FAILED("Failed to initialize media capture manager");
+
+ ComPtr<IVideoDeviceController> videoDeviceController;
+ hr = d->capture->get_VideoDeviceController(&videoDeviceController);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IMediaDeviceController> deviceController;
+ hr = videoDeviceController.As(&deviceController);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IVectorView<IMediaEncodingProperties *>> encodingPropertiesList;
+ hr = deviceController->GetAvailableMediaStreamProperties(MediaStreamType_Photo, &encodingPropertiesList);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ d->size = QSize();
+ ComPtr<IVideoEncodingProperties> videoEncodingProperties;
+ quint32 encodingPropertiesListSize;
+ hr = encodingPropertiesList->get_Size(&encodingPropertiesListSize);
+ Q_ASSERT_SUCCEEDED(hr);
+ for (quint32 i = 0; i < encodingPropertiesListSize; ++i) {
+ ComPtr<IMediaEncodingProperties> properties;
+ hr = encodingPropertiesList->GetAt(i, &properties);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IVideoEncodingProperties> videoProperties;
+ hr = properties.As(&videoEncodingProperties);
+ Q_ASSERT_SUCCEEDED(hr);
+ UINT32 width, height;
+ hr = videoEncodingProperties->get_Width(&width);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = videoEncodingProperties->get_Height(&height);
+ Q_ASSERT_SUCCEEDED(hr);
+ // Choose the highest-quality format
+ if (int(width * height) > d->size.width() * d->size.height()) {
+ d->size = QSize(width, height);
+ videoEncodingProperties = videoProperties;
+ }
+ }
+
+ if (!videoEncodingProperties || d->size.isEmpty()) {
+ hr = MF_E_INVALID_FORMAT;
+ RETURN_HR_IF_FAILED("Failed to find a suitable video format");
+ }
+
+ hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile).Get(),
+ &d->encodingProfile);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = d->encodingProfile->put_Video(videoEncodingProperties.Get());
+ Q_ASSERT_SUCCEEDED(hr);
+ if (d->videoRenderer)
+ d->videoRenderer->setSize(d->size);
+
+ if (SUCCEEDED(hr) && d->state != QCamera::LoadedState) {
+ d->state = QCamera::LoadedState;
+ emit stateChanged(d->state);
+ }
+ if (SUCCEEDED(hr) && d->status != QCamera::LoadedStatus) {
+ d->status = QCamera::LoadedStatus;
+ emit statusChanged(d->status);
+ }
+ return hr;
+}
+
+HRESULT QWinRTCameraControl::onCaptureFailed(IMediaCapture *, IMediaCaptureFailedEventArgs *args)
+{
+ HRESULT hr;
+ UINT32 code;
+ hr = args->get_Code(&code);
+ RETURN_HR_IF_FAILED("Failed to get error code");
+ HString message;
+ args->get_Message(message.GetAddressOf());
+ RETURN_HR_IF_FAILED("Failed to get error message");
+ quint32 messageLength;
+ const wchar_t *messageBuffer = message.GetRawBuffer(&messageLength);
+ emit error(QCamera::CameraError, QString::fromWCharArray(messageBuffer, messageLength));
+ setState(QCamera::LoadedState);
+ return S_OK;
+}
+
+HRESULT QWinRTCameraControl::onRecordLimitationExceeded(IMediaCapture *)
+{
+ emit error(QCamera::CameraError, QStringLiteral("Recording limit exceeded."));
+ setState(QCamera::LoadedState);
+ return S_OK;
+}