adpcmenc: add new adpcm encoder element.
authorPeter van Hardenberg <pvh@songbirdnest.com>
Sat, 12 Dec 2009 00:32:48 +0000 (16:32 -0800)
committerMichael Smith <msmith@songbirdnest.com>
Sat, 12 Dec 2009 00:32:48 +0000 (16:32 -0800)
configure.ac
gst-plugins-bad.spec.in
gst/adpcmenc/Makefile.am [new file with mode: 0644]
gst/adpcmenc/adpcmenc.c [new file with mode: 0644]

index 5322eb1c24a2b9d9b9f2a525918a6d08347357bc..b344ae1aa0289581dcdb6159b0c7b637201d502e 100644 (file)
@@ -254,6 +254,7 @@ dnl *** plug-ins to include ***
 
 dnl these are all the gst plug-ins, compilable without additional libs
 AG_GST_CHECK_PLUGIN(adpcmdec)
+AG_GST_CHECK_PLUGIN(adpcmenc)
 AG_GST_CHECK_PLUGIN(aiff)
 AG_GST_CHECK_PLUGIN(asfmux)
 AG_GST_CHECK_PLUGIN(audioparsers)
@@ -1632,6 +1633,7 @@ common/m4/Makefile
 gst-plugins-bad.spec
 gst/Makefile
 gst/adpcmdec/Makefile
+gst/adpcmenc/Makefile
 gst/aiff/Makefile
 gst/asfmux/Makefile
 gst/audioparsers/Makefile
index 56b2ea00c876d3331440e68f944cbd8055ae3a63..76647760fec0fc3872c1344ae4af0c9a8d8a9fc5 100644 (file)
@@ -121,6 +121,7 @@ rm -rf $RPM_BUILD_ROOT
 %{_libdir}/gstreamer-%{majorminor}/libgstrtpmux.so
 %{_libdir}/gstreamer-%{majorminor}/libgstsiren.so
 %{_libdir}/gstreamer-%{majorminor}/libgstadpcmdec.so
+%{_libdir}/gstreamer-%{majorminor}/libgstadpcmenc.so
 %{_libdir}/gstreamer-%{majorminor}/libgstid3tag.so
 %{_libdir}/gstreamer-%{majorminor}/libgsthdvparse.so
 %{_libdir}/gstreamer-%{majorminor}/libgstshapewipe.so
