camerabin: Adds 'idle' property
authorThiago Santos <thiago.sousa.santos@collabora.co.uk>
Thu, 26 Aug 2010 20:08:19 +0000 (17:08 -0300)
committerThiago Santos <thiago.sousa.santos@collabora.co.uk>
Fri, 5 Nov 2010 01:48:02 +0000 (22:48 -0300)
Adds 'idle', a read-only boolean property that tells applications
if there is any capturing/saving/encoding going on in camerabin. If
not, it is safe to set it to NULL and release resources without
losing data.

gst/camerabin/gstcamerabin-enum.h
gst/camerabin/gstcamerabin.c
gst/camerabin/gstcamerabin.h
tests/check/elements/camerabin.c

index aa45614..69d7ba2 100644 (file)
@@ -68,7 +68,8 @@ enum
   ARG_VIDEO_CAPTURE_HEIGHT,
   ARG_VIDEO_CAPTURE_FRAMERATE,
   ARG_PREVIEW_SOURCE_FILTER,
-  ARG_READY_FOR_CAPTURE
+  ARG_READY_FOR_CAPTURE,
+  ARG_IDLE
 };
 
 /**
index 4eedcf5..bb9d122 100644 (file)
@@ -224,6 +224,31 @@ static guint camerabin_signals[LAST_SIGNAL];
 #define PREVIEW_MESSAGE_NAME "preview-image"
 #define IMG_CAPTURED_MESSAGE_NAME "image-captured"
 
+#define CAMERABIN_PROCESSING_INC_UNLOCKED(c)  \
+  (c)->processing_counter += 1;               \
+  GST_DEBUG_OBJECT ((c), "Processing counter incremented to: %d", \
+      (c)->processing_counter);               \
+  if ((c)->processing_counter == 1)           \
+    g_object_notify (G_OBJECT (c), "idle");            \
+
+#define CAMERABIN_PROCESSING_DEC_UNLOCKED(c)  \
+  (c)->processing_counter -= 1;               \
+  GST_DEBUG_OBJECT ((c), "Processing counter decremented to: %d", \
+      (c)->processing_counter);               \
+  g_assert ((c)->processing_counter >= 0);    \
+  if ((c)->processing_counter == 0)           \
+    g_object_notify (G_OBJECT (c), "idle");            \
+
+#define CAMERABIN_PROCESSING_INC(c)           \
+  g_mutex_lock ((c)->capture_mutex);          \
+  CAMERABIN_PROCESSING_INC_UNLOCKED ((c));    \
+  g_mutex_unlock ((c)->capture_mutex);        \
+
+#define CAMERABIN_PROCESSING_DEC(c)           \
+  g_mutex_lock ((c)->capture_mutex);          \
+  CAMERABIN_PROCESSING_DEC_UNLOCKED ((c));    \
+  g_mutex_unlock ((c)->capture_mutex);        \
+
 /*
  * static helper functions declaration
  */
@@ -1540,6 +1565,7 @@ gst_camerabin_start_image_capture (GstCameraBin * camera)
   }
 
   if (!ret) {
+    CAMERABIN_PROCESSING_DEC_UNLOCKED (camera);
     GST_WARNING_OBJECT (camera, "starting image capture failed");
   }
 }
@@ -1644,6 +1670,8 @@ gst_camerabin_start_video_recording (GstCameraBin * camera)
     GST_WARNING_OBJECT (camera, "videobin state change failed");
     gst_element_set_state (camera->vidbin, GST_STATE_NULL);
     gst_camerabin_reset_to_view_finder (camera);
+
+    CAMERABIN_PROCESSING_DEC (camera);
   }
 }
 
