mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 18:21:04 +00:00
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:
parent
bc1b09c1c4
commit
60a6eca2a1
4 changed files with 114 additions and 1 deletions
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -3070,6 +3099,21 @@ gst_camerabin_class_init (GstCameraBinClass * klass)
|
|||
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
|
||||
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue