GstPadTemplate <-> gst_pad_template <-> GST_PAD_TEMPLATE same with *factory and typefind.
[platform/upstream/gstreamer.git] / gst / adder / gstadder.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wim.taymans@chello.be>
4  *                    2001 Thomas <thomas@apestaart.org>
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 #include "gstadder.h"
25 #include <string.h>             /* strcmp */
26
27 #define GST_ADDER_BUFFER_SIZE 4096
28 #define GST_ADDER_NUM_BUFFERS 8
29
30 GstElementDetails adder_details = {
31   "Adder",
32   "Filter/Effect",
33   "2-to-1 audio adder/mixer",
34   VERSION,
35   "Thomas <thomas@apestaart.org>",
36   "(C) 2001",
37 };
38
39 /* Adder signals and args */
40 enum {
41   /* FILL ME */
42   LAST_SIGNAL
43 };
44
45 enum {
46   ARG_0,
47   ARG_NUM_PADS,
48   /* FILL ME */
49 };
50
51 GST_PAD_TEMPLATE_FACTORY (gst_adder_src_template_factory,
52   "src",
53   GST_PAD_SRC,
54   GST_PAD_ALWAYS,
55   GST_CAPS_NEW (
56     "int_src",
57     "audio/raw",
58       "format",             GST_PROPS_STRING ("int"),
59         "law",              GST_PROPS_INT (0),
60         "endianness",       GST_PROPS_INT (G_BYTE_ORDER),
61         "signed",           GST_PROPS_BOOLEAN (TRUE),
62         "width",            GST_PROPS_LIST (GST_PROPS_INT (8), GST_PROPS_INT (16)),
63         "depth",            GST_PROPS_LIST (GST_PROPS_INT (8), GST_PROPS_INT (16)),
64         "rate",             GST_PROPS_INT_RANGE (4000, 48000), /* FIXME */
65         "channels",         GST_PROPS_INT_RANGE (1, 2)
66   ),
67   GST_CAPS_NEW (
68     "float_src",
69     "audio/raw",
70       "format",             GST_PROPS_STRING("float"),
71         "layout",           GST_PROPS_STRING ("gfloat"),
72         "intercept",        GST_PROPS_FLOAT (0.0),
73         "slope",            GST_PROPS_FLOAT (1.0),
74         "rate",             GST_PROPS_INT_RANGE (4000, 96000),
75         "channels",         GST_PROPS_INT_RANGE (1, 2)
76   )
77 );  
78
79 GST_PAD_TEMPLATE_FACTORY (gst_adder_sink_template_factory,
80   "sink%d",
81   GST_PAD_SINK,
82   GST_PAD_REQUEST,
83   GST_CAPS_NEW (
84     "int_sink",
85     "audio/raw",
86       "format",             GST_PROPS_STRING ("int"),
87         "law",              GST_PROPS_INT (0),
88         "endianness",       GST_PROPS_INT (G_BYTE_ORDER),
89         "signed",           GST_PROPS_BOOLEAN (TRUE),
90         "width",            GST_PROPS_LIST (GST_PROPS_INT (8), GST_PROPS_INT (16)),
91         "depth",            GST_PROPS_LIST (GST_PROPS_INT (8), GST_PROPS_INT (16)),
92         "rate",             GST_PROPS_INT_RANGE (4000, 48000), /* FIXME */
93         "channels",         GST_PROPS_INT_RANGE (1, 2)
94   ),
95   GST_CAPS_NEW (
96     "float_sink",
97     "audio/raw",
98       "format",             GST_PROPS_STRING("float"),
99         "layout",           GST_PROPS_STRING ("gfloat"),
100         "intercept",        GST_PROPS_FLOAT (0.0),
101         "slope",            GST_PROPS_FLOAT (1.0),
102         "rate",             GST_PROPS_INT_RANGE (4000, 96000),
103         "channels",         GST_PROPS_INT_RANGE (1, 2)
104   )
105 );  
106
107 static void             gst_adder_class_init            (GstAdderClass *klass);
108 static void             gst_adder_init                  (GstAdder *adder);
109
110 static void             gst_adder_get_property          (GObject *object, guint prop_id, 
111                                                          GValue *value, GParamSpec *pspec);
112
113 static GstPad*          gst_adder_request_new_pad       (GstElement *element, GstPadTemplate *temp,
114                                                          const gchar *unused);
115
116 /* we do need a loop function */
117 static void             gst_adder_loop                  (GstElement *element);
118
119 static GstElementClass *parent_class = NULL;
120 /* static guint gst_adder_signals[LAST_SIGNAL] = { 0 }; */
121
122 GType
123 gst_adder_get_type(void) {
124   static GType adder_type = 0;
125
126   if (!adder_type) {
127     static const GTypeInfo adder_info = {
128       sizeof(GstAdderClass),      NULL,
129       NULL,
130       (GClassInitFunc)gst_adder_class_init,
131       NULL,
132       NULL,
133       sizeof(GstAdder),
134       0,
135       (GInstanceInitFunc)gst_adder_init,
136     };
137     adder_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAdder", &adder_info, 0);
138   }
139   return adder_type;
140 }
141
142 static gboolean
143 gst_adder_parse_caps (GstAdder *adder, GstCaps *caps)
144 {
145   const gchar *format;
146   
147   gst_caps_get_string (caps, "format", &format);
148
149   if (adder->format == GST_ADDER_FORMAT_UNSET) {
150     /* the caps haven't been set yet at all, so we need to go ahead and set all
151        the relevant values. */
152     if (strcmp (format, "int") == 0) {
153       adder->format     = GST_ADDER_FORMAT_INT;
154       gst_caps_get_int     (caps, "width",      &adder->width);
155       gst_caps_get_int     (caps, "depth",      &adder->depth);
156       gst_caps_get_int     (caps, "law",        &adder->law);
157       gst_caps_get_int     (caps, "endianness", &adder->endianness);
158       gst_caps_get_boolean (caps, "signed",     &adder->is_signed);
159       gst_caps_get_int     (caps, "channels",   &adder->channels);
160     } else if (strcmp (format, "float") == 0) {
161       adder->format     = GST_ADDER_FORMAT_FLOAT;
162       gst_caps_get_string  (caps, "layout",    &adder->layout);
163       gst_caps_get_float   (caps, "intercept", &adder->intercept);
164       gst_caps_get_float   (caps, "slope",     &adder->slope);
165       gst_caps_get_int     (caps, "channels",  &adder->channels);
166     }
167   } else {
168     /* otherwise, a previously-connected pad has set all the values. we should
169        barf if some of the attempted new values don't match. */
170     if (strcmp (format, "int") == 0) {
171       gint width, channels;
172       gboolean is_signed;
173
174       gst_caps_get_int     (caps, "width",     &width);
175       gst_caps_get_int     (caps, "channels",  &channels);
176       gst_caps_get_boolean (caps, "signed",    &is_signed);
177
178       if ((adder->format != GST_ADDER_FORMAT_INT) ||
179           (adder->width  != width) ||
180           (adder->channels != channels) ||
181           (adder->is_signed != is_signed)) {
182         return FALSE;
183       }
184     } else if (strcmp (format, "float") == 0) {
185       gint channels;
186
187       gst_caps_get_int     (caps, "channels",  &channels);
188
189       if ((adder->format != GST_ADDER_FORMAT_FLOAT) ||
190           (adder->channels != channels)) {
191         return FALSE;
192       }
193     } else {
194       /* whoa, we don't know what's trying to connect with us ! barf ! */
195       return FALSE;
196     }
197   }
198   return TRUE;
199 }
200
201 static GstPadConnectReturn
202 gst_adder_connect (GstPad *pad, GstCaps *caps)
203 {
204   GstAdder *adder;
205   GList *sinkpads, *remove = NULL;
206   GSList *channels;
207   GstPad *p;
208   
209   g_return_val_if_fail (caps != NULL, GST_PAD_CONNECT_REFUSED);
210   g_return_val_if_fail (pad  != NULL, GST_PAD_CONNECT_REFUSED);
211
212   adder = GST_ADDER (GST_PAD_PARENT (pad));
213
214   if (GST_CAPS_IS_FIXED (caps)) {
215     if (!gst_adder_parse_caps (adder, caps))
216       return GST_PAD_CONNECT_REFUSED;
217   
218     if (pad == adder->srcpad || gst_pad_try_set_caps (adder->srcpad, caps)) {
219       sinkpads = gst_element_get_pad_list ((GstElement*) adder);
220       while (sinkpads) {
221         p = (GstPad*) sinkpads->data;
222         if (p != pad && p != adder->srcpad) {
223           if (!gst_pad_try_set_caps (p, caps)) {
224             GST_DEBUG (0, "caps mismatch; disconnecting and removing pad %s:%s (peer %s:%s)",
225                        GST_DEBUG_PAD_NAME (p), GST_DEBUG_PAD_NAME (GST_PAD_PEER (p)));
226             gst_pad_disconnect (GST_PAD (GST_PAD_PEER (p)), p);
227             remove = g_list_prepend (remove, p);
228           }
229         }
230         sinkpads = g_list_next (sinkpads);
231       }
232       while (remove) {
233         gst_element_remove_pad (GST_ELEMENT (adder), GST_PAD_CAST (remove->data));
234       restart:
235         channels = adder->input_channels;
236         while (channels) {
237           GstAdderInputChannel *channel = (GstAdderInputChannel*) channels->data;
238           if (channel->sinkpad == GST_PAD_CAST (remove->data)) {
239             gst_bytestream_destroy (channel->bytestream);
240             adder->input_channels = g_slist_remove_link (adder->input_channels, channels);
241             adder->numsinkpads--;
242             goto restart;
243           }
244           channels = g_slist_next (channels);
245         }
246         remove = g_list_next (remove);
247       }
248       return GST_PAD_CONNECT_OK;
249     } else {
250       return GST_PAD_CONNECT_REFUSED;
251     }
252   } else {
253     return GST_PAD_CONNECT_DELAYED;
254   }
255 }
256
257 static void
258 gst_adder_class_init (GstAdderClass *klass) 
259 {
260   GObjectClass *gobject_class;
261   GstElementClass *gstelement_class;
262
263   gobject_class = (GObjectClass*)klass;
264   gstelement_class = (GstElementClass*)klass;
265
266   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
267
268   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_NUM_PADS,
269     g_param_spec_int("num_pads","num_pads","num_pads",
270                      G_MININT, G_MAXINT, 0, G_PARAM_READABLE));
271
272   gobject_class->get_property = gst_adder_get_property;
273
274   gstelement_class->request_new_pad = gst_adder_request_new_pad;
275 }
276
277 static void 
278 gst_adder_init (GstAdder *adder) 
279 {
280   adder->srcpad = gst_pad_new_from_template (gst_adder_src_template_factory (), "src");
281   gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad);
282   gst_element_set_loop_function (GST_ELEMENT (adder), gst_adder_loop);
283   gst_pad_set_connect_function (adder->srcpad, gst_adder_connect);
284
285   adder->format = GST_ADDER_FORMAT_UNSET;
286
287   /* keep track of the sinkpads requested */
288  
289   adder->numsinkpads = 0;
290   adder->input_channels = NULL;
291 }
292
293 static GstPad*
294 gst_adder_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *unused) 
295 {
296   gchar                *name;
297   GstAdder             *adder;
298   GstAdderInputChannel *input;
299
300   g_return_val_if_fail (GST_IS_ADDER (element), NULL);
301
302   if (templ->direction != GST_PAD_SINK) {
303     g_warning ("gstadder: request new pad that is not a SINK pad\n");
304     return NULL;
305   }
306
307   /* allocate space for the input_channel */
308
309   input = (GstAdderInputChannel *) g_malloc (sizeof (GstAdderInputChannel));
310   if (input == NULL) {
311     g_warning ("gstadder: could not allocate memory for adder input channel !\n");
312     return NULL;
313   }
314   
315   adder = GST_ADDER (element);
316
317   /* fill in input_channel structure */
318
319   name = g_strdup_printf ("sink%d", adder->numsinkpads);
320   input->sinkpad = gst_pad_new_from_template (templ, name);
321   input->bytestream = gst_bytestream_new (input->sinkpad);
322
323   gst_element_add_pad (GST_ELEMENT (adder), input->sinkpad);
324   gst_pad_set_connect_function(input->sinkpad, gst_adder_connect);
325
326   /* add the input_channel to the list of input channels */
327   
328   adder->input_channels = g_slist_append (adder->input_channels, input);
329   adder->numsinkpads++;
330   
331   return input->sinkpad;
332 }
333
334 static void
335 gst_adder_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
336 {
337   GstAdder *adder;
338
339   /* it's not null if we got it, but it might not be ours */
340   g_return_if_fail (GST_IS_ADDER (object));
341
342   adder = GST_ADDER (object);
343
344   switch (prop_id) {
345     case ARG_NUM_PADS:
346       g_value_set_int (value, adder->numsinkpads);
347       break;
348     default:
349     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
350       break;
351   }
352 }
353
354 /* use this loop */
355 static void
356 gst_adder_loop (GstElement *element)
357 {
358   /*
359    * combine channels by adding sample values
360    * basic algorithm :
361    * - request an output buffer from the pool
362    * - repeat for each input pipe :
363    *   - get number of bytes from the channel's bytestream to fill output buffer
364    *   - if there's an EOS event, remove the input channel
365    *   - otherwise add the gotten bytes to the output buffer
366    * - push out the output buffer
367    */
368     
369   GstAdder  *adder;
370   GstBuffer *buf_out;
371   GstEvent  *event = NULL;
372
373   GSList               *inputs;
374   GstAdderInputChannel *input;
375
376   gint8     *raw_in, *zero_out;
377   guint32    waiting;
378   register guint i;
379
380   g_return_if_fail (element != NULL);
381   g_return_if_fail (GST_IS_ADDER (element));
382   
383   adder = GST_ADDER (element);
384   adder->bufpool = gst_pad_get_bufferpool (adder->srcpad);
385   if (adder->bufpool == NULL) {
386     adder->bufpool = gst_buffer_pool_get_default(GST_ADDER_BUFFER_SIZE, GST_ADDER_NUM_BUFFERS);
387   }
388   
389   do {
390     /* get new output buffer */
391     buf_out = gst_buffer_new_from_pool (adder->bufpool, 0, 0);
392   
393     if (buf_out == NULL)
394       GST_ERROR (0, "could not get new output buffer !\n");
395
396     /* initialize the output data to 0 */
397     zero_out = (gint8 *) GST_BUFFER_DATA (buf_out);      
398     for (i = 0; i < GST_BUFFER_SIZE (buf_out); i++)
399       zero_out[i] = 0;
400
401     /* get data from all of the sinks */
402     inputs = adder->input_channels;
403
404     GST_DEBUG (0, "starting to cycle through channels");
405
406     while (inputs) {
407       input = (GstAdderInputChannel *) inputs->data;
408
409       GST_DEBUG (0, "looking into channel %p", input);
410       
411       /* get data from the bytestream of each input channel. we need to check for
412          events before passing on the data to the output buffer. */
413       raw_in = gst_bytestream_peek_bytes (input->bytestream, GST_BUFFER_SIZE (buf_out));
414
415       if (raw_in == NULL) {
416         /* we need to check for an event. */
417         gst_bytestream_get_status (input->bytestream, &waiting, &event);
418
419         if (event) {
420           if (GST_EVENT_TYPE(event) == GST_EVENT_EOS) {
421             /* if we get an EOS event from one of our sink pads, we assume that
422                pad's finished handling data. delete the bytestream, free up the
423                pad, and free up the memory associated with the input channel. */
424             GST_DEBUG (0, "got an EOS event");
425
426             gst_bytestream_destroy (input->bytestream);
427             /* gst_object_unref (GST_OBJECT (input->sinkpad)); this causes problems */
428             g_free (input);
429
430             adder->input_channels = g_slist_delete_link (inputs, inputs);
431             inputs = adder->input_channels;
432
433             break;
434           }
435         }
436       } else {
437         /* here's where the data gets copied. this is a little nasty looking
438            because it's the same code pretty much 3 times, except each time uses
439            different data types and clamp limits. */
440         GST_DEBUG (0, "copying %d bytes from channel %p to output data %p in buffer %p",
441                    GST_BUFFER_SIZE (buf_out), input, GST_BUFFER_DATA (buf_out), buf_out);
442
443         if (adder->format == GST_ADDER_FORMAT_INT) {
444           if (adder->width == 16) {
445             gint16 *in  = (gint16 *) raw_in;
446             gint16 *out = (gint16 *) GST_BUFFER_DATA (buf_out);      
447             for (i = 0; i < GST_BUFFER_SIZE (buf_out) / 2; i++)
448               out[i] = CLAMP(out[i] + in[i], -32768, 32767);
449           } else if (adder->width == 8) {
450             gint8 *in  = (gint8 *) raw_in;
451             gint8 *out = (gint8 *) GST_BUFFER_DATA (buf_out);      
452             for (i = 0; i < GST_BUFFER_SIZE (buf_out); i++)
453               out[i] = CLAMP(out[i] + in[i], -128, 127);
454           } else {
455             GST_ERROR (0, "invalid width (%d) for int format in gstadder\n", adder->width);
456           }
457         } else if (adder->format == GST_ADDER_FORMAT_FLOAT) {
458           gfloat *in  = (gfloat *) raw_in;
459           gfloat *out = (gfloat *) GST_BUFFER_DATA (buf_out);
460           for (i = 0; i < GST_BUFFER_SIZE (buf_out) / sizeof (gfloat); i++)
461             out[i] += in[i];
462         } else {
463           GST_ERROR (0, "invalid audio format (%d) in gstadder\n", adder->format);
464         }
465
466         gst_bytestream_flush (input->bytestream, GST_BUFFER_SIZE (buf_out));
467
468         GST_DEBUG (0, "done copying data");
469       }
470       
471       inputs = g_slist_next (inputs);
472     }
473
474     /* send it out */
475
476     GST_DEBUG (0, "pushing buf_out");
477     gst_pad_push (adder->srcpad, buf_out);
478
479   } while (TRUE);
480 }
481
482 static gboolean
483 plugin_init (GModule *module, GstPlugin *plugin)
484 {
485   GstElementFactory *factory;
486
487   factory = gst_element_factory_new("adder",GST_TYPE_ADDER,
488                                    &adder_details);
489   g_return_val_if_fail(factory != NULL, FALSE);
490   
491   if (! gst_library_load ("gstbytestream")) {
492     gst_info ("gstadder: could not load support library: 'gstbytestream'\n");
493     return FALSE;
494   }
495     
496   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (gst_adder_src_template_factory));
497   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (gst_adder_sink_template_factory));
498       
499   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
500     
501   return TRUE;
502 }
503
504 GstPluginDesc plugin_desc = {
505   GST_VERSION_MAJOR,
506   GST_VERSION_MINOR,
507   "adder",
508   plugin_init
509 };
510