winrt: fix camera control after move to xaml thread

Camera control management has to happen inside the xaml thread,
otherwise the behavior is undefined. This results in ie. the first
capture not working due to synchronization issues.

When (de-)activating the videoRenderer we have to switch to the UI
thread first.

In addition add focus for Windows 10 (Mobile), previously it was compiled
for Windows Phone 8.1 only. On desktop this might return no focus mode
to be supported, but API-wise this is available.

Task-number: QTBUG-47803
Change-Id: I9b345ebc82502fc6e00aede43b9096893cd0ad53
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
This commit is contained in:
Maurice Kalinowski
2016-04-15 08:33:42 +02:00
committed by Maurice Kalinowski
parent 65c9e24c10
commit a0a20157ef
5 changed files with 233 additions and 196 deletions

View File

@@ -68,8 +68,6 @@ public:
void setScanLineDirection(QVideoSurfaceFormat::Direction direction); void setScanLineDirection(QVideoSurfaceFormat::Direction direction);
void setActive(bool active);
BlitMode blitMode() const; BlitMode blitMode() const;
void setBlitMode(BlitMode mode); void setBlitMode(BlitMode mode);
@@ -78,6 +76,9 @@ public:
static ID3D11Device *d3dDevice(); static ID3D11Device *d3dDevice();
public slots:
void setActive(bool active);
protected: protected:
void shutdown(); void shutdown();

View File