@@ -1772,6 +1800,7 @@ gst_camerabin_have_img_buffer (GstPad * pad, GstMiniObject * obj,
     if (g_str_equal (camera->filename->str, "")) {
       GST_DEBUG_OBJECT (camera, "filename not set, dropping buffer");
       ret = FALSE;
+      CAMERABIN_PROCESSING_DEC_UNLOCKED (camera);
       goto done;
     }
 
@@ -3071,6 +3100,21 @@ gst_camerabin_class_init (GstCameraBinClass * klass)
           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
   /**
+   * GstCameraBin:idle:
+   *
+   * When TRUE no capturing/encoding/saving is running and it is safe to set
+   * camerabin to NULL to release resources without losing data.
+   *
+   * In case of errors, this property is made unreliable. Set the pipeline
+   * back to READY or NULL to make it reliable again.
+   */
+  g_object_class_install_property (gobject_class, ARG_IDLE,
+      g_param_spec_boolean ("idle",
+          "Indicates if data is being processed (recording/capturing/saving)",
+          "Indicates if data is being processed (recording/capturing/saving)",
+          TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  /**
    * GstCameraBin::capture-start:
    * @camera: the camera bin element
    *
@@ -3244,6 +3288,7 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
   /* concurrency control */
   camera->capture_mutex = g_mutex_new ();
   camera->cond = g_cond_new ();
+  camera->processing_counter = 0;
 
   /* pad names for output and input selectors */
   camera->pad_src_view = NULL;
@@ -3723,6 +3768,9 @@ gst_camerabin_get_property (GObject * object, guint prop_id,
     case ARG_VIDEO_CAPTURE_FRAMERATE:
       gst_value_set_fraction (value, camera->app_fps_n, camera->app_fps_d);
       break;
+    case ARG_IDLE:
+      g_value_set_boolean (value, camera->processing_counter == 0);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -3788,6 +3836,11 @@ gst_camerabin_change_state (GstElement * element, GstStateChange transition)
         gst_camerabin_reset_to_view_finder (camera);
         g_cond_signal (camera->cond);
       }
+
+      /* reset processing counter */
+      GST_DEBUG_OBJECT (camera, "Reset processing counter to 0");
+      camera->processing_counter = 0;
+      g_object_notify (G_OBJECT (camera), "idle");
       g_mutex_unlock (camera->capture_mutex);
 
       /* unblock the viewfinder, but keep the property as is */
@@ -3835,6 +3888,7 @@ gst_camerabin_imgbin_finished (gpointer u_data)
   /* Close the file of saved image */
   gst_element_set_state (camera->imgbin, GST_STATE_READY);
   GST_DEBUG_OBJECT (camera, "Image pipeline set to READY");
+  CAMERABIN_PROCESSING_DEC (camera);
 
   /* Send image-done signal */
   gst_camerabin_image_capture_continue (camera, filename);
@@ -3871,6 +3925,8 @@ gst_camerabin_handle_message_func (GstBin * bin, GstMessage * msg)
         g_mutex_lock (camera->capture_mutex);
         camera->capturing = FALSE;
         g_cond_signal (camera->cond);
+
+        CAMERABIN_PROCESSING_DEC_UNLOCKED (camera);
         g_mutex_unlock (camera->capture_mutex);
       } else if (GST_MESSAGE_SRC (msg) == GST_OBJECT (camera->imgbin)) {
         /* Image eos */
@@ -3889,6 +3945,16 @@ gst_camerabin_handle_message_func (GstBin * bin, GstMessage * msg)
         camera->capturing = FALSE;
         g_cond_signal (camera->cond);
       }
+
+      /* Ideally we should check what error was and only decrement the
+       * counter if the error means that a 'processing' operation failed,
+       * instead of a setting up error. But this can be quite tricky to do
+       * and we expect the app to set the whole pipeline to READY/NULL
+       * when an error happens. For now we just mention that the
+       * processing counter and the 'idle' property are unreliable */
+      GST_DEBUG_OBJECT (camera, "An error makes the processing counter "
+          "unreliable");
+
       g_mutex_unlock (camera->capture_mutex);
       break;
     default:
@@ -3935,6 +4001,7 @@ gst_camerabin_capture_start (GstCameraBin * camera)
     g_mutex_unlock (camera->capture_mutex);
     return;
   }
+  CAMERABIN_PROCESSING_INC_UNLOCKED (camera);
   g_mutex_unlock (camera->capture_mutex);
 
   GST_OBJECT_LOCK (camera);
index a3de885..d53af1e 100644 (file)
@@ -130,6 +130,9 @@ struct _GstCameraBin
   GCond *cond;
   gboolean capturing;
   gboolean eos_handled;
+  /* everytime a new capture is started this is incremented, when it is
+   * finished/fails it is decremented. Used to know if camerabin is idle */
+  gint processing_counter;
 
   /* pad names for output and input selectors */
   GstPad *pad_src_view;
index 613c226..f5dbfdc 100644 (file)
@@ -493,6 +493,7 @@ check_file_validity (const gchar * filename, gint num, GstTagList * taglist)
 GST_START_TEST (test_single_image_capture)
 {
   gboolean ready = FALSE;
+  gboolean idle = FALSE;
   if (!camera)
     return;
 
@@ -514,6 +515,10 @@ GST_START_TEST (test_single_image_capture)
   g_object_get (camera, "ready-for-capture", &ready, NULL);
   fail_if (!ready, "not ready for capture");
 
+  /* check that the camera is idle */
+  g_object_get (camera, "idle", &idle, NULL);
+  fail_if (!idle, "camera should be idle");
+
   GST_INFO ("starting capture");
   g_signal_emit_by_name (camera, "capture-start", NULL);
 
@@ -521,6 +526,10 @@ GST_START_TEST (test_single_image_capture)
   fail_if (ready, "ready for capture during capture");
 
   g_main_loop_run (main_loop);
+
+  g_object_get (camera, "idle", &idle, NULL);
+  fail_if (!idle, "camera should be idle");
+
   gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
 
   check_file_validity (SINGLE_IMAGE_FILENAME, 0, NULL);
@@ -555,6 +564,7 @@ GST_END_TEST;
 GST_START_TEST (test_video_recording)
 {
   GstCaps *preview_caps;
+  gboolean idle = FALSE;
   preview_caps = gst_caps_from_string ("video/x-raw-rgb,width=320,height=240");
 
   if (!camera)
@@ -570,12 +580,24 @@ GST_START_TEST (test_video_recording)
   /* Set preview-caps */
   g_object_set (camera, "preview-caps", preview_caps, NULL);
 
+  /* check that the camera is idle */
+  g_object_get (camera, "idle", &idle, NULL);
+  fail_if (!idle, "camera should be idle");
+
   GST_INFO ("starting capture");
   g_signal_emit_by_name (camera, "capture-start", NULL);
+
+  g_object_get (camera, "idle", &idle, NULL);
+  fail_if (idle, "camera should not be idle");
+
   /* Record for one seconds  */
   g_usleep (G_USEC_PER_SEC);
+
   g_signal_emit_by_name (camera, "capture-stop", NULL);
 
+  g_object_get (camera, "idle", &idle, NULL);
+  fail_if (!idle, "camera should be idle");
+
   /* check if receiving the preview-image message */
   fail_if (!received_preview_msg,
       "creating video recording preview image failed");
@@ -624,6 +646,7 @@ GST_END_TEST;
 
 GST_START_TEST (test_video_recording_pause)
 {
+  gboolean idle = FALSE;
   if (!camera)
     return;
 
@@ -631,21 +654,40 @@ GST_START_TEST (test_video_recording_pause)
   g_object_set (camera, "mode", 1,
       "filename", make_test_file_name (VIDEO_PAUSE_FILENAME, 0), NULL);
 
+  g_object_get (camera, "idle", &idle, NULL);
+  fail_unless (idle, "camera should be idle");
+
   GST_INFO ("starting capture");
   g_signal_emit_by_name (camera, "capture-start", NULL);
+
+  g_object_get (camera, "idle", &idle, NULL);
+  fail_if (idle, "camera shouldn't be idle when recording");
+
   /* Record for one seconds  */
   g_usleep (G_USEC_PER_SEC);
 
   GST_INFO ("pause capture");
   g_signal_emit_by_name (camera, "capture-pause", NULL);
+
+  g_object_get (camera, "idle", &idle, NULL);
+  fail_if (idle, "camera shouldn't be idle when recording and paused");
+
   /* Record for one seconds  */
   g_usleep (G_USEC_PER_SEC);
 
   GST_INFO ("continue capture");
   g_signal_emit_by_name (camera, "capture-start", NULL);
+
+  g_object_get (camera, "idle", &idle, NULL);
+  fail_if (idle, "camera shouldn't be idle when recording");
+
   /* Record for one seconds  */
   g_usleep (G_USEC_PER_SEC);
   g_signal_emit_by_name (camera, "capture-stop", NULL);
+
+  g_object_get (camera, "idle", &idle, NULL);
+  fail_unless (idle, "camera should be idle after capture-stop");
+
   gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
 
   check_file_validity (VIDEO_PAUSE_FILENAME, 0, NULL);