Reworked CameraResources to be synchronous.
authorMohammed Sameer <msameer@foolab.org>
Sat, 20 Oct 2012 01:23:57 +0000 (04:23 +0300)
committerMohammed Sameer <msameer@foolab.org>
Sat, 20 Oct 2012 01:23:57 +0000 (04:23 +0300)
We also stop the camera when needed appropriately

qml/CameraPage.qml
qml/main.qml
src/cameraresources.cpp
src/cameraresources.h

index 2687a07..0fe5f3d 100644 (file)
@@ -36,15 +36,21 @@ Page {
 
         signal batteryLow
 
+        function updatePolicy() {
+                if (!resourcePolicy.acquire(page.policyMode)) {
+                        cam.stop(force);
+                }
+        }
+
         Component.onCompleted: {
                 if (Qt.application.active && needsPipeline) {
-                        resourcePolicy.acquire(page.policyMode);
+                        updatePolicy();
                 }
         }
 
         onStatusChanged: {
                 if (Qt.application.active && status == PageStatus.Activating) {
-                        resourcePolicy.acquire(page.policyMode);
+                        updatePolicy();
                 }
         }
 /*
@@ -57,23 +63,28 @@ Page {
 
         onPolicyModeChanged: {
                 if (Qt.application.active) {
-                        resourcePolicy.acquire(page.policyMode);
+                        updatePolicy();
                 }
         }
 
         function handlePipeline() {
-                if (!Qt.application.active) {
-                        // TODO: force if we lost resources ?
-                        cam.stop();
+                var acquired = resourcePolicy.acquired;
+                if (!acquired) {
+                        cam.stop(true);
+                        showError(qsTr("Resources lost. Stopping camera."));
                 }
-                else if (resourcePolicy.acquired && page.needsPipeline && !cam.running) {
-                        // TODO: error
-                        cam.start();
+                else if (!Qt.application.active && !acquired) {
+                        cam.stop(true);
+                        showError(qsTr("Resources lost. Stopping camera."));
                 }
-                else if (!resourcePolicy.acquired) {
-                        // TODO: force
+                else if (!Qt.application.active) {
                         cam.stop();
                 }
+                else if (acquired && page.needsPipeline && !cam.running) {
+                        if (!cam.start()) {
+                                showError(qsTr("Failed to start camera. Please restart the application."));
+                        }
+                }
         }
 
         Connections {
@@ -92,7 +103,7 @@ Page {
                                 }
                         }
                         else if (page.needsPipeline) {
-                                resourcePolicy.acquire(page.policyMode);
+                                updatePolicy();
                         }
                 }
         }
index b124fc6..59e1f56 100644 (file)
@@ -35,7 +35,6 @@ import QtMobility.location 1.2
 // TODO: stop viewfinder in settings pages ?
 // TODO: grid lines, face tracking, ambr
 // TODO: complete settings pages
-// TODO: stop camera properly when we get closed.
 // TODO: select primary/secondary camera.
 // TODO: disable debug builds.
 // TODO: a way to get buffers to the application
@@ -122,14 +121,6 @@ PageStackWindow {
 
         CameraResources {
                 id: resourcePolicy
-                onAcquiredChanged: {
-                        if (resourcePolicy.acquired) {
-                                // TODO:
-                        }
-                        else {
-                                // TODO: We need a way to force a stop.
-                        }
-                }
         }
 
         DeviceInfo {
@@ -249,9 +240,7 @@ PageStackWindow {
 
                 // TODO: hardcoding device id
                 Component.onCompleted: { cam.deviceId = 0; mode = settings.mode; }
-                Component.onDestruction: {
-                console.log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
-                }
+                Component.onDestruction: cam.stop();
 
                 // TODO: Hack
                 z: -1
index ef37f0f..007ebbc 100644 (file)
  */
 
 #include "cameraresources.h"
+#include <dbusconnectioneventloop.h>
 #include <QDebug>
 
-using namespace ResourcePolicy;
-
 CameraResources::CameraResources(QObject *parent) :
   QObject(parent),
-  m_set(new ResourceSet("camera", this, true, true)),
-  m_mode(None), m_acquired(false) {
+  m_worker(new CameraResourcesWorker) {
+
+  m_worker->moveToThread(&m_thread);
+  DBUSConnectionEventLoop::getInstance().moveToThread(&m_thread);
+
+  QObject::connect(&m_thread, SIGNAL(started()), m_worker, SLOT(init()));
+  m_thread.start();
+
+  qRegisterMetaType<CameraResources::Mode>("CameraResources::Mode");
+  qRegisterMetaType<CameraResources::ResourceType>("CameraResources::ResourceType");
+  qRegisterMetaType<bool *>("bool *");
+
+  QObject::connect(m_worker, SIGNAL(acquiredChanged()), this, SIGNAL(acquiredChanged()));
+  QObject::connect(m_worker, SIGNAL(hijackedChanged()), this, SIGNAL(hijackedChanged()));
+}
+
+CameraResources::~CameraResources() {
+  m_thread.quit();
+  m_thread.wait();
+
+  delete m_worker;
+  m_worker = 0;
+}
+
+bool CameraResources::isResourceGranted(const ResourceType& resource) {
+  bool ok = false;
+
+  QMetaObject::invokeMethod(m_worker, "isResourceGranted", Qt::BlockingQueuedConnection,
+                           Q_ARG(bool *, &ok), Q_ARG(CameraResources::ResourceType, resource));
+
+  return ok;
+}
+
+bool CameraResources::acquire(const Mode& mode) {
+  bool ok = false;
+
+  QMetaObject::invokeMethod(m_worker, "acquire", Qt::BlockingQueuedConnection,
+                           Q_ARG(bool *, &ok), Q_ARG(CameraResources::Mode, mode));
+
+  return ok;
+}
+
+bool CameraResources::acquired() const {
+  bool ok = false;
+
+  QMetaObject::invokeMethod(m_worker, "acquired", Qt::BlockingQueuedConnection,
+                           Q_ARG(bool *, &ok));
+
+  return ok;
+}
+
+bool CameraResources::hijacked() const {
+  bool ok = false;
+
+  QMetaObject::invokeMethod(m_worker, "hijacked", Qt::BlockingQueuedConnection,
+                           Q_ARG(bool *, &ok));
+
+  return ok;
+}
+
+CameraResourcesWorker::CameraResourcesWorker(QObject *parent) :
+  QObject(parent),
+  m_set(0),
+  m_mode(CameraResources::None),
+  m_acquired(false),
+  m_acquiring(false),
+  m_hijacked(false) {
+
+}
+
+CameraResourcesWorker::~CameraResourcesWorker() {
+  bool ok;
+
+  acquire(&ok, CameraResources::None);
+}
+
+void CameraResourcesWorker::init() {
+  m_set = new ResourcePolicy::ResourceSet("camera", this);
+  m_set->setAlwaysReply();
 
   QObject::connect(m_set, SIGNAL(resourcesReleased()), this, SLOT(resourcesReleased()));
   QObject::connect(m_set, SIGNAL(lostResources()), this, SLOT(lostResources()));
   QObject::connect(m_set, SIGNAL(resourcesGranted(const QList<ResourcePolicy::ResourceType>&)),
                   this, SLOT(resourcesGranted(const QList<ResourcePolicy::ResourceType>&)));
-  QObject::connect(m_set, SIGNAL(updateOK()), this, SLOT(updateOK()));
+  QObject::connect(m_set, SIGNAL(resourcesDenied()), this, SLOT(resourcesDenied()));
 
   if (!m_set->initAndConnect()) {
     qCritical() << "Failed to connect to resource policy engine";
   }
 }
 
-CameraResources::~CameraResources() {
-  acquire(None);
+void CameraResourcesWorker::resourcesReleased() {
+  setHijacked(false);
+  setAcquired(false);
+
+  m_acquiring = false;
 }
 
-void CameraResources::acquire(const CameraResources::Mode& mode) {
+void CameraResourcesWorker::lostResources() {
+  setAcquired(false);
+  setHijacked(true);
+
+  m_acquiring = false;
+}
+
+void CameraResourcesWorker::resourcesGranted(const QList<ResourcePolicy::ResourceType>& optional) {
+  Q_UNUSED(optional);
+
+  m_acquiring = false;
+
+  setAcquired(true);
+  setHijacked(false);
+}
+
+void CameraResourcesWorker::resourcesDenied() {
+  setAcquired(false);
+  setHijacked(true);
+
+  m_acquiring = false;
+}
+
+QList<ResourcePolicy::ResourceType> CameraResourcesWorker::listSet() {
+  QList<ResourcePolicy::Resource *> resources = m_set->resources();
+
+  QList<ResourcePolicy::ResourceType> set;
+
+  foreach (ResourcePolicy::Resource *r, resources) {
+    set << r->type();
+  }
+
+  return set;
+}
+
+void CameraResourcesWorker::acquire(bool *ok, const CameraResources::Mode& mode) {
   if (mode == m_mode) {
-    // We need to emit this because the UI migh be waiting
-    emit acquiredChanged();
+    *ok = true;
     return;
   }
 
   m_mode = mode;
 
+  *ok = false;
+
   switch (m_mode) {
-  case None:
-    m_set->release();
+  case CameraResources::None:
+    *ok = release();
     break;
 
-  case Image:
-    updateSet(QList<ResourcePolicy::ResourceType>()
-             << ResourcePolicy::VideoPlaybackType
-             << ResourcePolicy::VideoRecorderType
-             << ResourcePolicy::ScaleButtonType
-             << ResourcePolicy::SnapButtonType);
+  case CameraResources::Image:
+    *ok = updateSet(QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::VideoPlaybackType
+                   << ResourcePolicy::VideoRecorderType
+                   << ResourcePolicy::ScaleButtonType
+                   << ResourcePolicy::SnapButtonType);
     break;
 
-  case Video:
-    updateSet(QList<ResourcePolicy::ResourceType>()
-             << ResourcePolicy::VideoPlaybackType
-             << ResourcePolicy::VideoRecorderType
-             << ResourcePolicy::ScaleButtonType
-             << ResourcePolicy::SnapButtonType);
+  case CameraResources::Video:
+    *ok = updateSet(QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::VideoPlaybackType
+                   << ResourcePolicy::VideoRecorderType
+                   << ResourcePolicy::ScaleButtonType
+                   << ResourcePolicy::SnapButtonType);
     break;
 
-  case Recording:
-    updateSet(QList<ResourcePolicy::ResourceType>()
-             << ResourcePolicy::VideoPlaybackType
-             << ResourcePolicy::VideoRecorderType
-             << ResourcePolicy::ScaleButtonType
-             << ResourcePolicy::SnapButtonType
-             << ResourcePolicy::AudioRecorderType,
-             QList<ResourcePolicy::ResourceType>()
-             << ResourcePolicy::AudioPlaybackType);
+  case CameraResources::Recording:
+    *ok = updateSet(QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::VideoPlaybackType
+                   << ResourcePolicy::VideoRecorderType
+                   << ResourcePolicy::ScaleButtonType
+                   << ResourcePolicy::SnapButtonType,
+                   QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::AudioRecorderType
+                   << ResourcePolicy::AudioPlaybackType);
     break;
 
-  case PostCapture:
-    updateSet(QList<ResourcePolicy::ResourceType>()
-             << ResourcePolicy::VideoPlaybackType
-             << ResourcePolicy::ScaleButtonType,
-             QList<ResourcePolicy::ResourceType>()
-             << ResourcePolicy::AudioPlaybackType);
-
+  case CameraResources::PostCapture:
+    *ok = updateSet(QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::VideoPlaybackType
+                   << ResourcePolicy::ScaleButtonType,
+                   QList<ResourcePolicy::ResourceType>()
+                   << ResourcePolicy::AudioPlaybackType);
     break;
 
   default:
     qWarning() << "Unknown mode" << mode;
 
-    break;
+    *ok = false;
   }
 }
 
-bool CameraResources::acquired() const {
-  return m_acquired;
+void CameraResourcesWorker::acquired(bool *ok) {
+  *ok = m_acquired;
 }
 
-void CameraResources::resourcesReleased() {
-  m_mode = None;
-  m_acquired = false;
-  emit acquiredChanged();
+void CameraResourcesWorker::hijacked(bool *ok) {
+  *ok = m_hijacked;
 }
 
-void CameraResources::lostResources() {
-  m_mode = None;
-  m_acquired = false;
-  emit acquiredChanged();
-}
+bool CameraResourcesWorker::updateSet(const QList<ResourcePolicy::ResourceType>& required,
+                                     const QList<ResourcePolicy::ResourceType>& optional) {
 
-void CameraResources::resourcesGranted(const QList<ResourcePolicy::ResourceType>& optional) {
-  Q_UNUSED(optional);
 
-  m_acquired = true;
-  emit acquiredChanged();
-}
+  QList<ResourcePolicy::ResourceType> set = listSet();
 
-void CameraResources::updateOK() {
-  m_acquired = true;
-  emit acquiredChanged();
-}
+  foreach (ResourcePolicy::ResourceType r, set) {
+    if (required.indexOf(r) != -1) {
+      m_set->resource(r)->setOptional(false);
+    }
+    else if (optional.indexOf(r) != -1) {
+      m_set->resource(r)->setOptional(true);
+    }
+    else {
+      m_set->deleteResource(r);
+    }
+  }
 
-QList<ResourcePolicy::ResourceType> CameraResources::listSet() {
-  QList<Resource *> resources = m_set->resources();
-  QList<ResourcePolicy::ResourceType> set;
+  foreach (ResourcePolicy::ResourceType r, required) {
+    m_set->addResource(r);
+  }
 
-  foreach (Resource *r, resources) {
-    set << r->type();
+  foreach (ResourcePolicy::ResourceType r, optional) {
+    m_set->addResource(r);
+    ResourcePolicy::Resource *res = m_set->resource(r);
+    if (res) {
+      res->setOptional(true);
+    }
   }
 
-  return set;
+  if (m_set->contains(ResourcePolicy::AudioPlaybackType)) {
+    bool isOptional = m_set->resource(ResourcePolicy::AudioPlaybackType)->isOptional();
+
+    ResourcePolicy::AudioResource *audio = new ResourcePolicy::AudioResource("camera");
+    audio->setProcessID(QCoreApplication::applicationPid());
+    audio->setOptional(isOptional);
+    m_set->addResourceObject(audio);
+  }
+
+  m_acquiring = true;
+
+  m_set->update();
+  m_set->acquire();
+
+  while (m_acquiring) {
+    QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
+  }
+
+  return m_acquired;
 }
 
-void CameraResources::updateSet(const QList<ResourcePolicy::ResourceType>& required,
-                               const QList<ResourcePolicy::ResourceType>& optional) {
+bool CameraResourcesWorker::release() {
+  m_acquiring = true;
 
-  Q_UNUSED(optional);
+  m_set->release();
 
-  bool isEmpty = m_set->resources().isEmpty();
+  while (m_acquiring) {
+    QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
+  }
 
-  QList<ResourcePolicy::ResourceType> set = listSet();
+  m_mode = CameraResources::None;
 
-  foreach (ResourceType r, set) {
-    // Check for acquired resources that should be dropped.
-    if (required.indexOf(r) == -1) {
-      m_set->deleteResource(r);
-    }
+  return true;
+}
+
+void CameraResourcesWorker::setAcquired(bool acquired) {
+  if (m_acquired != acquired) {
+    m_acquired = acquired;
+    emit acquiredChanged();
   }
+}
 
-  foreach (ResourceType r, required) {
-    // TODO: AudioPlayback needs special handling
-    m_set->addResource(r);
+void CameraResourcesWorker::setHijacked(bool hijacked) {
+  if (m_hijacked != hijacked) {
+    m_hijacked = hijacked;
+    emit hijackedChanged();
   }
+}
 
-  // TODO: optional resources
+void CameraResourcesWorker::isResourceGranted(bool *ok,
+                                             const CameraResources::ResourceType& resource) {
 
-  // Odd. If we don't do it that way then policy ignores our requests
-  // when we get minimized then maximized.
-  if (isEmpty) {
-    m_set->update();
-  }
-  else {
-    m_set->acquire();
+  ResourcePolicy::ResourceType rt = (ResourcePolicy::ResourceType)resource;
+
+  ResourcePolicy::Resource *r = m_set->resource(rt);
+  if (r) {
+    *ok = r->isGranted();
   }
 }
index e5bc048..c6c7594 100644 (file)
 #define CAMERA_RESOURCES_H
 
 #include <QObject>
+#include <QThread>
 #include <policy/resource-set.h>
+class CameraResourcesWorker;
 
 class CameraResources : public QObject {
   Q_OBJECT
 
   Q_PROPERTY(bool acquired READ acquired NOTIFY acquiredChanged);
+  Q_PROPERTY(bool hijacked READ hijacked NOTIFY hijackedChanged);
 
   Q_ENUMS(Mode);
+  Q_ENUMS(ResourceType);
 
 public:
   typedef enum {
@@ -42,33 +46,79 @@ public:
     PostCapture,
   } Mode;
 
+  typedef enum {
+    AudioPlaybackType = ResourcePolicy::AudioPlaybackType,
+    VideoPlaybackType = ResourcePolicy::VideoPlaybackType,
+    AudioRecorderType = ResourcePolicy::AudioRecorderType,
+    VideoRecorderType = ResourcePolicy::VideoRecorderType,
+    ScaleButtonType = ResourcePolicy::ScaleButtonType,
+    SnapButtonType = ResourcePolicy::SnapButtonType,
+    LensCoverType = ResourcePolicy::LensCoverType,
+  } ResourceType;
+
   CameraResources(QObject *parent = 0);
   ~CameraResources();
 
+  Q_INVOKABLE bool acquire(const Mode& mode);
+
+  Q_INVOKABLE bool isResourceGranted(const ResourceType& resource);
+
   bool acquired() const;
+  bool hijacked() const;
+
+signals:
+  void acquiredChanged();
+  void hijackedChanged();
+
+private:
+  CameraResourcesWorker *m_worker;
+  QThread m_thread;
+};
+
+class CameraResourcesWorker : public QObject {
+  Q_OBJECT
+
+public:
+  CameraResourcesWorker(QObject *parent = 0);
+  ~CameraResourcesWorker();
 
 public slots:
-  void acquire(const Mode& mode);
+  void acquire(bool *ok, const CameraResources::Mode& mode);
+  void acquired(bool *ok);
+  void hijacked(bool *ok);
+  void isResourceGranted(bool *ok, const CameraResources::ResourceType& resource);
 
 signals:
   void acquiredChanged();
+  void hijackedChanged();
 
 private slots:
+  void init();
+
   void resourcesReleased();
   void lostResources();
   void resourcesGranted(const QList<ResourcePolicy::ResourceType>& optional);
-  void updateOK();
+  void resourcesDenied();
 
 private:
-  void updateSet(const QList<ResourcePolicy::ResourceType>& required,
+  bool release();
+
+  bool updateSet(const QList<ResourcePolicy::ResourceType>& required,
                 const QList<ResourcePolicy::ResourceType>& optional =
                 QList<ResourcePolicy::ResourceType>());
 
   QList<ResourcePolicy::ResourceType> listSet();
 
+  void setAcquired(bool acquired);
+  void setHijacked(bool hijacked);
+
   ResourcePolicy::ResourceSet *m_set;
-  Mode m_mode;
+
+  CameraResources::Mode m_mode;
+  QMutex m_mutex;
   bool m_acquired;
+  bool m_acquiring;
+  bool m_hijacked;
 };
 
 #endif /* CAMERA_RESOURCES_H */