@@ -45,7 +45,9 @@
#include <QtCore/qfunctions_winrt.h> #include <QtCore/qfunctions_winrt.h>
#include <QtCore/QPointer> #include <QtCore/QPointer>
#include <QtGui/QGuiApplication> #include <QtGui/QGuiApplication>
#include <private/qeventdispatcher_winrt_p.h>
#include <functional>
#include <mfapi.h> #include <mfapi.h>
#include <mferror.h> #include <mferror.h>
#include <mfidl.h> #include <mfidl.h>
@@ -446,7 +448,7 @@ public:
HRESULT __stdcall Shutdown() Q_DECL_OVERRIDE HRESULT __stdcall Shutdown() Q_DECL_OVERRIDE
{ {
m_stream->Flush(); m_stream->Flush();
m_videoRenderer->setActive(false); scheduleSetActive(false);
return m_presentationClock ? m_presentationClock->Stop() : S_OK; return m_presentationClock ? m_presentationClock->Stop() : S_OK;
} }
@@ -455,7 +457,7 @@ public:
Q_UNUSED(systemTime); Q_UNUSED(systemTime);
Q_UNUSED(clockStartOffset); Q_UNUSED(clockStartOffset);
m_videoRenderer->setActive(true); scheduleSetActive(true);
return S_OK; return S_OK;
} }
@@ -464,7 +466,7 @@ public:
{ {
Q_UNUSED(systemTime); Q_UNUSED(systemTime);
m_videoRenderer->setActive(false); scheduleSetActive(false);
return m_stream->QueueEvent(MEStreamSinkStopped, GUID_NULL, S_OK, Q_NULLPTR); return m_stream->QueueEvent(MEStreamSinkStopped, GUID_NULL, S_OK, Q_NULLPTR);
} }
@@ -473,7 +475,7 @@ public:
{ {
Q_UNUSED(systemTime); Q_UNUSED(systemTime);
m_videoRenderer->setActive(false); scheduleSetActive(false);
return m_stream->QueueEvent(MEStreamSinkPaused, GUID_NULL, S_OK, Q_NULLPTR); return m_stream->QueueEvent(MEStreamSinkPaused, GUID_NULL, S_OK, Q_NULLPTR);
} }
@@ -482,7 +484,7 @@ public:
{ {
Q_UNUSED(systemTime); Q_UNUSED(systemTime);
m_videoRenderer->setActive(true); scheduleSetActive(true);
return m_stream->QueueEvent(MEStreamSinkStarted, GUID_NULL, S_OK, Q_NULLPTR); return m_stream->QueueEvent(MEStreamSinkStarted, GUID_NULL, S_OK, Q_NULLPTR);
} }
@@ -495,6 +497,12 @@ public:
} }
private: private:
inline void scheduleSetActive(bool active)
{
QMetaObject::invokeMethod(m_videoRenderer, "setActive", Qt::QueuedConnection, Q_ARG(bool, active));
}
ComPtr<MediaStream> m_stream; ComPtr<MediaStream> m_stream;
ComPtr<IMFPresentationClock> m_presentationClock; ComPtr<IMFPresentationClock> m_presentationClock;
@@ -587,16 +595,21 @@ void QWinRTCameraControl::setState(QCamera::State state)
} }
Q_ASSERT(d->state == QCamera::LoadedState); Q_ASSERT(d->state == QCamera::LoadedState);
d->mediaSink = Make<MediaSink>(d->encodingProfile.Get(), d->videoRenderer);
ComPtr<IAsyncAction> op; ComPtr<IAsyncAction> op;
hr = d->capturePreview->StartPreviewToCustomSinkAsync(d->encodingProfile.Get(), d->mediaSink.Get(), &op); hr = QEventDispatcherWinRT::runOnXamlThread([d, &op]() {
RETURN_VOID_AND_EMIT_ERROR("Failed to initiate capture"); d->mediaSink = Make<MediaSink>(d->encodingProfile.Get(), d->videoRenderer);
HRESULT hr = d->capturePreview->StartPreviewToCustomSinkAsync(d->encodingProfile.Get(), d->mediaSink.Get(), &op);
return hr;
});
RETURN_VOID_AND_EMIT_ERROR("Failed to initiate capture.");
if (d->status != QCamera::StartingStatus) { if (d->status != QCamera::StartingStatus) {
d->status = QCamera::StartingStatus; d->status = QCamera::StartingStatus;
emit statusChanged(d->status); emit statusChanged(d->status);
} }
hr = QWinRTFunctions::await(op); hr = QEventDispatcherWinRT::runOnXamlThread([&op]() {
return QWinRTFunctions::await(op);
});
if (FAILED(hr)) { if (FAILED(hr)) {
emit error(QCamera::CameraError, qt_error_string(hr)); emit error(QCamera::CameraError, qt_error_string(hr));
setState(QCamera::UnloadedState); // Unload everything, as initialize() will need be called again setState(QCamera::UnloadedState); // Unload everything, as initialize() will need be called again
@@ -611,7 +624,7 @@ void QWinRTCameraControl::setState(QCamera::State state)
emit stateChanged(d->state); emit stateChanged(d->state);
d->status = QCamera::ActiveStatus; d->status = QCamera::ActiveStatus;
emit statusChanged(d->status); emit statusChanged(d->status);
d->mediaSink->RequestSample(); QEventDispatcherWinRT::runOnXamlThread([d]() { d->mediaSink->RequestSample(); return S_OK;});
break; break;
} }
case QCamera::LoadedState: { case QCamera::LoadedState: {
@@ -639,20 +652,28 @@ void QWinRTCameraControl::setState(QCamera::State state)
} }
ComPtr<IAsyncAction> op; ComPtr<IAsyncAction> op;
hr = d->capturePreview->StopPreviewAsync(&op); hr = QEventDispatcherWinRT::runOnXamlThread([d, &op]() {
HRESULT hr = d->capturePreview->StopPreviewAsync(&op);
return hr;
});
RETURN_VOID_AND_EMIT_ERROR("Failed to stop camera preview"); RETURN_VOID_AND_EMIT_ERROR("Failed to stop camera preview");
if (d->status != QCamera::StoppingStatus) { if (d->status != QCamera::StoppingStatus) {
d->status = QCamera::StoppingStatus; d->status = QCamera::StoppingStatus;
emit statusChanged(d->status); emit statusChanged(d->status);
} }
Q_ASSERT_SUCCEEDED(hr); Q_ASSERT_SUCCEEDED(hr);
hr = QWinRTFunctions::await(op); // Synchronize unloading hr = QEventDispatcherWinRT::runOnXamlThread([&op]() {
return QWinRTFunctions::await(op); // Synchronize unloading
});
if (FAILED(hr)) if (FAILED(hr))
emit error(QCamera::InvalidRequestError, qt_error_string(hr)); emit error(QCamera::InvalidRequestError, qt_error_string(hr));
if (d->mediaSink) { if (d->mediaSink) {
hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
d->mediaSink->Shutdown(); d->mediaSink->Shutdown();
d->mediaSink.Reset(); d->mediaSink.Reset();
return S_OK;
});
} }
d->state = QCamera::LoadedState; d->state = QCamera::LoadedState;
@@ -671,6 +692,8 @@ void QWinRTCameraControl::setState(QCamera::State state)
emit statusChanged(d->status); emit statusChanged(d->status);
} }
hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
HRESULT hr;
if (d->capture && d->captureFailedCookie.value) { if (d->capture && d->captureFailedCookie.value) {
hr = d->capture->remove_Failed(d->captureFailedCookie); hr = d->capture->remove_Failed(d->captureFailedCookie);
Q_ASSERT_SUCCEEDED(hr); Q_ASSERT_SUCCEEDED(hr);
@@ -685,8 +708,11 @@ void QWinRTCameraControl::setState(QCamera::State state)
hr = d->capture.As(&capture); hr = d->capture.As(&capture);
Q_ASSERT_SUCCEEDED(hr); Q_ASSERT_SUCCEEDED(hr);
hr = capture->Close(); hr = capture->Close();
RETURN_VOID_AND_EMIT_ERROR("Failed to close the capture manger"); RETURN_HR_IF_FAILED("");
d->capture.Reset(); d->capture.Reset();
return hr;
});
RETURN_VOID_AND_EMIT_ERROR("Failed to close the capture manger");
if (d->state != QCamera::UnloadedState) { if (d->state != QCamera::UnloadedState) {
d->state = QCamera::UnloadedState; d->state = QCamera::UnloadedState;
emit stateChanged(d->state); emit stateChanged(d->state);
@@ -824,6 +850,7 @@ HRESULT QWinRTCameraControl::initialize()
emit statusChanged(d->status); emit statusChanged(d->status);
} }
HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() {
HRESULT hr; HRESULT hr;
ComPtr<IInspectable> capture; ComPtr<IInspectable> capture;
hr = RoActivateInstance(Wrappers::HString::MakeReference(RuntimeClass_Windows_Media_Capture_MediaCapture).Get(), hr = RoActivateInstance(Wrappers::HString::MakeReference(RuntimeClass_Windows_Media_Capture_MediaCapture).Get(),
@@ -973,6 +1000,9 @@ HRESULT QWinRTCameraControl::initialize()
if (d->videoRenderer) if (d->videoRenderer)
d->videoRenderer->setSize(viewfinderResolution); d->videoRenderer->setSize(viewfinderResolution);
return S_OK;
});
if (SUCCEEDED(hr) && d->state != QCamera::LoadedState) { if (SUCCEEDED(hr) && d->state != QCamera::LoadedState) {
d->state = QCamera::LoadedState; d->state = QCamera::LoadedState;
emit stateChanged(d->state); emit stateChanged(d->state);
@@ -984,7 +1014,7 @@ HRESULT QWinRTCameraControl::initialize()
return hr; return hr;
} }
#ifdef Q_OS_WINPHONE #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
HRESULT QWinRTCameraControl::initializeFocus() HRESULT QWinRTCameraControl::initializeFocus()
{ {
@@ -1231,7 +1261,7 @@ bool QWinRTCameraControl::unlockFocus()
return QWinRTFunctions::await(op) == S_OK; return QWinRTFunctions::await(op) == S_OK;
} }
#else // Q_OS_WINPHONE #else // !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
HRESULT QWinRTCameraControl::initializeFocus() HRESULT QWinRTCameraControl::initializeFocus()
{ {
@@ -1272,7 +1302,7 @@ bool QWinRTCameraControl::unlockFocus()
return false; return false;
} }
#endif // !Q_OS_WINPHONE #endif // !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
void QWinRTCameraControl::frameMapped() void QWinRTCameraControl::frameMapped()
{ {

View File

@@ -46,8 +46,10 @@
#include <QtCore/QStandardPaths> #include <QtCore/QStandardPaths>
#include <QtCore/QVector> #include <QtCore/QVector>
#include <QtCore/qfunctions_winrt.h> #include <QtCore/qfunctions_winrt.h>
#include <QtCore/private/qeventdispatcher_winrt_p.h>
#include <QtMultimedia/private/qmediastoragelocation_p.h> #include <QtMultimedia/private/qmediastoragelocation_p.h>
#include <functional>
#include <wrl.h> #include <wrl.h>
#include <windows.media.capture.h> #include <windows.media.capture.h>
#include <windows.media.devices.h> #include <windows.media.devices.h>
@@ -161,6 +163,7 @@ int QWinRTCameraImageCaptureControl::capture(const QString &fileName)
fileName.isEmpty() ? QStringLiteral("jpg") : QFileInfo(fileName).suffix()) fileName.isEmpty() ? QStringLiteral("jpg") : QFileInfo(fileName).suffix())
}; };
HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, d, capture, &request]() {
HRESULT hr; HRESULT hr;
hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream).Get(), hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream).Get(),
&request.stream); &request.stream);
@@ -179,7 +182,7 @@ int QWinRTCameraImageCaptureControl::capture(const QString &fileName)
Q_ASSERT_SUCCEEDED(hr); Q_ASSERT_SUCCEEDED(hr);
if (!request.op) { if (!request.op) {
qErrnoWarning("Camera photo capture failed."); qErrnoWarning("Camera photo capture failed.");
return -1; return E_FAIL;
} }
emit captureQueueChanged(false); emit captureQueueChanged(false);
d->requests.insert(request.op.Get(), request); d->requests.insert(request.op.Get(), request);
@@ -187,7 +190,10 @@ int QWinRTCameraImageCaptureControl::capture(const QString &fileName)
hr = request.op->put_Completed(Callback<IAsyncActionCompletedHandler>( hr = request.op->put_Completed(Callback<IAsyncActionCompletedHandler>(
this, &QWinRTCameraImageCaptureControl::onCaptureCompleted).Get()); this, &QWinRTCameraImageCaptureControl::onCaptureCompleted).Get());
Q_ASSERT_SUCCEEDED(hr); Q_ASSERT_SUCCEEDED(hr);
return hr;
});
if (FAILED(hr))
return -1;
return request.id; return request.id;
} }

