winrt: Prevent camera device from being suspended.

On certain Lumia devices video buffer gets page locked when camera
is stopped. Subsequent call to video frame map/unmap leads to camera
device suspension. As a fix delay camera unload until all mapped
video frames are unmapped and return early from video frame map when
camera is not active.

Task-Id: QTBUG-48672
Change-Id: If547b9d430727bbe0e12cd8c07a30aeff81d13e3
Reviewed-by: Andrew Knight <andrew.knight@intopalo.com>
This commit is contained in:
Samuel Nevala
2015-10-14 10:48:00 +03:00
parent d50a09965a
commit 787dcd9e4d
3 changed files with 45 additions and 4 deletions

View File

@@ -524,6 +524,8 @@ public:
QPointer<QWinRTImageEncoderControl> imageEncoderControl;
QPointer<QWinRTCameraFocusControl> cameraFocusControl;
QPointer<QWinRTCameraLocksControl> cameraLocksControl;
QAtomicInt framesMapped;
QEventLoop *delayClose;
};
QWinRTCameraControl::QWinRTCameraControl(QObject *parent)
@@ -531,6 +533,7 @@ QWinRTCameraControl::QWinRTCameraControl(QObject *parent)
{
Q_D(QWinRTCameraControl);
d->delayClose = nullptr;
d->state = QCamera::UnloadedState;
d->status = QCamera::UnloadedStatus;
d->captureMode = QCamera::CaptureStillImage;
@@ -612,6 +615,14 @@ void QWinRTCameraControl::setState(QCamera::State state)
case QCamera::UnloadedState: {
// Stop the camera if it is running (transition to LoadedState)
if (d->status == QCamera::ActiveStatus) {
if (d->framesMapped > 0) {
qWarning("%d QVideoFrame(s) mapped when closing down camera. Camera will wait for unmap before closing down.",
d->framesMapped);
if (!d->delayClose)
d->delayClose = new QEventLoop(this);
d->delayClose->exec();
}
ComPtr<IAsyncAction> op;
hr = d->capturePreview->StopPreviewAsync(&op);
RETURN_VOID_AND_EMIT_ERROR("Failed to stop camera preview");
@@ -1208,6 +1219,21 @@ bool QWinRTCameraControl::unlockFocus()
#endif // !Q_OS_WINPHONE
void QWinRTCameraControl::frameMapped()
{
Q_D(QWinRTCameraControl);
++d->framesMapped;
}
void QWinRTCameraControl::frameUnmapped()
{
Q_D(QWinRTCameraControl);
--d->framesMapped;
Q_ASSERT(d->framesMapped >= 0);
if (!d->framesMapped && d->delayClose && d->delayClose->isRunning())
d->delayClose->exit();
}
HRESULT QWinRTCameraControl::onCaptureFailed(IMediaCapture *, IMediaCaptureFailedEventArgs *args)
{
HRESULT hr;

View File

@@ -99,6 +99,8 @@ public:
void emitError(int errorCode, const QString &errorString);
bool lockFocus();
bool unlockFocus();
void frameMapped();
void frameUnmapped();
private slots:
void onBufferRequested();

View File

@@ -38,6 +38,7 @@
#include <QtCore/qfunctions_winrt.h>
#include <QtCore/QSize>
#include <QtCore/QPointer>
#include <QtCore/QVector>
#include <QVideoFrame>
@@ -45,6 +46,8 @@
#include <mfapi.h>
#include <wrl.h>
#include "qwinrtcameracontrol.h"
#ifdef Q_OS_WINPHONE
#include <Windows.Security.ExchangeActiveSyncProvisioning.h>
using namespace ABI::Windows::Security::ExchangeActiveSyncProvisioning;
@@ -68,12 +71,14 @@ static bool blacklisted(const wchar_t (&blackListName)[n], const HString &device
class QWinRTCameraVideoBuffer : public QAbstractVideoBuffer
{
public:
QWinRTCameraVideoBuffer(IMF2DBuffer *buffer, int size)
QWinRTCameraVideoBuffer(IMF2DBuffer *buffer, int size, QWinRTCameraControl *control)
: QAbstractVideoBuffer(NoHandle)
, currentMode(NotMapped)
, buffer(buffer)
, size(size)
, control(control)
{
Q_ASSERT(control);
}
~QWinRTCameraVideoBuffer()
@@ -88,13 +93,14 @@ public:
uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) Q_DECL_OVERRIDE
{
if (currentMode != NotMapped || mode == NotMapped)
if (currentMode != NotMapped || mode == NotMapped || control && control->state() != QCamera::ActiveState)
return nullptr;
BYTE *bytes;
LONG stride;
HRESULT hr = buffer->Lock2D(&bytes, &stride);
RETURN_IF_FAILED("Failed to lock camera frame buffer", nullptr);
control->frameMapped();
if (bytesPerLine)
*bytesPerLine = stride;
@@ -111,12 +117,15 @@ public:
HRESULT hr = buffer->Unlock2D();
RETURN_VOID_IF_FAILED("Failed to unlock camera frame buffer");
currentMode = NotMapped;
if (control)
control->frameUnmapped();
}
private:
ComPtr<IMF2DBuffer> buffer;
MapMode currentMode;
int size;
QPointer<QWinRTCameraControl> control;
};
class D3DVideoBlitter
@@ -331,7 +340,9 @@ bool QWinRTCameraVideoRendererControl::dequeueFrame(QVideoFrame *frame)
return false;
}
QWinRTCameraVideoBuffer *videoBuffer = new QWinRTCameraVideoBuffer(buffer.Get(), d->cameraSampleSize);
QWinRTCameraVideoBuffer *videoBuffer = new QWinRTCameraVideoBuffer(buffer.Get(),
d->cameraSampleSize,
static_cast<QWinRTCameraControl *>(parent()));
*frame = QVideoFrame(videoBuffer, size(), d->cameraSampleformat);
emit bufferRequested();
@@ -350,7 +361,9 @@ void QWinRTCameraVideoRendererControl::queueBuffer(IMF2DBuffer *buffer)
}
if (d->videoProbesCounter > 0 && d->cameraSampleformat != QVideoFrame::Format_Invalid) {
QWinRTCameraVideoBuffer *videoBuffer = new QWinRTCameraVideoBuffer(buffer, d->cameraSampleSize);
QWinRTCameraVideoBuffer *videoBuffer = new QWinRTCameraVideoBuffer(buffer,
d->cameraSampleSize,
static_cast<QWinRTCameraControl *>(parent()));
QVideoFrame frame(videoBuffer, size(), d->cameraSampleformat);
emit videoFrameProbed(frame);
}