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