diff options
author | Andy Wingo <wingo@pobox.com> | 2007-01-07 22:03:54 +0000 |
---|---|---|
committer | Andy Wingo <wingo@pobox.com> | 2007-01-07 22:03:54 +0000 |
commit | 215773aadaaa2e7e6a96761735c26fc4c213d16e (patch) | |
tree | f81829e74b0a5c6c3c3eadb8fe830cd43d3ee4fd /gst/interleave/interleave.c | |
parent | c1429dc970ece9f8b177e4133db3f2cd61b23105 (diff) | |
download | gst-plugins-bad-215773aadaaa2e7e6a96761735c26fc4c213d16e.tar.gz gst-plugins-bad-215773aadaaa2e7e6a96761735c26fc4c213d16e.tar.bz2 gst-plugins-bad-215773aadaaa2e7e6a96761735c26fc4c213d16e.zip |
New elements interleave and deinterleave, implement channel interleaving and deinterleaving.
Original commit message from CVS:
2007-01-07 Andy Wingo <wingo@pobox.com>
* configure.ac:
* gst/interleave/Makefile.am:
* gst/interleave/plugin.h:
* gst/interleave/plugin.c:
* gst/interleave/interleave.c:
* gst/interleave/deinterleave.c: New elements interleave and
deinterleave, implement channel interleaving and deinterleaving.
The interleaver can operate in pull or push mode but the
deinterleaver is more like a demuxer and can only operate in push
mode.
Diffstat (limited to 'gst/interleave/interleave.c')
-rw-r--r-- | gst/interleave/interleave.c | 609 |
1 files changed, 609 insertions, 0 deletions
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; +} |