View File

@@ -48,7 +48,7 @@
#include "qwinrtcameracontrol.h" #include "qwinrtcameracontrol.h"
#ifdef Q_OS_WINPHONE #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
#include <Windows.Security.ExchangeActiveSyncProvisioning.h> #include <Windows.Security.ExchangeActiveSyncProvisioning.h>
using namespace ABI::Windows::Security::ExchangeActiveSyncProvisioning; using namespace ABI::Windows::Security::ExchangeActiveSyncProvisioning;
#endif #endif
@@ -58,7 +58,7 @@ using namespace Microsoft::WRL::Wrappers;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
#ifdef Q_OS_WINPHONE #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
template <int n> template <int n>
static bool blacklisted(const wchar_t (&blackListName)[n], const HString &deviceModel) static bool blacklisted(const wchar_t (&blackListName)[n], const HString &deviceModel)
{ {
@@ -282,7 +282,7 @@ QWinRTCameraVideoRendererControl::QWinRTCameraVideoRendererControl(const QSize &
d->cameraSampleformat = QVideoFrame::Format_User; d->cameraSampleformat = QVideoFrame::Format_User;
d->videoProbesCounter = 0; d->videoProbesCounter = 0;
#ifdef Q_OS_WINPHONE #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
// Workaround for certain devices which fail to blit. // Workaround for certain devices which fail to blit.
ComPtr<IEasClientDeviceInformation> deviceInfo; ComPtr<IEasClientDeviceInformation> deviceInfo;
HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Security_ExchangeActiveSyncProvisioning_EasClientDeviceInformation).Get(), HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Security_ExchangeActiveSyncProvisioning_EasClientDeviceInformation).Get(),

View File

@@ -332,7 +332,7 @@ QCamera::Position QWinRTVideoDeviceSelectorControl::cameraPosition(const QString
int QWinRTVideoDeviceSelectorControl::cameraOrientation(const QString &deviceName) int QWinRTVideoDeviceSelectorControl::cameraOrientation(const QString &deviceName)
{ {
#ifdef Q_OS_WINPHONE #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
switch (cameraPosition(deviceName)) { switch (cameraPosition(deviceName)) {
case QCamera::FrontFace: case QCamera::FrontFace:
case QCamera::BackFace: case QCamera::BackFace: