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>
This commit is contained in:
Andrew Knight
2014-08-29 16:59:09 +03:00
parent 80ba1d635d
commit 0c3438c9a1
16 changed files with 2375 additions and 4 deletions

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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));
}
}

View File

@@ -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

View File

@@ -1,4 +1,4 @@
{
"Keys": ["winrt"],
"Services": ["org.qt-project.qt.mediaplayer"]
"Services": ["org.qt-project.qt.mediaplayer", "org.qt-project.qt.camera"]
}

View File

@@ -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