diff options
-rw-r--r-- | src/plugins/winrt/qwinrtcameracontrol.cpp | 783 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtcameracontrol.h | 111 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp | 280 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtcameraimagecapturecontrol.h | 86 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtcamerainfocontrol.cpp | 60 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtcamerainfocontrol.h | 61 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtcameraservice.cpp | 104 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtcameraservice.h | 66 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp | 204 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtcameravideorenderercontrol.h | 74 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtserviceplugin.cpp | 39 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtserviceplugin.h | 14 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp | 383 | ||||
-rw-r--r-- | src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h | 94 | ||||
-rw-r--r-- | src/plugins/winrt/winrt.json | 2 | ||||
-rw-r--r-- | src/plugins/winrt/winrt.pro | 18 |
16 files changed, 2375 insertions, 4 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; +} diff --git a/src/plugins/winrt/qwinrtcameracontrol.h b/src/plugins/winrt/qwinrtcameracontrol.h new file mode 100644 index 000000000..e75f7e476 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameracontrol.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWINRTCAMERACONTROL_H +#define QWINRTCAMERACONTROL_H + +#include <QtMultimedia/QCameraControl> +#include <QtCore/qt_windows.h> + +namespace ABI { + namespace Windows { + namespace Media { + namespace Capture { + struct IMediaCapture; + struct IMediaCaptureFailedEventArgs; + } + } + namespace Foundation { + struct IAsyncAction; + enum class AsyncStatus; + } + } +} + +QT_BEGIN_NAMESPACE + +class QVideoRendererControl; +class QVideoDeviceSelectorControl; +class QCameraImageCaptureControl; + +class QWinRTCameraControlPrivate; +class QWinRTCameraControl : public QCameraControl +{ + Q_OBJECT +public: + explicit QWinRTCameraControl(QObject *parent = 0); + ~QWinRTCameraControl(); + + QCamera::State state() const Q_DECL_OVERRIDE; + void setState(QCamera::State state) Q_DECL_OVERRIDE; + + QCamera::Status status() const Q_DECL_OVERRIDE; + + QCamera::CaptureModes captureMode() const Q_DECL_OVERRIDE; + void setCaptureMode(QCamera::CaptureModes mode) Q_DECL_OVERRIDE; + bool isCaptureModeSupported(QCamera::CaptureModes mode) const Q_DECL_OVERRIDE; + + bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const Q_DECL_OVERRIDE; + + QVideoRendererControl *videoRenderer() const; + QVideoDeviceSelectorControl *videoDeviceSelector() const; + QCameraImageCaptureControl *imageCaptureControl() const; + + ABI::Windows::Media::Capture::IMediaCapture *handle() const; + QSize imageSize() const; + +private slots: + void onBufferRequested(); + +private: + HRESULT enumerateDevices(); + HRESULT initialize(); + HRESULT onCaptureFailed(ABI::Windows::Media::Capture::IMediaCapture *, + ABI::Windows::Media::Capture::IMediaCaptureFailedEventArgs *); + HRESULT onRecordLimitationExceeded(ABI::Windows::Media::Capture::IMediaCapture *); + + QScopedPointer<QWinRTCameraControlPrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTCameraControl) +}; + +QT_END_NAMESPACE + +#endif // QWINRTCAMERACONTROL_H diff --git a/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp new file mode 100644 index 000000000..f5151c619 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** 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 "qwinrtcameraimagecapturecontrol.h" +#include "qwinrtcameracontrol.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QGlobalStatic> +#include <QtCore/QPointer> +#include <QtCore/QStandardPaths> +#include <QtCore/QVector> +#include <QtCore/qfunctions_winrt.h> +#include <QtMultimedia/private/qmediastoragelocation_p.h> + +#include <wrl.h> +#include <windows.media.capture.h> +#include <windows.media.devices.h> +#include <windows.media.mediaproperties.h> +#include <windows.storage.streams.h> +#include <windows.graphics.imaging.h> +#include <robuffer.h> + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Media::Capture; +using namespace ABI::Windows::Media::Devices; +using namespace ABI::Windows::Media::MediaProperties; +using namespace ABI::Windows::Storage::Streams; +using namespace ABI::Windows::Graphics::Imaging; + +QT_USE_NAMESPACE + +#define wchar(str) reinterpret_cast<const wchar_t *>(str.utf16()) + +struct QWinRTCameraImageCaptureControlGlobal +{ + QWinRTCameraImageCaptureControlGlobal() + { + HRESULT hr; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Media_MediaProperties_ImageEncodingProperties).Get(), + &encodingPropertiesFactory); + Q_ASSERT_SUCCEEDED(hr); + + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), + &bufferFactory); + Q_ASSERT_SUCCEEDED(hr); + + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataReader).Get(), + &dataReaderFactory); + } + + ComPtr<IImageEncodingPropertiesStatics2> encodingPropertiesFactory; + ComPtr<IBufferFactory> bufferFactory; + ComPtr<IDataReaderFactory> dataReaderFactory; +}; +Q_GLOBAL_STATIC(QWinRTCameraImageCaptureControlGlobal, g) + +struct CaptureRequest +{ + quint16 id; + QString fileName; + ComPtr<IImageEncodingProperties> imageFormat; + ComPtr<IRandomAccessStream> stream; + ComPtr<IAsyncAction> op; +}; + +class QWinRTCameraImageCaptureControlPrivate +{ +public: + QPointer<QWinRTCameraControl> cameraControl; + QHash<IAsyncAction *, CaptureRequest> requests; + quint16 currentCaptureId; + QMediaStorageLocation location; + + void onCameraStateChanged() + { + + } +}; + +QWinRTCameraImageCaptureControl::QWinRTCameraImageCaptureControl(QWinRTCameraControl *parent) + : QCameraImageCaptureControl(parent), d_ptr(new QWinRTCameraImageCaptureControlPrivate) +{ + Q_D(QWinRTCameraImageCaptureControl); + + d->cameraControl = parent; + connect(d->cameraControl, &QCameraControl::stateChanged, + this, &QWinRTCameraImageCaptureControl::readyForCaptureChanged); + d->currentCaptureId = 0; +} + +bool QWinRTCameraImageCaptureControl::isReadyForCapture() const +{ + Q_D(const QWinRTCameraImageCaptureControl); + return d->cameraControl->state() == QCamera::ActiveState; +} + +QCameraImageCapture::DriveMode QWinRTCameraImageCaptureControl::driveMode() const +{ + return QCameraImageCapture::SingleImageCapture; +} + +void QWinRTCameraImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode) +{ + Q_UNUSED(mode); +} + +int QWinRTCameraImageCaptureControl::capture(const QString &fileName) +{ + Q_D(QWinRTCameraImageCaptureControl); + + ++d->currentCaptureId; + IMediaCapture *capture = d->cameraControl->handle(); + if (!capture) { + emit error(d->currentCaptureId, QCameraImageCapture::NotReadyError, tr("Camera not ready")); + return -1; + } + + CaptureRequest request = { + d->currentCaptureId, + d->location.generateFileName(fileName, QMediaStorageLocation::Pictures, QStringLiteral("IMG_"), + fileName.isEmpty() ? QStringLiteral("jpg") : QFileInfo(fileName).suffix()) + }; + + HRESULT hr; + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream).Get(), + &request.stream); + Q_ASSERT_SUCCEEDED(hr); + + hr = g->encodingPropertiesFactory->CreateBmp(&request.imageFormat); + Q_ASSERT_SUCCEEDED(hr); + + const QSize imageSize = d->cameraControl->imageSize(); + hr = request.imageFormat->put_Width(imageSize.width()); + Q_ASSERT_SUCCEEDED(hr); + hr = request.imageFormat->put_Height(imageSize.height()); + Q_ASSERT_SUCCEEDED(hr); + + hr = capture->CapturePhotoToStreamAsync(request.imageFormat.Get(), request.stream.Get(), &request.op); + Q_ASSERT_SUCCEEDED(hr); + d->requests.insert(request.op.Get(), request); + + hr = request.op->put_Completed(Callback<IAsyncActionCompletedHandler>( + this, &QWinRTCameraImageCaptureControl::onCaptureCompleted).Get()); + Q_ASSERT_SUCCEEDED(hr); + + return request.id; +} + +void QWinRTCameraImageCaptureControl::cancelCapture() +{ + Q_D(QWinRTCameraImageCaptureControl); + + QHash<IAsyncAction *, CaptureRequest>::iterator it = d->requests.begin(); + while (it != d->requests.end()) { + ComPtr<IAsyncInfo> info; + it->op.As(&info); + info->Cancel(); + it = d->requests.erase(it); + } +} + +HRESULT QWinRTCameraImageCaptureControl::onCaptureCompleted(IAsyncAction *asyncInfo, AsyncStatus status) +{ + Q_D(QWinRTCameraImageCaptureControl); + + if (status == Canceled || !d->requests.contains(asyncInfo)) + return S_OK; + + CaptureRequest request = d->requests.take(asyncInfo); + + HRESULT hr; + if (status == Error) { + hr = asyncInfo->GetResults(); + emit error(request.id, QCameraImageCapture::ResourceError, qt_error_string(hr)); + return S_OK; + } + + quint64 dataLength; + hr = request.stream->get_Size(&dataLength); + Q_ASSERT_SUCCEEDED(hr); + if (dataLength == 0 || dataLength > INT_MAX) { + emit error(request.id, QCameraImageCapture::FormatError, tr("Invalid photo data length.")); + return S_OK; + } + + ComPtr<IBitmapDecoderStatics> bitmapFactory; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_BitmapDecoder).Get(), + &bitmapFactory); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperation<BitmapDecoder *>> op; + hr = bitmapFactory->CreateAsync(request.stream.Get(), &op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IBitmapDecoder> decoder; + hr = QWinRTFunctions::await(op, decoder.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperation<BitmapFrame *>> op2; + hr = decoder->GetFrameAsync(0, &op2); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IBitmapFrame> frame; + hr = QWinRTFunctions::await(op2, frame.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IBitmapTransform> transform; + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_BitmapTransform).Get(), + &transform); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperation<PixelDataProvider *>> op3; + hr = frame->GetPixelDataTransformedAsync(BitmapPixelFormat_Rgba8, BitmapAlphaMode_Straight, + transform.Get(), ExifOrientationMode_IgnoreExifOrientation, + ColorManagementMode_DoNotColorManage, &op3); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IPixelDataProvider> pixelDataProvider; + hr = QWinRTFunctions::await(op3, pixelDataProvider.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + UINT32 pixelDataSize; + BYTE *pixelData; + hr = pixelDataProvider->DetachPixelData(&pixelDataSize, &pixelData); + + UINT32 pixelHeight; + hr = frame->get_PixelHeight(&pixelHeight); + Q_ASSERT_SUCCEEDED(hr); + UINT32 pixelWidth; + hr = frame->get_PixelWidth(&pixelWidth); + Q_ASSERT_SUCCEEDED(hr); + const QImage image(pixelData, pixelWidth, pixelHeight, QImage::Format_RGBA8888, + reinterpret_cast<QImageCleanupFunction>(&CoTaskMemFree), pixelData); + emit imageCaptured(request.id, image); + if (image.save(request.fileName)) + emit imageSaved(request.id, request.fileName); + else + emit error(request.id, QCameraImageCapture::ResourceError, tr("Image saving failed")); + + return S_OK; +} diff --git a/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h new file mode 100644 index 000000000..5150e4d3a --- /dev/null +++ b/src/plugins/winrt/qwinrtcameraimagecapturecontrol.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWINRTCAMERAIMAGECAPTURECONTROL_H +#define QWINRTCAMERAIMAGECAPTURECONTROL_H + +#include <QtMultimedia/QCameraImageCaptureControl> +#include <QtCore/qt_windows.h> + +namespace ABI { + namespace Windows { + namespace Foundation { + struct IAsyncAction; + enum class AsyncStatus; + } + } +} + +QT_BEGIN_NAMESPACE + +class QWinRTCameraControl; + +class QWinRTCameraImageCaptureControlPrivate; +class QWinRTCameraImageCaptureControl : public QCameraImageCaptureControl +{ + Q_OBJECT +public: + explicit QWinRTCameraImageCaptureControl(QWinRTCameraControl *parent); + + bool isReadyForCapture() const Q_DECL_OVERRIDE; + + QCameraImageCapture::DriveMode driveMode() const Q_DECL_OVERRIDE; + void setDriveMode(QCameraImageCapture::DriveMode mode) Q_DECL_OVERRIDE; + + int capture(const QString &fileName) Q_DECL_OVERRIDE; + void cancelCapture() Q_DECL_OVERRIDE; + +private: + HRESULT onCaptureCompleted(ABI::Windows::Foundation::IAsyncAction *, + ABI::Windows::Foundation::AsyncStatus); + + QScopedPointer<QWinRTCameraImageCaptureControlPrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTCameraImageCaptureControl) +}; + +QT_END_NAMESPACE + +#endif // QWINRTCAMERAIMAGECAPTURECONTROL_H diff --git a/src/plugins/winrt/qwinrtcamerainfocontrol.cpp b/src/plugins/winrt/qwinrtcamerainfocontrol.cpp new file mode 100644 index 000000000..c16b83be8 --- /dev/null +++ b/src/plugins/winrt/qwinrtcamerainfocontrol.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** 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 "qwinrtcamerainfocontrol.h" +#include "qwinrtvideodeviceselectorcontrol.h" + +QT_USE_NAMESPACE + +QWinRTCameraInfoControl::QWinRTCameraInfoControl(QObject *parent) + : QCameraInfoControl(parent) +{ +} + +QCamera::Position QWinRTCameraInfoControl::cameraPosition(const QString &deviceName) const +{ + return QWinRTVideoDeviceSelectorControl::cameraPosition(deviceName); +} + +int QWinRTCameraInfoControl::cameraOrientation(const QString &deviceName) const +{ + return QWinRTVideoDeviceSelectorControl::cameraOrientation(deviceName); +} diff --git a/src/plugins/winrt/qwinrtcamerainfocontrol.h b/src/plugins/winrt/qwinrtcamerainfocontrol.h new file mode 100644 index 000000000..bf430f038 --- /dev/null +++ b/src/plugins/winrt/qwinrtcamerainfocontrol.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWINRTCAMERAINFOCONTROL_H +#define QWINRTCAMERAINFOCONTROL_H + +#include <QtMultimedia/QCameraInfoControl> + +QT_BEGIN_NAMESPACE + +class QWinRTCameraInfoControl : public QCameraInfoControl +{ + Q_OBJECT +public: + explicit QWinRTCameraInfoControl(QObject *parent = 0); + + QCamera::Position cameraPosition(const QString &deviceName) const Q_DECL_OVERRIDE; + int cameraOrientation(const QString &deviceName) const Q_DECL_OVERRIDE; +}; + +QT_END_NAMESPACE + +#endif // QWINRTCAMERAINFOCONTROL_H diff --git a/src/plugins/winrt/qwinrtcameraservice.cpp b/src/plugins/winrt/qwinrtcameraservice.cpp new file mode 100644 index 000000000..239a1e883 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameraservice.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** 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 "qwinrtcameraservice.h" +#include "qwinrtcameracontrol.h" +#include "qwinrtcamerainfocontrol.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/qfunctions_winrt.h> +#include <QtCore/QPointer> +#include <QtMultimedia/QCameraImageCaptureControl> +#include <QtMultimedia/QVideoRendererControl> +#include <QtMultimedia/QVideoDeviceSelectorControl> + +QT_USE_NAMESPACE + +class QWinRTCameraServicePrivate +{ +public: + QPointer<QWinRTCameraControl> cameraControl; + QPointer<QWinRTCameraInfoControl> cameraInfoControl; +}; + +QWinRTCameraService::QWinRTCameraService(QObject *parent) + : QMediaService(parent), d_ptr(new QWinRTCameraServicePrivate) +{ +} + +QMediaControl *QWinRTCameraService::requestControl(const char *name) +{ + Q_D(QWinRTCameraService); + + if (qstrcmp(name, QCameraControl_iid) == 0) { + if (!d->cameraControl) + d->cameraControl = new QWinRTCameraControl(this); + return d->cameraControl; + } + + if (qstrcmp(name, QVideoRendererControl_iid) == 0) { + if (d->cameraControl) + return d->cameraControl->videoRenderer(); + } + + if (qstrcmp(name, QVideoDeviceSelectorControl_iid) == 0) { + if (d->cameraControl) + return d->cameraControl->videoDeviceSelector(); + } + + if (qstrcmp(name, QCameraInfoControl_iid) == 0) { + if (!d->cameraInfoControl) + d->cameraInfoControl = new QWinRTCameraInfoControl(this); + return d->cameraInfoControl; + } + + if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0) { + if (d->cameraControl) + return d->cameraControl->imageCaptureControl(); + } + + return Q_NULLPTR; +} + +void QWinRTCameraService::releaseControl(QMediaControl *control) +{ + Q_UNUSED(control); +} diff --git a/src/plugins/winrt/qwinrtcameraservice.h b/src/plugins/winrt/qwinrtcameraservice.h new file mode 100644 index 000000000..19e93a818 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameraservice.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWINRTCAMERASERVICE_H +#define QWINRTCAMERASERVICE_H + +#include <QtMultimedia/QMediaService> + +QT_BEGIN_NAMESPACE + +class QWinRTCameraServicePrivate; +class QWinRTCameraService : public QMediaService +{ + Q_OBJECT +public: + explicit QWinRTCameraService(QObject *parent = 0); + + QMediaControl *requestControl(const char *name) Q_DECL_OVERRIDE; + void releaseControl(QMediaControl *control) Q_DECL_OVERRIDE; + +private: + QScopedPointer<QWinRTCameraServicePrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTCameraService) +}; + +QT_END_NAMESPACE + +#endif // QWINRTCAMERASERVICE_H diff --git a/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp b/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp new file mode 100644 index 000000000..e7e75da27 --- /dev/null +++ b/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** 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 "qwinrtcameravideorenderercontrol.h" + +#include <QtCore/qfunctions_winrt.h> +#include <QtCore/QSize> +#include <QtCore/QVector> + +#include <d3d11.h> +#include <mfapi.h> +#include <wrl.h> +using namespace Microsoft::WRL; + +QT_USE_NAMESPACE + +class D3DVideoBlitter +{ +public: + D3DVideoBlitter(ID3D11Device *device, ID3D11Texture2D *target) + : m_d3dDevice(device), m_target(target) + { + HRESULT hr; + ComPtr<IDXGIResource> targetResource; + hr = target->QueryInterface(IID_PPV_ARGS(&targetResource)); + Q_ASSERT_SUCCEEDED(hr); + HANDLE sharedHandle; + hr = targetResource->GetSharedHandle(&sharedHandle); + Q_ASSERT_SUCCEEDED(hr); + hr = m_d3dDevice->OpenSharedResource(sharedHandle, IID_PPV_ARGS(&m_targetTexture)); + Q_ASSERT_SUCCEEDED(hr); + hr = m_d3dDevice.As(&m_videoDevice); + Q_ASSERT_SUCCEEDED(hr); + } + + ID3D11Device *device() const + { + return m_d3dDevice.Get(); + } + + ID3D11Texture2D *target() const + { + return m_target; + } + + void blit(ID3D11Texture2D *texture) + { + HRESULT hr; + D3D11_TEXTURE2D_DESC desc; + texture->GetDesc(&desc); + if (!m_videoEnumerator) { + D3D11_VIDEO_PROCESSOR_CONTENT_DESC videoProcessorDesc = { + D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE, + { 0 }, desc.Width, desc.Height, + { 0 }, desc.Width, desc.Height, + D3D11_VIDEO_USAGE_PLAYBACK_NORMAL + }; + hr = m_videoDevice->CreateVideoProcessorEnumerator(&videoProcessorDesc, &m_videoEnumerator); + RETURN_VOID_IF_FAILED("Failed to create video enumerator"); + } + + if (!m_videoProcessor) { + hr = m_videoDevice->CreateVideoProcessor(m_videoEnumerator.Get(), 0, &m_videoProcessor); + RETURN_VOID_IF_FAILED("Failed to create video processor"); + } + + if (!m_outputView) { + D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = { D3D11_VPOV_DIMENSION_TEXTURE2D }; + hr = m_videoDevice->CreateVideoProcessorOutputView( + m_targetTexture.Get(), m_videoEnumerator.Get(), &outputDesc, &m_outputView); + RETURN_VOID_IF_FAILED("Failed to create video output view"); + } + + D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputViewDesc = { + 0, D3D11_VPIV_DIMENSION_TEXTURE2D, { 0, 0 } + }; + ComPtr<ID3D11VideoProcessorInputView> inputView; + hr = m_videoDevice->CreateVideoProcessorInputView( + texture, m_videoEnumerator.Get(), &inputViewDesc, &inputView); + RETURN_VOID_IF_FAILED("Failed to create video input view"); + + ComPtr<ID3D11DeviceContext> context; + ComPtr<ID3D11VideoContext> videoContext; + m_d3dDevice->GetImmediateContext(&context); + hr = context.As(&videoContext); + RETURN_VOID_IF_FAILED("Failed to get video context"); + + D3D11_VIDEO_PROCESSOR_STREAM stream = { TRUE }; + stream.pInputSurface = inputView.Get(); + hr = videoContext->VideoProcessorBlt( + m_videoProcessor.Get(), m_outputView.Get(), 0, 1, &stream); + RETURN_VOID_IF_FAILED("Failed to get blit video frame"); + } + +private: + ComPtr<ID3D11Device> m_d3dDevice; + ComPtr<ID3D11Texture2D> m_targetTexture; + ID3D11Texture2D *m_target; + ComPtr<ID3D11VideoDevice> m_videoDevice; + ComPtr<ID3D11VideoProcessorEnumerator> m_videoEnumerator; + ComPtr<ID3D11VideoProcessor> m_videoProcessor; + ComPtr<ID3D11VideoProcessorOutputView> m_outputView; +}; + +class QWinRTCameraVideoRendererControlPrivate +{ +public: + QScopedPointer<D3DVideoBlitter> blitter; + QVector<ComPtr<IMF2DBuffer>> buffers; +}; + +QWinRTCameraVideoRendererControl::QWinRTCameraVideoRendererControl(const QSize &size, QObject *parent) + : QWinRTAbstractVideoRendererControl(size, parent), d_ptr(new QWinRTCameraVideoRendererControlPrivate) +{ +} + +QWinRTCameraVideoRendererControl::~QWinRTCameraVideoRendererControl() +{ + shutdown(); +} + +bool QWinRTCameraVideoRendererControl::render(ID3D11Texture2D *target) +{ + Q_D(QWinRTCameraVideoRendererControl); + + if (d->buffers.isEmpty()) { + emit bufferRequested(); + return false; + } + + HRESULT hr; + ComPtr<IMF2DBuffer> buffer = d->buffers.takeFirst(); + + ComPtr<ID3D11Texture2D> sourceTexture; + ComPtr<IMFDXGIBuffer> dxgiBuffer; + hr = buffer.As(&dxgiBuffer); + Q_ASSERT_SUCCEEDED(hr); + hr = dxgiBuffer->GetResource(IID_PPV_ARGS(&sourceTexture)); + if (FAILED(hr)) { + qErrnoWarning(hr, "The video frame does not support texture output; aborting rendering."); + return false; + } + + ComPtr<ID3D11Device> device; + sourceTexture->GetDevice(&device); + if (!d->blitter || d->blitter->device() != device.Get() || d->blitter->target() != target) + d->blitter.reset(new D3DVideoBlitter(device.Get(), target)); + + d->blitter->blit(sourceTexture.Get()); + + emit bufferRequested(); + return true; +} + +void QWinRTCameraVideoRendererControl::queueBuffer(IMF2DBuffer *buffer) +{ + Q_D(QWinRTCameraVideoRendererControl); + Q_ASSERT(buffer); + d->buffers.append(buffer); +} + +void QWinRTCameraVideoRendererControl::discardBuffers() +{ + Q_D(QWinRTCameraVideoRendererControl); + d->buffers.clear(); +} diff --git a/src/plugins/winrt/qwinrtcameravideorenderercontrol.h b/src/plugins/winrt/qwinrtcameravideorenderercontrol.h new file mode 100644 index 000000000..ed8b3388d --- /dev/null +++ b/src/plugins/winrt/qwinrtcameravideorenderercontrol.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWINRTCAMERAVIDEORENDERERCONTROL_H +#define QWINRTCAMERAVIDEORENDERERCONTROL_H + +#include "qwinrtabstractvideorenderercontrol.h" + +struct IMF2DBuffer; + +QT_BEGIN_NAMESPACE + +class QVideoSurfaceFormat; +class QWinRTCameraVideoRendererControlPrivate; +class QWinRTCameraVideoRendererControl : public QWinRTAbstractVideoRendererControl +{ + Q_OBJECT +public: + explicit QWinRTCameraVideoRendererControl(const QSize &size, QObject *parent); + ~QWinRTCameraVideoRendererControl(); + + bool render(ID3D11Texture2D *texture) Q_DECL_OVERRIDE; + void queueBuffer(IMF2DBuffer *buffer); + void discardBuffers(); + +signals: + void bufferRequested(); + +private: + QScopedPointer<QWinRTCameraVideoRendererControlPrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTCameraVideoRendererControl) +}; + +QT_END_NAMESPACE + +#endif // QWINRTCAMERAVIDEORENDERERCONTROL_H diff --git a/src/plugins/winrt/qwinrtserviceplugin.cpp b/src/plugins/winrt/qwinrtserviceplugin.cpp index 5d49b44a4..036d63e39 100644 --- a/src/plugins/winrt/qwinrtserviceplugin.cpp +++ b/src/plugins/winrt/qwinrtserviceplugin.cpp @@ -44,6 +44,8 @@ #include "qwinrtserviceplugin.h" #include "qwinrtmediaplayerservice.h" +#include "qwinrtcameraservice.h" +#include "qwinrtvideodeviceselectorcontrol.h" QT_USE_NAMESPACE @@ -52,6 +54,9 @@ QMediaService *QWinRTServicePlugin::create(QString const &key) if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) return new QWinRTMediaPlayerService(this); + if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) + return new QWinRTCameraService(this); + return Q_NULLPTR; } @@ -68,3 +73,37 @@ QMediaServiceProviderHint::Features QWinRTServicePlugin::supportedFeatures( return QMediaServiceProviderHint::Features(); } + +QCamera::Position QWinRTServicePlugin::cameraPosition(const QByteArray &device) const +{ + return QWinRTVideoDeviceSelectorControl::cameraPosition(device); +} + +int QWinRTServicePlugin::cameraOrientation(const QByteArray &device) const +{ + return QWinRTVideoDeviceSelectorControl::cameraOrientation(device); +} + +QList<QByteArray> QWinRTServicePlugin::devices(const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_CAMERA) + return QWinRTVideoDeviceSelectorControl::deviceNames(); + + return QList<QByteArray>(); +} + +QString QWinRTServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) +{ + if (service == Q_MEDIASERVICE_CAMERA) + return QWinRTVideoDeviceSelectorControl::deviceDescription(device); + + return QString(); +} + +QByteArray QWinRTServicePlugin::defaultDevice(const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_CAMERA) + return QWinRTVideoDeviceSelectorControl::defaultDeviceName(); + + return QByteArray(); +} diff --git a/src/plugins/winrt/qwinrtserviceplugin.h b/src/plugins/winrt/qwinrtserviceplugin.h index aaac79c7b..9fabadb4f 100644 --- a/src/plugins/winrt/qwinrtserviceplugin.h +++ b/src/plugins/winrt/qwinrtserviceplugin.h @@ -48,15 +48,29 @@ QT_USE_NAMESPACE class QWinRTServicePlugin : public QMediaServiceProviderPlugin , public QMediaServiceFeaturesInterface + , public QMediaServiceCameraInfoInterface + , public QMediaServiceSupportedDevicesInterface + , public QMediaServiceDefaultDeviceInterface { Q_OBJECT Q_INTERFACES(QMediaServiceFeaturesInterface) + Q_INTERFACES(QMediaServiceCameraInfoInterface) + Q_INTERFACES(QMediaServiceSupportedDevicesInterface) + Q_INTERFACES(QMediaServiceDefaultDeviceInterface) Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE "winrt.json") public: QMediaService *create(QString const &key); void release(QMediaService *service); QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const; + + QCamera::Position cameraPosition(const QByteArray &device) const Q_DECL_OVERRIDE; + int cameraOrientation(const QByteArray &device) const Q_DECL_OVERRIDE; + + QList<QByteArray> devices(const QByteArray &service) const Q_DECL_OVERRIDE; + QString deviceDescription(const QByteArray &service, const QByteArray &device) Q_DECL_OVERRIDE; + + QByteArray defaultDevice(const QByteArray &service) const Q_DECL_OVERRIDE; }; #endif // QWINRTSERVICEPLUGIN_H diff --git a/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp b/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp new file mode 100644 index 000000000..8058c3dad --- /dev/null +++ b/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.cpp @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** 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 "qwinrtvideodeviceselectorcontrol.h" + +#include <QtCore/qfunctions_winrt.h> +#include <QtCore/QVector> +#include <QtCore/QCoreApplication> +#include <QtCore/QEventLoop> +#include <QtCore/QGlobalStatic> + +#include <wrl.h> +#include <windows.devices.enumeration.h> + +using namespace ABI::Windows::Devices::Enumeration; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; + +typedef ITypedEventHandler<DeviceWatcher *, DeviceInformation *> DeviceInformationHandler; +typedef ITypedEventHandler<DeviceWatcher *, DeviceInformationUpdate *> DeviceInformationUpdateHandler; +typedef ITypedEventHandler<DeviceWatcher *, IInspectable *> DeviceEnumerationCompletedHandler; + +QT_USE_NAMESPACE + +static QString deviceName(IDeviceInformation *device) +{ + HRESULT hr; + HString id; + hr = device->get_Id(id.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + quint32 length; + const wchar_t *buffer = id.GetRawBuffer(&length); + return QString::fromWCharArray(buffer, length); +} + +static QString deviceDescription(IDeviceInformation *device) +{ + HRESULT hr; + HString name; + hr = device->get_Name(name.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + quint32 length; + const wchar_t *buffer = name.GetRawBuffer(&length); + return QString::fromWCharArray(buffer, length); +} + +struct QWinRTVideoDeviceSelectorControlGlobal +{ + QWinRTVideoDeviceSelectorControlGlobal() + : defaultDeviceIndex(-1) + { + HRESULT hr; + ComPtr<IDeviceInformationStatics> deviceWatcherFactory; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get(), + IID_PPV_ARGS(&deviceWatcherFactory)); + Q_ASSERT_SUCCEEDED(hr); + hr = deviceWatcherFactory->CreateWatcherDeviceClass(DeviceClass_VideoCapture, &deviceWatcher); + Q_ASSERT_SUCCEEDED(hr); + + hr = deviceWatcher->add_Added( + Callback<DeviceInformationHandler>(this, &QWinRTVideoDeviceSelectorControlGlobal::onDeviceAdded).Get(), + &deviceAddedToken); + Q_ASSERT_SUCCEEDED(hr); + + hr = deviceWatcher->add_Removed( + Callback<DeviceInformationUpdateHandler>(this, &QWinRTVideoDeviceSelectorControlGlobal::onDeviceRemoved).Get(), + &deviceRemovedToken); + Q_ASSERT_SUCCEEDED(hr); + + hr = deviceWatcher->add_Updated( + Callback<DeviceInformationUpdateHandler>(this, &QWinRTVideoDeviceSelectorControlGlobal::onDeviceUpdated).Get(), + &deviceUpdatedToken); + Q_ASSERT_SUCCEEDED(hr); + + // Synchronously populate the devices on construction + ComPtr<IAsyncOperation<DeviceInformationCollection *>> op; + hr = deviceWatcherFactory->FindAllAsyncDeviceClass(DeviceClass_VideoCapture, &op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<DeviceInformation *>> deviceList; + hr = QWinRTFunctions::await(op, deviceList.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + quint32 deviceCount; + hr = deviceList->get_Size(&deviceCount); + Q_ASSERT_SUCCEEDED(hr); + for (quint32 i = 0; i < deviceCount; ++i) { + IDeviceInformation *device; + hr = deviceList->GetAt(i, &device); + Q_ASSERT_SUCCEEDED(hr); + onDeviceAdded(Q_NULLPTR, device); + } + + // If there is no default device provided by the API, choose the first one + if (!devices.isEmpty() && defaultDeviceIndex < 0) + defaultDeviceIndex = 0; + } + + ~QWinRTVideoDeviceSelectorControlGlobal() + { + HRESULT hr; + hr = deviceWatcher->remove_Added(deviceAddedToken); + Q_ASSERT_SUCCEEDED(hr); + hr = deviceWatcher->remove_Removed(deviceRemovedToken); + Q_ASSERT_SUCCEEDED(hr); + hr = deviceWatcher->remove_Updated(deviceUpdatedToken); + Q_ASSERT_SUCCEEDED(hr); + } + +private: + HRESULT onDeviceAdded(IDeviceWatcher *, IDeviceInformation *device) + { + const QString name = deviceName(device); + if (deviceIndex.contains(name)) + return S_OK; + + devices.append(device); + const int index = devices.size() - 1; + deviceIndex.insert(name, index); + + HRESULT hr; + boolean isDefault; + hr = device->get_IsDefault(&isDefault); + Q_ASSERT_SUCCEEDED(hr); + if (isDefault) + defaultDeviceIndex = index; + + foreach (QWinRTVideoDeviceSelectorControl *watcher, watchers) + emit watcher->devicesChanged(); + + return S_OK; + } + + HRESULT onDeviceRemoved(IDeviceWatcher *, IDeviceInformationUpdate *device) + { + HRESULT hr; + HString id; + hr = device->get_Id(id.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + HString name; + hr = device->get_Id(name.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + quint32 nameLength; + const wchar_t *nameString = name.GetRawBuffer(&nameLength); + const int index = deviceIndex.take(QString::fromWCharArray(nameString, nameLength)); + if (index >= 0) + devices.remove(index); + + foreach (QWinRTVideoDeviceSelectorControl *watcher, watchers) + emit watcher->devicesChanged(); + + return S_OK; + } + + HRESULT onDeviceUpdated(IDeviceWatcher *, IDeviceInformationUpdate *) + { + // A name or description may have changed, so emit devicesChanged + foreach (QWinRTVideoDeviceSelectorControl *watcher, watchers) + emit watcher->devicesChanged(); + + return S_OK; + } + +public: + void addWatcher(QWinRTVideoDeviceSelectorControl *control) + { + watchers.append(control); + + HRESULT hr; + DeviceWatcherStatus status; + hr = deviceWatcher->get_Status(&status); + Q_ASSERT_SUCCEEDED(hr); + if (status != DeviceWatcherStatus_Started) { + // We can't immediately Start() if we have just called Stop() + while (status == DeviceWatcherStatus_Stopping) { + QThread::yieldCurrentThread(); + hr = deviceWatcher->get_Status(&status); + Q_ASSERT_SUCCEEDED(hr); + } + hr = deviceWatcher->Start(); + Q_ASSERT_SUCCEEDED(hr); + } + } + + void removeWatcher(QWinRTVideoDeviceSelectorControl *control) + { + watchers.removeAll(control); + + if (!watchers.isEmpty()) + return; + + HRESULT hr; + DeviceWatcherStatus status; + hr = deviceWatcher->get_Status(&status); + Q_ASSERT_SUCCEEDED(hr); + if (status == DeviceWatcherStatus_Stopped || status == DeviceWatcherStatus_Stopping) + return; + + hr = deviceWatcher->Stop(); + Q_ASSERT_SUCCEEDED(hr); + } + + QVector<ComPtr<IDeviceInformation>> devices; + QHash<QString, int> deviceIndex; + int defaultDeviceIndex; + +private: + ComPtr<IDeviceWatcher> deviceWatcher; + QList<QWinRTVideoDeviceSelectorControl *> watchers; + EventRegistrationToken deviceAddedToken; + EventRegistrationToken deviceRemovedToken; + EventRegistrationToken deviceUpdatedToken; +}; +Q_GLOBAL_STATIC(QWinRTVideoDeviceSelectorControlGlobal, g) + +class QWinRTVideoDeviceSelectorControlPrivate +{ +public: + int selectedDevice; +}; + +QWinRTVideoDeviceSelectorControl::QWinRTVideoDeviceSelectorControl(QObject *parent) + : QVideoDeviceSelectorControl(parent), d_ptr(new QWinRTVideoDeviceSelectorControlPrivate) +{ + Q_D(QWinRTVideoDeviceSelectorControl); + d->selectedDevice = -1; + g->addWatcher(this); +} + +QWinRTVideoDeviceSelectorControl::~QWinRTVideoDeviceSelectorControl() +{ + if (g.isDestroyed()) + return; + + g->removeWatcher(this); +} + +int QWinRTVideoDeviceSelectorControl::deviceCount() const +{ + return g->devices.size(); +} + +QString QWinRTVideoDeviceSelectorControl::deviceName(int index) const +{ + if (index < 0 || index >= g->devices.size()) + return QString(); + + return ::deviceName(g->devices.at(index).Get()); +} + +QString QWinRTVideoDeviceSelectorControl::deviceDescription(int index) const +{ + if (index < 0 || index >= g->devices.size()) + return QString(); + + return ::deviceDescription(g->devices.at(index).Get()); +} + +int QWinRTVideoDeviceSelectorControl::defaultDevice() const +{ + return g->defaultDeviceIndex; +} + +int QWinRTVideoDeviceSelectorControl::selectedDevice() const +{ + Q_D(const QWinRTVideoDeviceSelectorControl); + return d->selectedDevice; +} + +QCamera::Position QWinRTVideoDeviceSelectorControl::cameraPosition(const QString &deviceName) +{ + int deviceIndex = g->deviceIndex.value(deviceName); + IDeviceInformation *deviceInfo = g->devices.value(deviceIndex).Get(); + if (!deviceInfo) + return QCamera::UnspecifiedPosition; + + ComPtr<IEnclosureLocation> enclosureLocation; + HRESULT hr; + hr = deviceInfo->get_EnclosureLocation(&enclosureLocation); + RETURN_IF_FAILED("Failed to get camera enclosure location", return QCamera::UnspecifiedPosition); + if (!enclosureLocation) + return QCamera::UnspecifiedPosition; + + Panel panel; + hr = enclosureLocation->get_Panel(&panel); + RETURN_IF_FAILED("Failed to get camera panel location", return QCamera::UnspecifiedPosition); + + switch (panel) { + case Panel_Front: + return QCamera::FrontFace; + case Panel_Back: + return QCamera::BackFace; + default: + break; + } + return QCamera::UnspecifiedPosition; +} + +int QWinRTVideoDeviceSelectorControl::cameraOrientation(const QString &deviceName) +{ + Q_UNUSED(deviceName); + return 0; +} + +QList<QByteArray> QWinRTVideoDeviceSelectorControl::deviceNames() +{ + QList<QByteArray> devices; + foreach (const QString &device, g->deviceIndex.keys()) + devices.append(device.toUtf8()); + + return devices; +} + +QByteArray QWinRTVideoDeviceSelectorControl::deviceDescription(const QByteArray &deviceName) +{ + int deviceIndex = g->deviceIndex.value(QString::fromUtf8(deviceName), -1); + if (deviceIndex < 0) + return QByteArray(); + + return ::deviceDescription(g->devices.value(deviceIndex).Get()).toUtf8(); +} + +QByteArray QWinRTVideoDeviceSelectorControl::defaultDeviceName() +{ + if (g->defaultDeviceIndex < 0) + return QByteArray(); + + return ::deviceName(g->devices.value(g->defaultDeviceIndex).Get()).toUtf8(); +} + +void QWinRTVideoDeviceSelectorControl::setSelectedDevice(int index) +{ + Q_D(QWinRTVideoDeviceSelectorControl); + + int selectedDevice = index; + if (index < 0 || index >= g->devices.size()) + selectedDevice = -1; + + if (d->selectedDevice != selectedDevice) { + d->selectedDevice = selectedDevice; + emit selectedDeviceChanged(d->selectedDevice); + emit selectedDeviceChanged(deviceName(d->selectedDevice)); + } +} diff --git a/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h b/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h new file mode 100644 index 000000000..2143b3ea8 --- /dev/null +++ b/src/plugins/winrt/qwinrtvideodeviceselectorcontrol.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QWINRTVIDEODEVICESELECTORCONTROL_H +#define QWINRTVIDEODEVICESELECTORCONTROL_H + +#include <QtMultimedia/QVideoDeviceSelectorControl> +#include <QtMultimedia/QCameraInfoControl> +#include <QtCore/qt_windows.h> + +struct IInspectable; +namespace ABI { + namespace Windows { + namespace Devices { + namespace Enumeration { + struct IDeviceInformation; + } + } + } +} + +QT_BEGIN_NAMESPACE + +class QWinRTVideoDeviceSelectorControlPrivate; +class QWinRTVideoDeviceSelectorControl : public QVideoDeviceSelectorControl +{ + Q_OBJECT +public: + explicit QWinRTVideoDeviceSelectorControl(QObject *parent = 0); + ~QWinRTVideoDeviceSelectorControl(); + + int deviceCount() const Q_DECL_OVERRIDE; + + QString deviceName(int index) const Q_DECL_OVERRIDE; + QString deviceDescription(int index) const Q_DECL_OVERRIDE; + + int defaultDevice() const Q_DECL_OVERRIDE; + int selectedDevice() const Q_DECL_OVERRIDE; + + static QCamera::Position cameraPosition(const QString &deviceName); + static int cameraOrientation(const QString &deviceName); + static QList<QByteArray> deviceNames(); + static QByteArray deviceDescription(const QByteArray &deviceName); + static QByteArray defaultDeviceName(); + +public slots: + void setSelectedDevice(int index) Q_DECL_OVERRIDE; + +private: + QScopedPointer<QWinRTVideoDeviceSelectorControlPrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTVideoDeviceSelectorControl) +}; + +QT_END_NAMESPACE + +#endif // QWINRTVIDEODEVICESELECTORCONTROL_H diff --git a/src/plugins/winrt/winrt.json b/src/plugins/winrt/winrt.json index b85cfeb12..9af79cc37 100644 --- a/src/plugins/winrt/winrt.json +++ b/src/plugins/winrt/winrt.json @@ -1,4 +1,4 @@ { "Keys": ["winrt"], - "Services": ["org.qt-project.qt.mediaplayer"] + "Services": ["org.qt-project.qt.mediaplayer", "org.qt-project.qt.camera"] } diff --git a/src/plugins/winrt/winrt.pro b/src/plugins/winrt/winrt.pro index 0ea90d22e..04db71e75 100644 --- a/src/plugins/winrt/winrt.pro +++ b/src/plugins/winrt/winrt.pro @@ -5,21 +5,33 @@ PLUGIN_TYPE=mediaservice PLUGIN_CLASS_NAME = WinRTServicePlugin load(qt_plugin) -LIBS += -lmfplat -lmfuuid -loleaut32 -ld3d11 +LIBS += -lmfplat -lmfuuid -loleaut32 -ld3d11 -lruntimeobject HEADERS += \ qwinrtabstractvideorenderercontrol.h \ + qwinrtcameracontrol.h \ + qwinrtcamerainfocontrol.h \ + qwinrtcameraimagecapturecontrol.h \ + qwinrtcameraservice.h \ + qwinrtcameravideorenderercontrol.h \ qwinrtmediaplayercontrol.h \ qwinrtmediaplayerservice.h \ qwinrtplayerrenderercontrol.h \ - qwinrtserviceplugin.h + qwinrtserviceplugin.h \ + qwinrtvideodeviceselectorcontrol.h SOURCES += \ qwinrtabstractvideorenderercontrol.cpp \ + qwinrtcameracontrol.cpp \ + qwinrtcamerainfocontrol.cpp \ + qwinrtcameraimagecapturecontrol.cpp \ + qwinrtcameraservice.cpp \ + qwinrtcameravideorenderercontrol.cpp \ qwinrtmediaplayercontrol.cpp \ qwinrtmediaplayerservice.cpp \ qwinrtplayerrenderercontrol.cpp \ - qwinrtserviceplugin.cpp + qwinrtserviceplugin.cpp \ + qwinrtvideodeviceselectorcontrol.cpp OTHER_FILES += \ winrt.json |