camerabin: Adds 'idle' property

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.
This commit is contained in:
Thiago Santos 2010-08-26 17:08:19 -03:00
parent bc1b09c1c4
commit 60a6eca2a1
4 changed files with 114 additions and 1 deletions

View file

@ -68,7 +68,8 @@ enum
ARG_VIDEO_CAPTURE_HEIGHT, ARG_VIDEO_CAPTURE_HEIGHT,
ARG_VIDEO_CAPTURE_FRAMERATE, ARG_VIDEO_CAPTURE_FRAMERATE,
ARG_PREVIEW_SOURCE_FILTER, ARG_PREVIEW_SOURCE_FILTER,
ARG_READY_FOR_CAPTURE ARG_READY_FOR_CAPTURE,
ARG_IDLE
}; };
/** /**

View file

@ -224,6 +224,31 @@ static guint camerabin_signals[LAST_SIGNAL];
#define PREVIEW_MESSAGE_NAME "preview-image" #define PREVIEW_MESSAGE_NAME "preview-image"
#define IMG_CAPTURED_MESSAGE_NAME "image-captured" #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 * static helper functions declaration
*/ */
@ -1540,6 +1565,7 @@ gst_camerabin_start_image_capture (GstCameraBin * camera)
} }
if (!ret) { if (!ret) {
CAMERABIN_PROCESSING_DEC_UNLOCKED (camera);
GST_WARNING_OBJECT (camera, "starting image capture failed"); 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_WARNING_OBJECT (camera, "videobin state change failed");
gst_element_set_state (camera->vidbin, GST_STATE_NULL); gst_element_set_state (camera->vidbin, GST_STATE_NULL);
gst_camerabin_reset_to_view_finder (camera); 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, "")) { if (g_str_equal (camera->filename->str, "")) {
GST_DEBUG_OBJECT (camera, "filename not set, dropping buffer"); GST_DEBUG_OBJECT (camera, "filename not set, dropping buffer");
ret = FALSE; ret = FALSE;
CAMERABIN_PROCESSING_DEC_UNLOCKED (camera);
goto done; goto done;
} }
@ -3070,6 +3099,21 @@ gst_camerabin_class_init (GstCameraBinClass * klass)
DEFAULT_READY_FOR_CAPTURE, DEFAULT_READY_FOR_CAPTURE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); 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: * GstCameraBin::capture-start:
* @camera: the camera bin element * @camera: the camera bin element
@ -3244,6 +3288,7 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
/* concurrency control */ /* concurrency control */
camera->capture_mutex = g_mutex_new (); camera->capture_mutex = g_mutex_new ();
camera->cond = g_cond_new (); camera->cond = g_cond_new ();
camera->processing_counter = 0;
/* pad names for output and input selectors */ /* pad names for output and input selectors */
camera->pad_src_view = NULL; camera->pad_src_view = NULL;
@ -3723,6 +3768,9 @@ gst_camerabin_get_property (GObject * object, guint prop_id,
case ARG_VIDEO_CAPTURE_FRAMERATE: case ARG_VIDEO_CAPTURE_FRAMERATE:
gst_value_set_fraction (value, camera->app_fps_n, camera->app_fps_d); gst_value_set_fraction (value, camera->app_fps_n, camera->app_fps_d);
break; break;
case ARG_IDLE:
g_value_set_boolean (value, camera->processing_counter == 0);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -3788,6 +3836,11 @@ gst_camerabin_change_state (GstElement * element, GstStateChange transition)
gst_camerabin_reset_to_view_finder (camera); gst_camerabin_reset_to_view_finder (camera);
g_cond_signal (camera->cond); 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); g_mutex_unlock (camera->capture_mutex);
/* unblock the viewfinder, but keep the property as is */ /* 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 */ /* Close the file of saved image */
gst_element_set_state (camera->imgbin, GST_STATE_READY); gst_element_set_state (camera->imgbin, GST_STATE_READY);
GST_DEBUG_OBJECT (camera, "Image pipeline set to READY"); GST_DEBUG_OBJECT (camera, "Image pipeline set to READY");
CAMERABIN_PROCESSING_DEC (camera);
/* Send image-done signal */ /* Send image-done signal */
gst_camerabin_image_capture_continue (camera, filename); 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); g_mutex_lock (camera->capture_mutex);
camera->capturing = FALSE; camera->capturing = FALSE;
g_cond_signal (camera->cond); g_cond_signal (camera->cond);
CAMERABIN_PROCESSING_DEC_UNLOCKED (camera);
g_mutex_unlock (camera->capture_mutex); g_mutex_unlock (camera->capture_mutex);
} else if (GST_MESSAGE_SRC (msg) == GST_OBJECT (camera->imgbin)) { } else if (GST_MESSAGE_SRC (msg) == GST_OBJECT (camera->imgbin)) {
/* Image eos */ /* Image eos */
@ -3889,6 +3945,16 @@ gst_camerabin_handle_message_func (GstBin * bin, GstMessage * msg)
camera->capturing = FALSE; camera->capturing = FALSE;
g_cond_signal (camera->cond); 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); g_mutex_unlock (camera->capture_mutex);
break; break;
default: default:
@ -3935,6 +4001,7 @@ gst_camerabin_capture_start (GstCameraBin * camera)
g_mutex_unlock (camera->capture_mutex); g_mutex_unlock (camera->capture_mutex);
return; return;
} }
CAMERABIN_PROCESSING_INC_UNLOCKED (camera);
g_mutex_unlock (camera->capture_mutex); g_mutex_unlock (camera->capture_mutex);
GST_OBJECT_LOCK (camera); GST_OBJECT_LOCK (camera);

