expand tabs
[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 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 /* Element-Checklist-Version: 5 */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 #include "gstadder.h"
29 #include <gst/audio/audio.h>
30 #include <string.h>             /* strcmp */
31
32 /* highest positive/lowest negative x-bit value we can use for clamping */
33 #define MAX_INT_32  ((gint32) (0x7fffffff))
34 #define MAX_INT_16  ((gint16) (0x7fff))
35 #define MAX_INT_8   ((gint8)  (0x7f))
36 #define MAX_UINT_32 ((guint32)(0xffffffff))
37 #define MAX_UINT_16 ((guint16)(0xffff))
38 #define MAX_UINT_8  ((guint8) (0xff))
39
40 #define MIN_INT_32  ((gint32) (0x80000000))
41 #define MIN_INT_16  ((gint16) (0x8000))
42 #define MIN_INT_8   ((gint8)  (0x80))
43 #define MIN_UINT_32 ((guint32)(0x00000000))
44 #define MIN_UINT_16 ((guint16)(0x0000))
45 #define MIN_UINT_8  ((guint8) (0x00))
46
47 GST_DEBUG_CATEGORY_STATIC (gst_adder_debug);
48 #define GST_CAT_DEFAULT gst_adder_debug
49
50 /* elementfactory information */
51 static GstElementDetails adder_details = GST_ELEMENT_DETAILS ("Adder",
52     "Generic/Audio",
53     "Add N audio channels together",
54     "Thomas <thomas@apestaart.org>");
55
56 static GstStaticPadTemplate gst_adder_src_template =
57     GST_STATIC_PAD_TEMPLATE ("src",
58     GST_PAD_SRC,
59     GST_PAD_ALWAYS,
60     GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
61         GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS)
62     );
63
64 static GstStaticPadTemplate gst_adder_sink_template =
65     GST_STATIC_PAD_TEMPLATE ("sink%d",
66     GST_PAD_SINK,
67     GST_PAD_REQUEST,
68     GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
69         GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS)
70     );
71
72 static void gst_adder_class_init (GstAdderClass * klass);
73 static void gst_adder_init (GstAdder * adder);
74 static void gst_adder_dispose (GObject * object);
75
76 static GstPad *gst_adder_request_new_pad (GstElement * element,
77     GstPadTemplate * temp, const gchar * unused);
78 static GstStateChangeReturn gst_adder_change_state (GstElement * element,
79     GstStateChange transition);
80
81 static gboolean gst_adder_setcaps (GstPad * pad, GstCaps * caps);
82
83 static GstFlowReturn gst_adder_collected (GstCollectPads * pads,
84     gpointer user_data);
85
86 static GstElementClass *parent_class = NULL;
87
88 GType
89 gst_adder_get_type (void)
90 {
91   static GType adder_type = 0;
92
93   if (!adder_type) {
94     static const GTypeInfo adder_info = {
95       sizeof (GstAdderClass), NULL, NULL,
96       (GClassInitFunc) gst_adder_class_init, NULL, NULL,
97       sizeof (GstAdder), 0,
98       (GInstanceInitFunc) gst_adder_init,
99     };
100
101     adder_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAdder",
102         &adder_info, 0);
103     GST_DEBUG_CATEGORY_INIT (gst_adder_debug, "adder", 0,
104         "audio channel mixing element");
105   }
106   return adder_type;
107 }
108
109 #define MAKE_FUNC(name,type,ttype,min,max)                      \
110 static void name (type *out, type *in, gint bytes) {            \
111   gint i;                                                       \
112   for (i = 0; i < bytes / sizeof (type); i++)                   \
113     out[i] = CLAMP ((ttype)out[i] + (ttype)in[i], min, max);    \
114 }
115
116 MAKE_FUNC (add_int32, gint32, gint64, MIN_INT_32, MAX_INT_32)
117     MAKE_FUNC (add_int16, gint16, gint32, MIN_INT_16, MAX_INT_16)
118     MAKE_FUNC (add_int8, gint8, gint16, MIN_INT_8, MAX_INT_8)
119     MAKE_FUNC (add_uint32, guint32, guint64, MIN_UINT_32, MAX_UINT_32)
120     MAKE_FUNC (add_uint16, guint16, guint32, MIN_UINT_16, MAX_UINT_16)
121     MAKE_FUNC (add_uint8, guint8, guint16, MIN_UINT_8, MAX_UINT_8)
122     MAKE_FUNC (add_float64, gdouble, gdouble, -1.0, 1.0)
123     MAKE_FUNC (add_float32, gfloat, gfloat, -1.0, 1.0)
124
125      static gboolean gst_adder_setcaps (GstPad * pad, GstCaps * caps)
126 {
127   GstAdder *adder;
128   GList *pads;
129   GstStructure *structure;
130   const char *media_type;
131
132   adder = GST_ADDER (GST_PAD_PARENT (pad));
133
134   /* see if the other pads can accept the format */
135   GST_OBJECT_LOCK (adder);
136   pads = GST_ELEMENT (adder)->pads;
137   while (pads) {
138     GstPad *otherpad = GST_PAD (pads->data);
139
140     if (otherpad != pad) {
141       gst_caps_replace (&GST_PAD_CAPS (otherpad), caps);
142     }
143     pads = g_list_next (pads);
144   }
145   GST_OBJECT_UNLOCK (adder);
146
147   /* parse caps now */
148   structure = gst_caps_get_structure (caps, 0);
149   media_type = gst_structure_get_name (structure);
150   if (strcmp (media_type, "audio/x-raw-int") == 0) {
151     GST_DEBUG_OBJECT (adder, "parse_caps sets adder to format int");
152     adder->format = GST_ADDER_FORMAT_INT;
153     gst_structure_get_int (structure, "width", &adder->width);
154     gst_structure_get_int (structure, "depth", &adder->depth);
155     gst_structure_get_int (structure, "endianness", &adder->endianness);
156     gst_structure_get_boolean (structure, "signed", &adder->is_signed);
157
158     if (adder->endianness != G_BYTE_ORDER)
159       goto not_supported;
160
161     switch (adder->width) {
162       case 8:
163         adder->func = (adder->is_signed ?
164             (GstAdderFunction) add_int8 : (GstAdderFunction) add_uint8);
165         break;
166       case 16:
167         adder->func = (adder->is_signed ?
168             (GstAdderFunction) add_int16 : (GstAdderFunction) add_uint16);
169         break;
170       case 32:
171         adder->func = (adder->is_signed ?
172             (GstAdderFunction) add_int32 : (GstAdderFunction) add_uint32);
173         break;
174       default:
175         goto not_supported;
176     }
177   } else if (strcmp (media_type, "audio/x-raw-float") == 0) {
178     GST_DEBUG_OBJECT (adder, "parse_caps sets adder to format float");
179     adder->format = GST_ADDER_FORMAT_FLOAT;
180     gst_structure_get_int (structure, "width", &adder->width);
181
182     switch (adder->width) {
183       case 32:
184         adder->func = (GstAdderFunction) add_float32;
185         break;
186       case 64:
187         adder->func = (GstAdderFunction) add_float64;
188         break;
189       default:
190         goto not_supported;
191     }
192   } else {
193     goto not_supported;
194   }
195
196   gst_structure_get_int (structure, "channels", &adder->channels);
197   gst_structure_get_int (structure, "rate", &adder->rate);
198
199   return TRUE;
200
201 not_supported:
202   {
203     return FALSE;
204   }
205 }
206
207 static gboolean
208 gst_adder_query (GstPad * pad, GstQuery * query)
209 {
210   GstAdder *adder = GST_ADDER (gst_pad_get_parent (pad));
211   gboolean res = FALSE;
212
213   switch (GST_QUERY_TYPE (query)) {
214     case GST_QUERY_POSITION:
215     {
216       GstFormat format;
217
218       gst_query_parse_position (query, &format, NULL);
219
220       switch (format) {
221         case GST_FORMAT_TIME:
222           gst_query_set_position (query, GST_FORMAT_TIME, adder->timestamp);
223           res = TRUE;
224           break;
225         case GST_FORMAT_DEFAULT:
226           gst_query_set_position (query, GST_FORMAT_DEFAULT, adder->offset);
227           res = TRUE;
228           break;
229         default:
230           break;
231       }
232       break;
233     }
234       /* FIXME: what to do about the length? query all pads upstream and
235        * pick the longest length? or the shortest length? or what? */
236     case GST_QUERY_DURATION:
237       break;
238     default:
239       break;
240   }
241
242   gst_object_unref (adder);
243   return res;
244 }
245
246 static void
247 gst_adder_class_init (GstAdderClass * klass)
248 {
249   GObjectClass *gobject_class;
250   GstElementClass *gstelement_class;
251
252   gobject_class = (GObjectClass *) klass;
253
254   gobject_class->dispose = gst_adder_dispose;
255
256   gstelement_class = (GstElementClass *) klass;
257
258   gst_element_class_add_pad_template (gstelement_class,
259       gst_static_pad_template_get (&gst_adder_src_template));
260   gst_element_class_add_pad_template (gstelement_class,
261       gst_static_pad_template_get (&gst_adder_sink_template));
262   gst_element_class_set_details (gstelement_class, &adder_details);
263
264   parent_class = g_type_class_peek_parent (klass);
265
266   gstelement_class->request_new_pad = gst_adder_request_new_pad;
267   gstelement_class->change_state = gst_adder_change_state;
268 }
269
270 static void
271 gst_adder_init (GstAdder * adder)
272 {
273   adder->srcpad =
274       gst_pad_new_from_template (gst_static_pad_template_get
275       (&gst_adder_src_template), "src");
276   gst_pad_set_getcaps_function (adder->srcpad,
277       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
278   gst_pad_set_setcaps_function (adder->srcpad,
279       GST_DEBUG_FUNCPTR (gst_adder_setcaps));
280   gst_pad_set_query_function (adder->srcpad,
281       GST_DEBUG_FUNCPTR (gst_adder_query));
282   gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad);
283
284   adder->format = GST_ADDER_FORMAT_UNSET;
285   adder->numpads = 0;
286   adder->func = NULL;
287
288   /* keep track of the sinkpads requested */
289   adder->collect = gst_collect_pads_new ();
290   gst_collect_pads_set_function (adder->collect, gst_adder_collected, adder);
291 }
292
293 static void
294 gst_adder_dispose (GObject * object)
295 {
296   GstAdder *adder = GST_ADDER (object);
297
298   gst_object_unref (adder->collect);
299   adder->collect = NULL;
300 }
301
302 static GstPad *
303 gst_adder_request_new_pad (GstElement * element, GstPadTemplate * templ,
304     const gchar * unused)
305 {
306   gchar *name;
307   GstAdder *adder;
308   GstPad *newpad;
309
310   g_return_val_if_fail (GST_IS_ADDER (element), NULL);
311
312   if (templ->direction != GST_PAD_SINK)
313     goto not_sink;
314
315   adder = GST_ADDER (element);
316
317   name = g_strdup_printf ("sink%d", adder->numpads);
318   newpad = gst_pad_new_from_template (templ, name);
319
320   gst_pad_set_getcaps_function (newpad,
321       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
322   gst_pad_set_setcaps_function (newpad, GST_DEBUG_FUNCPTR (gst_adder_setcaps));
323   gst_collect_pads_add_pad (adder->collect, newpad, sizeof (GstCollectData));
324   if (!gst_element_add_pad (GST_ELEMENT (adder), newpad))
325     goto could_not_add;
326
327   adder->numpads++;
328
329   return newpad;
330
331   /* errors */
332 not_sink:
333   {
334     g_warning ("gstadder: request new pad that is not a SINK pad\n");
335     return NULL;
336   }
337 could_not_add:
338   {
339     gst_collect_pads_remove_pad (adder->collect, newpad);
340     gst_object_unref (newpad);
341     return NULL;
342   }
343 }
344
345 static GstFlowReturn
346 gst_adder_collected (GstCollectPads * pads, gpointer user_data)
347 {
348   /*
349    * combine channels by adding sample values
350    * basic algorithm :
351    * - this function is called when all pads have a buffer
352    * - get available bytes on all pads.
353    * - repeat for each input pad :
354    *   - read available bytes, copy or add to target buffer
355    *   - if there's an EOS event, remove the input channel
356    * - push out the output buffer
357    */
358   GstAdder *adder;
359   guint size;
360   GSList *collected;
361   GstBuffer *outbuf;
362   GstFlowReturn ret;
363   gpointer outbytes;
364
365   adder = GST_ADDER (user_data);
366
367   /* get available bytes for reading */
368   size = gst_collect_pads_available (pads);
369   if (size == 0)
370     return GST_FLOW_OK;
371
372   outbuf = NULL;
373   outbytes = NULL;
374
375   if (adder->func == NULL)
376     goto not_negotiated;
377
378   GST_LOG_OBJECT (adder,
379       "starting to cycle through channels, collecting %d bytes", size);
380
381   for (collected = pads->data; collected; collected = g_slist_next (collected)) {
382     GstCollectData *data;
383     guint8 *bytes;
384     guint len;
385
386     data = (GstCollectData *) collected->data;
387
388     GST_LOG_OBJECT (adder, "looking into channel %p", data);
389
390     /* get pointer to copy size bytes */
391     len = gst_collect_pads_read (pads, data, &bytes, size);
392     if (len == 0)
393       continue;
394
395     GST_LOG_OBJECT (adder, " copying %d bytes (format %d,%d)",
396         len, adder->format, adder->width);
397     GST_LOG_OBJECT (adder, " from channel %p from input data %p", data, bytes);
398
399     if (outbuf == NULL) {
400       /* first buffer, alloc size bytes */
401       outbuf = gst_buffer_new_and_alloc (size);
402       gst_buffer_set_caps (outbuf, GST_PAD_CAPS (adder->srcpad));
403       outbytes = GST_BUFFER_DATA (outbuf);
404
405       memset (outbytes, 0, size);
406
407       /* and copy the data into it */
408       memcpy (outbytes, bytes, len);
409     } else {
410       /* other buffers, need to add them */
411       adder->func ((gpointer) outbytes, (gpointer) bytes, len);
412     }
413     gst_collect_pads_flush (pads, data, len);
414   }
415
416   /* set timestamps on the output buffer */
417   {
418     guint64 samples;
419     guint64 duration;
420
421     /* width is in bits and we need bytes */
422     samples = size / ((adder->width / 8) * adder->channels);
423     duration = samples * GST_SECOND / adder->rate;
424
425     GST_BUFFER_TIMESTAMP (outbuf) = adder->timestamp;
426     GST_BUFFER_OFFSET (outbuf) = adder->offset;
427
428     adder->offset += samples;
429     adder->timestamp = adder->offset * GST_SECOND / adder->rate;
430
431     GST_BUFFER_DURATION (outbuf) = adder->timestamp -
432         GST_BUFFER_TIMESTAMP (outbuf);
433   }
434
435   /* send it out */
436   GST_LOG_OBJECT (adder, "pushing outbuf");
437   ret = gst_pad_push (adder->srcpad, outbuf);
438
439   return ret;
440
441   /* ERRORS */
442 not_negotiated:
443   {
444     return GST_FLOW_NOT_NEGOTIATED;
445   }
446 }
447
448 static GstStateChangeReturn
449 gst_adder_change_state (GstElement * element, GstStateChange transition)
450 {
451   GstAdder *adder;
452   GstStateChangeReturn ret;
453
454   adder = GST_ADDER (element);
455
456   switch (transition) {
457     case GST_STATE_CHANGE_NULL_TO_READY:
458       break;
459     case GST_STATE_CHANGE_READY_TO_PAUSED:
460       adder->timestamp = 0;
461       adder->offset = 0;
462       gst_collect_pads_start (adder->collect);
463       break;
464     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
465       break;
466     case GST_STATE_CHANGE_PAUSED_TO_READY:
467       /* need to unblock the collectpads before calling the
468        * parent change_state so that streaming can finish */
469       gst_collect_pads_stop (adder->collect);
470       break;
471     default:
472       break;
473   }
474
475   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
476
477   switch (transition) {
478     default:
479       break;
480   }
481
482   return ret;
483 }
484
485
486 static gboolean
487 plugin_init (GstPlugin * plugin)
488 {
489   if (!gst_element_register (plugin, "adder", GST_RANK_NONE, GST_TYPE_ADDER)) {
490     return FALSE;
491   }
492
493   return TRUE;
494 }
495
496 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
497     GST_VERSION_MINOR,
498     "adder",
499     "Adds multiple streams",
500     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)