flowcombiner: add a gst_flow_combiner_reset() method
[platform/upstream/gstreamer.git] / libs / gst / base / gstflowcombiner.c
1 /* GStreamer
2  *
3  * Copyright (C) 2014 Samsung Electronics. All rights reserved.
4  *   Author: Thiago Santos <ts.santos@sisa.samsung.com>
5  *
6  * gstflowcombiner.c: utility to combine multiple flow returns into a single one
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 /**
25  * SECTION:gstflowcombiner
26  * @short_description: Utility to combine multiple flow returns into one
27  *
28  * Utility struct to help handling #GstFlowReturn combination. Useful for
29  * #GstElement<!-- -->s that have multiple source pads and need to combine
30  * the different #GstFlowReturn for those pads.
31  *
32  * #GstFlowCombiner works by using the last #GstFlowReturn for all #GstPad
33  * it has in its list and computes the combined return value and provides
34  * it to the caller.
35  *
36  * To add a new pad to the #GstFlowCombiner use gst_flow_combiner_add_pad().
37  * The new #GstPad is stored with a default value of %GST_FLOW_OK.
38  *
39  * In case you want a #GstPad to be removed, use gst_flow_combiner_remove_pad().
40  *
41  * Please be aware that this struct isn't thread safe as its designed to be
42  *  used by demuxers, those usually will have a single thread operating it.
43  *
44  * These functions will take refs on the passed #GstPad<!-- -->s.
45  *
46  * Aside from reducing the user's code size, the main advantage of using this
47  * helper struct is to follow the standard rules for #GstFlowReturn combination.
48  * These rules are:
49  *
50  * * %GST_FLOW_EOS: only if all returns are EOS too
51  * * %GST_FLOW_NOT_LINKED: only if all returns are NOT_LINKED too
52  * * %GST_FLOW_ERROR or below: if at least one returns an error return
53  * * %GST_FLOW_NOT_NEGOTIATED: if at least one returns a not-negotiated return
54  * * %GST_FLOW_FLUSHING: if at least one returns flushing
55  * * %GST_FLOW_OK: otherwise
56  *
57  * %GST_FLOW_ERROR or below, GST_FLOW_NOT_NEGOTIATED and GST_FLOW_FLUSHING are
58  * returned immediatelly from the gst_flow_combiner_update_flow() function.
59  *
60  * Since: 1.4
61  */
62
63 #include <gst/gst.h>
64 #include "gstflowcombiner.h"
65
66 struct _GstFlowCombiner
67 {
68   GQueue pads;
69
70   GstFlowReturn last_ret;
71   volatile gint ref_count;
72 };
73
74 static GstFlowCombiner *gst_flow_combiner_ref (GstFlowCombiner * combiner);
75 static void gst_flow_combiner_unref (GstFlowCombiner * combiner);
76
77 G_DEFINE_BOXED_TYPE (GstFlowCombiner, gst_flow_combiner,
78     (GBoxedCopyFunc) gst_flow_combiner_ref,
79     (GBoxedFreeFunc) gst_flow_combiner_unref);
80
81 /**
82  * gst_flow_combiner_new:
83  *
84  * Creates a new #GstFlowCombiner, use gst_flow_combiner_free() to free it.
85  *
86  * Returns: A new #GstFlowCombiner
87  * Since: 1.4
88  */
89 GstFlowCombiner *
90 gst_flow_combiner_new (void)
91 {
92   GstFlowCombiner *combiner = g_slice_new (GstFlowCombiner);
93
94   g_queue_init (&combiner->pads);
95   combiner->last_ret = GST_FLOW_OK;
96   combiner->ref_count = 1;
97
98   return combiner;
99 }
100
101 /**
102  * gst_flow_combiner_free:
103  * @combiner: the #GstFlowCombiner to free
104  *
105  * Frees a #GstFlowCombiner struct and all its internal data.
106  *
107  * Since: 1.4
108  */
109 void
110 gst_flow_combiner_free (GstFlowCombiner * combiner)
111 {
112   gst_flow_combiner_unref (combiner);
113 }
114
115 static GstFlowCombiner *
116 gst_flow_combiner_ref (GstFlowCombiner * combiner)
117 {
118   g_return_val_if_fail (combiner != NULL, NULL);
119
120   g_atomic_int_inc (&combiner->ref_count);
121
122   return combiner;
123 }
124
125 static void
126 gst_flow_combiner_unref (GstFlowCombiner * combiner)
127 {
128   g_return_if_fail (combiner != NULL);
129   g_return_if_fail (combiner->ref_count > 0);
130
131   if (g_atomic_int_dec_and_test (&combiner->ref_count)) {
132     GstPad *pad;
133
134     while ((pad = g_queue_pop_head (&combiner->pads)))
135       gst_object_unref (pad);
136
137     g_slice_free (GstFlowCombiner, combiner);
138   }
139 }
140
141 /**
142  * gst_flow_combiner_clear:
143  * @combiner: the #GstFlowCombiner to clear
144  *
145  * Removes all pads from a #GstFlowCombiner and resets it to its initial state.
146  *
147  * Since: 1.6
148  */
149 void
150 gst_flow_combiner_clear (GstFlowCombiner * combiner)
151 {
152   GstPad *pad;
153
154   g_return_if_fail (combiner != NULL);
155
156   while ((pad = g_queue_pop_head (&combiner->pads)))
157     gst_object_unref (pad);
158   combiner->last_ret = GST_FLOW_OK;
159 }
160
161 /**
162  * gst_flow_combiner_reset:
163  * @combiner: the #GstFlowCombiner to clear
164  *
165  * Removes all pads from a #GstFlowCombiner and resets it to its initial state.
166  *
167  * Since: 1.6
168  */
169 void
170 gst_flow_combiner_reset (GstFlowCombiner * combiner)
171 {
172   GList *iter;
173
174   g_return_if_fail (combiner != NULL);
175
176   GST_DEBUG ("Reset flow returns");
177
178   for (iter = combiner->pads.head; iter; iter = iter->next) {
179     GST_PAD_LAST_FLOW_RETURN (iter->data) = GST_FLOW_OK;
180   }
181
182   combiner->last_ret = GST_FLOW_OK;
183 }
184
185 static GstFlowReturn
186 gst_flow_combiner_get_flow (GstFlowCombiner * combiner)
187 {
188   GstFlowReturn cret = GST_FLOW_OK;
189   gboolean all_eos = TRUE;
190   gboolean all_notlinked = TRUE;
191   GList *iter;
192
193   GST_DEBUG ("Combining flow returns");
194
195   for (iter = combiner->pads.head; iter; iter = iter->next) {
196     GstFlowReturn fret = GST_PAD_LAST_FLOW_RETURN (iter->data);
197
198     if (fret <= GST_FLOW_NOT_NEGOTIATED || fret == GST_FLOW_FLUSHING) {
199       GST_DEBUG ("Error flow return found, returning");
200       cret = fret;
201       goto done;
202     }
203
204     if (fret != GST_FLOW_NOT_LINKED) {
205       all_notlinked = FALSE;
206       if (fret != GST_FLOW_EOS)
207         all_eos = FALSE;
208     }
209   }
210   if (all_notlinked)
211     cret = GST_FLOW_NOT_LINKED;
212   else if (all_eos)
213     cret = GST_FLOW_EOS;
214
215 done:
216   GST_DEBUG ("Combined flow return: %s (%d)", gst_flow_get_name (cret), cret);
217   return cret;
218 }
219
220 /**
221  * gst_flow_combiner_update_flow:
222  * @combiner: the #GstFlowCombiner
223  * @fret: the latest #GstFlowReturn received for a pad in this #GstFlowCombiner
224  *
225  * Computes the combined flow return for the pads in it.
226  *
227  * The #GstFlowReturn parameter should be the last flow return update for a pad
228  * in this #GstFlowCombiner. It will use this value to be able to shortcut some
229  * combinations and avoid looking over all pads again. e.g. The last combined
230  * return is the same as the latest obtained #GstFlowReturn.
231  *
232  * Returns: The combined #GstFlowReturn
233  * Since: 1.4
234  */
235 GstFlowReturn
236 gst_flow_combiner_update_flow (GstFlowCombiner * combiner, GstFlowReturn fret)
237 {
238   GstFlowReturn ret;
239
240   g_return_val_if_fail (combiner != NULL, GST_FLOW_ERROR);
241
242   if (combiner->last_ret == fret) {
243     return fret;
244   }
245
246   if (fret <= GST_FLOW_NOT_NEGOTIATED || fret == GST_FLOW_FLUSHING) {
247     ret = fret;
248   } else {
249     ret = gst_flow_combiner_get_flow (combiner);
250   }
251   combiner->last_ret = ret;
252   return ret;
253 }
254
255 /**
256  * gst_flow_combiner_add_pad:
257  * @combiner: the #GstFlowCombiner
258  * @pad: (transfer none): the #GstPad that is being added
259  *
260  * Adds a new #GstPad to the #GstFlowCombiner.
261  *
262  * Since: 1.4
263  */
264 void
265 gst_flow_combiner_add_pad (GstFlowCombiner * combiner, GstPad * pad)
266 {
267   g_return_if_fail (combiner != NULL);
268   g_return_if_fail (pad != NULL);
269
270   g_queue_push_head (&combiner->pads, gst_object_ref (pad));
271 }
272
273 /**
274  * gst_flow_combiner_remove_pad:
275  * @combiner: the #GstFlowCombiner
276  * @pad: (transfer none): the #GstPad to remove
277  *
278  * Removes a #GstPad from the #GstFlowCombiner.
279  *
280  * Since: 1.4
281  */
282 void
283 gst_flow_combiner_remove_pad (GstFlowCombiner * combiner, GstPad * pad)
284 {
285   g_return_if_fail (combiner != NULL);
286   g_return_if_fail (pad != NULL);
287
288   if (g_queue_remove (&combiner->pads, pad))
289     gst_object_unref (pad);
290 }