From 00fd6496406b72cca222c577285242f22b865519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 14 Aug 2020 11:40:30 +0300 Subject: [PATCH] valve: Add modes to forward sticky events when dropping and to convert buffers into gap events Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/587 Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/84 Part-of: --- docs/plugins/gst_plugins_cache.json | 32 +++++++++++ plugins/elements/gstvalve.c | 82 +++++++++++++++++++++++++++-- plugins/elements/gstvalve.h | 19 +++++++ 3 files changed, 128 insertions(+), 5 deletions(-) diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index 17c3764939..5a64d4830b 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -2445,6 +2445,18 @@ "readable": true, "type": "gboolean", "writable": true + }, + "drop-mode": { + "blurb": "The drop mode to use", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "drop-all (0)", + "mutable": "ready", + "readable": true, + "type": "GstValveDropMode", + "writable": true } }, "rank": "none" @@ -2796,6 +2808,26 @@ "value": "1" } ] + }, + "GstValveDropMode": { + "kind": "enum", + "values": [ + { + "desc": "Drop all buffers and events", + "name": "drop-all", + "value": "0" + }, + { + "desc": "Drop all buffers but forward sticky events", + "name": "forward-sticky-events", + "value": "1" + }, + { + "desc": "Convert all dropped buffers into gap events and forward sticky events", + "name": "transform-to-gap", + "value": "2" + } + ] } }, "package": "GStreamer", diff --git a/plugins/elements/gstvalve.c b/plugins/elements/gstvalve.c index 9dbc7a6f68..04323bd5cc 100644 --- a/plugins/elements/gstvalve.c +++ b/plugins/elements/gstvalve.c @@ -43,6 +43,27 @@ GST_DEBUG_CATEGORY_STATIC (valve_debug); #define GST_CAT_DEFAULT (valve_debug) +#define GST_TYPE_VALVE_DROP_MODE (gst_valve_drop_mode_get_type ()) +static GType +gst_valve_drop_mode_get_type (void) +{ + static GType drop_mode_type = 0; + static const GEnumValue drop_mode[] = { + {GST_VALVE_DROP_MODE_DROP_ALL, "Drop all buffers and events", "drop-all"}, + {GST_VALVE_DROP_MODE_FORWARD_STICKY_EVENTS, + "Drop all buffers but forward sticky events", "forward-sticky-events"}, + {GST_VALVE_DROP_MODE_TRANSFORM_TO_GAP, + "Convert all dropped buffers into gap events and forward sticky events", + "transform-to-gap"}, + {0, NULL, NULL}, + }; + + if (!drop_mode_type) { + drop_mode_type = g_enum_register_static ("GstValveDropMode", drop_mode); + } + return drop_mode_type; +} + static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -56,10 +77,12 @@ static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", enum { PROP_0, - PROP_DROP + PROP_DROP, + PROP_DROP_MODE }; #define DEFAULT_DROP FALSE +#define DEFAULT_DROP_MODE GST_VALVE_DROP_MODE_DROP_ALL static void gst_valve_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -96,18 +119,35 @@ gst_valve_class_init (GstValveClass * klass) DEFAULT_DROP, G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_STATIC_STRINGS)); + /** + * GstValve:drop-mode + * + * Drop mode to use. By default all buffers and events are dropped. + * + * Since: 1.20 + */ + g_object_class_install_property (gobject_class, PROP_DROP_MODE, + g_param_spec_enum ("drop-mode", "Drop mode", + "The drop mode to use", GST_TYPE_VALVE_DROP_MODE, + DEFAULT_DROP_MODE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + gst_element_class_add_static_pad_template (gstelement_class, &srctemplate); gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate); gst_element_class_set_static_metadata (gstelement_class, "Valve element", "Filter", "Drops buffers and events or lets them through", "Olivier Crete "); + + gst_type_mark_as_plugin_api (GST_TYPE_VALVE_DROP_MODE, 0); } static void gst_valve_init (GstValve * valve) { valve->drop = FALSE; + valve->drop_mode = DEFAULT_DROP; valve->discont = FALSE; valve->srcpad = gst_pad_new_from_static_template (&srctemplate, "src"); @@ -140,6 +180,9 @@ gst_valve_set_property (GObject * object, g_atomic_int_set (&valve->drop, g_value_get_boolean (value)); gst_pad_push_event (valve->sinkpad, gst_event_new_reconfigure ()); break; + case PROP_DROP_MODE: + valve->drop_mode = g_value_get_enum (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -156,6 +199,9 @@ gst_valve_get_property (GObject * object, case PROP_DROP: g_value_set_boolean (value, g_atomic_int_get (&valve->drop)); break; + case PROP_DROP_MODE: + g_value_set_enum (value, valve->drop_mode); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -188,6 +234,11 @@ gst_valve_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) GstFlowReturn ret = GST_FLOW_OK; if (g_atomic_int_get (&valve->drop)) { + if (valve->drop_mode == GST_VALVE_DROP_MODE_TRANSFORM_TO_GAP) { + GstEvent *ev = gst_event_new_gap (GST_BUFFER_PTS (buffer), + GST_BUFFER_DURATION (buffer)); + gst_pad_push_event (valve->srcpad, ev); + } gst_buffer_unref (buffer); valve->discont = TRUE; } else { @@ -213,17 +264,37 @@ gst_valve_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) return ret; } +static inline gboolean +gst_valve_event_needs_dropping (GstValve * valve, GstEvent * event) +{ + if (!g_atomic_int_get (&valve->drop)) + return FALSE; + + switch (valve->drop_mode) { + case GST_VALVE_DROP_MODE_DROP_ALL: + return TRUE; + case GST_VALVE_DROP_MODE_FORWARD_STICKY_EVENTS: + return !GST_EVENT_IS_STICKY (event); + case GST_VALVE_DROP_MODE_TRANSFORM_TO_GAP: + return (!GST_EVENT_IS_STICKY (event) && + GST_EVENT_TYPE (event) != GST_EVENT_GAP); + default: + g_assert_not_reached (); + break; + } +} static gboolean gst_valve_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { - GstValve *valve; + GstValve *valve = GST_VALVE (parent); + gboolean needs_dropping = gst_valve_event_needs_dropping (valve, event); gboolean is_sticky = GST_EVENT_IS_STICKY (event); gboolean ret = TRUE; valve = GST_VALVE (parent); - if (g_atomic_int_get (&valve->drop)) { + if (needs_dropping) { valve->need_repush_sticky |= is_sticky; gst_event_unref (event); } else { @@ -233,10 +304,11 @@ gst_valve_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) } /* Ignore errors if "drop" was changed while the thread was blocked - * downwards. + * downwards, or if we're dropping but forwarding sticky events nonetheless. */ if (g_atomic_int_get (&valve->drop)) { - valve->need_repush_sticky |= is_sticky; + if (valve->drop_mode == GST_VALVE_DROP_MODE_DROP_ALL) + valve->need_repush_sticky |= is_sticky; ret = TRUE; } diff --git a/plugins/elements/gstvalve.h b/plugins/elements/gstvalve.h index eb3c29e74f..93c4af809b 100644 --- a/plugins/elements/gstvalve.h +++ b/plugins/elements/gstvalve.h @@ -44,6 +44,23 @@ G_BEGIN_DECLS typedef struct _GstValve GstValve; typedef struct _GstValveClass GstValveClass; +/** + * GstValveDropMode: + * @GST_VALVE_DROP_MODE_DROP_ALL: Drop all buffers, buffer lists and events. + * @GST_VALVE_DROP_MODE_FORWARD_STICKY_EVENTS: Drop all buffers, buffer lists + * and non-sticky events but forward sticky events. + * @GST_VALVE_DROP_MODE_TRANSFORM_TO_GAP: Drop all buffers, buffer lists and + * non-sticky events but forward sticky events and forward GAP events for each + * dropped buffer. + * + * Since: 1.20 + */ +typedef enum { + GST_VALVE_DROP_MODE_DROP_ALL, + GST_VALVE_DROP_MODE_FORWARD_STICKY_EVENTS, + GST_VALVE_DROP_MODE_TRANSFORM_TO_GAP, +} GstValveDropMode; + /** * GstValve: * @@ -57,6 +74,8 @@ struct _GstValve /* atomic boolean */ volatile gint drop; + GstValveDropMode drop_mode; + /* Protected by the stream lock */ gboolean discont; gboolean need_repush_sticky;