New elements interleave and deinterleave, implement channel interleaving and deinterl...
authorAndy Wingo <wingo@pobox.com>
Sun, 7 Jan 2007 22:03:54 +0000 (22:03 +0000)
committerAndy Wingo <wingo@pobox.com>
Sun, 7 Jan 2007 22:03:54 +0000 (22:03 +0000)
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.

ChangeLog
configure.ac
gst/interleave/Makefile.am [new file with mode: 0644]
gst/interleave/deinterleave.c [new file with mode: 0644]
gst/interleave/interleave.c [new file with mode: 0644]
gst/interleave/plugin.c [new file with mode: 0644]
gst/interleave/plugin.h [new file with mode: 0644]

index 08c1500a5db8aba57bcece584138383c1b0429ea..7ea9da9248529b753768af6f31de7b04022bfe80 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+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.
+       
 2007-01-06  Edward Hervey  <edward@fluendo.com>
 
        * configure.ac:
index a5b42b8b9027701aa6dcf9b74451eb6709a0dfc5..ac0bf2ce61aa1994306088c4119f0087f9ac8f85 100644 (file)
@@ -81,6 +81,7 @@ GST_PLUGINS_ALL="\
   filter \
   freeze \
   h264parse \
+  interleave \
   librfb \
   nuvdemux \
   modplug \
@@ -920,6 +921,7 @@ gst/deinterlace/Makefile
 gst/filter/Makefile
 gst/freeze/Makefile
 gst/h264parse/Makefile
+gst/interleave/Makefile
 gst/librfb/Makefile
 gst/modplug/Makefile
 gst/nuvdemux/Makefile
diff --git a/gst/interleave/Makefile.am b/gst/interleave/Makefile.am
new file mode 100644 (file)
index 0000000..9a09cdb
--- /dev/null
@@ -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 (file)
index 0000000..09f19e1
--- /dev/null
@@ -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 (file)
index 0000000..51b75a9
--- /dev/null
@@ -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 (file)
index 0000000..7017c45
--- /dev/null
@@ -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 (file)
index 0000000..bc722bd
--- /dev/null
@@ -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__ */