1 /*
2  * GStreamer
3  * Copyright (C) 2015 Vivia Nikolaidou <vivia@toolsonair.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:element-errorignore
23  * @title: errorignore
24  *
25  * Passes through all packets, until it encounters GST_FLOW_ERROR or
26  * GST_FLOW_NOT_NEGOTIATED (configurable). At that point it will unref the
27  * buffers and return GST_FLOW_OK (configurable) - until the next
28  * READY_TO_PAUSED, RECONFIGURE or FLUSH_STOP.
29  *
30  * ## Example launch line
31  * |[
32  * gst-launch-1.0 videotestsrc ! errorignore ! autovideosink
33  * ]|
34  *
35  */
36 
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40 
41 #include "gsterrorignore.h"
42 
43 #define GST_CAT_DEFAULT gst_error_ignore_debug
44 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
45 
46 enum
47 {
48   PROP_0,
49   PROP_IGNORE_ERROR,
50   PROP_IGNORE_NOTLINKED,
51   PROP_IGNORE_NOTNEGOTIATED,
52   PROP_CONVERT_TO
53 };
54 
55 static void gst_error_ignore_set_property (GObject * object, guint prop_id,
56     const GValue * value, GParamSpec * pspec);
57 static void gst_error_ignore_get_property (GObject * object, guint prop_id,
58     GValue * value, GParamSpec * pspec);
59 
60 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
61     GST_PAD_SINK,
62     GST_PAD_ALWAYS,
63     GST_STATIC_CAPS_ANY);
64 
65 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
66     GST_PAD_SRC,
67     GST_PAD_ALWAYS,
68     GST_STATIC_CAPS_ANY);
69 
70 #define parent_class gst_error_ignore_parent_class
71 G_DEFINE_TYPE (GstErrorIgnore, gst_error_ignore, GST_TYPE_ELEMENT);
72 
73 static GstFlowReturn gst_error_ignore_sink_chain (GstPad * pad,
74     GstObject * parent, GstBuffer * inbuf);
75 static gboolean gst_error_ignore_sink_event (GstPad * pad, GstObject * parent,
76     GstEvent * event);
77 static GstStateChangeReturn gst_error_ignore_change_state (GstElement * element,
78     GstStateChange transition);
79 
80 static void
gst_error_ignore_class_init(GstErrorIgnoreClass * klass)81 gst_error_ignore_class_init (GstErrorIgnoreClass * klass)
82 {
83   GstElementClass *gstelement_class;
84   GObjectClass *object_class = G_OBJECT_CLASS (klass);
85 
86   GST_DEBUG_CATEGORY_INIT (gst_error_ignore_debug, "errorignore", 0,
87       "Convert some GstFlowReturn types into others");
88 
89   gstelement_class = (GstElementClass *) klass;
90 
91   gst_element_class_set_static_metadata (gstelement_class,
92       "Convert some GstFlowReturn types into others", "Generic",
93       "Pass through all packets but ignore some GstFlowReturn types",
94       "Vivia Nikolaidou <vivia@toolsonair.com>");
95 
96   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
97   gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
98 
99   gstelement_class->change_state = gst_error_ignore_change_state;
100 
101   /* define virtual function pointers */
102   object_class->set_property = gst_error_ignore_set_property;
103   object_class->get_property = gst_error_ignore_get_property;
104 
105   /* define properties */
106   g_object_class_install_property (object_class, PROP_IGNORE_ERROR,
107       g_param_spec_boolean ("ignore-error", "Ignore GST_FLOW_ERROR",
108           "Whether to ignore GST_FLOW_ERROR",
109           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
110 
111   g_object_class_install_property (object_class, PROP_IGNORE_NOTLINKED,
112       g_param_spec_boolean ("ignore-notlinked", "Ignore GST_FLOW_NOT_LINKED",
113           "Whether to ignore GST_FLOW_NOT_LINKED",
114           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
115 
116   g_object_class_install_property (object_class, PROP_IGNORE_NOTNEGOTIATED,
117       g_param_spec_boolean ("ignore-notnegotiated",
118           "Ignore GST_FLOW_NOT_NEGOTIATED",
119           "Whether to ignore GST_FLOW_NOT_NEGOTIATED",
120           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
121 
122   g_object_class_install_property (object_class, PROP_CONVERT_TO,
123       g_param_spec_enum ("convert-to", "GstFlowReturn to convert to",
124           "Which GstFlowReturn value we should convert to when ignoring",
125           GST_TYPE_FLOW_RETURN,
126           GST_FLOW_NOT_LINKED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
127 }
128 
129 static void
gst_error_ignore_init(GstErrorIgnore * self)130 gst_error_ignore_init (GstErrorIgnore * self)
131 {
132   self->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
133   gst_pad_set_chain_function (self->sinkpad,
134       GST_DEBUG_FUNCPTR (gst_error_ignore_sink_chain));
135   gst_pad_set_event_function (self->sinkpad,
136       GST_DEBUG_FUNCPTR (gst_error_ignore_sink_event));
137   GST_PAD_SET_PROXY_ALLOCATION (self->sinkpad);
138   GST_PAD_SET_PROXY_CAPS (self->sinkpad);
139   GST_PAD_SET_PROXY_SCHEDULING (self->sinkpad);
140   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
141 
142   self->srcpad = gst_pad_new_from_static_template (&src_template, "src");
143   gst_pad_use_fixed_caps (self->srcpad);
144   GST_PAD_SET_PROXY_ALLOCATION (self->srcpad);
145   GST_PAD_SET_PROXY_CAPS (self->srcpad);
146   GST_PAD_SET_PROXY_SCHEDULING (self->srcpad);
147   gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
148 
149   self->keep_pushing = TRUE;
150   self->ignore_error = TRUE;
151   self->ignore_notlinked = FALSE;
152   self->ignore_notnegotiated = TRUE;
153   self->convert_to = GST_FLOW_NOT_LINKED;
154 }
155 
156 static void
gst_error_ignore_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)157 gst_error_ignore_set_property (GObject * object, guint prop_id,
158     const GValue * value, GParamSpec * pspec)
159 {
160   GstErrorIgnore *self = GST_ERROR_IGNORE (object);
161 
162   switch (prop_id) {
163     case PROP_IGNORE_ERROR:
164       self->ignore_error = g_value_get_boolean (value);
165       break;
166     case PROP_IGNORE_NOTLINKED:
167       self->ignore_notlinked = g_value_get_boolean (value);
168       break;
169     case PROP_IGNORE_NOTNEGOTIATED:
170       self->ignore_notnegotiated = g_value_get_boolean (value);
171       break;
172     case PROP_CONVERT_TO:
173       self->convert_to = g_value_get_enum (value);
174       break;
175     default:
176       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
177       break;
178   }
179 }
180 
181 static void
gst_error_ignore_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)182 gst_error_ignore_get_property (GObject * object, guint prop_id, GValue * value,
183     GParamSpec * pspec)
184 {
185   GstErrorIgnore *self = GST_ERROR_IGNORE (object);
186 
187   switch (prop_id) {
188     case PROP_IGNORE_ERROR:
189       g_value_set_boolean (value, self->ignore_error);
190       break;
191     case PROP_IGNORE_NOTLINKED:
192       g_value_set_boolean (value, self->ignore_notlinked);
193       break;
194     case PROP_IGNORE_NOTNEGOTIATED:
195       g_value_set_boolean (value, self->ignore_notnegotiated);
196       break;
197     case PROP_CONVERT_TO:
198       g_value_set_enum (value, self->convert_to);
199       break;
200     default:
201       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
202       break;
203   }
204 }
205 
206 static gboolean
gst_error_ignore_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)207 gst_error_ignore_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
208 {
209   GstErrorIgnore *self = GST_ERROR_IGNORE (parent);
210   gboolean ret;
211 
212   GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event));
213 
214   switch (GST_EVENT_TYPE (event)) {
215     case GST_EVENT_FLUSH_STOP:
216       self->keep_pushing = TRUE;
217       /* fall through */
218     default:
219       ret = gst_pad_event_default (pad, parent, event);
220       break;
221   }
222 
223   return ret;
224 }
225 
226 static GstFlowReturn
gst_error_ignore_sink_chain(GstPad * pad,GstObject * parent,GstBuffer * inbuf)227 gst_error_ignore_sink_chain (GstPad * pad, GstObject * parent,
228     GstBuffer * inbuf)
229 {
230   GstErrorIgnore *self = GST_ERROR_IGNORE (parent);
231   GstFlowReturn ret = GST_FLOW_OK;
232 
233   if (gst_pad_check_reconfigure (pad))
234     self->keep_pushing = TRUE;
235 
236   if (self->keep_pushing) {
237     ret = gst_pad_push (self->srcpad, inbuf);
238     self->keep_pushing = (ret == GST_FLOW_OK);
239   } else {
240     gst_buffer_unref (inbuf);
241   }
242 
243   if ((ret == GST_FLOW_ERROR && self->ignore_error) ||
244       (ret == GST_FLOW_NOT_LINKED && self->ignore_notlinked) ||
245       (ret == GST_FLOW_NOT_NEGOTIATED && self->ignore_notnegotiated))
246     return self->convert_to;
247   else
248     return ret;
249 }
250 
251 static GstStateChangeReturn
gst_error_ignore_change_state(GstElement * element,GstStateChange transition)252 gst_error_ignore_change_state (GstElement * element, GstStateChange transition)
253 {
254   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
255   GstErrorIgnore *self = GST_ERROR_IGNORE (element);
256 
257   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
258   if (ret == GST_STATE_CHANGE_FAILURE)
259     return ret;
260 
261   switch (transition) {
262     case GST_STATE_CHANGE_READY_TO_PAUSED:
263       self->keep_pushing = TRUE;
264       break;
265     default:
266       break;
267   }
268 
269   return ret;
270 }
271