From c17d5e36ad5dadfbbab4c725c9ac1a3eaf4e9bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 10 Jan 2020 14:54:26 +0200 Subject: [PATCH] imagefreeze: Add support for replacing the output buffer By default imagefreeze will still reject new buffers after the first one and immediately return GST_FLOW_EOS but the new allow-replace property allows to change this. Whenever updating the buffer we now also keep track of the configured caps of the buffer and from the source pad task negotiate correctly based on the potentially updated caps. Only the very first time negotiation of a framerate with downstream is performed, afterwards only the caps themselves apart from the framerate are updated. --- gst/imagefreeze/gstimagefreeze.c | 76 ++++++++++++++++++++++++++------ gst/imagefreeze/gstimagefreeze.h | 6 +++ 2 files changed, 68 insertions(+), 14 deletions(-) diff --git a/gst/imagefreeze/gstimagefreeze.c b/gst/imagefreeze/gstimagefreeze.c index f2933ceef7..424ca0c432 100644 --- a/gst/imagefreeze/gstimagefreeze.c +++ b/gst/imagefreeze/gstimagefreeze.c @@ -46,11 +46,13 @@ #include "gstimagefreeze.h" #define DEFAULT_NUM_BUFFERS -1 +#define DEFAULT_ALLOW_REPLACE FALSE enum { PROP_0, - PROP_NUM_BUFFERS + PROP_NUM_BUFFERS, + PROP_ALLOW_REPLACE, }; static void gst_image_freeze_finalize (GObject * object); @@ -107,11 +109,16 @@ gst_image_freeze_class_init (GstImageFreezeClass * klass) gobject_class->get_property = gst_image_freeze_get_property; g_object_class_install_property (gobject_class, PROP_NUM_BUFFERS, - g_param_spec_int ("num-buffers", "num-buffers", + g_param_spec_int ("num-buffers", "Number of buffers", "Number of buffers to output before sending EOS (-1 = unlimited)", -1, G_MAXINT, DEFAULT_NUM_BUFFERS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ALLOW_REPLACE, + g_param_spec_boolean ("allow-replace", "Allow Replace", + "Allow replacing the input buffer and always output the latest", + DEFAULT_ALLOW_REPLACE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_image_freeze_change_state); @@ -151,6 +158,7 @@ gst_image_freeze_init (GstImageFreeze * self) g_mutex_init (&self->lock); self->num_buffers = DEFAULT_NUM_BUFFERS; + self->allow_replace = DEFAULT_ALLOW_REPLACE; gst_image_freeze_reset (self); } @@ -176,11 +184,15 @@ gst_image_freeze_reset (GstImageFreeze * self) g_mutex_lock (&self->lock); gst_buffer_replace (&self->buffer, NULL); + gst_caps_replace (&self->buffer_caps, NULL); + gst_caps_replace (&self->current_caps, NULL); + self->buffer_caps_updated = FALSE; self->num_buffers_left = self->num_buffers; gst_segment_init (&self->segment, GST_FORMAT_TIME); self->need_segment = TRUE; + self->negotiated_framerate = FALSE; self->fps_n = self->fps_d = 0; self->offset = 0; self->seqnum = 0; @@ -200,8 +212,22 @@ gst_image_freeze_sink_setcaps (GstImageFreeze * self, GstCaps * caps) GstPad *pad; pad = self->sinkpad; + caps = gst_caps_copy (caps); + /* If we already negotiated a framerate then only update for the + * caps of the new buffer */ + if (self->negotiated_framerate) { + gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, self->fps_n, + self->fps_d, NULL); + GST_DEBUG_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, caps); + ret = gst_pad_set_caps (self->srcpad, caps); + gst_caps_unref (caps); + return ret; + } + + /* Else negotiate a framerate with downstream */ + GST_DEBUG_OBJECT (pad, "Setting caps: %" GST_PTR_FORMAT, caps); s = gst_caps_get_structure (caps, 0); @@ -246,6 +272,7 @@ gst_image_freeze_sink_setcaps (GstImageFreeze * self, GstCaps * caps) self->fps_n = fps_n; self->fps_d = fps_d; g_mutex_unlock (&self->lock); + self->negotiated_framerate = TRUE; GST_DEBUG_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, candidate); ret = TRUE; gst_caps_unref (candidate); @@ -540,8 +567,10 @@ gst_image_freeze_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstCaps *caps; + g_mutex_lock (&self->lock); gst_event_parse_caps (event, &caps); - gst_image_freeze_sink_setcaps (self, caps); + gst_caps_replace (&self->current_caps, caps); + g_mutex_unlock (&self->lock); gst_event_unref (event); ret = TRUE; break; @@ -707,6 +736,9 @@ gst_image_freeze_set_property (GObject * object, guint prop_id, case PROP_NUM_BUFFERS: self->num_buffers = g_value_get_int (value); break; + case PROP_ALLOW_REPLACE: + self->allow_replace = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -725,6 +757,9 @@ gst_image_freeze_get_property (GObject * object, guint prop_id, GValue * value, case PROP_NUM_BUFFERS: g_value_set_int (value, self->num_buffers); break; + case PROP_ALLOW_REPLACE: + g_value_set_boolean (value, self->allow_replace); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -736,21 +771,33 @@ gst_image_freeze_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) { GstImageFreeze *self = GST_IMAGE_FREEZE (parent); + GstFlowReturn flow_ret; g_mutex_lock (&self->lock); - if (self->buffer) { + if (self->buffer && !self->allow_replace) { GST_DEBUG_OBJECT (pad, "Already have a buffer, dropping"); gst_buffer_unref (buffer); g_mutex_unlock (&self->lock); return GST_FLOW_EOS; } - self->buffer = buffer; + if (!self->current_caps) { + GST_ERROR_OBJECT (pad, "Not negotiated yet"); + g_mutex_unlock (&self->lock); + return GST_FLOW_NOT_NEGOTIATED; + } + + gst_buffer_replace (&self->buffer, buffer); + self->buffer_caps_updated = !self->buffer_caps + || !gst_caps_is_equal (self->buffer_caps, self->current_caps); + gst_caps_replace (&self->buffer_caps, self->current_caps); + gst_buffer_unref (buffer); gst_pad_start_task (self->srcpad, (GstTaskFunction) gst_image_freeze_src_loop, self->srcpad, NULL); + flow_ret = self->allow_replace ? GST_FLOW_OK : GST_FLOW_EOS; g_mutex_unlock (&self->lock); - return GST_FLOW_EOS; + return flow_ret; } static void @@ -766,13 +813,6 @@ gst_image_freeze_src_loop (GstPad * pad) gboolean first = FALSE; g_mutex_lock (&self->lock); - if (!gst_pad_has_current_caps (self->srcpad)) { - GST_ERROR_OBJECT (pad, "Not negotiated yet"); - flow_ret = GST_FLOW_NOT_NEGOTIATED; - g_mutex_unlock (&self->lock); - goto pause_task; - } - if (!self->buffer) { GST_ERROR_OBJECT (pad, "Have no buffer yet"); flow_ret = GST_FLOW_ERROR; @@ -793,7 +833,15 @@ gst_image_freeze_src_loop (GstPad * pad) } buffer = gst_buffer_copy (self->buffer); - g_mutex_unlock (&self->lock); + if (self->buffer_caps_updated) { + GstCaps *buffer_caps = gst_caps_ref (self->buffer_caps); + self->buffer_caps_updated = FALSE; + g_mutex_unlock (&self->lock); + gst_image_freeze_sink_setcaps (self, buffer_caps); + gst_caps_unref (buffer_caps); + } else { + g_mutex_unlock (&self->lock); + } if (self->need_segment) { GstEvent *e; diff --git a/gst/imagefreeze/gstimagefreeze.h b/gst/imagefreeze/gstimagefreeze.h index 4d03118e9e..779c676f19 100644 --- a/gst/imagefreeze/gstimagefreeze.h +++ b/gst/imagefreeze/gstimagefreeze.h @@ -49,6 +49,10 @@ struct _GstImageFreeze GMutex lock; GstBuffer *buffer; + GstCaps *buffer_caps, *current_caps; + gboolean buffer_caps_updated; + + gboolean negotiated_framerate; gint fps_n, fps_d; GstSegment segment; @@ -58,6 +62,8 @@ struct _GstImageFreeze gint num_buffers; gint num_buffers_left; + gboolean allow_replace; + guint64 offset; /* TRUE if currently doing a flushing seek, protected