View file

@ -130,6 +130,9 @@ struct _GstCameraBin
GCond *cond; GCond *cond;
gboolean capturing; gboolean capturing;
gboolean eos_handled; 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 */ /* pad names for output and input selectors */
GstPad *pad_src_view; GstPad *pad_src_view;

View file

@ -493,6 +493,7 @@ check_file_validity (const gchar * filename, gint num, GstTagList * taglist)
GST_START_TEST (test_single_image_capture) GST_START_TEST (test_single_image_capture)
{ {
gboolean ready = FALSE; gboolean ready = FALSE;
gboolean idle = FALSE;
if (!camera) if (!camera)
return; return;
@ -514,6 +515,10 @@ GST_START_TEST (test_single_image_capture)
g_object_get (camera, "ready-for-capture", &ready, NULL); g_object_get (camera, "ready-for-capture", &ready, NULL);
fail_if (!ready, "not ready for capture"); 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"); GST_INFO ("starting capture");
g_signal_emit_by_name (camera, "capture-start", NULL); 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"); fail_if (ready, "ready for capture during capture");
g_main_loop_run (main_loop); 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); gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
check_file_validity (SINGLE_IMAGE_FILENAME, 0, NULL); check_file_validity (SINGLE_IMAGE_FILENAME, 0, NULL);
@ -555,6 +564,7 @@ GST_END_TEST;
GST_START_TEST (test_video_recording) GST_START_TEST (test_video_recording)
{ {
GstCaps *preview_caps; GstCaps *preview_caps;
gboolean idle = FALSE;
preview_caps = gst_caps_from_string ("video/x-raw-rgb,width=320,height=240"); preview_caps = gst_caps_from_string ("video/x-raw-rgb,width=320,height=240");
if (!camera) if (!camera)
@ -570,12 +580,24 @@ GST_START_TEST (test_video_recording)
/* Set preview-caps */ /* Set preview-caps */
g_object_set (camera, "preview-caps", preview_caps, NULL); 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"); GST_INFO ("starting capture");
g_signal_emit_by_name (camera, "capture-start", NULL); 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 */ /* Record for one seconds */
g_usleep (G_USEC_PER_SEC); g_usleep (G_USEC_PER_SEC);
g_signal_emit_by_name (camera, "capture-stop", NULL); 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 */ /* check if receiving the preview-image message */
fail_if (!received_preview_msg, fail_if (!received_preview_msg,
"creating video recording preview image failed"); "creating video recording preview image failed");
@ -624,6 +646,7 @@ GST_END_TEST;
GST_START_TEST (test_video_recording_pause) GST_START_TEST (test_video_recording_pause)
{ {
gboolean idle = FALSE;
if (!camera) if (!camera)
return; return;
@ -631,21 +654,40 @@ GST_START_TEST (test_video_recording_pause)
g_object_set (camera, "mode", 1, g_object_set (camera, "mode", 1,
"filename", make_test_file_name (VIDEO_PAUSE_FILENAME, 0), NULL); "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"); GST_INFO ("starting capture");
g_signal_emit_by_name (camera, "capture-start", NULL); 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 */ /* Record for one seconds */
g_usleep (G_USEC_PER_SEC); g_usleep (G_USEC_PER_SEC);
GST_INFO ("pause capture"); GST_INFO ("pause capture");
g_signal_emit_by_name (camera, "capture-pause", NULL); 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 */ /* Record for one seconds */
g_usleep (G_USEC_PER_SEC); g_usleep (G_USEC_PER_SEC);
GST_INFO ("continue capture"); GST_INFO ("continue capture");
g_signal_emit_by_name (camera, "capture-start", NULL); 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 */ /* Record for one seconds */
g_usleep (G_USEC_PER_SEC); g_usleep (G_USEC_PER_SEC);
g_signal_emit_by_name (camera, "capture-stop", NULL); 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); gst_element_set_state (GST_ELEMENT (camera), GST_STATE_NULL);
check_file_validity (VIDEO_PAUSE_FILENAME, 0, NULL); check_file_validity (VIDEO_PAUSE_FILENAME, 0, NULL);