Merge branch 'jb42936' into 'mer-5.6'

Fix crashes and lockups caused by changing the capture mode during an image capture

See merge request mer-core/qtmultimedia!20
This commit is contained in:
Andrew den Exter
2019-06-19 01:07:14 +00:00
7 changed files with 200 additions and 277 deletions

View File

@@ -49,30 +49,10 @@ QT_BEGIN_NAMESPACE
CameraBinControl::CameraBinControl(CameraBinSession *session) CameraBinControl::CameraBinControl(CameraBinSession *session)
:QCameraControl(session), :QCameraControl(session),
m_session(session), m_session(session)
m_state(QCamera::UnloadedState),
m_reloadPending(false)
{ {
connect(m_session, SIGNAL(statusChanged(QCamera::Status)), connect(m_session, SIGNAL(statusChanged(QCamera::Status)),
this, SIGNAL(statusChanged(QCamera::Status))); this, SIGNAL(statusChanged(QCamera::Status)));
connect(m_session, SIGNAL(viewfinderChanged()),
SLOT(reloadLater()));
connect(m_session, SIGNAL(readyChanged(bool)),
SLOT(reloadLater()));
connect(m_session, SIGNAL(error(int,QString)),
SLOT(handleCameraError(int,QString)));
m_resourcePolicy = new CamerabinResourcePolicy(this);
connect(m_resourcePolicy, SIGNAL(resourcesGranted()),
SLOT(handleResourcesGranted()));
connect(m_resourcePolicy, SIGNAL(resourcesDenied()),
SLOT(handleResourcesLost()));
connect(m_resourcePolicy, SIGNAL(resourcesLost()),
SLOT(handleResourcesLost()));
connect(m_session, SIGNAL(busyChanged(bool)),
SLOT(handleBusyChanged(bool)));
} }
CameraBinControl::~CameraBinControl() CameraBinControl::~CameraBinControl()
@@ -86,17 +66,7 @@ QCamera::CaptureModes CameraBinControl::captureMode() const
void CameraBinControl::setCaptureMode(QCamera::CaptureModes mode) void CameraBinControl::setCaptureMode(QCamera::CaptureModes mode)
{ {
if (m_session->captureMode() != mode) { m_session->setCaptureMode(mode);
m_session->setCaptureMode(mode);
if (m_state == QCamera::ActiveState) {
m_resourcePolicy->setResourceSet(
captureMode() == QCamera::CaptureStillImage ?
CamerabinResourcePolicy::ImageCaptureResources :
CamerabinResourcePolicy::VideoCaptureResources);
}
emit captureModeChanged(mode);
}
} }
bool CameraBinControl::isCaptureModeSupported(QCamera::CaptureModes mode) const bool CameraBinControl::isCaptureModeSupported(QCamera::CaptureModes mode) const
@@ -109,59 +79,12 @@ void CameraBinControl::setState(QCamera::State state)
#ifdef CAMEABIN_DEBUG #ifdef CAMEABIN_DEBUG
qDebug() << Q_FUNC_INFO << ENUM_NAME(QCamera, "State", state); qDebug() << Q_FUNC_INFO << ENUM_NAME(QCamera, "State", state);
#endif #endif
if (m_state != state) { m_session->setState(state);
m_state = state;
//special case for stopping the camera while it's busy,
//it should be delayed until the camera is idle
if ((state == QCamera::LoadedState || state == QCamera::UnloadedState) &&
m_session->status() == QCamera::ActiveStatus &&
m_session->isBusy()) {
#ifdef CAMEABIN_DEBUG
qDebug() << Q_FUNC_INFO << "Camera is busy, QCamera::stop() is delayed";
#endif
emit stateChanged(m_state);
return;
}
CamerabinResourcePolicy::ResourceSet resourceSet = CamerabinResourcePolicy::NoResources;
switch (state) {
case QCamera::UnloadedState:
resourceSet = CamerabinResourcePolicy::NoResources;
break;
case QCamera::LoadedState:
resourceSet = CamerabinResourcePolicy::LoadedResources;
break;
case QCamera::ActiveState:
resourceSet = captureMode() == QCamera::CaptureStillImage ?
CamerabinResourcePolicy::ImageCaptureResources :
CamerabinResourcePolicy::VideoCaptureResources;
break;
}
m_resourcePolicy->setResourceSet(resourceSet);
if (m_resourcePolicy->isResourcesGranted()) {
//postpone changing to Active if the session is nor ready yet
if (state == QCamera::ActiveState) {
if (m_session->isReady()) {
m_session->setState(state);
} else {
#ifdef CAMEABIN_DEBUG
qDebug() << "Camera session is not ready yet, postpone activating";
#endif
}
} else
m_session->setState(state);
}
emit stateChanged(m_state);
}
} }
QCamera::State CameraBinControl::state() const QCamera::State CameraBinControl::state() const
{ {
return m_state; return m_session->requestedState();
} }
QCamera::Status CameraBinControl::status() const QCamera::Status CameraBinControl::status() const
@@ -169,81 +92,6 @@ QCamera::Status CameraBinControl::status() const
return m_session->status(); return m_session->status();
} }
void CameraBinControl::reloadLater()
{
#ifdef CAMEABIN_DEBUG
qDebug() << "CameraBinControl: reload pipeline requested" << ENUM_NAME(QCamera, "State", m_state);
#endif
if (!m_reloadPending && m_state == QCamera::ActiveState) {
m_reloadPending = true;
if (!m_session->isBusy()) {
m_session->setState(QCamera::LoadedState);
QMetaObject::invokeMethod(this, "delayedReload", Qt::QueuedConnection);
}
}
}
void CameraBinControl::handleResourcesLost()
{
#ifdef CAMEABIN_DEBUG
qDebug() << Q_FUNC_INFO << ENUM_NAME(QCamera, "State", m_state);
#endif
m_session->setState(QCamera::UnloadedState);
}
void CameraBinControl::handleResourcesGranted()
{
#ifdef CAMEABIN_DEBUG
qDebug() << Q_FUNC_INFO << ENUM_NAME(QCamera, "State", m_state);
#endif
//camera will be started soon by delayedReload()
if (m_reloadPending && m_state == QCamera::ActiveState)
return;
if (m_state == QCamera::ActiveState && m_session->isReady())
m_session->setState(QCamera::ActiveState);
else if (m_state == QCamera::LoadedState)
m_session->setState(QCamera::LoadedState);
}
void CameraBinControl::handleBusyChanged(bool busy)
{
if (!busy && m_session->status() == QCamera::ActiveStatus) {
if (m_state == QCamera::LoadedState) {
//handle delayed stop() because of busy camera
m_resourcePolicy->setResourceSet(CamerabinResourcePolicy::LoadedResources);
m_session->setState(QCamera::LoadedState);
} else if (m_state == QCamera::ActiveState && m_reloadPending) {
//handle delayed reload because of busy camera
m_session->setState(QCamera::LoadedState);
QMetaObject::invokeMethod(this, "delayedReload", Qt::QueuedConnection);
}
}
}
void CameraBinControl::handleCameraError(int errorCode, const QString &errorString)
{
emit error(errorCode, errorString);
setState(QCamera::UnloadedState);
}
void CameraBinControl::delayedReload()
{
#ifdef CAMEABIN_DEBUG
qDebug() << "CameraBinControl: reload pipeline";
#endif
if (m_reloadPending) {
m_reloadPending = false;
if (m_state == QCamera::ActiveState &&
m_session->isReady() &&
m_resourcePolicy->isResourcesGranted()) {
m_session->setState(QCamera::ActiveState);
}
}
}
bool CameraBinControl::canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const bool CameraBinControl::canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const
{ {
Q_UNUSED(status); Q_UNUSED(status);

View File

@@ -65,29 +65,13 @@ public:
bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const; bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const;
bool viewfinderColorSpaceConversion() const; bool viewfinderColorSpaceConversion() const;
CamerabinResourcePolicy *resourcePolicy() { return m_resourcePolicy; }
public slots: public slots:
void reloadLater();
void setViewfinderColorSpaceConversion(bool enabled); void setViewfinderColorSpaceConversion(bool enabled);
private slots:
void delayedReload();
void handleResourcesGranted();
void handleResourcesLost();
void handleBusyChanged(bool);
void handleCameraError(int error, const QString &errorString);
private: private:
void updateSupportedResolutions(const QString &device); void updateSupportedResolutions(const QString &device);
CameraBinSession *m_session; CameraBinSession *m_session;
QCamera::State m_state;
CamerabinResourcePolicy *m_resourcePolicy;
bool m_reloadPending;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@@ -64,7 +64,7 @@ CameraBinImageCapture::CameraBinImageCapture(CameraBinSession *session)
connect(m_session, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateState())); connect(m_session, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateState()));
connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int))); connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int)));
connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage))); connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage)));
connect(m_session->cameraControl()->resourcePolicy(), SIGNAL(canCaptureChanged()), this, SLOT(updateState())); connect(m_session->resourcePolicy(), SIGNAL(canCaptureChanged()), this, SLOT(updateState()));
connect(m_session, SIGNAL(handleReadyForCaptureChanged(bool)), this, SLOT(updateState())); connect(m_session, SIGNAL(handleReadyForCaptureChanged(bool)), this, SLOT(updateState()));
m_session->bus()->installMessageFilter(this); m_session->bus()->installMessageFilter(this);
@@ -102,7 +102,7 @@ void CameraBinImageCapture::cancelCapture()
void CameraBinImageCapture::updateState() void CameraBinImageCapture::updateState()
{ {
bool ready = m_session->status() == QCamera::ActiveStatus bool ready = m_session->status() == QCamera::ActiveStatus
&& m_session->cameraControl()->resourcePolicy()->canCapture() && m_session->resourcePolicy()->canCapture()
&& m_session->isReadyForCapture(); && m_session->isReadyForCapture();
if (m_ready != ready) { if (m_ready != ready) {
#ifdef DEBUG_CAPTURE #ifdef DEBUG_CAPTURE

View File

@@ -54,7 +54,7 @@ CameraBinRecorder::CameraBinRecorder(CameraBinSession *session)
connect(m_session, SIGNAL(durationChanged(qint64)), SIGNAL(durationChanged(qint64))); connect(m_session, SIGNAL(durationChanged(qint64)), SIGNAL(durationChanged(qint64)));
connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool))); connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool)));
connect(m_session->cameraControl()->resourcePolicy(), SIGNAL(canCaptureChanged()), connect(m_session->resourcePolicy(), SIGNAL(canCaptureChanged()),
this, SLOT(updateStatus())); this, SLOT(updateStatus()));
} }
@@ -93,7 +93,7 @@ void CameraBinRecorder::updateStatus()
if (sessionStatus == QCamera::ActiveStatus && if (sessionStatus == QCamera::ActiveStatus &&
m_session->captureMode().testFlag(QCamera::CaptureVideo)) { m_session->captureMode().testFlag(QCamera::CaptureVideo)) {
if (!m_session->cameraControl()->resourcePolicy()->canCapture()) { if (!m_session->resourcePolicy()->canCapture()) {
m_status = QMediaRecorder::UnavailableStatus; m_status = QMediaRecorder::UnavailableStatus;
m_state = QMediaRecorder::StoppedState; m_state = QMediaRecorder::StoppedState;
m_session->stopVideoRecording(); m_session->stopVideoRecording();
@@ -109,7 +109,7 @@ void CameraBinRecorder::updateStatus()
m_state = QMediaRecorder::StoppedState; m_state = QMediaRecorder::StoppedState;
m_session->stopVideoRecording(); m_session->stopVideoRecording();
} }
m_status = m_session->pendingState() == QCamera::ActiveState m_status = m_session->requestedState() == QCamera::ActiveState
&& m_session->captureMode().testFlag(QCamera::CaptureVideo) && m_session->captureMode().testFlag(QCamera::CaptureVideo)
? QMediaRecorder::LoadingStatus ? QMediaRecorder::LoadingStatus
: QMediaRecorder::UnloadedStatus; : QMediaRecorder::UnloadedStatus;
@@ -221,7 +221,7 @@ void CameraBinRecorder::setState(QMediaRecorder::State state)
if (m_session->status() != QCamera::ActiveStatus) { if (m_session->status() != QCamera::ActiveStatus) {
emit error(QMediaRecorder::ResourceError, tr("Service has not been started")); emit error(QMediaRecorder::ResourceError, tr("Service has not been started"));
} else if (!m_session->cameraControl()->resourcePolicy()->canCapture()) { } else if (!m_session->resourcePolicy()->canCapture()) {
emit error(QMediaRecorder::ResourceError, tr("Recording permissions are not available")); emit error(QMediaRecorder::ResourceError, tr("Recording permissions are not available"));
} else { } else {
m_session->recordVideo(); m_session->recordVideo();

View File

@@ -53,7 +53,7 @@ public:
VideoCaptureResources VideoCaptureResources
}; };
CamerabinResourcePolicy(QObject *parent); CamerabinResourcePolicy(QObject *parent = nullptr);
~CamerabinResourcePolicy(); ~CamerabinResourcePolicy();
ResourceSet resourceSet() const; ResourceSet resourceSet() const;

View File

@@ -113,11 +113,15 @@ CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *pa
:QObject(parent), :QObject(parent),
m_recordingActive(false), m_recordingActive(false),
m_status(QCamera::UnloadedStatus), m_status(QCamera::UnloadedStatus),
m_pendingState(QCamera::UnloadedState), m_requestedState(QCamera::UnloadedState),
m_transientState(QCamera::UnloadedState),
m_acceptedState(QCamera::UnloadedState),
m_muted(false), m_muted(false),
m_busy(false),
m_readyForCapture(false), m_readyForCapture(false),
m_captureMode(QCamera::CaptureStillImage), m_captureMode(QCamera::CaptureStillImage),
m_appliedCaptureMode(QCamera::CaptureStillImage),
m_busy(false),
m_reportedReadyForCapture(false),
m_audioInputFactory(0), m_audioInputFactory(0),
m_videoInputFactory(0), m_videoInputFactory(0),
m_viewfinder(0), m_viewfinder(0),
@@ -187,6 +191,10 @@ CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *pa
g_object_set(G_OBJECT(m_camerabin), PREVIEW_CAPS_PROPERTY, previewCaps, NULL); g_object_set(G_OBJECT(m_camerabin), PREVIEW_CAPS_PROPERTY, previewCaps, NULL);
gst_caps_unref(previewCaps); gst_caps_unref(previewCaps);
connect(&m_resourcePolicy, &CamerabinResourcePolicy::resourcesGranted, this, &CameraBinSession::applyState);
connect(&m_resourcePolicy, &CamerabinResourcePolicy::resourcesDenied, this, &CameraBinSession::resourcesLost);
connect(&m_resourcePolicy, &CamerabinResourcePolicy::resourcesLost, this, &CameraBinSession::resourcesLost);
} }
CameraBinSession::~CameraBinSession() CameraBinSession::~CameraBinSession()
@@ -270,7 +278,7 @@ bool CameraBinSession::setupCameraBin()
#endif #endif
m_viewfinderHasChanged = false; m_viewfinderHasChanged = false;
if (!m_viewfinderElement) { if (!m_viewfinderElement) {
if (m_pendingState == QCamera::ActiveState) if (m_requestedState == QCamera::ActiveState)
qWarning() << "Starting camera without viewfinder available"; qWarning() << "Starting camera without viewfinder available";
m_viewfinderElement = gst_element_factory_make("fakesink", NULL); m_viewfinderElement = gst_element_factory_make("fakesink", NULL);
} }
@@ -561,22 +569,25 @@ void CameraBinSession::captureImage(int requestId, const QString &fileName)
g_signal_emit_by_name(G_OBJECT(m_camerabin), CAPTURE_START, NULL); g_signal_emit_by_name(G_OBJECT(m_camerabin), CAPTURE_START, NULL);
m_imageFileName = actualFileName; m_imageFileName = actualFileName;
updateCaptureStatus();
}
void CameraBinSession::updateCaptureStatus()
{
if (m_reportedReadyForCapture != m_readyForCapture) {
m_readyForCapture = m_reportedReadyForCapture;
emit handleReadyForCaptureChanged(m_readyForCapture);
}
} }
void CameraBinSession::setCaptureMode(QCamera::CaptureModes mode) void CameraBinSession::setCaptureMode(QCamera::CaptureModes mode)
{ {
m_captureMode = mode; if (m_captureMode != mode) {
m_captureMode = mode;
switch (m_captureMode) { emit m_cameraControl->captureModeChanged(m_captureMode);
case QCamera::CaptureStillImage:
g_object_set(m_camerabin, MODE_PROPERTY, CAMERABIN_IMAGE_MODE, NULL);
break;
case QCamera::CaptureVideo:
g_object_set(m_camerabin, MODE_PROPERTY, CAMERABIN_VIDEO_MODE, NULL);
break;
} }
m_recorderControl->updateStatus();
} }
QUrl CameraBinSession::outputLocation() const QUrl CameraBinSession::outputLocation() const
@@ -631,32 +642,29 @@ void CameraBinSession::setViewfinder(QObject *viewfinder)
viewfinder = 0; viewfinder = 0;
if (m_viewfinder != viewfinder) { if (m_viewfinder != viewfinder) {
bool oldReady = isReady();
if (m_viewfinder) { if (m_viewfinder) {
disconnect(m_viewfinder, SIGNAL(sinkChanged()), disconnect(m_viewfinder, SIGNAL(sinkChanged()),
this, SLOT(handleViewfinderChange())); this, SLOT(handleViewfinderChange()));
disconnect(m_viewfinder, SIGNAL(readyChanged(bool)), disconnect(m_viewfinder, SIGNAL(readyChanged(bool)),
this, SIGNAL(readyChanged(bool))); this, SLOT(applyState()));
m_busHelper->removeMessageFilter(m_viewfinder); m_busHelper->removeMessageFilter(m_viewfinder);
} }
m_viewfinder = viewfinder; m_viewfinder = viewfinder;
m_viewfinderHasChanged = true; m_viewfinderHasChanged = true;
m_transientState = QCamera::UnloadedState;
if (m_viewfinder) { if (m_viewfinder) {
connect(m_viewfinder, SIGNAL(sinkChanged()), connect(m_viewfinder, SIGNAL(sinkChanged()),
this, SLOT(handleViewfinderChange())); this, SLOT(handleViewfinderChange()));
connect(m_viewfinder, SIGNAL(readyChanged(bool)), connect(m_viewfinder, SIGNAL(readyChanged(bool)),
this, SIGNAL(readyChanged(bool))); this, SLOT(applyState()));
m_busHelper->installMessageFilter(m_viewfinder); m_busHelper->installMessageFilter(m_viewfinder);
} }
emit viewfinderChanged(); applyState();
if (oldReady != isReady())
emit readyChanged(isReady());
} }
} }
@@ -687,7 +695,9 @@ void CameraBinSession::handleViewfinderChange()
//the viewfinder will be reloaded //the viewfinder will be reloaded
//shortly when the pipeline is started //shortly when the pipeline is started
m_viewfinderHasChanged = true; m_viewfinderHasChanged = true;
emit viewfinderChanged(); m_transientState = QCamera::UnloadedState;
applyState();
} }
void CameraBinSession::setStatus(QCamera::Status status) void CameraBinSession::setStatus(QCamera::Status status)
@@ -697,8 +707,6 @@ void CameraBinSession::setStatus(QCamera::Status status)
m_status = status; m_status = status;
emit statusChanged(m_status); emit statusChanged(m_status);
setStateHelper(m_pendingState);
} }
QCamera::Status CameraBinSession::status() const QCamera::Status CameraBinSession::status() const
@@ -706,60 +714,122 @@ QCamera::Status CameraBinSession::status() const
return m_status; return m_status;
} }
QCamera::State CameraBinSession::pendingState() const QCamera::State CameraBinSession::requestedState() const
{ {
return m_pendingState; return m_requestedState;
} }
void CameraBinSession::setState(QCamera::State newState) void CameraBinSession::setState(QCamera::State newState)
{ {
if (newState == m_pendingState) if (newState == m_requestedState)
return; return;
m_pendingState = newState; if (newState < m_transientState) {
emit pendingStateChanged(m_pendingState); m_transientState = newState;
}
m_requestedState = newState;
emit pendingStateChanged(m_requestedState);
m_cameraControl->stateChanged(m_requestedState);
#if CAMERABIN_DEBUG #if CAMERABIN_DEBUG
qDebug() << Q_FUNC_INFO << newState; qDebug() << Q_FUNC_INFO << newState;
#endif #endif
setStateHelper(newState); applyState();
} }
void CameraBinSession::setStateHelper(QCamera::State state) void CameraBinSession::applyState()
{ {
switch (state) { CamerabinResourcePolicy::ResourceSet resourceSet = CamerabinResourcePolicy::NoResources;
switch (m_requestedState) {
case QCamera::UnloadedState: case QCamera::UnloadedState:
unload(); resourceSet = CamerabinResourcePolicy::NoResources;
break; break;
case QCamera::LoadedState: case QCamera::LoadedState:
if (m_status == QCamera::ActiveStatus) resourceSet = CamerabinResourcePolicy::LoadedResources;
stop();
else if (m_status == QCamera::UnloadedStatus)
load();
break; break;
case QCamera::ActiveState: case QCamera::ActiveState:
// If the viewfinder changed while in the loaded state, we need to reload the pipeline resourceSet = m_captureMode == QCamera::CaptureStillImage
if (m_status == QCamera::LoadedStatus && !m_viewfinderHasChanged) ? CamerabinResourcePolicy::ImageCaptureResources
start(); : CamerabinResourcePolicy::VideoCaptureResources;
else if (m_status == QCamera::UnloadedStatus || m_viewfinderHasChanged) break;
load();
} }
if (m_resourcePolicy.resourceSet() != resourceSet) {
m_resourcePolicy.setResourceSet(resourceSet);
}
if (!m_resourcePolicy.isResourcesGranted()) {
m_transientState = QCamera::UnloadedState;
}
if (m_transientState < m_acceptedState) {
// Ensure that if something tried to unload or stop the pipeline that it happens and
// settings changes are applied.
switch (m_transientState) {
case QCamera::UnloadedState:
unload();
break;
case QCamera::LoadedState:
stop();
break;
case QCamera::ActiveState:
// Unreachable
break;
}
}
if (m_requestedState > m_acceptedState
&& !m_busy
&& isReady()
&& m_resourcePolicy.isResourcesGranted()) {
m_transientState = m_requestedState;
switch (m_requestedState) {
case QCamera::UnloadedState:
// Unreachable.
break;
case QCamera::LoadedState:
load();
break;
case QCamera::ActiveState:
if (m_status == QCamera::UnloadedStatus) {
load();
} else if (m_status == QCamera::LoadedStatus) {
start();
}
}
}
}
void CameraBinSession::resourcesLost()
{
m_transientState = QCamera::UnloadedState;
applyState();
} }
void CameraBinSession::setError(int err, const QString &errorString) void CameraBinSession::setError(int err, const QString &errorString)
{ {
m_pendingState = QCamera::UnloadedState; m_requestedState = QCamera::UnloadedState;
emit error(err, errorString); m_transientState = QCamera::UnloadedState;
setStatus(QCamera::UnloadedStatus);
emit m_cameraControl->error(err, errorString);
applyState();
emit m_cameraControl->stateChanged(m_requestedState);
} }
void CameraBinSession::load() void CameraBinSession::load()
{ {
if (m_status != QCamera::UnloadedStatus && !m_viewfinderHasChanged) if (m_status != QCamera::UnloadedStatus)
return; return;
setStatus(QCamera::LoadingStatus); m_acceptedState = QCamera::LoadedState;
m_status = QCamera::LoadingStatus;
if (!setupCameraBin()) { if (!setupCameraBin()) {
setError(QCamera::CameraError, QStringLiteral("No camera source available")); setError(QCamera::CameraError, QStringLiteral("No camera source available"));
@@ -780,23 +850,34 @@ void CameraBinSession::load()
setupCaptureResolution(); setupCaptureResolution();
gst_element_set_state(m_camerabin, GST_STATE_READY); gst_element_set_state(m_camerabin, GST_STATE_READY);
emit statusChanged(m_status);
} }
void CameraBinSession::unload() void CameraBinSession::unload()
{ {
if (m_status == QCamera::UnloadedStatus || m_status == QCamera::UnloadingStatus) if (m_status == QCamera::UnloadedStatus)
return; return;
// We save the recording state in case something reacted to setStatus() and bool changed = m_status != QCamera::UnloadingStatus;
// stopped recording. m_status = QCamera::UnloadingStatus;
bool wasRecording = m_recordingActive;
setStatus(QCamera::UnloadingStatus);
if (m_recordingActive) if (m_recordingActive)
stopVideoRecording(); stopVideoRecording();
else if (!wasRecording)
handleBusyChanged(false); if (!m_busy) {
m_acceptedState = QCamera::UnloadedState;
if (m_viewfinderInterface)
m_viewfinderInterface->stopRenderer();
gst_element_set_state(m_camerabin, GST_STATE_NULL);
m_status = QCamera::UnloadedStatus;
emit statusChanged(m_status);
} else if (changed) {
emit statusChanged(m_status);
}
} }
void CameraBinSession::start() void CameraBinSession::start()
@@ -804,7 +885,20 @@ void CameraBinSession::start()
if (m_status != QCamera::LoadedStatus) if (m_status != QCamera::LoadedStatus)
return; return;
setStatus(QCamera::StartingStatus); m_acceptedState = QCamera::ActiveState;
m_status = QCamera::StartingStatus;
m_appliedCaptureMode = m_captureMode;
switch (m_captureMode) {
case QCamera::CaptureStillImage:
g_object_set(m_camerabin, MODE_PROPERTY, CAMERABIN_IMAGE_MODE, NULL);
break;
case QCamera::CaptureVideo:
g_object_set(m_camerabin, MODE_PROPERTY, CAMERABIN_VIDEO_MODE, NULL);
break;
}
m_recorderControl->applySettings(); m_recorderControl->applySettings();
@@ -822,22 +916,30 @@ void CameraBinSession::start()
setupCaptureResolution(); setupCaptureResolution();
gst_element_set_state(m_camerabin, GST_STATE_PLAYING); gst_element_set_state(m_camerabin, GST_STATE_PLAYING);
emit statusChanged(m_status);
} }
void CameraBinSession::stop() void CameraBinSession::stop()
{ {
if (m_status != QCamera::ActiveStatus) if (m_status != QCamera::ActiveStatus && m_status != QCamera::StartingStatus)
return; return;
setStatus(QCamera::StoppingStatus); m_status = QCamera::StoppingStatus;
if (m_recordingActive) if (m_recordingActive)
stopVideoRecording(); stopVideoRecording();
if (m_viewfinderInterface) if (!m_busy) {
m_viewfinderInterface->stopRenderer(); m_acceptedState = QCamera::LoadedState;
gst_element_set_state(m_camerabin, GST_STATE_READY); if (m_viewfinderInterface)
m_viewfinderInterface->stopRenderer();
gst_element_set_state(m_camerabin, GST_STATE_READY);
}
emit statusChanged(m_status);
} }
bool CameraBinSession::isBusy() const bool CameraBinSession::isBusy() const
@@ -859,11 +961,8 @@ void CameraBinSession::updateBusyStatus(GObject *o, GParamSpec *p, gpointer d)
g_object_get(o, "idle", &idle, NULL); g_object_get(o, "idle", &idle, NULL);
bool busy = !idle; bool busy = !idle;
if (session->m_busy != busy) { if (session->m_busy.fetchAndStoreRelaxed(busy) != busy) {
session->m_busy = busy; QMetaObject::invokeMethod(session, "applyState", Qt::QueuedConnection);
QMetaObject::invokeMethod(session, "handleBusyChanged",
Qt::QueuedConnection,
Q_ARG(bool, busy));
} }
} }
@@ -875,10 +974,9 @@ void CameraBinSession::updateReadyForCapture(GObject *o, GParamSpec *p, gpointer
CameraBinSession *session = reinterpret_cast<CameraBinSession *>(d); CameraBinSession *session = reinterpret_cast<CameraBinSession *>(d);
gboolean ready = false; gboolean ready = false;
g_object_get(o, "ready-for-capture", &ready, NULL); g_object_get(o, "ready-for-capture", &ready, NULL);
if (session->m_readyForCapture != ready) {
session->m_readyForCapture = ready; if (session->m_reportedReadyForCapture.fetchAndStoreRelaxed(ready) != ready) {
QMetaObject::invokeMethod(session, "handleReadyForCaptureChanged", QMetaObject::invokeMethod(session, "updateCaptureStatus", Qt::QueuedConnection);
Qt::QueuedConnection, Q_ARG(bool, ready));
} }
} }
@@ -1091,6 +1189,9 @@ bool CameraBinSession::processBusMessage(const QGstreamerMessage &message)
default: default:
break; break;
} }
// Update any chained state changes.
applyState();
} }
break; break;
default: default:
@@ -1125,6 +1226,10 @@ QString CameraBinSession::currentContainerFormat() const
void CameraBinSession::recordVideo() void CameraBinSession::recordVideo()
{ {
if (!m_reportedReadyForCapture || m_appliedCaptureMode != QCamera::CaptureVideo || m_busy) {
return;
}
QString format = currentContainerFormat(); QString format = currentContainerFormat();
if (format.isEmpty()) if (format.isEmpty())
format = m_mediaContainerControl->actualContainerFormat(); format = m_mediaContainerControl->actualContainerFormat();
@@ -1535,28 +1640,4 @@ void CameraBinSession::elementRemoved(GstBin *, GstElement *element, CameraBinSe
session->m_muxer = 0; session->m_muxer = 0;
} }
void CameraBinSession::handleBusyChanged(bool busy)
{
// don't do anything if we are not unloading.
// It could be that the camera is starting again while it is being unloaded
if (m_status != QCamera::UnloadingStatus) {
if (m_busy != busy)
emit busyChanged(m_busy = busy);
return;
}
// Now we can really stop
if (m_viewfinderInterface)
m_viewfinderInterface->stopRenderer();
gst_element_set_state(m_camerabin, GST_STATE_NULL);
if (m_busy != busy)
emit busyChanged(m_busy = busy);
m_supportedViewfinderSettings.clear();
setStatus(QCamera::UnloadedStatus);
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@@ -49,6 +49,8 @@
#include <private/qmediastoragelocation_p.h> #include <private/qmediastoragelocation_p.h>
#include "qcamera.h" #include "qcamera.h"
#include "camerabinresourcepolicy.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QGstreamerMessage; class QGstreamerMessage;
@@ -118,6 +120,7 @@ public:
CameraBinLocks *cameraLocksControl(); CameraBinLocks *cameraLocksControl();
#endif #endif
CamerabinResourcePolicy *resourcePolicy() { return &m_resourcePolicy; }
CameraBinZoom *cameraZoomControl() const { return m_cameraZoomControl; } CameraBinZoom *cameraZoomControl() const { return m_cameraZoomControl; }
CameraBinImageProcessing *imageProcessingControl() const { return m_imageProcessingControl; } CameraBinImageProcessing *imageProcessingControl() const { return m_imageProcessingControl; }
CameraBinCaptureDestination *captureDestinationControl() const { return m_captureDestinationControl; } CameraBinCaptureDestination *captureDestinationControl() const { return m_captureDestinationControl; }
@@ -143,7 +146,7 @@ public:
void captureImage(int requestId, const QString &fileName); void captureImage(int requestId, const QString &fileName);
QCamera::Status status() const; QCamera::Status status() const;
QCamera::State pendingState() const; QCamera::State requestedState() const;
bool isBusy() const; bool isBusy() const;
bool isReadyForCapture() const; bool isReadyForCapture() const;
@@ -182,7 +185,8 @@ public slots:
private slots: private slots:
void handleViewfinderChange(); void handleViewfinderChange();
void setupCaptureResolution(); void setupCaptureResolution();
void handleBusyChanged(bool busy); void updateCaptureStatus();
void applyState();
private: private:
void load(); void load();
@@ -191,7 +195,7 @@ private:
void stop(); void stop();
void setStatus(QCamera::Status status); void setStatus(QCamera::Status status);
void setStateHelper(QCamera::State state); void resourcesLost();
void setError(int error, const QString &errorString); void setError(int error, const QString &errorString);
bool setupCameraBin(); bool setupCameraBin();
@@ -211,16 +215,21 @@ private:
bool m_recordingActive; bool m_recordingActive;
QString m_captureDevice; QString m_captureDevice;
QCamera::Status m_status; QCamera::Status m_status;
QCamera::State m_pendingState; QCamera::State m_requestedState; // The desired state, transitioning to this may be blocked resource policy or outstanding actions.
QCamera::State m_transientState; // A state that must be passed through. If the pipeline is busy and can't be unloaded immediately it should still go through unloaded when it can to apply settings changes.
QCamera::State m_acceptedState; // The current state or the state the pipeline is actively transitioning to.
QString m_inputDevice; QString m_inputDevice;
bool m_muted; bool m_muted;
bool m_busy;
bool m_readyForCapture; bool m_readyForCapture;
QMediaStorageLocation m_mediaStorageLocation; QMediaStorageLocation m_mediaStorageLocation;
QCamera::CaptureModes m_captureMode; QCamera::CaptureModes m_captureMode;
QCamera::CaptureModes m_appliedCaptureMode;
QMap<QByteArray, QVariant> m_metaData; QMap<QByteArray, QVariant> m_metaData;
QAtomicInteger<bool> m_busy;
QAtomicInteger<bool> m_reportedReadyForCapture;
QGstreamerElementFactory *m_audioInputFactory; QGstreamerElementFactory *m_audioInputFactory;
QGstreamerElementFactory *m_videoInputFactory; QGstreamerElementFactory *m_videoInputFactory;
QObject *m_viewfinder; QObject *m_viewfinder;
@@ -229,6 +238,7 @@ private:
QCameraViewfinderSettings m_viewfinderSettings; QCameraViewfinderSettings m_viewfinderSettings;
QCameraViewfinderSettings m_actualViewfinderSettings; QCameraViewfinderSettings m_actualViewfinderSettings;
CamerabinResourcePolicy m_resourcePolicy;
CameraBinControl *m_cameraControl; CameraBinControl *m_cameraControl;
CameraBinAudioEncoder *m_audioEncodeControl; CameraBinAudioEncoder *m_audioEncodeControl;
CameraBinVideoEncoder *m_videoEncodeControl; CameraBinVideoEncoder *m_videoEncodeControl;