imagefreeze: Add support for replacing the output buffer
authorSebastian Dröge <sebastian@centricular.com>
Fri, 10 Jan 2020 12:54:26 +0000 (14:54 +0200)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Sat, 11 Jan 2020 08:04:43 +0000 (08:04 +0000)
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
gst/imagefreeze/gstimagefreeze.h

index f2933ceef7665de04163a0026cfaeb1b85c4c0b9..424ca0c432011e8eabeaa896aeb290dddc4b6143 100644 (file)
 #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;
index 4d03118e9eac22f181fb6696d9dda864e7452c9b..779c676f19c6c30106bb86b894c100753edb5815 100644 (file)
@@ -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