#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
*/
}
if (!ret) {
+ CAMERABIN_PROCESSING_DEC_UNLOCKED (camera);
GST_WARNING_OBJECT (camera, "starting image capture failed");
}
}
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);
}
}
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;
}
DEFAULT_READY_FOR_CAPTURE,
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
/* 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;
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;
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 */
/* 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);
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 */
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:
g_mutex_unlock (camera->capture_mutex);
return;
}
+ CAMERABIN_PROCESSING_INC_UNLOCKED (camera);
g_mutex_unlock (camera->capture_mutex);
GST_OBJECT_LOCK (camera);
GST_START_TEST (test_single_image_capture)
{
gboolean ready = FALSE;
+ gboolean idle = FALSE;
if (!camera)
return;
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);
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);
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)
/* 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");
GST_START_TEST (test_video_recording_pause)
{
+ gboolean idle = FALSE;
if (!camera)
return;
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);