diff --git a/gst-libs/gst/basecamerabinsrc/gstbasecamerasrc.c b/gst-libs/gst/basecamerabinsrc/gstbasecamerasrc.c index d5b8ed7728..7d159e04db 100644 --- a/gst-libs/gst/basecamerabinsrc/gstbasecamerasrc.c +++ b/gst-libs/gst/basecamerabinsrc/gstbasecamerasrc.c @@ -474,6 +474,9 @@ gst_base_camera_src_change_state (GstElement * element, ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_element_set_state (self->preview_pipeline->pipeline, GST_STATE_READY); + break; case GST_STATE_CHANGE_READY_TO_NULL: gst_element_set_state (self->preview_pipeline->pipeline, GST_STATE_NULL); break; diff --git a/gst/camerabin/camerabinvideo.c b/gst/camerabin/camerabinvideo.c index b54e9abdff..d88d1f4dee 100644 --- a/gst/camerabin/camerabinvideo.c +++ b/gst/camerabin/camerabinvideo.c @@ -638,6 +638,13 @@ gst_camerabin_video_create_elements (GstCameraBinVideo * vid) G_CALLBACK (gst_camerabin_drop_eos_probe), vid); gst_object_unref (vid_srcpad); + /* audio source is not always present and might be set to NULL during operation */ + if (vid->aud_src + && g_object_class_find_property (G_OBJECT_GET_CLASS (vid->aud_src), + "provide-clock")) { + g_object_set (vid->aud_src, "provide-clock", FALSE, NULL); + } + GST_DEBUG ("created video elements"); return TRUE; diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index fe41e38199..1a2b05f76a 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -1641,6 +1641,7 @@ static void gst_camerabin_start_video_recording (GstCameraBin * camera) { GstStateChangeReturn state_ret; + GstCameraBinVideo *vidbin = (GstCameraBinVideo *) camera->vidbin; /* FIXME: how to ensure resolution and fps is supported by CPU? * use a queue overrun signal? */ @@ -1654,9 +1655,14 @@ gst_camerabin_start_video_recording (GstCameraBin * camera) gst_camerabin_rewrite_tags (camera); /* Pause the pipeline in order to distribute new clock in paused_to_playing */ + /* Audio source needs to go to null to reset the ringbuffer */ + if (vidbin->aud_src) + gst_element_set_state (vidbin->aud_src, GST_STATE_NULL); state_ret = gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PAUSED); if (state_ret != GST_STATE_CHANGE_FAILURE) { + GstClock *clock = gst_element_get_clock (GST_ELEMENT (camera)); + g_mutex_lock (camera->capture_mutex); camera->capturing = TRUE; g_mutex_unlock (camera->capture_mutex); @@ -1672,6 +1678,11 @@ gst_camerabin_start_video_recording (GstCameraBin * camera) g_object_set (G_OBJECT (camera->src_vid_src), "capture-mode", 2, NULL); } + /* Clock might be distributed as NULL to audiosrc, messing timestamping */ + if (vidbin->aud_src) + gst_element_set_clock (vidbin->aud_src, clock); + gst_object_unref (clock); + /* videobin will not go to playing if file is not writable */ if (gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { diff --git a/gst/camerabin2/gstcamerabin2.c b/gst/camerabin2/gstcamerabin2.c index a91f109c76..8d1de91408 100644 --- a/gst/camerabin2/gstcamerabin2.c +++ b/gst/camerabin2/gstcamerabin2.c @@ -406,8 +406,10 @@ gst_camera_bin_start_capture (GstCameraBin2 * camerabin) } /* store the next preview filename */ + g_mutex_lock (camerabin->preview_list_mutex); camerabin->preview_location_list = g_slist_append (camerabin->preview_location_list, location); + g_mutex_unlock (camerabin->preview_list_mutex); g_signal_emit_by_name (camerabin->src, "start-capture", NULL); if (camerabin->mode == MODE_VIDEO && camerabin->audio_src) @@ -518,6 +520,7 @@ gst_camera_bin_dispose (GObject * object) GstCameraBin2 *camerabin = GST_CAMERA_BIN2_CAST (object); g_free (camerabin->location); + g_mutex_free (camerabin->preview_list_mutex); if (camerabin->src_capture_notify_id) g_signal_handler_disconnect (camerabin->src, @@ -873,6 +876,7 @@ gst_camera_bin_init (GstCameraBin2 * camera) camera->zoom = DEFAULT_ZOOM; camera->max_zoom = MAX_ZOOM; camera->flags = DEFAULT_FLAGS; + camera->preview_list_mutex = g_mutex_new (); /* capsfilters are created here as we proxy their caps properties and * this way we avoid having to store the caps while on NULL state to @@ -945,20 +949,31 @@ gst_camera_bin_handle_message (GstBin * bin, GstMessage * message) } } else if (gst_structure_has_name (structure, "preview-image")) { GValue *value; - gchar *location; + gchar *location = NULL; - location = camerabin->preview_location_list->data; - camerabin->preview_location_list = - g_slist_delete_link (camerabin->preview_location_list, - camerabin->preview_location_list); - GST_DEBUG_OBJECT (camerabin, "Adding preview location to preview " - "message '%s'", location); + g_mutex_lock (camerabin->preview_list_mutex); + if (camerabin->preview_location_list) { + location = camerabin->preview_location_list->data; + camerabin->preview_location_list = + g_slist_delete_link (camerabin->preview_location_list, + camerabin->preview_location_list); + GST_DEBUG_OBJECT (camerabin, "Adding preview location to preview " + "message '%s'", location); + } else { + GST_WARNING_OBJECT (camerabin, "Unexpected preview message received, " + "won't be able to put location field into the message. This can " + "happen if the source is posting previews while camerabin2 is " + "shutting down"); + } + g_mutex_unlock (camerabin->preview_list_mutex); - value = g_new0 (GValue, 1); - g_value_init (value, G_TYPE_STRING); - g_value_take_string (value, location); - gst_structure_take_value ((GstStructure *) structure, "location", - value); + if (location) { + value = g_new0 (GValue, 1); + g_value_init (value, G_TYPE_STRING); + g_value_take_string (value, location); + gst_structure_take_value ((GstStructure *) structure, "location", + value); + } } } break; @@ -1725,9 +1740,11 @@ gst_camera_bin_change_state (GstElement * element, GstStateChange trans) g_slist_free (camera->image_location_list); camera->image_location_list = NULL; + g_mutex_lock (camera->preview_list_mutex); g_slist_foreach (camera->preview_location_list, (GFunc) g_free, NULL); g_slist_free (camera->preview_location_list); camera->preview_location_list = NULL; + g_mutex_unlock (camera->preview_list_mutex); /* explicitly set to READY as they might be outside of the bin */ gst_element_set_state (camera->audio_volume, GST_STATE_READY); diff --git a/gst/camerabin2/gstcamerabin2.h b/gst/camerabin2/gstcamerabin2.h index 05af34f4de..3b4c9c92fd 100644 --- a/gst/camerabin2/gstcamerabin2.h +++ b/gst/camerabin2/gstcamerabin2.h @@ -94,8 +94,22 @@ struct _GstCameraBin2 * each buffer capture */ GSList *image_location_list; - /* similar to above, but used for giving names to previews */ + /* + * Similar to above, but used for giving names to previews + * + * Need to protect with a mutex as this list is used when the + * camera-source posts a preview image. As we have no control + * on how the camera-source will behave (we can only tell how + * it should), the preview location list might be used in an + * inconsistent way. + * One example is the camera-source posting a preview image after + * camerabin2 was put to ready, when this preview list will be + * freed and set to NULL. Concurrent access might lead to crashes in + * this situation. (Concurrency from the state-change freeing the + * list and the message handling function looking at preview names) + */ GSList *preview_location_list; + GMutex *preview_list_mutex; gboolean video_profile_switch; gboolean image_profile_switch; diff --git a/sys/shm/gstshmsink.c b/sys/shm/gstshmsink.c index a7d7e52073..cb6c92bfa6 100644 --- a/sys/shm/gstshmsink.c +++ b/sys/shm/gstshmsink.c @@ -45,7 +45,8 @@ enum PROP_SOCKET_PATH, PROP_PERMS, PROP_SHM_SIZE, - PROP_WAIT_FOR_CONNECTION + PROP_WAIT_FOR_CONNECTION, + PROP_BUFFER_TIME }; struct GstShmClient @@ -162,6 +163,13 @@ gst_shm_sink_class_init (GstShmSinkClass * klass) DEFAULT_WAIT_FOR_CONNECTION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_BUFFER_TIME, + g_param_spec_uint64 ("buffer-time", + "Buffer Time of the shm buffer", + "Maximum Size of the shm buffer in nanoseconds (-1 to disable)", + 0, G_MAXUINT64, GST_CLOCK_TIME_NONE, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + signals[SIGNAL_CLIENT_CONNECTED] = g_signal_new ("client-connected", GST_TYPE_SHM_SINK, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); @@ -229,6 +237,12 @@ gst_shm_sink_set_property (GObject * object, guint prop_id, GST_OBJECT_UNLOCK (object); g_cond_broadcast (self->cond); break; + case PROP_BUFFER_TIME: + GST_OBJECT_LOCK (object); + self->buffer_time = g_value_get_uint64 (value); + GST_OBJECT_UNLOCK (object); + g_cond_broadcast (self->cond); + break; default: break; } @@ -263,6 +277,9 @@ gst_shm_sink_get_property (GObject * object, guint prop_id, case PROP_WAIT_FOR_CONNECTION: g_value_set_boolean (value, self->wait_for_connection); break; + case PROP_BUFFER_TIME: + g_value_set_uint64 (value, self->buffer_time); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -358,6 +375,24 @@ gst_shm_sink_stop (GstBaseSink * bsink) return TRUE; } +static gboolean +gst_shm_sink_can_render (GstShmSink * self, GstClockTime time) +{ + ShmBuffer *b; + + if (time == GST_CLOCK_TIME_NONE || self->buffer_time == GST_CLOCK_TIME_NONE) + return TRUE; + + b = sp_writer_get_pending_buffers (self->pipe); + for (; b != NULL; b = sp_writer_get_next_buffer (b)) { + GstClockTime t = sp_writer_buf_get_tag (b); + if (GST_CLOCK_DIFF (time, t) > self->buffer_time) + return FALSE; + } + + return TRUE; +} + static GstFlowReturn gst_shm_sink_render (GstBaseSink * bsink, GstBuffer * buf) { @@ -373,8 +408,16 @@ gst_shm_sink_render (GstBaseSink * bsink, GstBuffer * buf) } } + while (!gst_shm_sink_can_render (self, GST_BUFFER_TIMESTAMP (buf))) { + g_cond_wait (self->cond, GST_OBJECT_GET_LOCK (self)); + if (self->unlock) { + GST_OBJECT_UNLOCK (self); + return GST_FLOW_WRONG_STATE; + } + } + rv = sp_writer_send_buf (self->pipe, (char *) GST_BUFFER_DATA (buf), - GST_BUFFER_SIZE (buf)); + GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf)); if (rv == -1) { ShmBlock *block = NULL; @@ -396,10 +439,10 @@ gst_shm_sink_render (GstBaseSink * bsink, GstBuffer * buf) } } - shmbuf = sp_writer_block_get_buf (block); memcpy (shmbuf, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); - sp_writer_send_buf (self->pipe, shmbuf, GST_BUFFER_SIZE (buf)); + sp_writer_send_buf (self->pipe, shmbuf, GST_BUFFER_SIZE (buf), + GST_BUFFER_TIMESTAMP (buf)); sp_writer_free_block (block); } diff --git a/sys/shm/gstshmsink.h b/sys/shm/gstshmsink.h index bc6da74bc7..83dad393e5 100644 --- a/sys/shm/gstshmsink.h +++ b/sys/shm/gstshmsink.h @@ -61,6 +61,7 @@ struct _GstShmSink gboolean wait_for_connection; gboolean stop; gboolean unlock; + GstClockTime buffer_time; GCond *cond; }; diff --git a/sys/shm/shmpipe.c b/sys/shm/shmpipe.c index c629ea05c2..583aa3846a 100644 --- a/sys/shm/shmpipe.c +++ b/sys/shm/shmpipe.c @@ -78,7 +78,6 @@ enum }; typedef struct _ShmArea ShmArea; -typedef struct _ShmBuffer ShmBuffer; struct _ShmArea { @@ -112,6 +111,8 @@ struct _ShmBuffer int num_clients; int clients[0]; + + uint64_t tag; }; @@ -542,7 +543,7 @@ sp_writer_free_block (ShmBlock * block) /* Returns the number of client this has successfully been sent to */ int -sp_writer_send_buf (ShmPipe * self, char *buf, size_t size) +sp_writer_send_buf (ShmPipe * self, char *buf, size_t size, uint64_t tag) { ShmArea *area = NULL; unsigned long offset = 0; @@ -577,6 +578,7 @@ sp_writer_send_buf (ShmPipe * self, char *buf, size_t size) sb->size = size; sb->num_clients = self->num_clients; sb->ablock = ablock; + sb->tag = tag; for (client = self->clients; client; client = client->next) { struct CommandBuffer cb = { 0 }; @@ -892,3 +894,21 @@ sp_writer_get_path (ShmPipe * pipe) { return pipe->socket_path; } + +ShmBuffer * +sp_writer_get_pending_buffers (ShmPipe * self) +{ + return self->buffers; +} + +ShmBuffer * +sp_writer_get_next_buffer (ShmBuffer * buffer) +{ + return buffer->next; +} + +uint64_t +sp_writer_buf_get_tag (ShmBuffer * buffer) +{ + return buffer->tag; +} diff --git a/sys/shm/shmpipe.h b/sys/shm/shmpipe.h index 9cf0d6c61c..eef8877d14 100644 --- a/sys/shm/shmpipe.h +++ b/sys/shm/shmpipe.h @@ -63,6 +63,7 @@ #define __SHMPIPE_H__ #include <stdlib.h> +#include <stdint.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> @@ -75,6 +76,7 @@ extern "C" { typedef struct _ShmClient ShmClient; typedef struct _ShmPipe ShmPipe; typedef struct _ShmBlock ShmBlock; +typedef struct _ShmBuffer ShmBuffer; ShmPipe *sp_writer_create (const char *path, size_t size, mode_t perms); const char *sp_writer_get_path (ShmPipe *pipe); @@ -90,7 +92,7 @@ int sp_writer_get_client_fd (ShmClient * client); ShmBlock *sp_writer_alloc_block (ShmPipe * self, size_t size); void sp_writer_free_block (ShmBlock *block); -int sp_writer_send_buf (ShmPipe * self, char *buf, size_t size); +int sp_writer_send_buf (ShmPipe * self, char *buf, size_t size, uint64_t tag); char *sp_writer_block_get_buf (ShmBlock *block); ShmPipe *sp_writer_block_get_pipe (ShmBlock *block); @@ -104,6 +106,10 @@ ShmPipe *sp_client_open (const char *path); long int sp_client_recv (ShmPipe * self, char **buf); int sp_client_recv_finish (ShmPipe * self, char *buf); +ShmBuffer *sp_writer_get_pending_buffers (ShmPipe * self); +ShmBuffer *sp_writer_get_next_buffer (ShmBuffer * buffer); +uint64_t sp_writer_buf_get_tag (ShmBuffer * buffer); + #ifdef __cplusplus } #endif