adder: be a lot smarter with buffer management
[platform/upstream/gstreamer.git] / gst / adder / gstadder.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2001 Thomas <thomas@apestaart.org>
4  *               2005,2006 Wim Taymans <wim@fluendo.com>
5  *
6  * adder.c: Adder element, N in, one out, samples are added
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., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 /**
24  * SECTION:element-adder
25  *
26  * The adder allows to mix several streams into one by adding the data.
27  * Mixed data is clamped to the min/max values of the data format.
28  *
29  * The adder currently mixes all data received on the sinkpads as soon as
30  * possible without trying to synchronize the streams.
31  *
32  * <refsect2>
33  * <title>Example launch line</title>
34  * |[
35  * gst-launch audiotestsrc freq=100 ! adder name=mix ! audioconvert ! alsasink audiotestsrc freq=500 ! mix.
36  * ]| This pipeline produces two sine waves mixed together.
37  * </refsect2>
38  *
39  * Last reviewed on 2006-05-09 (0.10.7)
40  */
41 /* Element-Checklist-Version: 5 */
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46 #include "gstadder.h"
47 #include <gst/audio/audio.h>
48 #include <string.h>             /* strcmp */
49 /*#include <liboil/liboil.h>*/
50
51 /* highest positive/lowest negative x-bit value we can use for clamping */
52 #define MAX_INT_32  ((gint32) (0x7fffffff))
53 #define MAX_INT_16  ((gint16) (0x7fff))
54 #define MAX_INT_8   ((gint8)  (0x7f))
55 #define MAX_UINT_32 ((guint32)(0xffffffff))
56 #define MAX_UINT_16 ((guint16)(0xffff))
57 #define MAX_UINT_8  ((guint8) (0xff))
58
59 #define MIN_INT_32  ((gint32) (0x80000000))
60 #define MIN_INT_16  ((gint16) (0x8000))
61 #define MIN_INT_8   ((gint8)  (0x80))
62 #define MIN_UINT_32 ((guint32)(0x00000000))
63 #define MIN_UINT_16 ((guint16)(0x0000))
64 #define MIN_UINT_8  ((guint8) (0x00))
65
66 enum
67 {
68   PROP_0,
69   PROP_FILTER_CAPS
70 };
71
72 #define GST_CAT_DEFAULT gst_adder_debug
73 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
74
75 /* elementfactory information */
76
77 #define CAPS \
78   "audio/x-raw-int, " \
79   "rate = (int) [ 1, MAX ], " \
80   "channels = (int) [ 1, MAX ], " \
81   "endianness = (int) BYTE_ORDER, " \
82   "width = (int) 32, " \
83   "depth = (int) 32, " \
84   "signed = (boolean) { true, false } ;" \
85   "audio/x-raw-int, " \
86   "rate = (int) [ 1, MAX ], " \
87   "channels = (int) [ 1, MAX ], " \
88   "endianness = (int) BYTE_ORDER, " \
89   "width = (int) 16, " \
90   "depth = (int) 16, " \
91   "signed = (boolean) { true, false } ;" \
92   "audio/x-raw-int, " \
93   "rate = (int) [ 1, MAX ], " \
94   "channels = (int) [ 1, MAX ], " \
95   "endianness = (int) BYTE_ORDER, " \
96   "width = (int) 8, " \
97   "depth = (int) 8, " \
98   "signed = (boolean) { true, false } ;" \
99   "audio/x-raw-float, " \
100   "rate = (int) [ 1, MAX ], " \
101   "channels = (int) [ 1, MAX ], " \
102   "endianness = (int) BYTE_ORDER, " \
103   "width = (int) { 32, 64 }"
104
105 static GstStaticPadTemplate gst_adder_src_template =
106 GST_STATIC_PAD_TEMPLATE ("src",
107     GST_PAD_SRC,
108     GST_PAD_ALWAYS,
109     GST_STATIC_CAPS (CAPS)
110     );
111
112 static GstStaticPadTemplate gst_adder_sink_template =
113 GST_STATIC_PAD_TEMPLATE ("sink%d",
114     GST_PAD_SINK,
115     GST_PAD_REQUEST,
116     GST_STATIC_CAPS (CAPS)
117     );
118
119 static void gst_adder_class_init (GstAdderClass * klass);
120 static void gst_adder_init (GstAdder * adder);
121 static void gst_adder_dispose (GObject * object);
122 static void gst_adder_set_property (GObject * object, guint prop_id,
123     const GValue * value, GParamSpec * pspec);
124 static void gst_adder_get_property (GObject * object, guint prop_id,
125     GValue * value, GParamSpec * pspec);
126
127 static gboolean gst_adder_setcaps (GstPad * pad, GstCaps * caps);
128 static gboolean gst_adder_query (GstPad * pad, GstQuery * query);
129 static gboolean gst_adder_src_event (GstPad * pad, GstEvent * event);
130 static gboolean gst_adder_sink_event (GstPad * pad, GstEvent * event);
131
132 static GstPad *gst_adder_request_new_pad (GstElement * element,
133     GstPadTemplate * temp, const gchar * unused);
134 static void gst_adder_release_pad (GstElement * element, GstPad * pad);
135
136 static GstStateChangeReturn gst_adder_change_state (GstElement * element,
137     GstStateChange transition);
138
139 static GstBuffer *gst_adder_do_clip (GstCollectPads * pads,
140     GstCollectData * data, GstBuffer * buffer, gpointer user_data);
141 static GstFlowReturn gst_adder_collected (GstCollectPads * pads,
142     gpointer user_data);
143
144 static GstElementClass *parent_class = NULL;
145
146 GType
147 gst_adder_get_type (void)
148 {
149   static GType adder_type = 0;
150
151   if (G_UNLIKELY (adder_type == 0)) {
152     static const GTypeInfo adder_info = {
153       sizeof (GstAdderClass), NULL, NULL,
154       (GClassInitFunc) gst_adder_class_init, NULL, NULL,
155       sizeof (GstAdder), 0,
156       (GInstanceInitFunc) gst_adder_init,
157     };
158
159     adder_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAdder",
160         &adder_info, 0);
161     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "adder", 0,
162         "audio channel mixing element");
163   }
164   return adder_type;
165 }
166
167 /* clipping versions (for int)
168  * FIXME: what about: oil_add_s16 (out, out, in, bytes / sizeof (type))
169  */
170 #define MAKE_FUNC(name,type,ttype,min,max)                      \
171 static void name (type *out, type *in, gint bytes) {            \
172   gint i;                                                       \
173   ttype add;                                                    \
174   for (i = 0; i < bytes / sizeof (type); i++) {                 \
175     add = (ttype)out[i] + (ttype)in[i];                         \
176     out[i] = CLAMP (add, min, max);                             \
177   }                                                             \
178 }
179
180 /* unsigned versions (for int) */
181 #define MAKE_FUNC_US(name,type,ttype,max)                       \
182 static void name (type *out, type *in, gint bytes) {            \
183   gint i;                                                       \
184   ttype add;                                                    \
185   for (i = 0; i < bytes / sizeof (type); i++) {                 \
186     add = (ttype)out[i] + (ttype)in[i];                         \
187     out[i] = ((add <= max) ? add : max);                        \
188   }                                                             \
189 }
190
191 /* non-clipping versions (for float) */
192 #define MAKE_FUNC_NC(name,type)                                 \
193 static void name (type *out, type *in, gint bytes) {            \
194   gint i;                                                       \
195   for (i = 0; i < bytes / sizeof (type); i++)                   \
196     out[i] += in[i];                                            \
197 }
198
199 #if 0
200 /* right now, the liboil function don't seems to be faster on x86
201  * time gst-launch audiotestsrc num-buffers=50000 ! audio/x-raw-float ! adder name=m ! fakesink audiotestsrc num-buffers=50000 ! audio/x-raw-float ! m.
202  * time gst-launch audiotestsrc num-buffers=50000 ! audio/x-raw-float,width=32 ! adder name=m ! fakesink audiotestsrc num-buffers=50000 ! audio/x-raw-float,width=32 ! m.
203  */
204 static void
205 add_float32 (gfloat * out, gfloat * in, gint bytes)
206 {
207   oil_add_f32 (out, out, in, bytes / sizeof (gfloat));
208 }
209
210 static void
211 add_float64 (gdouble * out, gdouble * in, gint bytes)
212 {
213   oil_add_f64 (out, out, in, bytes / sizeof (gdouble));
214 }
215 #endif
216
217 /* *INDENT-OFF* */
218 MAKE_FUNC (add_int32, gint32, gint64, MIN_INT_32, MAX_INT_32)
219 MAKE_FUNC (add_int16, gint16, gint32, MIN_INT_16, MAX_INT_16)
220 MAKE_FUNC (add_int8, gint8, gint16, MIN_INT_8, MAX_INT_8)
221 MAKE_FUNC_US (add_uint32, guint32, guint64, MAX_UINT_32)
222 MAKE_FUNC_US (add_uint16, guint16, guint32, MAX_UINT_16)
223 MAKE_FUNC_US (add_uint8, guint8, guint16, MAX_UINT_8)
224 MAKE_FUNC_NC (add_float64, gdouble)
225 MAKE_FUNC_NC (add_float32, gfloat)
226 /* *INDENT-ON* */
227
228 /* we can only accept caps that we and downstream can handle.
229  * if we have filtercaps set, use those to constrain the target caps.
230  */
231 static GstCaps *
232 gst_adder_sink_getcaps (GstPad * pad)
233 {
234   GstAdder *adder;
235   GstCaps *result, *peercaps, *sinkcaps;
236
237   adder = GST_ADDER (GST_PAD_PARENT (pad));
238
239   GST_OBJECT_LOCK (adder);
240   /* get the downstream possible caps */
241   peercaps = gst_pad_peer_get_caps (adder->srcpad);
242
243   /* get the allowed caps on this sinkpad, we use the fixed caps function so
244    * that it does not call recursively in this function. */
245   sinkcaps = gst_pad_get_fixed_caps_func (pad);
246   if (peercaps) {
247     /* restrict with filter-caps if any */
248     if (adder->filter_caps) {
249       GST_DEBUG_OBJECT (adder, "filtering peer caps");
250       result = gst_caps_intersect (peercaps, adder->filter_caps);
251       gst_caps_unref (peercaps);
252       peercaps = result;
253     }
254     /* if the peer has caps, intersect */
255     GST_DEBUG_OBJECT (adder, "intersecting peer and template caps");
256     result = gst_caps_intersect (peercaps, sinkcaps);
257     gst_caps_unref (peercaps);
258     gst_caps_unref (sinkcaps);
259   } else {
260     /* the peer has no caps (or there is no peer), just use the allowed caps
261      * of this sinkpad. */
262     /* restrict with filter-caps if any */
263     if (adder->filter_caps) {
264       GST_DEBUG_OBJECT (adder, "no peer caps, using filtered sinkcaps");
265       result = gst_caps_intersect (sinkcaps, adder->filter_caps);
266       gst_caps_unref (sinkcaps);
267     } else {
268       GST_DEBUG_OBJECT (adder, "no peer caps, using sinkcaps");
269       result = sinkcaps;
270     }
271   }
272   GST_OBJECT_UNLOCK (adder);
273
274   GST_LOG_OBJECT (adder, "getting caps on pad %p,%s to %" GST_PTR_FORMAT, pad,
275       GST_PAD_NAME (pad), result);
276
277   return result;
278 }
279
280 /* the first caps we receive on any of the sinkpads will define the caps for all
281  * the other sinkpads because we can only mix streams with the same caps.
282  */
283 static gboolean
284 gst_adder_setcaps (GstPad * pad, GstCaps * caps)
285 {
286   GstAdder *adder;
287   GList *pads;
288   GstStructure *structure;
289   const char *media_type;
290
291   adder = GST_ADDER (GST_PAD_PARENT (pad));
292
293   GST_LOG_OBJECT (adder, "setting caps on pad %p,%s to %" GST_PTR_FORMAT, pad,
294       GST_PAD_NAME (pad), caps);
295
296   /* FIXME, see if the other pads can accept the format. Also lock the
297    * format on the other pads to this new format. */
298   GST_OBJECT_LOCK (adder);
299   pads = GST_ELEMENT (adder)->pads;
300   while (pads) {
301     GstPad *otherpad = GST_PAD (pads->data);
302
303     if (otherpad != pad) {
304       gst_caps_replace (&GST_PAD_CAPS (otherpad), caps);
305     }
306     pads = g_list_next (pads);
307   }
308   GST_OBJECT_UNLOCK (adder);
309
310   /* parse caps now */
311   structure = gst_caps_get_structure (caps, 0);
312   media_type = gst_structure_get_name (structure);
313   if (strcmp (media_type, "audio/x-raw-int") == 0) {
314     adder->format = GST_ADDER_FORMAT_INT;
315     gst_structure_get_int (structure, "width", &adder->width);
316     gst_structure_get_int (structure, "depth", &adder->depth);
317     gst_structure_get_int (structure, "endianness", &adder->endianness);
318     gst_structure_get_boolean (structure, "signed", &adder->is_signed);
319
320     GST_INFO_OBJECT (pad, "parse_caps sets adder to format int, %d bit",
321         adder->width);
322
323     if (adder->endianness != G_BYTE_ORDER)
324       goto not_supported;
325
326     switch (adder->width) {
327       case 8:
328         adder->func = (adder->is_signed ?
329             (GstAdderFunction) add_int8 : (GstAdderFunction) add_uint8);
330         break;
331       case 16:
332         adder->func = (adder->is_signed ?
333             (GstAdderFunction) add_int16 : (GstAdderFunction) add_uint16);
334         break;
335       case 32:
336         adder->func = (adder->is_signed ?
337             (GstAdderFunction) add_int32 : (GstAdderFunction) add_uint32);
338         break;
339       default:
340         goto not_supported;
341     }
342   } else if (strcmp (media_type, "audio/x-raw-float") == 0) {
343     adder->format = GST_ADDER_FORMAT_FLOAT;
344     gst_structure_get_int (structure, "width", &adder->width);
345     gst_structure_get_int (structure, "endianness", &adder->endianness);
346
347     GST_INFO_OBJECT (pad, "parse_caps sets adder to format float, %d bit",
348         adder->width);
349
350     if (adder->endianness != G_BYTE_ORDER)
351       goto not_supported;
352
353     switch (adder->width) {
354       case 32:
355         adder->func = (GstAdderFunction) add_float32;
356         break;
357       case 64:
358         adder->func = (GstAdderFunction) add_float64;
359         break;
360       default:
361         goto not_supported;
362     }
363   } else {
364     goto not_supported;
365   }
366
367   gst_structure_get_int (structure, "channels", &adder->channels);
368   gst_structure_get_int (structure, "rate", &adder->rate);
369   /* precalc bps */
370   adder->bps = (adder->width / 8) * adder->channels;
371
372   return TRUE;
373
374   /* ERRORS */
375 not_supported:
376   {
377     GST_DEBUG_OBJECT (adder, "unsupported format set as caps");
378     return FALSE;
379   }
380 }
381
382 /* FIXME, the duration query should reflect how long you will produce
383  * data, that is the amount of stream time until you will emit EOS.
384  *
385  * For synchronized mixing this is always the max of all the durations
386  * of upstream since we emit EOS when all of them finished.
387  *
388  * We don't do synchronized mixing so this really depends on where the
389  * streams where punched in and what their relative offsets are against
390  * eachother which we can get from the first timestamps we see.
391  *
392  * When we add a new stream (or remove a stream) the duration might
393  * also become invalid again and we need to post a new DURATION
394  * message to notify this fact to the parent.
395  * For now we take the max of all the upstream elements so the simple
396  * cases work at least somewhat.
397  */
398 static gboolean
399 gst_adder_query_duration (GstAdder * adder, GstQuery * query)
400 {
401   gint64 max;
402   gboolean res;
403   GstFormat format;
404   GstIterator *it;
405   gboolean done;
406
407   /* parse format */
408   gst_query_parse_duration (query, &format, NULL);
409
410   max = -1;
411   res = TRUE;
412   done = FALSE;
413
414   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
415   while (!done) {
416     GstIteratorResult ires;
417
418     gpointer item;
419
420     ires = gst_iterator_next (it, &item);
421     switch (ires) {
422       case GST_ITERATOR_DONE:
423         done = TRUE;
424         break;
425       case GST_ITERATOR_OK:
426       {
427         GstPad *pad = GST_PAD_CAST (item);
428
429         gint64 duration;
430
431         /* ask sink peer for duration */
432         res &= gst_pad_query_peer_duration (pad, &format, &duration);
433         /* take max from all valid return values */
434         if (res) {
435           /* valid unknown length, stop searching */
436           if (duration == -1) {
437             max = duration;
438             done = TRUE;
439           }
440           /* else see if bigger than current max */
441           else if (duration > max)
442             max = duration;
443         }
444         gst_object_unref (pad);
445         break;
446       }
447       case GST_ITERATOR_RESYNC:
448         max = -1;
449         res = TRUE;
450         gst_iterator_resync (it);
451         break;
452       default:
453         res = FALSE;
454         done = TRUE;
455         break;
456     }
457   }
458   gst_iterator_free (it);
459
460   if (res) {
461     /* and store the max */
462     GST_DEBUG_OBJECT (adder, "Total duration in format %s: %"
463         GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max));
464     gst_query_set_duration (query, format, max);
465   }
466
467   return res;
468 }
469
470 static gboolean
471 gst_adder_query_latency (GstAdder * adder, GstQuery * query)
472 {
473   GstClockTime min, max;
474   gboolean live;
475   gboolean res;
476   GstIterator *it;
477   gboolean done;
478
479   res = TRUE;
480   done = FALSE;
481
482   live = FALSE;
483   min = 0;
484   max = GST_CLOCK_TIME_NONE;
485
486   /* Take maximum of all latency values */
487   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
488   while (!done) {
489     GstIteratorResult ires;
490
491     gpointer item;
492
493     ires = gst_iterator_next (it, &item);
494     switch (ires) {
495       case GST_ITERATOR_DONE:
496         done = TRUE;
497         break;
498       case GST_ITERATOR_OK:
499       {
500         GstPad *pad = GST_PAD_CAST (item);
501         GstQuery *peerquery;
502         GstClockTime min_cur, max_cur;
503         gboolean live_cur;
504
505         peerquery = gst_query_new_latency ();
506
507         /* Ask peer for latency */
508         res &= gst_pad_peer_query (pad, peerquery);
509
510         /* take max from all valid return values */
511         if (res) {
512           gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur);
513
514           if (min_cur > min)
515             min = min_cur;
516
517           if (max_cur != GST_CLOCK_TIME_NONE &&
518               ((max != GST_CLOCK_TIME_NONE && max_cur > max) ||
519                   (max == GST_CLOCK_TIME_NONE)))
520             max = max_cur;
521
522           live = live || live_cur;
523         }
524
525         gst_query_unref (peerquery);
526         gst_object_unref (pad);
527         break;
528       }
529       case GST_ITERATOR_RESYNC:
530         live = FALSE;
531         min = 0;
532         max = GST_CLOCK_TIME_NONE;
533         res = TRUE;
534         gst_iterator_resync (it);
535         break;
536       default:
537         res = FALSE;
538         done = TRUE;
539         break;
540     }
541   }
542   gst_iterator_free (it);
543
544   if (res) {
545     /* store the results */
546     GST_DEBUG_OBJECT (adder, "Calculated total latency: live %s, min %"
547         GST_TIME_FORMAT ", max %" GST_TIME_FORMAT,
548         (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max));
549     gst_query_set_latency (query, live, min, max);
550   }
551
552   return res;
553 }
554
555 static gboolean
556 gst_adder_query (GstPad * pad, GstQuery * query)
557 {
558   GstAdder *adder = GST_ADDER (gst_pad_get_parent (pad));
559   gboolean res = FALSE;
560
561   switch (GST_QUERY_TYPE (query)) {
562     case GST_QUERY_POSITION:
563     {
564       GstFormat format;
565
566       gst_query_parse_position (query, &format, NULL);
567
568       switch (format) {
569         case GST_FORMAT_TIME:
570           /* FIXME, bring to stream time, might be tricky */
571           gst_query_set_position (query, format, adder->timestamp);
572           res = TRUE;
573           break;
574         case GST_FORMAT_DEFAULT:
575           gst_query_set_position (query, format, adder->offset);
576           res = TRUE;
577           break;
578         default:
579           break;
580       }
581       break;
582     }
583     case GST_QUERY_DURATION:
584       res = gst_adder_query_duration (adder, query);
585       break;
586     case GST_QUERY_LATENCY:
587       res = gst_adder_query_latency (adder, query);
588       break;
589     default:
590       /* FIXME, needs a custom query handler because we have multiple
591        * sinkpads */
592       res = gst_pad_query_default (pad, query);
593       break;
594   }
595
596   gst_object_unref (adder);
597   return res;
598 }
599
600 typedef struct
601 {
602   GstEvent *event;
603   gboolean flush;
604 } EventData;
605
606 static gboolean
607 forward_event_func (GstPad * pad, GValue * ret, EventData * data)
608 {
609   GstEvent *event = data->event;
610
611   gst_event_ref (event);
612   GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
613   if (!gst_pad_push_event (pad, event)) {
614     GST_WARNING_OBJECT (pad, "Sending event  %p (%s) failed.",
615         event, GST_EVENT_TYPE_NAME (event));
616     /* quick hack to unflush the pads, ideally we need a way to just unflush
617      * this single collect pad */
618     if (data->flush)
619       gst_pad_send_event (pad, gst_event_new_flush_stop ());
620   } else {
621     g_value_set_boolean (ret, TRUE);
622     GST_LOG_OBJECT (pad, "Sent event  %p (%s).",
623         event, GST_EVENT_TYPE_NAME (event));
624   }
625   gst_object_unref (pad);
626
627   /* continue on other pads, even if one failed */
628   return TRUE;
629 }
630
631 /* forwards the event to all sinkpads, takes ownership of the
632  * event
633  *
634  * Returns: TRUE if the event could be forwarded on all
635  * sinkpads.
636  */
637 static gboolean
638 forward_event (GstAdder * adder, GstEvent * event, gboolean flush)
639 {
640   gboolean ret;
641   GstIterator *it;
642   GstIteratorResult ires;
643   GValue vret = { 0 };
644   EventData data;
645
646   GST_LOG_OBJECT (adder, "Forwarding event %p (%s)", event,
647       GST_EVENT_TYPE_NAME (event));
648
649   data.event = event;
650   data.flush = flush;
651
652   g_value_init (&vret, G_TYPE_BOOLEAN);
653   g_value_set_boolean (&vret, FALSE);
654   it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
655   while (TRUE) {
656     ires = gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func,
657         &vret, &data);
658     switch (ires) {
659       case GST_ITERATOR_RESYNC:
660         GST_WARNING ("resync");
661         gst_iterator_resync (it);
662         g_value_set_boolean (&vret, TRUE);
663         break;
664       case GST_ITERATOR_OK:
665       case GST_ITERATOR_DONE:
666         ret = g_value_get_boolean (&vret);
667         goto done;
668       default:
669         ret = FALSE;
670         goto done;
671     }
672   }
673 done:
674   gst_iterator_free (it);
675   GST_LOG_OBJECT (adder, "Forwarded event %p (%s), ret=%d", event,
676       GST_EVENT_TYPE_NAME (event), ret);
677   gst_event_unref (event);
678
679   return ret;
680 }
681
682 static gboolean
683 gst_adder_src_event (GstPad * pad, GstEvent * event)
684 {
685   GstAdder *adder;
686   gboolean result;
687
688   adder = GST_ADDER (gst_pad_get_parent (pad));
689
690   switch (GST_EVENT_TYPE (event)) {
691     case GST_EVENT_SEEK:
692     {
693       GstSeekFlags flags;
694       GstSeekType curtype;
695       gint64 cur;
696       gboolean flush;
697
698       /* parse the seek parameters */
699       gst_event_parse_seek (event, &adder->segment_rate, NULL, &flags, &curtype,
700           &cur, NULL, NULL);
701
702       flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH;
703
704       /* check if we are flushing */
705       if (flush) {
706         /* make sure we accept nothing anymore and return WRONG_STATE */
707         gst_collect_pads_set_flushing (adder->collect, TRUE);
708
709         /* flushing seek, start flush downstream, the flush will be done
710          * when all pads received a FLUSH_STOP. */
711         gst_pad_push_event (adder->srcpad, gst_event_new_flush_start ());
712       }
713       GST_DEBUG_OBJECT (adder, "handling seek event: %" GST_PTR_FORMAT, event);
714
715       /* now wait for the collected to be finished and mark a new
716        * segment. After we have the lock, no collect function is running and no
717        * new collect function will be called for as long as we're flushing. */
718       GST_OBJECT_LOCK (adder->collect);
719       if (curtype == GST_SEEK_TYPE_SET)
720         adder->segment_position = cur;
721       else
722         adder->segment_position = 0;
723       /* make sure we push a new segment, to inform about new basetime
724        * see FIXME in gst_adder_collected() */
725       adder->segment_pending = TRUE;
726       if (flush) {
727         /* Yes, we need to call _set_flushing again *WHEN* the streaming threads
728          * have stopped so that the cookie gets properly updated. */
729         gst_collect_pads_set_flushing (adder->collect, TRUE);
730       }
731       /* we might have a pending flush_stop event now. This event will either be
732        * sent by an upstream element when it completes the seek or we will push
733        * one in the collected callback ourself */
734       adder->flush_stop_pending = flush;
735       GST_OBJECT_UNLOCK (adder->collect);
736       GST_DEBUG_OBJECT (adder, "forwarding seek event: %" GST_PTR_FORMAT,
737           event);
738
739       result = forward_event (adder, event, flush);
740       if (!result) {
741         /* seek failed. maybe source is a live source. */
742         GST_DEBUG_OBJECT (adder, "seeking failed");
743       }
744       /* FIXME: ideally we would like to send a flush-stop event from here but
745        * collectpads does not have a method that allows us to do that. Instead
746        * we forward all flush-stop events we receive on the sinkpads. We might
747        * be sending too many flush-stop events. */
748       break;
749     }
750     case GST_EVENT_QOS:
751       /* QoS might be tricky */
752       result = FALSE;
753       break;
754     case GST_EVENT_NAVIGATION:
755       /* navigation is rather pointless. */
756       result = FALSE;
757       break;
758     default:
759       /* just forward the rest for now */
760       GST_DEBUG_OBJECT (adder, "forward unhandled event: %s",
761           GST_EVENT_TYPE_NAME (event));
762       result = forward_event (adder, event, FALSE);
763       break;
764   }
765   gst_object_unref (adder);
766
767   return result;
768 }
769
770 static gboolean
771 gst_adder_sink_event (GstPad * pad, GstEvent * event)
772 {
773   GstAdder *adder;
774   gboolean ret = TRUE;
775
776   adder = GST_ADDER (gst_pad_get_parent (pad));
777
778   GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event),
779       GST_DEBUG_PAD_NAME (pad));
780
781   switch (GST_EVENT_TYPE (event)) {
782     case GST_EVENT_FLUSH_STOP:
783       /* we received a flush-stop. The collect_event function will push the
784        * event past our element. We simply forward all flush-stop events, even
785        * when no flush-stop was pending, this is required because collectpads
786        * does not provide an API to handle-but-not-forward the flush-stop.
787        * We unset the pending flush-stop flag so that we don't send anymore
788        * flush-stop from the collect function later.
789        */
790       GST_OBJECT_LOCK (adder->collect);
791       adder->segment_pending = TRUE;
792       adder->flush_stop_pending = FALSE;
793       /* Clear pending tags */
794       if (adder->pending_events) {
795         g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL);
796         g_list_free (adder->pending_events);
797         adder->pending_events = NULL;
798       }
799       GST_OBJECT_UNLOCK (adder->collect);
800       break;
801     case GST_EVENT_TAG:
802       GST_OBJECT_LOCK (adder->collect);
803       /* collect tags here so we can push them out when we collect data */
804       adder->pending_events = g_list_append (adder->pending_events, event);
805       GST_OBJECT_UNLOCK (adder->collect);
806       goto beach;
807     default:
808       break;
809   }
810
811   /* now GstCollectPads can take care of the rest, e.g. EOS */
812   ret = adder->collect_event (pad, event);
813
814 beach:
815   gst_object_unref (adder);
816   return ret;
817 }
818
819 static void
820 gst_adder_class_init (GstAdderClass * klass)
821 {
822   GObjectClass *gobject_class = (GObjectClass *) klass;
823   GstElementClass *gstelement_class = (GstElementClass *) klass;
824
825   gobject_class->set_property = gst_adder_set_property;
826   gobject_class->get_property = gst_adder_get_property;
827   gobject_class->dispose = gst_adder_dispose;
828
829   gst_element_class_add_pad_template (gstelement_class,
830       gst_static_pad_template_get (&gst_adder_src_template));
831   gst_element_class_add_pad_template (gstelement_class,
832       gst_static_pad_template_get (&gst_adder_sink_template));
833   gst_element_class_set_details_simple (gstelement_class, "Adder",
834       "Generic/Audio",
835       "Add N audio channels together",
836       "Thomas Vander Stichele <thomas at apestaart dot org>");
837
838   parent_class = g_type_class_peek_parent (klass);
839
840   /**
841    * GstAdder:caps:
842    *
843    * Since: 0.10.24
844    */
845   g_object_class_install_property (gobject_class, PROP_FILTER_CAPS,
846       g_param_spec_boxed ("caps", "Target caps",
847           "Set target format for mixing (NULL means ANY). "
848           "Setting this property takes a reference to the supplied GstCaps "
849           "object.", GST_TYPE_CAPS,
850           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
851
852   gstelement_class->request_new_pad =
853       GST_DEBUG_FUNCPTR (gst_adder_request_new_pad);
854   gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_adder_release_pad);
855   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_adder_change_state);
856 }
857
858 static void
859 gst_adder_init (GstAdder * adder)
860 {
861   GstPadTemplate *template;
862
863   template = gst_static_pad_template_get (&gst_adder_src_template);
864   adder->srcpad = gst_pad_new_from_template (template, "src");
865   gst_object_unref (template);
866
867   gst_pad_set_getcaps_function (adder->srcpad,
868       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
869   gst_pad_set_setcaps_function (adder->srcpad,
870       GST_DEBUG_FUNCPTR (gst_adder_setcaps));
871   gst_pad_set_query_function (adder->srcpad,
872       GST_DEBUG_FUNCPTR (gst_adder_query));
873   gst_pad_set_event_function (adder->srcpad,
874       GST_DEBUG_FUNCPTR (gst_adder_src_event));
875   gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad);
876
877   adder->format = GST_ADDER_FORMAT_UNSET;
878   adder->padcount = 0;
879   adder->func = NULL;
880
881   adder->filter_caps = NULL;
882
883   /* keep track of the sinkpads requested */
884   adder->collect = gst_collect_pads_new ();
885   gst_collect_pads_set_function (adder->collect,
886       GST_DEBUG_FUNCPTR (gst_adder_collected), adder);
887   gst_collect_pads_set_clip_function (adder->collect,
888       GST_DEBUG_FUNCPTR (gst_adder_do_clip), adder);
889 }
890
891 static void
892 gst_adder_dispose (GObject * object)
893 {
894   GstAdder *adder = GST_ADDER (object);
895
896   if (adder->collect) {
897     gst_object_unref (adder->collect);
898     adder->collect = NULL;
899   }
900   gst_caps_replace (&adder->filter_caps, NULL);
901   if (adder->pending_events) {
902     g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL);
903     g_list_free (adder->pending_events);
904     adder->pending_events = NULL;
905   }
906
907   G_OBJECT_CLASS (parent_class)->dispose (object);
908 }
909
910 static void
911 gst_adder_set_property (GObject * object, guint prop_id,
912     const GValue * value, GParamSpec * pspec)
913 {
914   GstAdder *adder = GST_ADDER (object);
915
916   switch (prop_id) {
917     case PROP_FILTER_CAPS:{
918       GstCaps *new_caps = NULL;
919       GstCaps *old_caps;
920       const GstCaps *new_caps_val = gst_value_get_caps (value);
921
922       if (new_caps_val != NULL) {
923         new_caps = (GstCaps *) new_caps_val;
924         gst_caps_ref (new_caps);
925       }
926
927       GST_OBJECT_LOCK (adder);
928       old_caps = adder->filter_caps;
929       adder->filter_caps = new_caps;
930       GST_OBJECT_UNLOCK (adder);
931
932       if (old_caps)
933         gst_caps_unref (old_caps);
934
935       GST_DEBUG_OBJECT (adder, "set new caps %" GST_PTR_FORMAT, new_caps);
936       break;
937     }
938     default:
939       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
940       break;
941   }
942 }
943
944 static void
945 gst_adder_get_property (GObject * object, guint prop_id, GValue * value,
946     GParamSpec * pspec)
947 {
948   GstAdder *adder = GST_ADDER (object);
949
950   switch (prop_id) {
951     case PROP_FILTER_CAPS:
952       GST_OBJECT_LOCK (adder);
953       gst_value_set_caps (value, adder->filter_caps);
954       GST_OBJECT_UNLOCK (adder);
955       break;
956     default:
957       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
958       break;
959   }
960 }
961
962
963 static GstPad *
964 gst_adder_request_new_pad (GstElement * element, GstPadTemplate * templ,
965     const gchar * unused)
966 {
967   gchar *name;
968   GstAdder *adder;
969   GstPad *newpad;
970   gint padcount;
971
972   if (templ->direction != GST_PAD_SINK)
973     goto not_sink;
974
975   adder = GST_ADDER (element);
976
977   /* increment pad counter */
978   padcount = g_atomic_int_exchange_and_add (&adder->padcount, 1);
979
980   name = g_strdup_printf ("sink%d", padcount);
981   newpad = gst_pad_new_from_template (templ, name);
982   GST_DEBUG_OBJECT (adder, "request new pad %s", name);
983   g_free (name);
984
985   gst_pad_set_getcaps_function (newpad,
986       GST_DEBUG_FUNCPTR (gst_adder_sink_getcaps));
987   gst_pad_set_setcaps_function (newpad, GST_DEBUG_FUNCPTR (gst_adder_setcaps));
988   gst_collect_pads_add_pad (adder->collect, newpad, sizeof (GstCollectData));
989
990   /* FIXME: hacked way to override/extend the event function of
991    * GstCollectPads; because it sets its own event function giving the
992    * element no access to events */
993   adder->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
994   gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_adder_sink_event));
995
996   /* takes ownership of the pad */
997   if (!gst_element_add_pad (GST_ELEMENT (adder), newpad))
998     goto could_not_add;
999
1000   return newpad;
1001
1002   /* errors */
1003 not_sink:
1004   {
1005     g_warning ("gstadder: request new pad that is not a SINK pad\n");
1006     return NULL;
1007   }
1008 could_not_add:
1009   {
1010     GST_DEBUG_OBJECT (adder, "could not add pad");
1011     gst_collect_pads_remove_pad (adder->collect, newpad);
1012     gst_object_unref (newpad);
1013     return NULL;
1014   }
1015 }
1016
1017 static void
1018 gst_adder_release_pad (GstElement * element, GstPad * pad)
1019 {
1020   GstAdder *adder;
1021
1022   adder = GST_ADDER (element);
1023
1024   GST_DEBUG_OBJECT (adder, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
1025
1026   gst_collect_pads_remove_pad (adder->collect, pad);
1027   gst_element_remove_pad (element, pad);
1028 }
1029
1030 static GstBuffer *
1031 gst_adder_do_clip (GstCollectPads * pads, GstCollectData * data,
1032     GstBuffer * buffer, gpointer user_data)
1033 {
1034   GstAdder *adder = GST_ADDER (user_data);
1035
1036   buffer = gst_audio_buffer_clip (buffer, &data->segment, adder->rate,
1037       adder->bps);
1038
1039   return buffer;
1040 }
1041
1042 static GstFlowReturn
1043 gst_adder_collected (GstCollectPads * pads, gpointer user_data)
1044 {
1045   /*
1046    * combine streams by adding data values
1047    * basic algorithm :
1048    * - this function is called when all pads have a buffer
1049    * - get available bytes on all pads.
1050    * - repeat for each input pad :
1051    *   - read available bytes, copy or add to target buffer
1052    *   - if there's an EOS event, remove the input channel
1053    * - push out the output buffer
1054    *
1055    * todo:
1056    * - would be nice to have a mixing mode, where instead of adding we mix
1057    *   - for float we could downscale after collect loop
1058    *   - for int we need to downscale each input to avoid clipping or
1059    *     mix into a temp (float) buffer and scale afterwards as well
1060    */
1061   GstAdder *adder;
1062   GSList *collected, *next = NULL;
1063   GstFlowReturn ret;
1064   GstBuffer *outbuf = NULL, *gapbuf = NULL;
1065   gpointer outdata = NULL;
1066   guint outsize;
1067
1068   adder = GST_ADDER (user_data);
1069
1070   /* this is fatal */
1071   if (G_UNLIKELY (adder->func == NULL))
1072     goto not_negotiated;
1073
1074   if (adder->flush_stop_pending) {
1075     gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop ());
1076     adder->flush_stop_pending = FALSE;
1077   }
1078
1079   /* get available bytes for reading, this can be 0 which could mean empty
1080    * buffers or EOS, which we will catch when we loop over the pads. */
1081   outsize = gst_collect_pads_available (pads);
1082   /* can only happen when no pads to collect or all EOS */
1083   if (outsize == 0)
1084     goto eos;
1085
1086   GST_LOG_OBJECT (adder,
1087       "starting to cycle through channels, %d bytes available (bps = %d)",
1088       outsize, adder->bps);
1089
1090   for (collected = pads->data; collected; collected = next) {
1091     GstCollectData *collect_data;
1092     GstBuffer *inbuf;
1093     gboolean is_gap;
1094
1095     /* take next to see if this is the last collectdata */
1096     next = g_slist_next (collected);
1097
1098     collect_data = (GstCollectData *) collected->data;
1099
1100     /* get a buffer of size bytes, if we get a buffer, it is at least outsize
1101      * bytes big. */
1102     inbuf = gst_collect_pads_take_buffer (pads, collect_data, outsize);
1103     /* NULL means EOS or an empty buffer so we still need to flush in
1104      * case of an empty buffer. */
1105     if (inbuf == NULL) {
1106       GST_LOG_OBJECT (adder, "channel %p: no bytes available", collect_data);
1107       continue;
1108     }
1109
1110     is_gap = GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP);
1111
1112     /* Try to make an output buffer */
1113     if (outbuf == NULL) {
1114       /* if this is a gap buffer but we have some more pads to check, skip it.
1115        * If we are at the last buffer, take it, regardless if it is a GAP
1116        * buffer or not. */
1117       if (is_gap && next) {
1118         GST_DEBUG_OBJECT (adder, "skipping, non-last GAP buffer");
1119         /* we keep the GAP buffer, if we don't have anymore buffers (all pads
1120          * EOS, we can use this one as the output buffer. */
1121         if (gapbuf == NULL)
1122           gapbuf = inbuf;
1123         else
1124           gst_buffer_unref (inbuf);
1125         continue;
1126       }
1127
1128       GST_LOG_OBJECT (adder, "channel %p: preparing output buffer of %d bytes",
1129           collect_data, outsize);
1130       /* make data and metadata writable, can simply return the inbuf when we
1131        * are the only one referencing this buffer. If this is the last (and
1132        * only) GAP buffer, it will automatically copy the GAP flag. */
1133       outbuf = gst_buffer_make_writable (inbuf);
1134       outdata = GST_BUFFER_DATA (outbuf);
1135       gst_buffer_set_caps (outbuf, GST_PAD_CAPS (adder->srcpad));
1136     } else {
1137       if (!is_gap) {
1138         /* we had a previous output buffer, mix this non-GAP buffer */
1139         guint8 *indata;
1140         guint insize;
1141
1142         indata = GST_BUFFER_DATA (inbuf);
1143         insize = GST_BUFFER_SIZE (inbuf);
1144
1145         /* all buffers should have outsize, there are no short buffers because we
1146          * asked for the max size above */
1147         g_assert (insize == outsize);
1148
1149         GST_LOG_OBJECT (adder, "channel %p: mixing %d bytes from data %p",
1150             collect_data, insize, indata);
1151
1152         /* further buffers, need to add them */
1153         adder->func ((gpointer) outdata, (gpointer) indata, insize);
1154       } else {
1155         /* skip gap buffer */
1156         GST_LOG_OBJECT (adder, "channel %p: skipping GAP buffer", collect_data);
1157       }
1158       gst_buffer_unref (inbuf);
1159     }
1160   }
1161
1162   if (outbuf == NULL) {
1163     /* no output buffer, reuse one of the GAP buffers then if we have one */
1164     if (gapbuf) {
1165       GST_LOG_OBJECT (adder, "reusing GAP buffer %p", gapbuf);
1166       outbuf = gapbuf;
1167     } else
1168       /* assume EOS otherwise, this should not happen, really */
1169       goto eos;
1170   } else if (gapbuf)
1171     /* we had an output buffer, unref the gapbuffer we kept */
1172     gst_buffer_unref (gapbuf);
1173
1174   /* our timestamping is very simple, just an ever incrementing
1175    * counter, the new segment time will take care of their respective
1176    * stream time. */
1177   if (adder->segment_pending) {
1178     GstEvent *event;
1179
1180     /* FIXME, use rate/applied_rate as set on all sinkpads.
1181      * - currently we just set rate as received from last seek-event
1182      * We could potentially figure out the duration as well using
1183      * the current segment positions and the stated stop positions.
1184      * Also we just start from stream time 0 which is rather
1185      * weird. For non-synchronized mixing, the time should be
1186      * the min of the stream times of all received segments,
1187      * rationale being that the duration is at least going to
1188      * be as long as the earliest stream we start mixing. This
1189      * would also be correct for synchronized mixing but then
1190      * the later streams would be delayed until the stream times
1191      * match.
1192      */
1193     event = gst_event_new_new_segment_full (FALSE, adder->segment_rate,
1194         1.0, GST_FORMAT_TIME, adder->timestamp, -1, adder->segment_position);
1195
1196     if (event) {
1197       if (!gst_pad_push_event (adder->srcpad, event)) {
1198         GST_WARNING_OBJECT (adder->srcpad, "Sending event  %p (%s) failed.",
1199             event, GST_EVENT_TYPE_NAME (event));
1200       }
1201       adder->segment_pending = FALSE;
1202       adder->segment_position = 0;
1203     } else {
1204       GST_WARNING_OBJECT (adder->srcpad, "Creating new segment event for "
1205           "start:%" G_GINT64_FORMAT "  pos:%" G_GINT64_FORMAT " failed",
1206           adder->timestamp, adder->segment_position);
1207     }
1208   }
1209
1210   if (G_UNLIKELY (adder->pending_events)) {
1211     GList *tmp = adder->pending_events;
1212
1213     while (tmp) {
1214       GstEvent *ev = (GstEvent *) tmp->data;
1215
1216       gst_pad_push_event (adder->srcpad, ev);
1217       tmp = g_list_next (tmp);
1218     }
1219     g_list_free (adder->pending_events);
1220     adder->pending_events = NULL;
1221   }
1222
1223   /* set timestamps on the output buffer */
1224   GST_BUFFER_TIMESTAMP (outbuf) = adder->timestamp;
1225   GST_BUFFER_OFFSET (outbuf) = adder->offset;
1226
1227   /* for the next timestamp, use the sample counter, which will
1228    * never accumulate rounding errors */
1229   adder->offset += outsize / adder->bps;
1230   adder->timestamp = gst_util_uint64_scale_int (adder->offset,
1231       GST_SECOND, adder->rate);
1232
1233   /* now we can set the duration of the buffer */
1234   GST_BUFFER_DURATION (outbuf) = adder->timestamp -
1235       GST_BUFFER_TIMESTAMP (outbuf);
1236
1237   /* send it out */
1238   GST_LOG_OBJECT (adder, "pushing outbuf %p, timestamp %" GST_TIME_FORMAT,
1239       outbuf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
1240   ret = gst_pad_push (adder->srcpad, outbuf);
1241
1242   GST_LOG_OBJECT (adder, "pushed outbuf, result = %s", gst_flow_get_name (ret));
1243
1244   return ret;
1245
1246   /* ERRORS */
1247 not_negotiated:
1248   {
1249     GST_ELEMENT_ERROR (adder, STREAM, FORMAT, (NULL),
1250         ("Unknown data received, not negotiated"));
1251     return GST_FLOW_NOT_NEGOTIATED;
1252   }
1253 eos:
1254   {
1255     GST_DEBUG_OBJECT (adder, "no data available, must be EOS");
1256     gst_pad_push_event (adder->srcpad, gst_event_new_eos ());
1257     return GST_FLOW_UNEXPECTED;
1258   }
1259 }
1260
1261 static GstStateChangeReturn
1262 gst_adder_change_state (GstElement * element, GstStateChange transition)
1263 {
1264   GstAdder *adder;
1265   GstStateChangeReturn ret;
1266
1267   adder = GST_ADDER (element);
1268
1269   switch (transition) {
1270     case GST_STATE_CHANGE_NULL_TO_READY:
1271       break;
1272     case GST_STATE_CHANGE_READY_TO_PAUSED:
1273       adder->timestamp = 0;
1274       adder->offset = 0;
1275       adder->flush_stop_pending = FALSE;
1276       adder->segment_pending = TRUE;
1277       adder->segment_position = 0;
1278       adder->segment_rate = 1.0;
1279       gst_segment_init (&adder->segment, GST_FORMAT_UNDEFINED);
1280       gst_collect_pads_start (adder->collect);
1281       break;
1282     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1283       break;
1284     case GST_STATE_CHANGE_PAUSED_TO_READY:
1285       /* need to unblock the collectpads before calling the
1286        * parent change_state so that streaming can finish */
1287       gst_collect_pads_stop (adder->collect);
1288       break;
1289     default:
1290       break;
1291   }
1292
1293   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1294
1295   switch (transition) {
1296     default:
1297       break;
1298   }
1299
1300   return ret;
1301 }
1302
1303
1304 static gboolean
1305 plugin_init (GstPlugin * plugin)
1306 {
1307   /*oil_init (); */
1308
1309   if (!gst_element_register (plugin, "adder", GST_RANK_NONE, GST_TYPE_ADDER)) {
1310     return FALSE;
1311   }
1312
1313   return TRUE;
1314 }
1315
1316 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1317     GST_VERSION_MINOR,
1318     "adder",
1319     "Adds multiple streams",
1320     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)