• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer valve element
2  *  Copyright 2007-2009 Collabora Ltd
3  *   @author: Olivier Crete <olivier.crete@collabora.co.uk>
4  *  Copyright 2007-2009 Nokia Corporation
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 /**
24  * SECTION:element-valve
25  * @title: valve
26  *
27  * The valve is a simple element that drops buffers when the #GstValve:drop
28  * property is set to %TRUE and lets then through otherwise.
29  *
30  * Any downstream error received while the #GstValve:drop property is %TRUE
31  * is ignored. So downstream element can be set to  %GST_STATE_NULL and removed,
32  * without using pad blocking.
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 
39 #include "gstvalve.h"
40 #include "gstcoreelementselements.h"
41 
42 #include <string.h>
43 
44 GST_DEBUG_CATEGORY_STATIC (valve_debug);
45 #define GST_CAT_DEFAULT (valve_debug)
46 
47 #define GST_TYPE_VALVE_DROP_MODE (gst_valve_drop_mode_get_type ())
48 static GType
gst_valve_drop_mode_get_type(void)49 gst_valve_drop_mode_get_type (void)
50 {
51   static GType drop_mode_type = 0;
52   static const GEnumValue drop_mode[] = {
53     {GST_VALVE_DROP_MODE_DROP_ALL, "Drop all buffers and events", "drop-all"},
54     {GST_VALVE_DROP_MODE_FORWARD_STICKY_EVENTS,
55         "Drop all buffers but forward sticky events", "forward-sticky-events"},
56     {GST_VALVE_DROP_MODE_TRANSFORM_TO_GAP,
57           "Convert all dropped buffers into gap events and forward sticky events",
58         "transform-to-gap"},
59     {0, NULL, NULL},
60   };
61 
62   if (!drop_mode_type) {
63     drop_mode_type = g_enum_register_static ("GstValveDropMode", drop_mode);
64   }
65   return drop_mode_type;
66 }
67 
68 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
69     GST_PAD_SINK,
70     GST_PAD_ALWAYS,
71     GST_STATIC_CAPS_ANY);
72 
73 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
74     GST_PAD_SRC,
75     GST_PAD_ALWAYS,
76     GST_STATIC_CAPS_ANY);
77 
78 enum
79 {
80   PROP_0,
81   PROP_DROP,
82   PROP_DROP_MODE
83 };
84 
85 #define DEFAULT_DROP FALSE
86 #define DEFAULT_DROP_MODE GST_VALVE_DROP_MODE_DROP_ALL
87 
88 static void gst_valve_set_property (GObject * object,
89     guint prop_id, const GValue * value, GParamSpec * pspec);
90 static void gst_valve_get_property (GObject * object,
91     guint prop_id, GValue * value, GParamSpec * pspec);
92 
93 static GstFlowReturn gst_valve_chain (GstPad * pad, GstObject * parent,
94     GstBuffer * buffer);
95 static gboolean gst_valve_sink_event (GstPad * pad, GstObject * parent,
96     GstEvent * event);
97 static gboolean gst_valve_query (GstPad * pad, GstObject * parent,
98     GstQuery * query);
99 
100 #define _do_init \
101   GST_DEBUG_CATEGORY_INIT (valve_debug, "valve", 0, "Valve");
102 #define gst_valve_parent_class parent_class
103 G_DEFINE_TYPE_WITH_CODE (GstValve, gst_valve, GST_TYPE_ELEMENT, _do_init);
104 GST_ELEMENT_REGISTER_DEFINE (valve, "valve", GST_RANK_NONE, GST_TYPE_VALVE);
105 
106 static void
gst_valve_class_init(GstValveClass * klass)107 gst_valve_class_init (GstValveClass * klass)
108 {
109   GObjectClass *gobject_class;
110   GstElementClass *gstelement_class;
111 
112   gobject_class = (GObjectClass *) klass;
113   gstelement_class = (GstElementClass *) (klass);
114 
115   gobject_class->set_property = gst_valve_set_property;
116   gobject_class->get_property = gst_valve_get_property;
117 
118   g_object_class_install_property (gobject_class, PROP_DROP,
119       g_param_spec_boolean ("drop", "Drop buffers and events",
120           "Whether to drop buffers and events or let them through",
121           DEFAULT_DROP, G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
122           G_PARAM_STATIC_STRINGS));
123 
124   /**
125    * GstValve:drop-mode
126    *
127    * Drop mode to use. By default all buffers and events are dropped.
128    *
129    * Since: 1.20
130    */
131   g_object_class_install_property (gobject_class, PROP_DROP_MODE,
132       g_param_spec_enum ("drop-mode", "Drop mode",
133           "The drop mode to use", GST_TYPE_VALVE_DROP_MODE,
134           DEFAULT_DROP_MODE,
135           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
136           GST_PARAM_MUTABLE_READY));
137 
138   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
139   gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
140 
141   gst_element_class_set_static_metadata (gstelement_class, "Valve element",
142       "Filter", "Drops buffers and events or lets them through",
143       "Olivier Crete <olivier.crete@collabora.co.uk>");
144 
145   gst_type_mark_as_plugin_api (GST_TYPE_VALVE_DROP_MODE, 0);
146 }
147 
148 static void
gst_valve_init(GstValve * valve)149 gst_valve_init (GstValve * valve)
150 {
151   valve->drop = FALSE;
152   valve->drop_mode = DEFAULT_DROP;
153   valve->discont = FALSE;
154 
155   valve->srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
156   gst_pad_set_query_function (valve->srcpad,
157       GST_DEBUG_FUNCPTR (gst_valve_query));
158   GST_PAD_SET_PROXY_CAPS (valve->srcpad);
159   gst_element_add_pad (GST_ELEMENT (valve), valve->srcpad);
160 
161   valve->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
162   gst_pad_set_chain_function (valve->sinkpad,
163       GST_DEBUG_FUNCPTR (gst_valve_chain));
164   gst_pad_set_event_function (valve->sinkpad,
165       GST_DEBUG_FUNCPTR (gst_valve_sink_event));
166   gst_pad_set_query_function (valve->sinkpad,
167       GST_DEBUG_FUNCPTR (gst_valve_query));
168   GST_PAD_SET_PROXY_CAPS (valve->sinkpad);
169   GST_PAD_SET_PROXY_ALLOCATION (valve->sinkpad);
170   gst_element_add_pad (GST_ELEMENT (valve), valve->sinkpad);
171 }
172 
173 
174 static void
gst_valve_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)175 gst_valve_set_property (GObject * object,
176     guint prop_id, const GValue * value, GParamSpec * pspec)
177 {
178   GstValve *valve = GST_VALVE (object);
179 
180   switch (prop_id) {
181     case PROP_DROP:
182       g_atomic_int_set (&valve->drop, g_value_get_boolean (value));
183       gst_pad_push_event (valve->sinkpad, gst_event_new_reconfigure ());
184       break;
185     case PROP_DROP_MODE:
186       valve->drop_mode = g_value_get_enum (value);
187       break;
188     default:
189       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
190       break;
191   }
192 }
193 
194 static void
gst_valve_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)195 gst_valve_get_property (GObject * object,
196     guint prop_id, GValue * value, GParamSpec * pspec)
197 {
198   GstValve *valve = GST_VALVE (object);
199 
200   switch (prop_id) {
201     case PROP_DROP:
202       g_value_set_boolean (value, g_atomic_int_get (&valve->drop));
203       break;
204     case PROP_DROP_MODE:
205       g_value_set_enum (value, valve->drop_mode);
206       break;
207     default:
208       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
209       break;
210   }
211 }
212 
213 
214 static gboolean
forward_sticky_events(GstPad * pad,GstEvent ** event,gpointer user_data)215 forward_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
216 {
217   GstValve *valve = user_data;
218 
219   if (!gst_pad_push_event (valve->srcpad, gst_event_ref (*event)))
220     valve->need_repush_sticky = TRUE;
221 
222   return TRUE;
223 }
224 
225 static void
gst_valve_repush_sticky(GstValve * valve)226 gst_valve_repush_sticky (GstValve * valve)
227 {
228   valve->need_repush_sticky = FALSE;
229   gst_pad_sticky_events_foreach (valve->sinkpad, forward_sticky_events, valve);
230 }
231 
232 static GstFlowReturn
gst_valve_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)233 gst_valve_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
234 {
235   GstValve *valve = GST_VALVE (parent);
236   GstFlowReturn ret = GST_FLOW_OK;
237 
238   if (g_atomic_int_get (&valve->drop)) {
239     if (valve->drop_mode == GST_VALVE_DROP_MODE_TRANSFORM_TO_GAP) {
240       GstEvent *ev = gst_event_new_gap (GST_BUFFER_PTS (buffer),
241           GST_BUFFER_DURATION (buffer));
242       gst_pad_push_event (valve->srcpad, ev);
243     }
244     gst_buffer_unref (buffer);
245     valve->discont = TRUE;
246   } else {
247     if (valve->discont) {
248       buffer = gst_buffer_make_writable (buffer);
249       GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
250       valve->discont = FALSE;
251     }
252 
253     if (valve->need_repush_sticky)
254       gst_valve_repush_sticky (valve);
255 
256     ret = gst_pad_push (valve->srcpad, buffer);
257   }
258 
259 
260   /* Ignore errors if "drop" was changed while the thread was blocked
261    * downwards
262    */
263   if (g_atomic_int_get (&valve->drop))
264     ret = GST_FLOW_OK;
265 
266   return ret;
267 }
268 
269 static inline gboolean
gst_valve_event_needs_dropping(GstValve * valve,GstEvent * event)270 gst_valve_event_needs_dropping (GstValve * valve, GstEvent * event)
271 {
272   if (!g_atomic_int_get (&valve->drop))
273     return FALSE;
274 
275   switch (valve->drop_mode) {
276     case GST_VALVE_DROP_MODE_DROP_ALL:
277       return TRUE;
278     case GST_VALVE_DROP_MODE_FORWARD_STICKY_EVENTS:
279       return !GST_EVENT_IS_STICKY (event);
280     case GST_VALVE_DROP_MODE_TRANSFORM_TO_GAP:
281       return (!GST_EVENT_IS_STICKY (event) &&
282           GST_EVENT_TYPE (event) != GST_EVENT_GAP);
283     default:
284       g_assert_not_reached ();
285       break;
286   }
287 
288   return FALSE;
289 }
290 
291 static gboolean
gst_valve_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)292 gst_valve_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
293 {
294   GstValve *valve = GST_VALVE (parent);
295   gboolean needs_dropping = gst_valve_event_needs_dropping (valve, event);
296   gboolean is_sticky = GST_EVENT_IS_STICKY (event);
297   gboolean ret = TRUE;
298 
299   valve = GST_VALVE (parent);
300 
301   if (needs_dropping) {
302     valve->need_repush_sticky |= is_sticky;
303     gst_event_unref (event);
304   } else {
305     if (valve->need_repush_sticky)
306       gst_valve_repush_sticky (valve);
307     ret = gst_pad_event_default (pad, parent, event);
308   }
309 
310   /* Ignore errors if "drop" was changed while the thread was blocked
311    * downwards, or if we're dropping but forwarding sticky events nonetheless.
312    */
313   if (g_atomic_int_get (&valve->drop)) {
314     if (valve->drop_mode == GST_VALVE_DROP_MODE_DROP_ALL)
315       valve->need_repush_sticky |= is_sticky;
316     ret = TRUE;
317   }
318 
319   return ret;
320 }
321 
322 
323 
324 static gboolean
gst_valve_query(GstPad * pad,GstObject * parent,GstQuery * query)325 gst_valve_query (GstPad * pad, GstObject * parent, GstQuery * query)
326 {
327   GstValve *valve = GST_VALVE (parent);
328 
329   if (GST_QUERY_IS_SERIALIZED (query) && g_atomic_int_get (&valve->drop))
330     return FALSE;
331 
332   return gst_pad_query_default (pad, parent, query);
333 }
334