diff options
Diffstat (limited to 'gst')
-rw-r--r-- | gst/interleave/Makefile.am | 9 | ||||
-rw-r--r-- | gst/interleave/deinterleave.c | 337 | ||||
-rw-r--r-- | gst/interleave/interleave.c | 609 | ||||
-rw-r--r-- | gst/interleave/plugin.c | 44 | ||||
-rw-r--r-- | gst/interleave/plugin.h | 36 |
5 files changed, 1035 insertions, 0 deletions
diff --git a/gst/interleave/Makefile.am b/gst/interleave/Makefile.am new file mode 100644 index 00000000..9a09cdbc --- /dev/null +++ b/gst/interleave/Makefile.am @@ -0,0 +1,9 @@ + +plugin_LTLIBRARIES = libgstinterleave.la + +libgstinterleave_la_SOURCES = plugin.c interleave.c deinterleave.c +libgstinterleave_la_CFLAGS = $(GST_CFLAGS) +libgstinterleave_la_LIBADD = $(GST_LIBS) +libgstinterleave_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = plugin.h diff --git a/gst/interleave/deinterleave.c b/gst/interleave/deinterleave.c new file mode 100644 index 00000000..09f19e15 --- /dev/null +++ b/gst/interleave/deinterleave.c @@ -0,0 +1,337 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> + * 2000 Wim Taymans <wtay@chello.be> + * 2005 Wim Taymans <wim@fluendo.com> + * 2007 Andy Wingo <wingo at pobox.com> + * + * deinterleave.c: deinterleave samples, based on interleave.c + * + * 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. + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/gst.h> + + +#define GST_TYPE_DEINTERLEAVE (gst_deinterleave_get_type()) +#define GST_DEINTERLEAVE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DEINTERLEAVE,GstDeinterleave)) +#define GST_DEINTERLEAVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DEINTERLEAVE,GstDeinterleaveClass)) +#define GST_DEINTERLEAVE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_DEINTERLEAVE,GstDeinterleaveClass)) +#define GST_IS_DEINTERLEAVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DEINTERLEAVE)) +#define GST_IS_DEINTERLEAVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DEINTERLEAVE)) + +typedef struct _GstDeinterleave GstDeinterleave; +typedef struct _GstDeinterleaveClass GstDeinterleaveClass; + + +struct _GstDeinterleave +{ + GstElement element; + + GstCaps *sinkcaps; + gint channels; + + GstPad *sink; +}; + +struct _GstDeinterleaveClass +{ + GstElementClass parent_class; +}; + + +GST_DEBUG_CATEGORY_STATIC (gst_deinterleave_debug); +#define GST_CAT_DEFAULT gst_deinterleave_debug + + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src%d", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS ("audio/x-raw-float, " + "rate = (int) [ 1, MAX ], " + "channels = (int) 1, " + "endianness = (int) BYTE_ORDER, " "width = (int) 32") + ); +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-float, " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, MAX ], " + "endianness = (int) BYTE_ORDER, " "width = (int) 32") + ); + + +GST_BOILERPLATE (GstDeinterleave, gst_deinterleave, GstElement, + GST_TYPE_ELEMENT); + + +static GstFlowReturn gst_deinterleave_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_deinterleave_sink_setcaps (GstPad * pad, GstCaps * caps); +static gboolean gst_deinterleave_sink_activate_push (GstPad * pad, + gboolean active); + + +static const GstElementDetails details = +GST_ELEMENT_DETAILS ("Audio deinterleaver", + "Filter/Converter/Audio", + "Splits one interleaved multichannel audio stream into many mono audio streams", + "Andy Wingo <wingo at pobox.com>, " "Iain <iain@prettypeople.org>"); + +static void +gst_deinterleave_base_init (gpointer g_class) +{ + GST_DEBUG_CATEGORY_INIT (gst_deinterleave_debug, "interleave", 0, + "interleave element"); + + gst_element_class_set_details (g_class, &details); + + gst_element_class_add_pad_template (g_class, + gst_static_pad_template_get (&sink_template)); + gst_element_class_add_pad_template (g_class, + gst_static_pad_template_get (&src_template)); +} + +static void +gst_deinterleave_class_init (GstDeinterleaveClass * klass) +{ + /* pass */ +} + +static void +gst_deinterleave_init (GstDeinterleave * self, GstDeinterleaveClass * klass) +{ + self->sink = gst_pad_new_from_static_template (&sink_template, "sink"); + + gst_pad_set_chain_function (self->sink, + GST_DEBUG_FUNCPTR (gst_deinterleave_chain)); + gst_pad_set_setcaps_function (self->sink, + GST_DEBUG_FUNCPTR (gst_deinterleave_sink_setcaps)); + gst_pad_set_activatepush_function (self->sink, + GST_DEBUG_FUNCPTR (gst_deinterleave_sink_activate_push)); + + gst_element_add_pad (GST_ELEMENT (self), self->sink); +} + +static void +gst_deinterleave_add_new_pads (GstDeinterleave * self, GstCaps * caps) +{ + GstPad *pad; + guint i; + + for (i = 0; i < self->channels; i++) { + gchar *name = g_strdup_printf ("src%d", i); + + pad = gst_pad_new_from_static_template (&src_template, name); + g_free (name); + gst_pad_set_caps (pad, caps); + GST_PAD_UNSET_FLUSHING (pad); + gst_element_add_pad (GST_ELEMENT (self), pad); + } + + gst_element_no_more_pads (GST_ELEMENT (self)); +} + +static void +gst_deinterleave_remove_pads (GstDeinterleave * self) +{ + GstElement *elem; + GList *sinks, *l; + + elem = GST_ELEMENT (self); + + GST_INFO_OBJECT (self, "remove_pads()"); + + sinks = g_list_copy (elem->sinkpads); + + for (l = sinks; l; l = l->next) + /* force set_caps when going to RUNNING, see note in set_caps */ + gst_element_remove_pad (elem, GST_PAD (l->data)); + + gst_pad_set_caps (self->sink, NULL); + gst_caps_replace (&self->sinkcaps, NULL); + + g_list_free (sinks); +} + +static gboolean +gst_deinterleave_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstDeinterleave *self; + + self = GST_DEINTERLEAVE (gst_pad_get_parent (pad)); + + if (self->sinkcaps && !gst_caps_is_equal (caps, self->sinkcaps)) { + goto cannot_change_caps_dog; + } else { + GST_DEBUG_OBJECT (self, "got caps: %" GST_PTR_FORMAT, caps); + gst_caps_replace (&self->sinkcaps, caps); + } + + { + GstCaps *srccaps; + GstStructure *s; + + srccaps = gst_caps_copy (caps); + s = gst_caps_get_structure (srccaps, 0); + if (!gst_structure_get_int (s, "channels", &self->channels)) + goto no_channels; + gst_structure_set (s, "channels", G_TYPE_INT, 1, NULL); + gst_deinterleave_add_new_pads (self, srccaps); + gst_caps_unref (srccaps); + } + + gst_object_unref (self); + + return TRUE; + +cannot_change_caps_dog: + { + gst_object_unref (self); + return FALSE; + } +no_channels: + { + g_warning ("yarr, shiver me timbers"); + gst_object_unref (self); + return FALSE; + } +} + +static GstFlowReturn +gst_deinterleave_process (GstDeinterleave * self, GstBuffer * buf) +{ + GstFlowReturn ret; + GstElement *elem; + GList *srcs; + guint bufsize, i, j, channels, pads_pushed, nframes; + GstBuffer **buffers_out; + gfloat *in, *out; + + elem = GST_ELEMENT (self); + + channels = self->channels; + buffers_out = g_alloca (sizeof (GstBuffer *) * channels); + nframes = GST_BUFFER_SIZE (buf) / channels / sizeof (gfloat); + bufsize = nframes * sizeof (gfloat); + pads_pushed = 0; + + for (i = 0; i < channels; i++) + buffers_out[i] = NULL; + + for (srcs = elem->srcpads, i = 0; srcs; srcs = srcs->next, i++) { + GstPad *pad = (GstPad *) srcs->data; + + buffers_out[i] = NULL; + ret = gst_pad_alloc_buffer (pad, -1, bufsize, GST_PAD_CAPS (pad), + &buffers_out[i]); + + if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) + goto alloc_buffer_failed; + if (buffers_out[i] && GST_BUFFER_SIZE (buffers_out[i]) != bufsize) + goto alloc_buffer_bad_size; + } + + /* do the thing */ + for (srcs = elem->srcpads, i = 0; srcs; srcs = srcs->next, i++) { + GstPad *pad = (GstPad *) srcs->data; + + in = (gfloat *) GST_BUFFER_DATA (buf); + in += i; /* gfloat * arith */ + if (buffers_out[i]) { + out = (gfloat *) GST_BUFFER_DATA (buffers_out[i]); + for (j = 0; j < nframes; j++) + out[j] = in[j * channels]; + + ret = gst_pad_push (pad, buffers_out[i]); + buffers_out[i] = NULL; + if (ret == GST_FLOW_OK) + pads_pushed++; + else if (ret == GST_FLOW_NOT_LINKED) + ret = GST_FLOW_OK; + else + goto push_failed; + } + } + + if (!pads_pushed) + ret = GST_FLOW_NOT_LINKED; + + return ret; + +alloc_buffer_failed: + { + GST_WARNING ("gst_pad_alloc_buffer() returned %d", ret); + goto clean_buffers; + + } +alloc_buffer_bad_size: + { + GST_WARNING ("called alloc_buffer(), but didn't get requested bytes"); + ret = GST_FLOW_NOT_NEGOTIATED; + goto clean_buffers; + } +push_failed: + { + GST_DEBUG ("push() failed"); + goto clean_buffers; + } +clean_buffers: + { + for (i = 0; i < channels; i++) + if (buffers_out[i]) + gst_buffer_unref (buffers_out[i]); + return ret; + } +} + +static GstFlowReturn +gst_deinterleave_chain (GstPad * pad, GstBuffer * buffer) +{ + GstDeinterleave *self; + GstFlowReturn ret; + + self = GST_DEINTERLEAVE (gst_pad_get_parent (pad)); + + ret = gst_deinterleave_process (self, buffer); + + if (ret != GST_FLOW_OK) + GST_WARNING_OBJECT (self, "process failed"); + + gst_object_unref (self); + + return ret; +} + +static gboolean +gst_deinterleave_sink_activate_push (GstPad * pad, gboolean active) +{ + GstDeinterleave *self; + + self = GST_DEINTERLEAVE (gst_pad_get_parent (pad)); + + if (!active) + gst_deinterleave_remove_pads (self); + + gst_object_unref (self); + + return TRUE; +} diff --git a/gst/interleave/interleave.c b/gst/interleave/interleave.c new file mode 100644 index 00000000..51b75a9e --- /dev/null +++ b/gst/interleave/interleave.c @@ -0,0 +1,609 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> + * 2000 Wim Taymans <wtay@chello.be> + * 2005 Wim Taymans <wim@fluendo.com> + * 2007 Andy Wingo <wingo at pobox.com> + * + * interleave.c: interleave samples, based on gstsignalprocessor.c + * + * 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. + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/gst.h> + + +#define GST_TYPE_INTERLEAVE (gst_interleave_get_type()) +#define GST_INTERLEAVE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_INTERLEAVE,GstInterleave)) +#define GST_INTERLEAVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_INTERLEAVE,GstInterleaveClass)) +#define GST_INTERLEAVE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_INTERLEAVE,GstInterleaveClass)) +#define GST_IS_INTERLEAVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_INTERLEAVE)) +#define GST_IS_INTERLEAVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_INTERLEAVE)) + + +typedef struct _GstInterleave GstInterleave; +typedef struct _GstInterleaveClass GstInterleaveClass; + + +struct _GstInterleave +{ + GstElement element; + + GstCaps *sinkcaps; + guint channels; + + GstPad *src; + + GstActivateMode mode; + + guint pending_in; +}; + +struct _GstInterleaveClass +{ + GstElementClass parent_class; +}; + + +GST_DEBUG_CATEGORY_STATIC (gst_interleave_debug); +#define GST_CAT_DEFAULT gst_interleave_debug + + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("audio/x-raw-float, " + "rate = (int) [ 1, MAX ], " + "channels = (int) 1, " + "endianness = (int) BYTE_ORDER, " "width = (int) 32") + ); +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-float, " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, MAX ], " + "endianness = (int) BYTE_ORDER, " "width = (int) 32") + ); + + +#define GST_TYPE_INTERLEAVE_PAD (gst_interleave_pad_get_type ()) +#define GST_INTERLEAVE_PAD(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_INTERLEAVE_PAD,\ + GstInterleavePad)) +typedef struct _GstInterleavePad GstInterleavePad; +typedef GstPadClass GstInterleavePadClass; + +struct _GstInterleavePad +{ + GstPad parent; + + GstBuffer *pen; + + guint channel; + + /* these are only used for sink pads */ + guint samples_avail; + gfloat *data; +}; + +static GType +gst_interleave_pad_get_type (void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof (GstInterleavePadClass), NULL, NULL, NULL, NULL, + NULL, sizeof (GstInterleavePad), 0, NULL + }; + + type = g_type_register_static (GST_TYPE_PAD, "GstInterleavePad", &info, 0); + } + return type; +} + + +GST_BOILERPLATE (GstInterleave, gst_interleave, GstElement, GST_TYPE_ELEMENT); + + +static gboolean gst_interleave_src_activate_pull (GstPad * pad, + gboolean active); +static gboolean gst_interleave_sink_activate_push (GstPad * pad, + gboolean active); +static GstPad *gst_interleave_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name); + +static GstFlowReturn gst_interleave_getrange (GstPad * pad, + guint64 offset, guint length, GstBuffer ** buffer); +static GstFlowReturn gst_interleave_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_interleave_setcaps (GstPad * pad, GstCaps * caps); + + +static const GstElementDetails details = +GST_ELEMENT_DETAILS ("Audio interleaver", + "Filter/Converter/Audio", + "Folds many mono channels into one interleaved audio stream", + "Andy Wingo <wingo at pobox.com>"); + +static void +gst_interleave_base_init (gpointer g_class) +{ + GST_DEBUG_CATEGORY_INIT (gst_interleave_debug, "interleave", 0, + "interleave element"); + + gst_element_class_set_details (g_class, &details); + + gst_element_class_add_pad_template (g_class, + gst_static_pad_template_get (&sink_template)); + gst_element_class_add_pad_template (g_class, + gst_static_pad_template_get (&src_template)); +} + +static void +gst_interleave_class_init (GstInterleaveClass * klass) +{ + GstElementClass *gstelement_class; + + gstelement_class = GST_ELEMENT_CLASS (klass); + + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_interleave_request_new_pad); +} + +static void +gst_interleave_init (GstInterleave * self, GstInterleaveClass * klass) +{ + self->pending_in = 0; + + self->src = gst_pad_new_from_static_template (&src_template, "src"); + + gst_pad_set_getrange_function (self->src, + GST_DEBUG_FUNCPTR (gst_interleave_getrange)); + gst_pad_set_activatepull_function (self->src, + GST_DEBUG_FUNCPTR (gst_interleave_src_activate_pull)); + + gst_element_add_pad (GST_ELEMENT (self), self->src); +} + +static GstPad * +gst_interleave_request_new_pad (GstElement * element, GstPadTemplate * templ, + const gchar * name) +{ + GstPad *new; + GstInterleave *self = GST_INTERLEAVE (element); + + new = g_object_new (GST_TYPE_INTERLEAVE_PAD, + "name", GST_OBJECT_NAME (templ), "direction", templ->direction, + "template", templ, NULL); + GST_INTERLEAVE_PAD (new)->channel = self->channels++; + + gst_pad_set_setcaps_function (new, + GST_DEBUG_FUNCPTR (gst_interleave_setcaps)); + + gst_pad_set_chain_function (new, GST_DEBUG_FUNCPTR (gst_interleave_chain)); + gst_pad_set_activatepush_function (new, + GST_DEBUG_FUNCPTR (gst_interleave_sink_activate_push)); + + self->pending_in++; + + GST_PAD_UNSET_FLUSHING (new); + gst_element_add_pad (element, new); + + return new; +} + +static void +gst_interleave_unset_caps (GstInterleave * self) +{ + GstElement *elem; + GList *sinks; + + elem = GST_ELEMENT (self); + + GST_INFO_OBJECT (self, "unset_caps()"); + + for (sinks = elem->sinkpads; sinks; sinks = sinks->next) + gst_pad_set_caps (GST_PAD (sinks->data), NULL); +} + +static gboolean +gst_interleave_setcaps (GstPad * pad, GstCaps * caps) +{ + GstInterleave *self; + + self = GST_INTERLEAVE (gst_pad_get_parent (pad)); + + if (self->sinkcaps && !gst_caps_is_equal (caps, self->sinkcaps)) { + goto cannot_change_caps_dog; + } else { + GST_DEBUG_OBJECT (self, "got caps: %" GST_PTR_FORMAT, caps); + gst_caps_replace (&self->sinkcaps, caps); + } + + { + GstCaps *srccaps; + GstStructure *s; + + srccaps = gst_caps_copy (caps); + s = gst_caps_get_structure (srccaps, 0); + gst_structure_set (s, "channels", G_TYPE_INT, self->channels, NULL); + gst_pad_set_caps (self->src, srccaps); + gst_caps_unref (srccaps); + } + + gst_object_unref (self); + + return TRUE; + +cannot_change_caps_dog: + { + gst_object_unref (self); + return FALSE; + } +} + +static void +gst_interleave_update_inputs (GstInterleave * self, guint nprocessed) +{ + GstElement *elem = (GstElement *) self; + GList *sinks; + + for (sinks = elem->sinkpads; sinks; sinks = sinks->next) { + GstInterleavePad *sinkpad; + + sinkpad = (GstInterleavePad *) sinks->data; + g_assert (sinkpad->samples_avail >= nprocessed); + + if (sinkpad->pen && sinkpad->samples_avail == nprocessed) { + /* used up this buffer, unpen */ + gst_buffer_unref (sinkpad->pen); + sinkpad->pen = NULL; + } + + if (!sinkpad->pen) { + /* this buffer was used up */ + self->pending_in++; + sinkpad->data = NULL; + sinkpad->samples_avail = 0; + } else { + /* advance ->data pointers and decrement ->samples_avail, unreffing buffer + if no samples are left */ + sinkpad->samples_avail -= nprocessed; + sinkpad->data += nprocessed; /* gfloat* arithmetic */ + } + } +} + +static GstFlowReturn +gst_interleave_process (GstInterleave * self, guint nframes, GstBuffer ** buf) +{ + GstFlowReturn ret; + GstElement *elem; + GList *sinks; + guint bufsize, i, j, channels; + gfloat *in, *out; + + g_return_val_if_fail (self->pending_in == 0, GST_FLOW_ERROR); + + elem = GST_ELEMENT (self); + + /* determine the number of samples that we can process */ + for (sinks = elem->sinkpads; sinks; sinks = sinks->next) { + GstInterleavePad *sinkpad = (GstInterleavePad *) sinks->data; + + g_assert (sinkpad->samples_avail > 0); + nframes = MIN (nframes, sinkpad->samples_avail); + } + + channels = self->channels; + bufsize = nframes * channels * sizeof (gfloat); + + ret = gst_pad_alloc_buffer (GST_PAD (self->src), -1, + bufsize, GST_PAD_CAPS (self->src), buf); + + if (ret != GST_FLOW_OK) + goto alloc_buffer_failed; + + if (GST_BUFFER_SIZE (*buf) != bufsize) + goto alloc_buffer_bad_size; + + /* do the thing */ + for (sinks = elem->sinkpads, i = 0; sinks; sinks = sinks->next, i++) { + GstInterleavePad *sinkpad = (GstInterleavePad *) sinks->data; + + out = (gfloat *) GST_BUFFER_DATA (*buf); + out += i; /* gfloat* arith */ + in = sinkpad->data; + for (j = 0; j < nframes; j++) + out[j * channels] = in[j]; + } + + gst_interleave_update_inputs (self, nframes); + + return ret; + +alloc_buffer_failed: + { + GST_WARNING ("gst_pad_alloc_buffer() returned %d", ret); + return ret; + } +alloc_buffer_bad_size: + { + GST_WARNING ("called alloc_buffer() for %d bytes but got %d", bufsize, + GST_BUFFER_SIZE (*buf)); + gst_buffer_unref (*buf); + return GST_FLOW_NOT_NEGOTIATED; + } +} + +static GstFlowReturn +gst_interleave_pen_buffer (GstInterleave * self, GstPad * pad, + GstBuffer * buffer) +{ + GstInterleavePad *spad = (GstInterleavePad *) pad; + + if (spad->pen) + goto had_buffer; + + /* keep the reference */ + spad->pen = buffer; + spad->data = (gfloat *) GST_BUFFER_DATA (buffer); + spad->samples_avail = GST_BUFFER_SIZE (buffer) / sizeof (float); + + g_assert (self->pending_in != 0); + + self->pending_in--; + + return GST_FLOW_OK; + + /* ERRORS */ +had_buffer: + { + GST_WARNING ("Pad %s:%s already has penned buffer", + GST_DEBUG_PAD_NAME (pad)); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } +} + +static void +gst_interleave_flush (GstInterleave * self) +{ + GList *pads; + + GST_INFO_OBJECT (self, "flush()"); + + for (pads = GST_ELEMENT (self)->sinkpads; pads; pads = pads->next) { + GstInterleavePad *spad = (GstInterleavePad *) pads->data; + + if (spad->pen) { + gst_buffer_unref (spad->pen); + spad->pen = NULL; + spad->data = NULL; + spad->samples_avail = 0; + } + } + + self->pending_in = GST_ELEMENT (self)->numsinkpads; +} + +static GstFlowReturn +gst_interleave_do_pulls (GstInterleave * self, guint nframes) +{ + GList *sinkpads; + GstFlowReturn ret = GST_FLOW_OK; + + /* FIXME: not threadsafe atm */ + + sinkpads = GST_ELEMENT (self)->sinkpads; + + for (; sinkpads; sinkpads = sinkpads->next) { + GstInterleavePad *spad = (GstInterleavePad *) sinkpads->data; + GstBuffer *buf; + + if (spad->pen) { + g_warning ("Unexpectedly full buffer pen for pad %s:%s", + GST_DEBUG_PAD_NAME (spad)); + continue; + } + + ret = + gst_pad_pull_range (GST_PAD (spad), -1, nframes * sizeof (gfloat), + &buf); + if (ret != GST_FLOW_OK) + goto pull_failed; + + if (!buf) + goto no_buffer; + + ret = gst_interleave_pen_buffer (self, GST_PAD (spad), buf); + if (ret != GST_FLOW_OK) + goto pull_failed; + } + + return ret; + +pull_failed: + { + gst_interleave_flush (self); + return ret; + } +no_buffer: + { + g_critical ("Pull failed to make a buffer!"); + return GST_FLOW_ERROR; + } +} + +static GstFlowReturn +gst_interleave_getrange (GstPad * pad, guint64 offset, + guint length, GstBuffer ** buffer) +{ + GstInterleave *self; + GstFlowReturn ret = GST_FLOW_ERROR; + guint nframes; + + self = GST_INTERLEAVE (gst_pad_get_parent (pad)); + + nframes = length / self->channels / sizeof (gfloat); + + ret = gst_interleave_do_pulls (self, nframes); + + if (ret == GST_FLOW_OK) + ret = gst_interleave_process (self, nframes, buffer); + + GST_DEBUG_OBJECT (self, "returns %s", gst_flow_get_name (ret)); + + gst_object_unref (self); + + return ret; +} + +static GstFlowReturn +gst_interleave_chain (GstPad * pad, GstBuffer * buffer) +{ + GstFlowReturn ret; + GstInterleave *self; + + self = GST_INTERLEAVE (gst_pad_get_parent (pad)); + + ret = gst_interleave_pen_buffer (self, pad, buffer); + if (ret != GST_FLOW_OK) + goto pen_failed; + + if (self->pending_in == 0) { + GstBuffer *out; + + ret = gst_interleave_process (self, G_MAXUINT, &out); + if (ret != GST_FLOW_OK) + goto process_failed; + + ret = gst_pad_push (self->src, out); + } + +done: + gst_object_unref (self); + return ret; + +pen_failed: + { + GST_WARNING_OBJECT (self, "pen failed"); + goto done; + } +process_failed: + { + GST_WARNING_OBJECT (self, "process failed"); + goto done; + } +} + +static gboolean +gst_interleave_sink_activate_push (GstPad * pad, gboolean active) +{ + gboolean result = TRUE; + GstInterleave *self; + + self = GST_INTERLEAVE (gst_pad_get_parent (pad)); + + if (active) { + if (self->mode == GST_ACTIVATE_NONE) { + self->mode = GST_ACTIVATE_PUSH; + result = TRUE; + } else if (self->mode == GST_ACTIVATE_PUSH) { + result = TRUE; + } else { + g_warning ("foo"); + result = FALSE; + } + } else { + if (self->mode == GST_ACTIVATE_NONE) { + result = TRUE; + } else if (self->mode == GST_ACTIVATE_PUSH) { + self->mode = GST_ACTIVATE_NONE; + result = TRUE; + } else { + g_warning ("foo"); + result = FALSE; + } + } + + GST_DEBUG_OBJECT (self, "result : %d", result); + + gst_object_unref (self); + + return result; +} + +static gboolean +gst_interleave_src_activate_pull (GstPad * pad, gboolean active) +{ + gboolean result = TRUE; + GstInterleave *self; + + self = GST_INTERLEAVE (gst_pad_get_parent (pad)); + + if (active) { + if (self->mode == GST_ACTIVATE_NONE) { + GList *l; + + if (GST_ELEMENT (self)->sinkpads) { + for (l = GST_ELEMENT (self)->sinkpads; l; l = l->next) + result &= gst_pad_activate_pull (pad, active); + } else { + /* nobody has requested pads, seems i am operating in delayed-request + push mode */ + result = FALSE; + } + if (result) + self->mode = GST_ACTIVATE_PULL; + } else if (self->mode == GST_ACTIVATE_PULL) { + result = TRUE; + } else { + g_warning ("foo"); + result = FALSE; + } + } else { + if (self->mode == GST_ACTIVATE_NONE) { + result = TRUE; + } else if (self->mode == GST_ACTIVATE_PULL) { + GList *l; + + for (l = GST_ELEMENT (self)->sinkpads; l; l = l->next) + result &= gst_pad_activate_pull (pad, active); + if (result) + self->mode = GST_ACTIVATE_NONE; + result = TRUE; + } else { + g_warning ("foo"); + result = FALSE; + } + + gst_interleave_unset_caps (self); + gst_interleave_flush (self); + } + + GST_DEBUG_OBJECT (self, "result : %d", result); + + gst_object_unref (self); + + return result; +} diff --git a/gst/interleave/plugin.c b/gst/interleave/plugin.c new file mode 100644 index 00000000..7017c45c --- /dev/null +++ b/gst/interleave/plugin.c @@ -0,0 +1,44 @@ +/* GStreamer interleave plugin + * Copyright (C) 2004,2007 Andy Wingo <wingo at pobox.com> + * + * plugin.c: the stubs for the interleave plugin + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "plugin.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "interleave", + GST_RANK_NONE, gst_interleave_get_type ()) || + !gst_element_register (plugin, "deinterleave", + GST_RANK_NONE, gst_deinterleave_get_type ())) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "interleave", + "Audio interleaver/deinterleaver", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/gst/interleave/plugin.h b/gst/interleave/plugin.h new file mode 100644 index 00000000..bc722bdb --- /dev/null +++ b/gst/interleave/plugin.h @@ -0,0 +1,36 @@ +/* GStreamer interleave plugin + * Copyright (C) 2004,2007 Andy Wingo <wingo at pobox.com> + * + * plugin.h: the stubs for the interleave plugin + * + * 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_PLUGIN_INTERLEAVE_H__ +#define __GST_PLUGIN_INTERLEAVE_H__ + + +#include <gst/gst.h> + +G_BEGIN_DECLS + +GType gst_interleave_get_type (void); +GType gst_deinterleave_get_type (void); + +G_END_DECLS + +#endif /* __GST_PLUGIN_INTERLEAVE_H__ */ |