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;
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;
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?
*/
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);
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) {
}
/* 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)
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,
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
}
} else if (gst_structure_has_name (structure, "preview-image")) {
GValue *value;
- gchar *location;
-
- 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);
-
- 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);
+ gchar *location = NULL;
+
+ 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);
+
+ 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;
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);
* 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;
PROP_SOCKET_PATH,
PROP_PERMS,
PROP_SHM_SIZE,
- PROP_WAIT_FOR_CONNECTION
+ PROP_WAIT_FOR_CONNECTION,
+ PROP_BUFFER_TIME
};
struct GstShmClient
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);
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;
}
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;
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)
{
}
}
+ 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;
}
}
-
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);
}
gboolean wait_for_connection;
gboolean stop;
gboolean unlock;
+ GstClockTime buffer_time;
GCond *cond;
};
};
typedef struct _ShmArea ShmArea;
-typedef struct _ShmBuffer ShmBuffer;
struct _ShmArea
{
int num_clients;
int clients[0];
+
+ uint64_t tag;
};
/* 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;
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 };
{
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;
+}
#define __SHMPIPE_H__
#include <stdlib.h>
+#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
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);
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);
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