diff --git a/gst/adder/.gitignore b/gst/adder/.gitignore new file mode 100644 index 0000000000..08f5ed37d8 --- /dev/null +++ b/gst/adder/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs diff --git a/gst/adder/Makefile.am b/gst/adder/Makefile.am new file mode 100644 index 0000000000..d0aef16572 --- /dev/null +++ b/gst/adder/Makefile.am @@ -0,0 +1,8 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgstadder.la + +libgstadder_la_SOURCES = gstadder.c +libgstadder_la_CFLAGS = $(GST_CFLAGS) + +noinst_HEADERS = gstadder.h diff --git a/gst/adder/gstadder.c b/gst/adder/gstadder.c new file mode 100644 index 0000000000..dfdc264a42 --- /dev/null +++ b/gst/adder/gstadder.c @@ -0,0 +1,397 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * 2001 Thomas + * + * adder.c: Adder element, N in, one out, samples are added + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gstadder.h" + +//#define DEBUG + +GstElementDetails adder_details = { + "Adder", + "Filter/Effect", + "2-to-1 audio adder/mixer", + VERSION, + "Thomas ", + "(C) 2001", +}; + +/* Adder signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_NUM_PADS, + /* FILL ME */ +}; + +GST_PADTEMPLATE_FACTORY (adder_src_template_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "test_src", + "audio/raw", + "format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "signed", GST_PROPS_BOOLEAN (TRUE), + "width", GST_PROPS_INT_RANGE (8, 16), + "depth", GST_PROPS_INT_RANGE (8, 16), + "rate", GST_PROPS_INT_RANGE (4000, 48000), //FIXME + "channels", GST_PROPS_INT_RANGE (1, 2) + ) +); + +GST_PADTEMPLATE_FACTORY (adder_sink_template_factory, + "sink%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_CAPS_NEW ( + "test_sink", + "audio/raw", + "format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "signed", GST_PROPS_BOOLEAN (TRUE), + "width", GST_PROPS_INT_RANGE (8, 16), + "depth", GST_PROPS_INT_RANGE (8, 16), + "rate", GST_PROPS_INT_RANGE (4000, 48000), //FIXME + "channels", GST_PROPS_INT_RANGE (1, 2) + ) +); + +static void gst_adder_class_init (GstAdderClass *klass); +static void gst_adder_init (GstAdder *adder); + +static void gst_adder_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec); + +static GstPad* gst_adder_request_new_pad (GstElement *element, GstPadTemplate *temp, + const gchar *unused); + +/* we do need a loop function */ +static void gst_adder_loop (GstElement *element); + +static GstElementClass *parent_class = NULL; +//static guint gst_adder_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_adder_get_type(void) { + static GType adder_type = 0; + + if (!adder_type) { + static const GTypeInfo adder_info = { + sizeof(GstAdderClass), NULL, + NULL, + (GClassInitFunc)gst_adder_class_init, + NULL, + NULL, + sizeof(GstAdder), + 0, + (GInstanceInitFunc)gst_adder_init, + }; + adder_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAdder", &adder_info, 0); + } + return adder_type; +} + +static void +gst_adder_class_init (GstAdderClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_NUM_PADS, + g_param_spec_int("num_pads","num_pads","num_pads", + G_MININT,G_MAXINT,0,G_PARAM_READABLE)); // CHECKME + + gobject_class->get_property = gst_adder_get_property; + + gstelement_class->request_new_pad = gst_adder_request_new_pad; +} + +static void +gst_adder_init (GstAdder *adder) +{ + adder->srcpad = gst_pad_new ("src", GST_PAD_SRC); + gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad); + gst_element_set_loop_function (GST_ELEMENT (adder), gst_adder_loop); + + /* keep track of the sinkpads requested */ + + adder->numsinkpads = 0; + adder->input_channels = NULL; +} + +static GstPad* +gst_adder_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *unused) +{ + gchar *name; + adder_input_channel_t *input_channel; + GstAdder *adder; + + g_return_val_if_fail (GST_IS_ADDER (element), NULL); + + if (templ->direction != GST_PAD_SINK) { + g_warning ("gstadder: request new pad that is not a SINK pad\n"); + return NULL; + } + + adder = GST_ADDER (element); + + /* allocate space for the input_channel */ + + input_channel = (adder_input_channel_t *) g_malloc (sizeof (adder_input_channel_t)); + if (input_channel == NULL) + printf ("Could not allocate memory for adder input channel !\n"); + + /* fill in input_channel structure */ + + name = g_strdup_printf ("sink%d",adder->numsinkpads); + input_channel->sinkpad = gst_pad_new_from_template (templ, name); + gst_element_add_pad (GST_ELEMENT (adder), input_channel->sinkpad); + input_channel->bytes_waiting = 0; + input_channel->p_input_data = NULL; + input_channel->input_buffer = NULL; + + /* add the input_channel to the list of input channels */ + + adder->input_channels = g_slist_append (adder->input_channels, input_channel); + adder->numsinkpads++; + + return input_channel->sinkpad; +} + +static void +gst_adder_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstAdder *adder; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_ADDER (object)); + + adder = GST_ADDER (object); + + switch (prop_id) { + case ARG_NUM_PADS: + g_value_set_int (value, adder->numsinkpads); + break; + default: + break; + } +} + + +/* use this loop */ +static void +gst_adder_loop (GstElement *element) +{ + /* + * combine channels by adding sample values + * basic algorithm : + * - each input pipe has a bytes_waiting to it + * - repeat for each input pipe : + * - if bytes_waiting is zero, request a new buffer + * - check which input pipe has the smalles bytes_waiting + * - allocate an output buffer of that size + * - repeat for each input pipe : + * - get this much data from each of the input channels + * - clear each buffer that has been depleted completely and set it's value + * to 0 + * - add this to the output buffer + * - push out the output buffer + */ + + GstAdder *adder; + + GSList *p_input_channel_GSL; + adder_input_channel_t *p_input_channel; + + GstBuffer *buf_in, *buf_out; + GstPad *sinkpad; + + guint i; + + guint pad_number, smallest_pad_size; + + gint16 *data_out, *data_in; + + g_return_if_fail (element != NULL); + g_return_if_fail (GST_IS_ADDER (element)); + + adder = GST_ADDER (element); + + do + { + +#ifdef DEBUG + printf ("DEBUG : gst_adder_loop iteration\n"); +#endif + + /* first, request all buffers that have a zero bytes_waiting value */ + + +#ifdef DEBUG + printf ("\tDEBUG : gst_adder_loop : getting needed input buffers\n"); +#endif + + pad_number = 0; + p_input_channel_GSL = adder->input_channels; + smallest_pad_size = 0; + + while (p_input_channel_GSL) + { + guint16 bw; + + p_input_channel = (adder_input_channel_t *) p_input_channel_GSL->data; + + ++pad_number; + bw = p_input_channel->bytes_waiting; + sinkpad = p_input_channel->sinkpad; + +#ifdef DEBUG + printf ("\tDEBUG : gst_adder_loop : input channel %d has %d bytes left\n", + pad_number, bw); +#endif + if (bw == 0) + { + /* QUESTION : maybe free previous buffer ? where do we do this ? */ + + /* no more data left; get more */ + buf_in = gst_pad_pull (sinkpad); + if (buf_in == NULL) + printf ("ERROR : could not get input buffer !\n"); + bw = GST_BUFFER_SIZE (buf_in); + p_input_channel->bytes_waiting = bw; + p_input_channel->input_buffer = buf_in; + p_input_channel->p_input_data = (guint16 *) GST_BUFFER_DATA (buf_in); + +#ifdef DEBUG + printf ("\tDEBUG : gst_adder_loop : input channel %d got %d new bytes\n", + pad_number, bw); +#endif + } + /* update smallest pad size */ + if (smallest_pad_size == 0) + { + /* not set yet, set it ! */ + smallest_pad_size = bw; + } + if (bw < smallest_pad_size) smallest_pad_size = bw; + + p_input_channel_GSL = g_slist_next (p_input_channel_GSL); + } + +#ifdef DEBUG + printf ("\tDEBUG : gst_adder_loop : smallest pad size %d\n", + smallest_pad_size); +#endif + + /* get new output buffer */ + + buf_out = gst_buffer_new (); + if (buf_out == NULL) + printf ("ERROR : could not get new output buffer !\n"); + + GST_BUFFER_SIZE (buf_out) = smallest_pad_size; + GST_BUFFER_DATA (buf_out) = g_malloc0 (smallest_pad_size); + + data_out = (guint16 *) GST_BUFFER_DATA (buf_out); + if (data_out == NULL) + printf ("ERROR : could not allocate output buffer !\n"); + + /* get data from all of the sinks */ + + pad_number = 0; + p_input_channel_GSL = adder->input_channels; + + while (p_input_channel_GSL) + { + ++pad_number; + p_input_channel = (adder_input_channel_t *) p_input_channel_GSL->data; + data_out = (gint16 *) GST_BUFFER_DATA (buf_out); + + data_in = p_input_channel->p_input_data; + + /* add to the output buffer */ + for (i = 0; i < smallest_pad_size / 2; ++i, ++data_out, ++data_in) + { + *data_out += *data_in; + } + /* adjust bytes_waiting and data pointer */ + p_input_channel->bytes_waiting -= smallest_pad_size; + if (p_input_channel->bytes_waiting == 0) + { +#ifdef DEBUG + printf ("\tDEBUG : gst_adder_loop : channel %d is empty, unref...\n", + pad_number); +#endif + gst_buffer_unref (p_input_channel->input_buffer); + p_input_channel->p_input_data = NULL; + } + p_input_channel->p_input_data = data_in; + + p_input_channel_GSL = g_slist_next (p_input_channel_GSL); + } + + /* send it out */ + GST_DEBUG (0, "pushing buf_out\n"); + gst_pad_push (adder->srcpad, buf_out); + GST_DEBUG (0, "pushed buf_out\n"); + +/* thomas : quick fix try */ +// GST_FLAG_SET (element, GST_ELEMENT_COTHREAD_STOPPING); + } while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element)); +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + factory = gst_elementfactory_new("adder",GST_TYPE_ADDER, + &adder_details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (adder_src_template_factory)); + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (adder_sink_template_factory)); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "adder", + plugin_init +}; + diff --git a/gst/adder/gstadder.h b/gst/adder/gstadder.h new file mode 100644 index 0000000000..ecd55c14a3 --- /dev/null +++ b/gst/adder/gstadder.h @@ -0,0 +1,81 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * + * gstadder.h: Header for GstAdder element + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_ADDER_H__ +#define __GST_ADDER_H__ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern GstElementDetails gst_adder_details; + +#define GST_TYPE_ADDER \ + (gst_adder_get_type()) +#define GST_ADDER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADDER,GstAdder)) +#define GST_ADDER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ADDER,GstAdderClass)) +#define GST_IS_ADDER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ADDER)) +#define GST_IS_ADDER_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ADDER)) + +typedef struct _GstAdder GstAdder; +typedef struct _GstAdderClass GstAdderClass; + +typedef struct { + GstPad *sinkpad; + + GstBuffer *input_buffer; /* input buffer */ + gint16 *p_input_data; /* pointer to current position in input buffer */ + guint16 bytes_waiting; /* how many bytes are still left in input buffer ? */ +} adder_input_channel_t; + +struct _GstAdder { + GstElement element; + + GstPad *srcpad; + + /* keep track of the sinkpads */ + gint numsinkpads; + GSList *input_channels; +}; + +struct _GstAdderClass { + GstElementClass parent_class; +}; + +GType gst_adder_get_type (void); + +gboolean gst_adder_factory_init (GstElementFactory *factory); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_ADDER_H__ */