From f31554bc4f8d0451778680fa4dfd432a95100b35 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Wed, 22 Jun 2011 16:27:00 -0300 Subject: [PATCH] camerabin2: Send serialized custom events for filename changes in image sink Using serialized custom events for switching image capture saving location makes camerabin2 save each capture correctly to the location that was set during the moment start-capture was called, and not the moment the filesink was writing to disk. This prevents captures to be overwriten by racyness among start-capture and setting location for images. --- gst/camerabin2/gstcamerabin2.c | 128 ++++++++++++++++++++++++++++----- gst/camerabin2/gstcamerabin2.h | 8 ++- 2 files changed, 117 insertions(+), 19 deletions(-) diff --git a/gst/camerabin2/gstcamerabin2.c b/gst/camerabin2/gstcamerabin2.c index 10a996c535..b2b14b5801 100644 --- a/gst/camerabin2/gstcamerabin2.c +++ b/gst/camerabin2/gstcamerabin2.c @@ -206,6 +206,14 @@ gst_camera_bin_new_event_renegotiate (void) gst_structure_new ("renegotiate", NULL)); } +static GstEvent * +gst_camera_bin_new_event_file_location (const gchar * location) +{ + return gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, + gst_structure_new ("new-location", "location", G_TYPE_STRING, location, + NULL)); +} + static void gst_camera_bin_start_capture (GstCameraBin * camerabin) { @@ -224,18 +232,28 @@ gst_camera_bin_start_capture (GstCameraBin * camerabin) GST_CAMERA_BIN_PROCESSING_INC (camerabin); - if (camerabin->mode == MODE_VIDEO && camerabin->audio_src) { - gst_element_set_state (camerabin->audio_src, GST_STATE_READY); - /* need to reset eos status (pads could be flushing) */ - gst_element_set_state (camerabin->audio_queue, GST_STATE_READY); - gst_element_set_state (camerabin->audio_convert, GST_STATE_READY); - gst_element_set_state (camerabin->audio_capsfilter, GST_STATE_READY); - gst_element_set_state (camerabin->audio_volume, GST_STATE_READY); + if (camerabin->mode == MODE_VIDEO) { + if (camerabin->audio_src) { + gst_element_set_state (camerabin->audio_src, GST_STATE_READY); + /* need to reset eos status (pads could be flushing) */ + gst_element_set_state (camerabin->audio_queue, GST_STATE_READY); + gst_element_set_state (camerabin->audio_convert, GST_STATE_READY); + gst_element_set_state (camerabin->audio_capsfilter, GST_STATE_READY); + gst_element_set_state (camerabin->audio_volume, GST_STATE_READY); - gst_element_sync_state_with_parent (camerabin->audio_queue); - gst_element_sync_state_with_parent (camerabin->audio_convert); - gst_element_sync_state_with_parent (camerabin->audio_capsfilter); - gst_element_sync_state_with_parent (camerabin->audio_volume); + gst_element_sync_state_with_parent (camerabin->audio_queue); + gst_element_sync_state_with_parent (camerabin->audio_convert); + gst_element_sync_state_with_parent (camerabin->audio_capsfilter); + gst_element_sync_state_with_parent (camerabin->audio_volume); + } + } else { + gchar *location; + + /* store the next capture buffer filename */ + location = + g_strdup_printf (camerabin->image_location, camerabin->image_index++); + camerabin->image_location_list = + g_slist_append (camerabin->image_location_list, location); } g_signal_emit_by_name (camerabin->src, "start-capture", NULL); @@ -323,13 +341,6 @@ gst_camera_bin_src_notify_readyforcapture (GObject * obj, GParamSpec * pspec, gst_element_set_state (camera->video_encodebin, GST_STATE_PLAYING); gst_element_set_state (camera->videobin_capsfilter, GST_STATE_PLAYING); gst_element_set_state (camera->videobin_queue, GST_STATE_PLAYING); - } else if (camera->mode == MODE_IMAGE) { - gst_element_set_state (camera->imagesink, GST_STATE_NULL); - GST_DEBUG_OBJECT (camera, "Switching imagebin location to %s", - camera->image_location); - g_object_set (camera->imagesink, "location", camera->image_location, - NULL); - gst_element_set_state (camera->imagesink, GST_STATE_PLAYING); } } @@ -951,6 +962,65 @@ gst_camera_bin_src_notify_max_zoom_cb (GObject * self, GParamSpec * pspec, g_object_notify (G_OBJECT (camera), "max-zoom"); } +static gboolean +gst_camera_bin_image_src_buffer_probe (GstPad * pad, GstBuffer * buf, + gpointer data) +{ + GstCameraBin *camerabin = data; + GstEvent *evt; + gchar *location = NULL; + GstPad *peer; + + if (camerabin->image_location_list) { + location = camerabin->image_location_list->data; + camerabin->image_location_list = + g_slist_delete_link (camerabin->image_location_list, + camerabin->image_location_list); + GST_DEBUG_OBJECT (camerabin, "Sending image location change to %s", + location); + } else { + GST_DEBUG_OBJECT (camerabin, "No filename location change to send"); + return TRUE; + } + + evt = gst_camera_bin_new_event_file_location (location); + peer = gst_pad_get_peer (pad); + gst_pad_send_event (peer, evt); + g_free (location); + gst_object_unref (peer); + + return TRUE; +} + + +static gboolean +gst_camera_bin_image_sink_event_probe (GstPad * pad, GstEvent * event, + gpointer data) +{ + GstCameraBin *camerabin = data; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_DOWNSTREAM:{ + if (gst_event_has_name (event, "new-location")) { + const GstStructure *structure = gst_event_get_structure (event); + const gchar *filename = gst_structure_get_string (structure, + "location"); + + gst_element_set_state (camerabin->imagesink, GST_STATE_NULL); + GST_DEBUG_OBJECT (camerabin, "Setting filename to imagesink: %s", + filename); + g_object_set (camerabin->imagesink, "location", filename, NULL); + gst_element_set_state (camerabin->imagesink, GST_STATE_PLAYING); + } + } + break; + default: + break; + } + + return TRUE; +} + /** * gst_camera_bin_create_elements: * @param camera: the #GstCameraBin @@ -1101,6 +1171,19 @@ gst_camera_bin_create_elements (GstCameraBin * camera) gst_element_link (camera->image_encodebin, camera->imagesink); gst_element_link_many (camera->viewfinderbin_queue, camera->viewfinderbin_capsfilter, camera->viewfinderbin, NULL); + + { + /* set an event probe to watch for custom location changes */ + GstPad *srcpad; + + srcpad = gst_element_get_static_pad (camera->image_encodebin, "src"); + + gst_pad_add_event_probe (srcpad, + (GCallback) gst_camera_bin_image_sink_event_probe, camera); + + gst_object_unref (srcpad); + } + /* * Video can't get into playing as its internal filesink will open * a file for writing and leave it empty if unused. @@ -1183,6 +1266,8 @@ gst_camera_bin_create_elements (GstCameraBin * camera) (GCallback) gst_camera_bin_src_notify_max_zoom_cb, camera); } if (new_src) { + GstPad *imgsrc = gst_element_get_static_pad (camera->src, "imgsrc"); + gst_bin_add (GST_BIN_CAST (camera), gst_object_ref (camera->src)); camera->src_capture_notify_id = g_signal_connect (G_OBJECT (camera->src), "notify::ready-for-capture", @@ -1193,6 +1278,10 @@ gst_camera_bin_create_elements (GstCameraBin * camera) "sink"); gst_element_link_pads (camera->src, "vidsrc", camera->videobin_queue, "sink"); + + gst_pad_add_buffer_probe (imgsrc, + (GCallback) gst_camera_bin_image_src_buffer_probe, camera); + gst_object_unref (imgsrc); } gst_camera_bin_check_and_replace_filter (camera, &camera->image_filter, @@ -1308,6 +1397,9 @@ gst_camera_bin_change_state (GstElement * element, GstStateChange trans) gst_tag_setter_reset_tags (GST_TAG_SETTER (camera)); GST_CAMERA_BIN_RESET_PROCESSING_COUNTER (camera); + g_slist_free_full (camera->image_location_list, g_free); + camera->image_location_list = NULL; + /* explicitly set to READY as they might be outside of the bin */ gst_element_set_state (camera->audio_queue, GST_STATE_READY); gst_element_set_state (camera->audio_volume, GST_STATE_READY); diff --git a/gst/camerabin2/gstcamerabin2.h b/gst/camerabin2/gstcamerabin2.h index 449b300f66..07c18d10f3 100644 --- a/gst/camerabin2/gstcamerabin2.h +++ b/gst/camerabin2/gstcamerabin2.h @@ -74,8 +74,14 @@ struct _GstCameraBin gint processing_counter; /* atomic int */ - /* Index of the auto incrementing file index for video recordings */ + /* Index of the auto incrementing file index for captures */ gint video_index; + gint image_index; + + /* stores list of image locations to be pushed to the image sink + * as file location change notifications, they are pushed before + * each buffer capture */ + GSList *image_location_list; gboolean video_profile_switch; gboolean image_profile_switch;