GStreamer: fix camerabin state and status changes.

Not all status changes where reported and setting the QCamera
to LoadedState was not actually loading anything.

State and status changes have been refactored.
Camera status is now reported directly by the camera session.
Setting the camera state to LoadedState now sets the camerabin to
GST_STATE_READY, that allows to query for camera capabilities without
having to start the camera (and have a valid viewfinder).

Change-Id: I249b1ad32690679ff34a427410bc709ed3ab461c
Reviewed-by: Christian Stromme <christian.stromme@theqtcompany.com>
This commit is contained in:
Yoann Lopes
2015-01-21 14:11:42 +01:00
parent e49d92959c
commit d9354b2299
8 changed files with 157 additions and 143 deletions

View File

@@ -51,11 +51,10 @@ CameraBinControl::CameraBinControl(CameraBinSession *session)
:QCameraControl(session), :QCameraControl(session),
m_session(session), m_session(session),
m_state(QCamera::UnloadedState), m_state(QCamera::UnloadedState),
m_status(QCamera::UnloadedStatus),
m_reloadPending(false) m_reloadPending(false)
{ {
connect(m_session, SIGNAL(stateChanged(QCamera::State)), connect(m_session, SIGNAL(statusChanged(QCamera::Status)),
this, SLOT(updateStatus())); this, SIGNAL(statusChanged(QCamera::Status)));
connect(m_session, SIGNAL(viewfinderChanged()), connect(m_session, SIGNAL(viewfinderChanged()),
SLOT(reloadLater())); SLOT(reloadLater()));
@@ -116,7 +115,7 @@ void CameraBinControl::setState(QCamera::State state)
//special case for stopping the camera while it's busy, //special case for stopping the camera while it's busy,
//it should be delayed until the camera is idle //it should be delayed until the camera is idle
if (state == QCamera::LoadedState && if (state == QCamera::LoadedState &&
m_session->state() == QCamera::ActiveState && m_session->status() == QCamera::ActiveStatus &&
m_session->isBusy()) { m_session->isBusy()) {
#ifdef CAMEABIN_DEBUG #ifdef CAMEABIN_DEBUG
qDebug() << Q_FUNC_INFO << "Camera is busy, QCamera::stop() is delayed"; qDebug() << Q_FUNC_INFO << "Camera is busy, QCamera::stop() is delayed";
@@ -165,52 +164,9 @@ QCamera::State CameraBinControl::state() const
return m_state; return m_state;
} }
void CameraBinControl::updateStatus() QCamera::Status CameraBinControl::status() const
{ {
QCamera::State sessionState = m_session->state(); return m_session->status();
QCamera::Status oldStatus = m_status;
switch (m_state) {
case QCamera::UnloadedState:
m_status = QCamera::UnloadedStatus;
break;
case QCamera::LoadedState:
switch (sessionState) {
case QCamera::UnloadedState:
m_status = m_resourcePolicy->isResourcesGranted()
? QCamera::LoadingStatus
: QCamera::UnavailableStatus;
break;
case QCamera::LoadedState:
m_status = QCamera::LoadedStatus;
break;
case QCamera::ActiveState:
m_status = QCamera::ActiveStatus;
break;
}
break;
case QCamera::ActiveState:
switch (sessionState) {
case QCamera::UnloadedState:
m_status = m_resourcePolicy->isResourcesGranted()
? QCamera::LoadingStatus
: QCamera::UnavailableStatus;
break;
case QCamera::LoadedState:
m_status = QCamera::StartingStatus;
break;
case QCamera::ActiveState:
m_status = QCamera::ActiveStatus;
break;
}
}
if (m_status != oldStatus) {
#ifdef CAMEABIN_DEBUG
qDebug() << "Camera status changed" << ENUM_NAME(QCamera, "Status", m_status);
#endif
emit statusChanged(m_status);
}
} }
void CameraBinControl::reloadLater() void CameraBinControl::reloadLater()
@@ -254,7 +210,7 @@ void CameraBinControl::handleResourcesGranted()
void CameraBinControl::handleBusyChanged(bool busy) void CameraBinControl::handleBusyChanged(bool busy)
{ {
if (!busy && m_session->state() == QCamera::ActiveState) { if (!busy && m_session->status() == QCamera::ActiveStatus) {
if (m_state == QCamera::LoadedState) { if (m_state == QCamera::LoadedState) {
//handle delayed stop() because of busy camera //handle delayed stop() because of busy camera
m_resourcePolicy->setResourceSet(CamerabinResourcePolicy::LoadedResources); m_resourcePolicy->setResourceSet(CamerabinResourcePolicy::LoadedResources);

View File

@@ -56,7 +56,7 @@ public:
QCamera::State state() const; QCamera::State state() const;
void setState(QCamera::State state); void setState(QCamera::State state);
QCamera::Status status() const { return m_status; } QCamera::Status status() const;
QCamera::CaptureModes captureMode() const; QCamera::CaptureModes captureMode() const;
void setCaptureMode(QCamera::CaptureModes mode); void setCaptureMode(QCamera::CaptureModes mode);
@@ -72,7 +72,6 @@ public slots:
void setViewfinderColorSpaceConversion(bool enabled); void setViewfinderColorSpaceConversion(bool enabled);
private slots: private slots:
void updateStatus();
void delayedReload(); void delayedReload();
void handleResourcesGranted(); void handleResourcesGranted();
@@ -86,7 +85,6 @@ private:
CameraBinSession *m_session; CameraBinSession *m_session;
QCamera::State m_state; QCamera::State m_state;
QCamera::Status m_status;
CamerabinResourcePolicy *m_resourcePolicy; CamerabinResourcePolicy *m_resourcePolicy;
bool m_reloadPending; bool m_reloadPending;

View File

@@ -56,7 +56,7 @@ CameraBinFocus::CameraBinFocus(CameraBinSession *session)
QGstreamerBufferProbe(ProbeBuffers), QGstreamerBufferProbe(ProbeBuffers),
#endif #endif
m_session(session), m_session(session),
m_cameraState(QCamera::UnloadedState), m_cameraStatus(QCamera::UnloadedStatus),
m_focusMode(QCameraFocus::AutoFocus), m_focusMode(QCameraFocus::AutoFocus),
m_focusPointMode(QCameraFocus::FocusPointAuto), m_focusPointMode(QCameraFocus::FocusPointAuto),
m_focusStatus(QCamera::Unlocked), m_focusStatus(QCamera::Unlocked),
@@ -68,8 +68,8 @@ CameraBinFocus::CameraBinFocus(CameraBinSession *session)
gst_photography_set_focus_mode(m_session->photography(), GST_PHOTOGRAPHY_FOCUS_MODE_AUTO); gst_photography_set_focus_mode(m_session->photography(), GST_PHOTOGRAPHY_FOCUS_MODE_AUTO);
connect(m_session, SIGNAL(stateChanged(QCamera::State)), connect(m_session, SIGNAL(statusChanged(QCamera::Status)),
this, SLOT(_q_handleCameraStateChange(QCamera::State))); this, SLOT(_q_handleCameraStatusChange(QCamera::Status)));
} }
CameraBinFocus::~CameraBinFocus() CameraBinFocus::~CameraBinFocus()
@@ -319,10 +319,10 @@ void CameraBinFocus::_q_setFocusStatus(QCamera::LockStatus status, QCamera::Lock
} }
} }
void CameraBinFocus::_q_handleCameraStateChange(QCamera::State state) void CameraBinFocus::_q_handleCameraStatusChange(QCamera::Status status)
{ {
m_cameraState = state; m_cameraStatus = status;
if (state == QCamera::ActiveState) { if (status == QCamera::ActiveStatus) {
if (GstPad *pad = gst_element_get_static_pad(m_session->cameraSource(), "vfsrc")) { if (GstPad *pad = gst_element_get_static_pad(m_session->cameraSource(), "vfsrc")) {
if (GstCaps *caps = qt_gst_pad_get_current_caps(pad)) { if (GstCaps *caps = qt_gst_pad_get_current_caps(pad)) {
if (GstStructure *structure = gst_caps_get_structure(caps, 0)) { if (GstStructure *structure = gst_caps_get_structure(caps, 0)) {
@@ -415,7 +415,7 @@ void CameraBinFocus::updateRegionOfInterest(const QRectF &rectangle)
void CameraBinFocus::updateRegionOfInterest(const QVector<QRect> &rectangles) void CameraBinFocus::updateRegionOfInterest(const QVector<QRect> &rectangles)
{ {
if (m_cameraState != QCamera::ActiveState) if (m_cameraStatus != QCamera::ActiveStatus)
return; return;
GstElement * const cameraSource = m_session->cameraSource(); GstElement * const cameraSource = m_session->cameraSource();

View File

@@ -93,7 +93,7 @@ protected:
private Q_SLOTS: private Q_SLOTS:
void _q_setFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason); void _q_setFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason);
void _q_handleCameraStateChange(QCamera::State state); void _q_handleCameraStatusChange(QCamera::Status status);
#if GST_CHECK_VERSION(1,0,0) #if GST_CHECK_VERSION(1,0,0)
void _q_updateFaces(); void _q_updateFaces();
@@ -109,7 +109,7 @@ private:
#endif #endif
CameraBinSession *m_session; CameraBinSession *m_session;
QCamera::State m_cameraState; QCamera::Status m_cameraStatus;
QCameraFocus::FocusModes m_focusMode; QCameraFocus::FocusModes m_focusMode;
QCameraFocus::FocusPointMode m_focusPointMode; QCameraFocus::FocusPointMode m_focusPointMode;
QCamera::LockStatus m_focusStatus; QCamera::LockStatus m_focusStatus;

View File

@@ -61,7 +61,7 @@ CameraBinImageCapture::CameraBinImageCapture(CameraBinSession *session)
, m_requestId(0) , m_requestId(0)
, m_ready(false) , m_ready(false)
{ {
connect(m_session, SIGNAL(stateChanged(QCamera::State)), 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->cameraControl()->resourcePolicy(), SIGNAL(canCaptureChanged()), this, SLOT(updateState()));
@@ -100,7 +100,7 @@ void CameraBinImageCapture::cancelCapture()
void CameraBinImageCapture::updateState() void CameraBinImageCapture::updateState()
{ {
bool ready = m_session->state() == QCamera::ActiveState bool ready = m_session->status() == QCamera::ActiveStatus
&& m_session->cameraControl()->resourcePolicy()->canCapture(); && m_session->cameraControl()->resourcePolicy()->canCapture();
if (m_ready != ready) { if (m_ready != ready) {
#ifdef DEBUG_CAPTURE #ifdef DEBUG_CAPTURE

View File

@@ -49,7 +49,7 @@ CameraBinRecorder::CameraBinRecorder(CameraBinSession *session)
m_state(QMediaRecorder::StoppedState), m_state(QMediaRecorder::StoppedState),
m_status(QMediaRecorder::UnloadedStatus) m_status(QMediaRecorder::UnloadedStatus)
{ {
connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateStatus())); connect(m_session, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateStatus()));
connect(m_session, SIGNAL(pendingStateChanged(QCamera::State)), SLOT(updateStatus())); connect(m_session, SIGNAL(pendingStateChanged(QCamera::State)), SLOT(updateStatus()));
connect(m_session, SIGNAL(busyChanged(bool)), SLOT(updateStatus())); connect(m_session, SIGNAL(busyChanged(bool)), SLOT(updateStatus()));
@@ -86,12 +86,12 @@ QMediaRecorder::Status CameraBinRecorder::status() const
void CameraBinRecorder::updateStatus() void CameraBinRecorder::updateStatus()
{ {
QCamera::State sessionState = m_session->state(); QCamera::Status sessionStatus = m_session->status();
QMediaRecorder::State oldState = m_state; QMediaRecorder::State oldState = m_state;
QMediaRecorder::Status oldStatus = m_status; QMediaRecorder::Status oldStatus = m_status;
if (sessionState == QCamera::ActiveState && 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->cameraControl()->resourcePolicy()->canCapture()) {
@@ -214,7 +214,7 @@ void CameraBinRecorder::setState(QMediaRecorder::State state)
break; break;
case QMediaRecorder::RecordingState: case QMediaRecorder::RecordingState:
if (m_session->state() != QCamera::ActiveState) { 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->cameraControl()->resourcePolicy()->canCapture()) {
emit error(QMediaRecorder::ResourceError, tr("Recording permissions are not available")); emit error(QMediaRecorder::ResourceError, tr("Recording permissions are not available"));

View File

@@ -106,17 +106,12 @@
#define PREVIEW_CAPS_4_3 \ #define PREVIEW_CAPS_4_3 \
"video/x-raw-rgb, width = (int) 640, height = (int) 480" "video/x-raw-rgb, width = (int) 640, height = (int) 480"
//using GST_STATE_READY for QCamera::LoadedState
//may not work reliably at least with some webcams.
//#define USE_READY_STATE_ON_LOADED
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *parent) CameraBinSession::CameraBinSession(GstElementFactory *sourceFactory, QObject *parent)
:QObject(parent), :QObject(parent),
m_recordingActive(false), m_recordingActive(false),
m_state(QCamera::UnloadedState), m_status(QCamera::UnloadedStatus),
m_pendingState(QCamera::UnloadedState), m_pendingState(QCamera::UnloadedState),
m_muted(false), m_muted(false),
m_busy(false), m_busy(false),
@@ -230,9 +225,6 @@ CameraBinSession::CameraRole CameraBinSession::cameraRole() const
return BackCamera; return BackCamera;
} }
/*
Configure camera during Loaded->Active states stansition.
*/
bool CameraBinSession::setupCameraBin() bool CameraBinSession::setupCameraBin()
{ {
if (!buildCameraSource()) if (!buildCameraSource())
@@ -248,7 +240,8 @@ bool CameraBinSession::setupCameraBin()
#endif #endif
m_viewfinderHasChanged = false; m_viewfinderHasChanged = false;
if (!m_viewfinderElement) { if (!m_viewfinderElement) {
qWarning() << "Staring camera without viewfinder available"; if (m_pendingState == QCamera::ActiveState)
qWarning() << "Starting camera without viewfinder available";
m_viewfinderElement = gst_element_factory_make("fakesink", NULL); m_viewfinderElement = gst_element_factory_make("fakesink", NULL);
} }
g_object_set(G_OBJECT(m_viewfinderElement), "sync", FALSE, NULL); g_object_set(G_OBJECT(m_viewfinderElement), "sync", FALSE, NULL);
@@ -663,9 +656,20 @@ void CameraBinSession::handleViewfinderChange()
emit viewfinderChanged(); emit viewfinderChanged();
} }
QCamera::State CameraBinSession::state() const void CameraBinSession::setStatus(QCamera::Status status)
{ {
return m_state; if (m_status == status)
return;
m_status = status;
emit statusChanged(m_status);
setStateHelper(m_pendingState);
}
QCamera::Status CameraBinSession::status() const
{
return m_status;
} }
QCamera::State CameraBinSession::pendingState() const QCamera::State CameraBinSession::pendingState() const
@@ -685,66 +689,114 @@ void CameraBinSession::setState(QCamera::State newState)
qDebug() << Q_FUNC_INFO << newState; qDebug() << Q_FUNC_INFO << newState;
#endif #endif
switch (newState) { setStateHelper(newState);
}
void CameraBinSession::setStateHelper(QCamera::State state)
{
switch (state) {
case QCamera::UnloadedState: case QCamera::UnloadedState:
if (m_recordingActive) unload();
stopVideoRecording();
if (m_viewfinderInterface)
m_viewfinderInterface->stopRenderer();
gst_element_set_state(m_camerabin, GST_STATE_NULL);
m_state = newState;
if (m_busy)
emit busyChanged(m_busy = false);
emit stateChanged(m_state);
break; break;
case QCamera::LoadedState: case QCamera::LoadedState:
if (m_recordingActive) if (m_status == QCamera::ActiveStatus)
stopVideoRecording(); stop();
else if (m_status == QCamera::UnloadedStatus)
if (m_videoInputHasChanged) { load();
if (m_viewfinderInterface)
m_viewfinderInterface->stopRenderer();
gst_element_set_state(m_camerabin, GST_STATE_NULL);
buildCameraSource();
}
#ifdef USE_READY_STATE_ON_LOADED
gst_element_set_state(m_camerabin, GST_STATE_READY);
#else
m_state = QCamera::LoadedState;
if (m_viewfinderInterface)
m_viewfinderInterface->stopRenderer();
gst_element_set_state(m_camerabin, GST_STATE_NULL);
emit stateChanged(m_state);
#endif
break; break;
case QCamera::ActiveState: case QCamera::ActiveState:
if (setupCameraBin()) { // If the viewfinder changed while in the loaded state, we need to reload the pipeline
GstState binState = GST_STATE_NULL; if (m_status == QCamera::LoadedStatus && !m_viewfinderHasChanged)
GstState pending = GST_STATE_NULL; start();
gst_element_get_state(m_camerabin, &binState, &pending, 0); else if (m_status == QCamera::UnloadedStatus || m_viewfinderHasChanged)
load();
m_recorderControl->applySettings();
GstEncodingContainerProfile *profile = m_recorderControl->videoProfile();
g_object_set (G_OBJECT(m_camerabin),
"video-profile",
profile,
NULL);
gst_encoding_profile_unref(profile);
setAudioCaptureCaps();
setupCaptureResolution();
gst_element_set_state(m_camerabin, GST_STATE_PLAYING);
}
} }
} }
void CameraBinSession::setError(int err, const QString &errorString)
{
m_pendingState = QCamera::UnloadedState;
emit error(err, errorString);
setStatus(QCamera::UnloadedStatus);
}
void CameraBinSession::load()
{
if (m_status != QCamera::UnloadedStatus && !m_viewfinderHasChanged)
return;
setStatus(QCamera::LoadingStatus);
gst_element_set_state(m_camerabin, GST_STATE_NULL);
if (!setupCameraBin()) {
setError(QCamera::CameraError, QStringLiteral("No camera source available"));
return;
}
gst_element_set_state(m_camerabin, GST_STATE_READY);
}
void CameraBinSession::unload()
{
if (m_status == QCamera::UnloadedStatus || m_status == QCamera::UnloadingStatus)
return;
setStatus(QCamera::UnloadingStatus);
if (m_recordingActive)
stopVideoRecording();
if (m_viewfinderInterface)
m_viewfinderInterface->stopRenderer();
gst_element_set_state(m_camerabin, GST_STATE_NULL);
if (m_busy)
emit busyChanged(m_busy = false);
setStatus(QCamera::UnloadedStatus);
}
void CameraBinSession::start()
{
if (m_status != QCamera::LoadedStatus)
return;
setStatus(QCamera::StartingStatus);
m_recorderControl->applySettings();
GstEncodingContainerProfile *profile = m_recorderControl->videoProfile();
g_object_set (G_OBJECT(m_camerabin),
"video-profile",
profile,
NULL);
gst_encoding_profile_unref(profile);
setAudioCaptureCaps();
setupCaptureResolution();
gst_element_set_state(m_camerabin, GST_STATE_PLAYING);
}
void CameraBinSession::stop()
{
if (m_status != QCamera::ActiveStatus)
return;
setStatus(QCamera::StoppingStatus);
if (m_recordingActive)
stopVideoRecording();
if (m_viewfinderInterface)
m_viewfinderInterface->stopRenderer();
gst_element_set_state(m_camerabin, GST_STATE_READY);
}
bool CameraBinSession::isBusy() const bool CameraBinSession::isBusy() const
{ {
return m_busy; return m_busy;
@@ -889,7 +941,7 @@ bool CameraBinSession::processBusMessage(const QGstreamerMessage &message)
if (message.isEmpty()) if (message.isEmpty())
message = tr("Camera error"); message = tr("Camera error");
emit error(int(QMediaRecorder::ResourceError), message); setError(int(QMediaRecorder::ResourceError), message);
} }
#ifdef CAMERABIN_DEBUG_DUMP_BIN #ifdef CAMERABIN_DEBUG_DUMP_BIN
@@ -955,17 +1007,17 @@ bool CameraBinSession::processBusMessage(const QGstreamerMessage &message)
switch (newState) { switch (newState) {
case GST_STATE_VOID_PENDING: case GST_STATE_VOID_PENDING:
case GST_STATE_NULL: case GST_STATE_NULL:
if (m_state != QCamera::UnloadedState) setStatus(QCamera::UnloadedStatus);
emit stateChanged(m_state = QCamera::UnloadedState);
break; break;
case GST_STATE_READY: case GST_STATE_READY:
setMetaData(m_metaData); setMetaData(m_metaData);
if (m_state != QCamera::LoadedState) setStatus(QCamera::LoadedStatus);
emit stateChanged(m_state = QCamera::LoadedState); break;
case GST_STATE_PLAYING:
setStatus(QCamera::ActiveStatus);
break; break;
case GST_STATE_PAUSED: case GST_STATE_PAUSED:
case GST_STATE_PLAYING: default:
emit stateChanged(m_state = QCamera::ActiveState);
break; break;
} }
} }
@@ -973,7 +1025,6 @@ bool CameraBinSession::processBusMessage(const QGstreamerMessage &message)
default: default:
break; break;
} }
//qDebug() << "New session state:" << ENUM_NAME(CameraBinSession,"State",m_state);
} }
} }

View File

@@ -148,7 +148,7 @@ public:
void captureImage(int requestId, const QString &fileName); void captureImage(int requestId, const QString &fileName);
QCamera::State state() const; QCamera::Status status() const;
QCamera::State pendingState() const; QCamera::State pendingState() const;
bool isBusy() const; bool isBusy() const;
@@ -163,7 +163,7 @@ public:
bool processBusMessage(const QGstreamerMessage &message); bool processBusMessage(const QGstreamerMessage &message);
signals: signals:
void stateChanged(QCamera::State state); void statusChanged(QCamera::Status status);
void pendingStateChanged(QCamera::State state); void pendingStateChanged(QCamera::State state);
void durationChanged(qint64 duration); void durationChanged(qint64 duration);
void error(int error, const QString &errorString); void error(int error, const QString &errorString);
@@ -185,6 +185,15 @@ private slots:
void handleViewfinderChange(); void handleViewfinderChange();
private: private:
void load();
void unload();
void start();
void stop();
void setStatus(QCamera::Status status);
void setStateHelper(QCamera::State state);
void setError(int error, const QString &errorString);
bool setupCameraBin(); bool setupCameraBin();
void setupCaptureResolution(); void setupCaptureResolution();
void setAudioCaptureCaps(); void setAudioCaptureCaps();
@@ -197,7 +206,7 @@ private:
QUrl m_actualSink; QUrl m_actualSink;
bool m_recordingActive; bool m_recordingActive;
QString m_captureDevice; QString m_captureDevice;
QCamera::State m_state; QCamera::Status m_status;
QCamera::State m_pendingState; QCamera::State m_pendingState;
QString m_inputDevice; QString m_inputDevice;
bool m_muted; bool m_muted;