diff --git a/gst/adpcmenc/Makefile.am b/gst/adpcmenc/Makefile.am
new file mode 100644 (file)
index 0000000..3cc258e
--- /dev/null
@@ -0,0 +1,12 @@
+plugin_LTLIBRARIES = libgstadpcmenc.la
+
+# sources used to compile this plug-in
+libgstadpcmenc_la_SOURCES = adpcmenc.c
+
+# flags used to compile this plugin
+# add other _CFLAGS and _LIBS as needed
+libgstadpcmenc_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS)
+libgstadpcmenc_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS)
+libgstadpcmenc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstadpcmenc_la_LIBTOOLFLAGS = --tag=disable-static
+
diff --git a/gst/adpcmenc/adpcmenc.c b/gst/adpcmenc/adpcmenc.c
new file mode 100644 (file)
index 0000000..d91d496
--- /dev/null
@@ -0,0 +1,571 @@
+/* GStreamer
+ * Copyright (C) 2009 Pioneers of the Inevitable <songbird@songbirdnest.com>
+ *
+ * Authors: Peter van Hardenberg <pvh@songbirdnest.com>
+ *
+ * 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.
+ */
+
+/* Based on ADPCM encoders in libsndfile, 
+   Copyright (C) 1999-2002 Erik de Castro Lopo <erikd@zip.com.au
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+
+#define GST_TYPE_ADPCM_ENC \
+    (adpcmenc_get_type ())
+
+#define GST_TYPE_ADPCMENC_LAYOUT \
+    (adpcmenc_layout_get_type ())
+
+#define GST_ADPCM_ENC(obj) \
+    (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_ADPCM_ENC, ADPCMEnc))
+
+#define GST_CAT_DEFAULT adpcmenc_debug
+GST_DEBUG_CATEGORY_STATIC (adpcmenc_debug);
+
+static const GstElementDetails adpcmenc_details =
+GST_ELEMENT_DETAILS ("ADPCM encoder",
+    "Codec/Encoder/Audio",
+    "Encode ADPCM audio",
+    "Pioneers of the Inevitable <songbird@songbirdnest.com");
+
+static GstStaticPadTemplate adpcmenc_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw-int, "
+        "depth = (int)16, "
+        "width = (int)16, " "channels = (int) [1,2], " "rate = (int)[1, MAX]")
+    );
+
+static GstStaticPadTemplate adpcmenc_src_template =
+    GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-adpcm, "
+        " layout=(string){dvi}, "
+        " block_align = (int) [64, 8192], "
+        " rate = (int)[ 1, MAX ], " "channels = (int)[1,2];")
+    );
+
+#define MIN_ADPCM_BLOCK_SIZE 64
+#define MAX_ADPCM_BLOCK_SIZE 8192
+#define DEFAULT_ADPCM_BLOCK_SIZE 1024
+#define DEFAULT_ADPCM_LAYOUT LAYOUT_ADPCM_DVI
+
+static int ima_indx_adjust[16] = {
+  -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8,
+};
+
+static int ima_step_size[89] = {
+  7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+  50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230,
+  253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963,
+  1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
+  3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442,
+  11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
+  32767
+};
+
+
+enum adpcm_properties
+{
+  ARG_0,
+  ARG_BLOCK_SIZE,
+  ARG_LAYOUT
+};
+
+enum adpcm_layout
+{
+  LAYOUT_ADPCM_DVI
+};
+
+static GType
+adpcmenc_layout_get_type (void)
+{
+  static GType adpcmenc_layout_type = 0;
+
+  if (!adpcmenc_layout_type) {
+    static GEnumValue layout_types[] = {
+      {LAYOUT_ADPCM_DVI, "DVI/IMA APDCM", "dvi"},
+      {0, NULL, NULL},
+    };
+
+    adpcmenc_layout_type = g_enum_register_static ("GstADPCMEncLayout",
+        layout_types);
+  }
+
+  return adpcmenc_layout_type;
+}
+
+typedef struct _ADPCMEncClass
+{
+  GstElementClass parent_class;
+} ADPCMEncClass;
+
+typedef struct _ADPCMEnc
+{
+  GstElement parent;
+
+  GstPad *sinkpad;
+  GstPad *srcpad;
+
+  GstCaps *output_caps;
+
+  enum adpcm_layout layout;
+  int rate;
+  int channels;
+  int blocksize;
+  int samples_per_block;
+
+  guint8 step_index[2];
+
+  gboolean is_setup;
+
+  GstClockTime timestamp;
+  GstClockTime base_timestamp;
+
+  guint64 out_samples;
+
+  GstAdapter *adapter;
+
+} ADPCMEnc;
+
+GST_BOILERPLATE (ADPCMEnc, adpcmenc, GstElement, GST_TYPE_ELEMENT);
+static gboolean
+adpcmenc_setup (ADPCMEnc * enc)
+{
+  const int DVI_IMA_HEADER_SIZE = 4;
+  const int ADPCM_SAMPLES_PER_BYTE = 2;
+  guint64 sample_bytes;
+
+  char *layout;
+  switch (enc->layout) {
+    case LAYOUT_ADPCM_DVI:
+      layout = "dvi";
+      /* IMA ADPCM includes a 4-byte header per channel, */
+      sample_bytes = enc->blocksize - (DVI_IMA_HEADER_SIZE * enc->channels);
+      /* two samples per byte, plus a single sample in the header. */
+      enc->samples_per_block =
+          ((sample_bytes * ADPCM_SAMPLES_PER_BYTE) / enc->channels) + 1;
+      break;
+    default:
+      GST_WARNING_OBJECT (enc, "Invalid layout");
+      return FALSE;
+  }
+
+  enc->output_caps = gst_caps_new_simple ("audio/x-adpcm",
+      "rate", G_TYPE_INT, enc->rate,
+      "channels", G_TYPE_INT, enc->channels,
+      "layout", G_TYPE_STRING, layout,
+      "block_align", G_TYPE_INT, enc->blocksize, NULL);
+
+  if (enc->output_caps) {
+    gst_pad_set_caps (enc->srcpad, enc->output_caps);
+  }
+
+  enc->is_setup = TRUE;
+  enc->timestamp = GST_CLOCK_TIME_NONE;
+  enc->base_timestamp = GST_CLOCK_TIME_NONE;
+  enc->adapter = gst_adapter_new ();
+  enc->out_samples = 0;
+
+  /* Step index state is carried between blocks. */
+  enc->step_index[0] = 0;
+  enc->step_index[1] = 0;
+
+  return TRUE;
+}
+
+static void
+adpcmenc_teardown (ADPCMEnc * enc)
+{
+  if (enc->output_caps) {
+    gst_caps_unref (enc->output_caps);
+    enc->output_caps = NULL;
+  }
+  if (enc->adapter) {
+    g_object_unref (enc->adapter);
+    enc->adapter = NULL;
+  }
+  enc->is_setup = FALSE;
+}
+
+static gboolean
+adpcmenc_sink_setcaps (GstPad * pad, GstCaps * caps)
+{
+  ADPCMEnc *enc = (ADPCMEnc *) gst_pad_get_parent (pad);
+  GstStructure *structure = gst_caps_get_structure (caps, 0);
+
+  if (!gst_structure_get_int (structure, "rate", &enc->rate))
+    return FALSE;
+  if (!gst_structure_get_int (structure, "channels", &enc->channels))
+    return FALSE;
+
+  if (enc->is_setup) {
+    adpcmenc_teardown (enc);
+  }
+  adpcmenc_setup (enc);
+
+  gst_object_unref (enc);
+
+  return TRUE;
+}
+
+static void
+adpcmenc_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  ADPCMEnc *enc = GST_ADPCM_ENC (object);
+
+  switch (prop_id) {
+    case ARG_BLOCK_SIZE:
+      enc->blocksize = g_value_get_int (value);
+      break;
+    case ARG_LAYOUT:
+      enc->layout = g_value_get_enum (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+adpcmenc_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  ADPCMEnc *enc = GST_ADPCM_ENC (object);
+
+  switch (prop_id) {
+    case ARG_BLOCK_SIZE:
+      g_value_set_int (value, enc->blocksize);
+      break;
+    case ARG_LAYOUT:
+      g_value_set_enum (value, enc->layout);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static guint8
+adpcmenc_encode_ima_sample (gint16 sample, gint16 * prev_sample,
+    guint8 * stepindex)
+{
+  const int NEGATIVE_SIGN_BIT = 0x8;
+  int diff, vpdiff, mask, step;
+  int bytecode = 0x0;
+  diff = sample - *prev_sample;
+  step = ima_step_size[*stepindex];
+  vpdiff = step >> 3;
+
+  if (diff < 0) {
+    diff = -diff;
+    bytecode = NEGATIVE_SIGN_BIT;
+  }
+
+  mask = 0x4;
+  while (mask > 0) {
+    if (diff >= step) {
+      bytecode |= mask;
+      diff -= step;
+      vpdiff += step;
+    }
+    step >>= 1;
+    mask >>= 1;
+  }
+
+  if (bytecode & 8) {
+    vpdiff = -vpdiff;
+  }
+
+  *prev_sample = CLAMP (*prev_sample + vpdiff, G_MININT16, G_MAXINT16);
+  *stepindex = CLAMP (*stepindex + ima_indx_adjust[bytecode], 0, 88);
+
+  return bytecode;
+}
+
+static gboolean
+adpcmenc_encode_ima_block (ADPCMEnc * enc, gint16 * samples, guint8 * outbuf)
+{
+  const int HEADER_SIZE = 4;
+  gint16 prev_sample[2] = { 0, 0 };
+  guint32 write_pos = 0;
+  guint32 read_pos = 0;
+  guint8 channel = 0;
+
+  /* Write a header for each channel.
+   * The header consists of a sixteen-bit predicted sound value,
+   * and an eight bit step_index, carried forward from any previous block.
+   * These allow seeking within the file.
+   */
+  for (channel = 0; channel < enc->channels; channel++) {
+    write_pos = channel * HEADER_SIZE;
+    outbuf[write_pos + 0] = (samples[channel] & 0xFF);
+    outbuf[write_pos + 1] = (samples[channel] >> 8) & 0xFF;
+    outbuf[write_pos + 2] = enc->step_index[channel];
+    outbuf[write_pos + 3] = 0;
+    prev_sample[channel] = samples[channel];
+  }
+
+  /* raw-audio looks like this for a stereo stream:
+   * [ L, R, L, R, L, R ... ]
+   * encoded audio is in eight-sample blocks, two samples to a byte thusly:
+   * [ LL, LL, LL, LL, RR, RR, RR, RR ... ] 
+   */
+  write_pos = HEADER_SIZE * enc->channels;
+  read_pos = enc->channels;     /* the first sample is in the header. */
+  while (write_pos < enc->blocksize) {
+    gint8 CHANNEL_CHUNK_SIZE = 8;
+    for (channel = 0; channel < enc->channels; channel++) {
+      /* convert eight samples (four bytes) per channel, then swap */
+      guint32 channel_chunk_base = read_pos + channel;
+      gint8 chunk;
+      for (chunk = 0; chunk < CHANNEL_CHUNK_SIZE; chunk++) {
+        guint8 packed_byte = 0, encoded_sample;
+        encoded_sample =
+            adpcmenc_encode_ima_sample (samples[channel_chunk_base +
+                (chunk * enc->channels)], &prev_sample[channel],
+            &enc->step_index[channel]);
+        packed_byte |= encoded_sample & 0x0F;
+
+        chunk++;
+
+        encoded_sample =
+            adpcmenc_encode_ima_sample (samples[channel_chunk_base +
+                (chunk * enc->channels)], &prev_sample[channel],
+            &enc->step_index[channel]);
+        packed_byte |= encoded_sample << 4 & 0xF0;
+
+        outbuf[write_pos++] = packed_byte;
+      }
+    }
+    /* advance to the next block of 8 samples per channel */
+    read_pos += CHANNEL_CHUNK_SIZE * enc->channels;
+    if (read_pos > enc->samples_per_block * enc->channels) {
+      GST_LOG ("Ran past the end. (Reading %i of %i.)", read_pos,
+          enc->samples_per_block);
+    }
+  }
+
+  return TRUE;
+}
+
+static GstFlowReturn
+adpcmenc_encode_block (ADPCMEnc * enc, gint16 * samples, int blocksize)
+{
+  gboolean res;
+  GstBuffer *outbuf = NULL;
+
+  if (enc->layout == LAYOUT_ADPCM_DVI) {
+    outbuf = gst_buffer_new_and_alloc (enc->blocksize);
+    res = adpcmenc_encode_ima_block (enc, samples, GST_BUFFER_DATA (outbuf));
+  } else {
+    GST_WARNING_OBJECT (enc, "Unknown layout");
+    return GST_FLOW_ERROR;
+  }
+
+  if (!res) {
+    gst_buffer_unref (outbuf);
+    GST_WARNING_OBJECT (enc, "Encode of block failed");
+    return GST_FLOW_ERROR;
+  }
+
+  gst_buffer_set_caps (outbuf, enc->output_caps);
+  GST_BUFFER_TIMESTAMP (outbuf) = enc->timestamp;
+
+  enc->out_samples += enc->samples_per_block;
+  enc->timestamp = enc->base_timestamp +
+      gst_util_uint64_scale_int (enc->out_samples, GST_SECOND, enc->rate);
+  GST_BUFFER_DURATION (outbuf) = enc->timestamp - GST_BUFFER_TIMESTAMP (outbuf);
+
+  return gst_pad_push (enc->srcpad, outbuf);
+}
+
+static GstFlowReturn
+adpcmenc_chain (GstPad * pad, GstBuffer * buf)
+{
+  ADPCMEnc *enc = (ADPCMEnc *) gst_pad_get_parent (pad);
+  GstFlowReturn ret = GST_FLOW_OK;
+  gint16 *samples;
+  GstBuffer *databuf = NULL;
+  int input_bytes_per_block;
+  const int BYTES_PER_SAMPLE = 2;
+
+  if (enc->base_timestamp == GST_CLOCK_TIME_NONE) {
+    enc->base_timestamp = GST_BUFFER_TIMESTAMP (buf);
+    if (enc->base_timestamp == GST_CLOCK_TIME_NONE)
+      enc->base_timestamp = 0;
+    enc->timestamp = enc->base_timestamp;
+  }
+
+  gst_adapter_push (enc->adapter, buf);
+
+  input_bytes_per_block =
+      enc->samples_per_block * BYTES_PER_SAMPLE * enc->channels;
+
+  while (gst_adapter_available (enc->adapter) >= input_bytes_per_block) {
+    databuf = gst_adapter_take_buffer (enc->adapter, input_bytes_per_block);
+    samples = (gint16 *) GST_BUFFER_DATA (databuf);
+    ret = adpcmenc_encode_block (enc, samples, enc->blocksize);
+    gst_buffer_unref (databuf);
+    if (ret != GST_FLOW_OK)
+      goto done;
+  }
+
+done:
+  gst_object_unref (enc);
+  return ret;
+}
+
+static gboolean
+adpcmenc_sink_event (GstPad * pad, GstEvent * event)
+{
+  ADPCMEnc *enc = (ADPCMEnc *) gst_pad_get_parent (pad);
+  gboolean res;
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_FLUSH_STOP:
+      enc->out_samples = 0;
+      enc->timestamp = GST_CLOCK_TIME_NONE;
+      enc->base_timestamp = GST_CLOCK_TIME_NONE;
+      gst_adapter_clear (enc->adapter);
+      /* Fall through */
+    default:
+      res = gst_pad_push_event (enc->srcpad, event);
+      break;
+  }
+  gst_object_unref (enc);
+  return res;
+}
+
+static GstStateChangeReturn
+adpcmenc_change_state (GstElement * element, GstStateChange transition)
+{
+  GstStateChangeReturn ret;
+  ADPCMEnc *enc = (ADPCMEnc *) element;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      break;
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      adpcmenc_teardown (enc);
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      break;
+    default:
+      break;
+  }
+  return ret;
+}
+
+static void
+adpcmenc_dispose (GObject * obj)
+{
+  G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+static void
+adpcmenc_init (ADPCMEnc * enc, ADPCMEncClass * klass)
+{
+  enc->sinkpad =
+      gst_pad_new_from_static_template (&adpcmenc_sink_template, "sink");
+  gst_pad_set_setcaps_function (enc->sinkpad,
+      GST_DEBUG_FUNCPTR (adpcmenc_sink_setcaps));
+  gst_pad_set_chain_function (enc->sinkpad, GST_DEBUG_FUNCPTR (adpcmenc_chain));
+  gst_pad_set_event_function (enc->sinkpad,
+      GST_DEBUG_FUNCPTR (adpcmenc_sink_event));
+  gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad);
+
+  enc->srcpad =
+      gst_pad_new_from_static_template (&adpcmenc_src_template, "src");
+  gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad);
+
+  /* Set defaults. */
+  enc->blocksize = DEFAULT_ADPCM_BLOCK_SIZE;
+  enc->layout = DEFAULT_ADPCM_LAYOUT;
+}
+
+static void
+adpcmenc_class_init (ADPCMEncClass * klass)
+{
+  GObjectClass *gobjectclass = (GObjectClass *) klass;
+  GstElementClass *gstelement_class = (GstElementClass *) klass;
+
+  gobjectclass->set_property = adpcmenc_set_property;
+  gobjectclass->get_property = adpcmenc_get_property;
+
+  g_object_class_install_property (gobjectclass, ARG_LAYOUT,
+      g_param_spec_enum ("layout", "Layout",
+          "Layout for output stream",
+          GST_TYPE_ADPCMENC_LAYOUT, DEFAULT_ADPCM_LAYOUT,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobjectclass, ARG_BLOCK_SIZE,
+      g_param_spec_int ("blockalign", "Block Align",
+          "Block size for output stream",
+          MIN_ADPCM_BLOCK_SIZE, MAX_ADPCM_BLOCK_SIZE,
+          DEFAULT_ADPCM_BLOCK_SIZE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  gobjectclass->dispose = adpcmenc_dispose;
+  gstelement_class->change_state = adpcmenc_change_state;
+} static void
+
+adpcmenc_base_init (gpointer klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&adpcmenc_sink_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&adpcmenc_src_template));
+  gst_element_class_set_details (element_class, &adpcmenc_details);
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (adpcmenc_debug, "adpcmenc", 0, "ADPCM Encoders");
+  if (!gst_element_register (plugin, "adpcmenc", GST_RANK_PRIMARY,
+          GST_TYPE_ADPCM_ENC)) {
+    return FALSE;
+  }
+  return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "adpcmenc",
+    "ADPCM encoder", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
+    GST_PACKAGE_ORIGIN);