mad: replace with 0.10 version ported to GstAudioDecoder
authorMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Tue, 16 Aug 2011 19:12:06 +0000 (21:12 +0200)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Sun, 13 Nov 2011 00:23:03 +0000 (00:23 +0000)
Taken from commit 6e7e3657396454fe95fbd89170281865d4d1cec3
of Mark's baseaudio branch.

Would probably be too risky to drop this into 0.10 given
all the things mad is doing.

ext/mad/gstmad.c
ext/mad/gstmad.h

index 40b5d5bffe428da94578abe79dde98ae147e8810..6c6c4197aff296275127c8699fae7f2b59dcb96d 100644 (file)
@@ -26,7 +26,7 @@
  * <refsect2>
  * <title>Example pipelines</title>
  * |[
- * gst-launch filesrc location=music.mp3 ! mpegaudioparse ! mad ! audioconvert ! audioresample ! autoaudiosink
+ * gst-launch filesrc location=music.mp3 ! mad ! audioconvert ! audioresample ! autoaudiosink
  * ]| Decode the mp3 file and play
  * </refsect2>
  */
@@ -54,8 +54,11 @@ static GstStaticPadTemplate mad_src_template_factory =
 GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("audio/x-raw, "
-        "format = (string) " GST_AUDIO_NE (S32) ", "
+    GST_STATIC_CAPS ("audio/x-raw-int, "
+        "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
+        "signed = (boolean) true, "
+        "width = (int) 32, "
+        "depth = (int) 32, "
         "rate = (int) { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }, "
         "channels = (int) [ 1, 2 ]")
     );
@@ -72,108 +75,57 @@ GST_STATIC_PAD_TEMPLATE ("sink",
         "channels = (int) [ 1, 2 ]")
     );
 
-static void gst_mad_dispose (GObject * object);
-static void gst_mad_clear_queues (GstMad * mad);
+
+static gboolean gst_mad_start (GstBaseAudioDecoder * dec);
+static gboolean gst_mad_stop (GstBaseAudioDecoder * dec);
+static gboolean gst_mad_parse (GstBaseAudioDecoder * dec, GstAdapter * adapter,
+    gint * offset, gint * length);
+static GstFlowReturn gst_mad_handle_frame (GstBaseAudioDecoder * dec,
+    GstBuffer * buffer);
+static void gst_mad_flush (GstBaseAudioDecoder * dec, gboolean hard);
 
 static void gst_mad_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
 static void gst_mad_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
 
-static gboolean gst_mad_src_event (GstPad * pad, GstEvent * event);
-
-static gboolean gst_mad_src_query (GstPad * pad, GstQuery * query);
-static gboolean gst_mad_convert_sink (GstPad * pad, GstFormat src_format,
-    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
-static gboolean gst_mad_convert_src (GstPad * pad, GstFormat src_format,
-    gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
-
-static gboolean gst_mad_sink_event (GstPad * pad, GstEvent * event);
-static GstFlowReturn gst_mad_chain (GstPad * pad, GstBuffer * buffer);
-static GstFlowReturn gst_mad_chain_reverse (GstMad * mad, GstBuffer * buf);
-
-static GstStateChangeReturn gst_mad_change_state (GstElement * element,
-    GstStateChange transition);
 
-static void gst_mad_set_index (GstElement * element, GstIndex * index);
-static GstIndex *gst_mad_get_index (GstElement * element);
+typedef GstMad GstBMad;
+typedef GstMadClass GstBMadClass;
 
-#define gst_mad_parent_class parent_class
-G_DEFINE_TYPE (GstMad, gst_mad, GST_TYPE_ELEMENT);
-
-/*
-#define GST_TYPE_MAD_LAYER (gst_mad_layer_get_type())
-static GType
-gst_mad_layer_get_type (void)
-{
-  static GType mad_layer_type = 0;
-  static GEnumValue mad_layer[] = {
-    {0, "Unknown", "unknown"},
-    {MAD_LAYER_I, "Layer I", "1"},
-    {MAD_LAYER_II, "Layer II", "2"},
-    {MAD_LAYER_III, "Layer III", "3"},
-    {0, NULL, NULL},
-  };
-
-  if (!mad_layer_type) {
-    mad_layer_type = g_enum_register_static ("GstMadLayer", mad_layer);
-  }
-  return mad_layer_type;
-}
-*/
+GST_BOILERPLATE (GstBMad, gst_mad, GstBaseAudioDecoder,
+    GST_TYPE_BASE_AUDIO_DECODER);
 
-#define GST_TYPE_MAD_MODE (gst_mad_mode_get_type())
-static GType
-gst_mad_mode_get_type (void)
+static void
+gst_mad_base_init (gpointer g_class)
 {
-  static GType mad_mode_type = 0;
-  static GEnumValue mad_mode[] = {
-    {-1, "Unknown", "unknown"},
-    {MAD_MODE_SINGLE_CHANNEL, "Mono", "mono"},
-    {MAD_MODE_DUAL_CHANNEL, "Dual Channel", "dual"},
-    {MAD_MODE_JOINT_STEREO, "Joint Stereo", "joint"},
-    {MAD_MODE_STEREO, "Stereo", "stereo"},
-    {0, NULL, NULL},
-  };
-
-  if (!mad_mode_type) {
-    mad_mode_type = g_enum_register_static ("GstMadMode", mad_mode);
-  }
-  return mad_mode_type;
-}
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
-#define GST_TYPE_MAD_EMPHASIS (gst_mad_emphasis_get_type())
-static GType
-gst_mad_emphasis_get_type (void)
-{
-  static GType mad_emphasis_type = 0;
-  static GEnumValue mad_emphasis[] = {
-    {-1, "Unknown", "unknown"},
-    {MAD_EMPHASIS_NONE, "None", "none"},
-    {MAD_EMPHASIS_50_15_US, "50/15 Microseconds", "50-15"},
-    {MAD_EMPHASIS_CCITT_J_17, "CCITT J.17", "j-17"},
-    {MAD_EMPHASIS_RESERVED, "Reserved", "reserved"},
-    {0, NULL, NULL},
-  };
-
-  if (!mad_emphasis_type) {
-    mad_emphasis_type = g_enum_register_static ("GstMadEmphasis", mad_emphasis);
-  }
-  return mad_emphasis_type;
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&mad_sink_template_factory));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&mad_src_template_factory));
+  gst_element_class_set_details_simple (element_class, "mad mp3 decoder",
+      "Codec/Decoder/Audio",
+      "Uses mad code to decode mp3 streams", "Wim Taymans <wim@fluendo.com>");
 }
 
 static void
 gst_mad_class_init (GstMadClass * klass)
 {
-  GObjectClass *gobject_class;
-  GstElementClass *gstelement_class;
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstBaseAudioDecoderClass *base_class = (GstBaseAudioDecoderClass *) klass;
 
-  gobject_class = (GObjectClass *) klass;
-  gstelement_class = (GstElementClass *) klass;
+  parent_class = g_type_class_peek_parent (klass);
+
+  base_class->start = GST_DEBUG_FUNCPTR (gst_mad_start);
+  base_class->stop = GST_DEBUG_FUNCPTR (gst_mad_stop);
+  base_class->parse = GST_DEBUG_FUNCPTR (gst_mad_parse);
+  base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_mad_handle_frame);
+  base_class->flush = GST_DEBUG_FUNCPTR (gst_mad_flush);
 
   gobject_class->set_property = gst_mad_set_property;
   gobject_class->get_property = gst_mad_get_property;
-  gobject_class->dispose = gst_mad_dispose;
 
   /* init properties */
   /* currently, string representations are used, we might want to change that */
@@ -185,602 +137,59 @@ gst_mad_class_init (GstMadClass * klass)
   g_object_class_install_property (gobject_class, ARG_IGNORE_CRC,
       g_param_spec_boolean ("ignore-crc", "Ignore CRC", "Ignore CRC errors",
           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
-  gstelement_class->change_state = gst_mad_change_state;
-  gstelement_class->set_index = gst_mad_set_index;
-  gstelement_class->get_index = gst_mad_get_index;
-
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&mad_sink_template_factory));
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&mad_src_template_factory));
-
-  gst_element_class_set_details_simple (gstelement_class, "mad mp3 decoder",
-      "Codec/Decoder/Audio",
-      "Uses mad code to decode mp3 streams", "Wim Taymans <wim@fluendo.com>");
-
-  /* register tags */
-#define GST_TAG_LAYER    "layer"
-#define GST_TAG_MODE     "mode"
-#define GST_TAG_EMPHASIS "emphasis"
-
-  /* FIXME 0.11: strings!? why? */
-  gst_tag_register (GST_TAG_LAYER, GST_TAG_FLAG_ENCODED, G_TYPE_UINT,
-      "layer", "MPEG audio layer", NULL);
-  gst_tag_register (GST_TAG_MODE, GST_TAG_FLAG_ENCODED, G_TYPE_STRING,
-      "mode", "MPEG audio channel mode", NULL);
-  gst_tag_register (GST_TAG_EMPHASIS, GST_TAG_FLAG_ENCODED, G_TYPE_STRING,
-      "emphasis", "MPEG audio emphasis", NULL);
-
-  /* ref these here from a thread-safe context (ie. not the streaming thread) */
-  g_type_class_ref (GST_TYPE_MAD_MODE);
-  g_type_class_ref (GST_TYPE_MAD_EMPHASIS);
 }
 
 static void
-gst_mad_init (GstMad * mad)
+gst_mad_init (GstMad * mad, GstMadClass * klass)
 {
-  GstPadTemplate *template;
-
-  /* create the sink and src pads */
-  template = gst_static_pad_template_get (&mad_sink_template_factory);
-  mad->sinkpad = gst_pad_new_from_template (template, "sink");
-  gst_object_unref (template);
-  gst_element_add_pad (GST_ELEMENT (mad), mad->sinkpad);
-  gst_pad_set_chain_function (mad->sinkpad, GST_DEBUG_FUNCPTR (gst_mad_chain));
-  gst_pad_set_event_function (mad->sinkpad,
-      GST_DEBUG_FUNCPTR (gst_mad_sink_event));
-
-  template = gst_static_pad_template_get (&mad_src_template_factory);
-  mad->srcpad = gst_pad_new_from_template (template, "src");
-  gst_object_unref (template);
-  gst_element_add_pad (GST_ELEMENT (mad), mad->srcpad);
-  gst_pad_set_event_function (mad->srcpad,
-      GST_DEBUG_FUNCPTR (gst_mad_src_event));
-  gst_pad_set_query_function (mad->srcpad,
-      GST_DEBUG_FUNCPTR (gst_mad_src_query));
-  gst_pad_use_fixed_caps (mad->srcpad);
-
-  mad->tempbuffer = g_malloc (MAD_BUFFER_MDLEN * 3);
-  mad->tempsize = 0;
-  mad->base_byte_offset = 0;
-  mad->bytes_consumed = 0;
-  mad->total_samples = 0;
-  mad->new_header = TRUE;
-  mad->framecount = 0;
-  mad->vbr_average = 0;
-  mad->vbr_rate = 0;
-  mad->restart = TRUE;
-  mad->segment_start = 0;
-  gst_segment_init (&mad->segment, GST_FORMAT_TIME);
-  mad->header.mode = -1;
-  mad->header.emphasis = -1;
-  mad->tags = NULL;
+  GstBaseAudioDecoder *dec;
+
+  dec = GST_BASE_AUDIO_DECODER (mad);
+  gst_base_audio_decoder_set_tolerance (dec, 20 * GST_MSECOND);
 
   mad->half = FALSE;
   mad->ignore_crc = TRUE;
-  mad->check_for_xing = TRUE;
-  mad->xing_found = FALSE;
-}
-
-static void
-gst_mad_dispose (GObject * object)
-{
-  GstMad *mad = GST_MAD (object);
-
-  gst_mad_set_index (GST_ELEMENT (object), NULL);
-
-  g_free (mad->tempbuffer);
-  mad->tempbuffer = NULL;
-
-  g_list_foreach (mad->pending_events, (GFunc) gst_mini_object_unref, NULL);
-  g_list_free (mad->pending_events);
-  mad->pending_events = NULL;
-
-  G_OBJECT_CLASS (parent_class)->dispose (object);
-}
-
-static void
-gst_mad_set_index (GstElement * element, GstIndex * index)
-{
-  GstMad *mad = GST_MAD (element);
-
-  mad->index = index;
-
-  if (index)
-    gst_index_get_writer_id (index, GST_OBJECT (element), &mad->index_id);
-}
-
-static GstIndex *
-gst_mad_get_index (GstElement * element)
-{
-  GstMad *mad = GST_MAD (element);
-
-  return mad->index;
-}
-
-static gboolean
-gst_mad_convert_sink (GstPad * pad, GstFormat src_format, gint64 src_value,
-    GstFormat * dest_format, gint64 * dest_value)
-{
-  gboolean res = TRUE;
-  GstMad *mad;
-
-  if (src_format == *dest_format) {
-    *dest_value = src_value;
-    return TRUE;
-  }
-
-  /* -1 always maps to -1, and 0 to 0, we don't need any more info for that */
-  if (src_value == -1 || src_value == 0) {
-    *dest_value = src_value;
-    return TRUE;
-  }
-
-  mad = GST_MAD (GST_PAD_PARENT (pad));
-
-  if (mad->vbr_average == 0)
-    return FALSE;
-
-  switch (src_format) {
-    case GST_FORMAT_BYTES:
-      switch (*dest_format) {
-        case GST_FORMAT_TIME:
-          /* multiply by 8 because vbr is in bits/second */
-          *dest_value = gst_util_uint64_scale (src_value, 8 * GST_SECOND,
-              mad->vbr_average);
-          break;
-        default:
-          res = FALSE;
-      }
-      break;
-    case GST_FORMAT_TIME:
-      switch (*dest_format) {
-        case GST_FORMAT_BYTES:
-          /* multiply by 8 because vbr is in bits/second */
-          *dest_value = gst_util_uint64_scale (src_value, mad->vbr_average,
-              8 * GST_SECOND);
-          break;
-        default:
-          res = FALSE;
-      }
-      break;
-    default:
-      res = FALSE;
-  }
-  return res;
-}
-
-static gboolean
-gst_mad_convert_src (GstPad * pad, GstFormat src_format, gint64 src_value,
-    GstFormat * dest_format, gint64 * dest_value)
-{
-  gboolean res = TRUE;
-  guint scale = 1;
-  gint bytes_per_sample;
-  GstMad *mad;
-
-  if (src_format == *dest_format) {
-    *dest_value = src_value;
-    return TRUE;
-  }
-
-  /* -1 always maps to -1, and 0 to 0, we don't need any more info for that */
-  if (src_value == -1 || src_value == 0) {
-    *dest_value = src_value;
-    return TRUE;
-  }
-
-  mad = GST_MAD (GST_PAD_PARENT (pad));
-
-  bytes_per_sample = mad->channels * 4;
-
-  switch (src_format) {
-    case GST_FORMAT_BYTES:
-      switch (*dest_format) {
-        case GST_FORMAT_DEFAULT:
-          if (bytes_per_sample == 0)
-            return FALSE;
-          *dest_value = src_value / bytes_per_sample;
-          break;
-        case GST_FORMAT_TIME:
-        {
-          gint byterate = bytes_per_sample * mad->rate;
-
-          if (byterate == 0)
-            return FALSE;
-          *dest_value =
-              gst_util_uint64_scale_int (src_value, GST_SECOND, byterate);
-          break;
-        }
-        default:
-          res = FALSE;
-      }
-      break;
-    case GST_FORMAT_DEFAULT:
-      switch (*dest_format) {
-        case GST_FORMAT_BYTES:
-          *dest_value = src_value * bytes_per_sample;
-          break;
-        case GST_FORMAT_TIME:
-          if (mad->rate == 0)
-            return FALSE;
-          *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
-              mad->rate);
-          break;
-        default:
-          res = FALSE;
-      }
-      break;
-    case GST_FORMAT_TIME:
-      switch (*dest_format) {
-        case GST_FORMAT_BYTES:
-          scale = bytes_per_sample;
-          /* fallthrough */
-        case GST_FORMAT_DEFAULT:
-          *dest_value = gst_util_uint64_scale_int (src_value,
-              scale * mad->rate, GST_SECOND);
-          break;
-        default:
-          res = FALSE;
-      }
-      break;
-    default:
-      res = FALSE;
-  }
-  return res;
 }
 
 static gboolean
-gst_mad_src_query (GstPad * pad, GstQuery * query)
+gst_mad_start (GstBaseAudioDecoder * dec)
 {
-  gboolean res = TRUE;
-  GstPad *peer;
-  GstMad *mad;
-
-  mad = GST_MAD (GST_PAD_PARENT (pad));
-
-  peer = gst_pad_get_peer (mad->sinkpad);
-
-  switch (GST_QUERY_TYPE (query)) {
-    case GST_QUERY_FORMATS:
-      gst_query_set_formats (query, 3, GST_FORMAT_DEFAULT, GST_FORMAT_TIME,
-          GST_FORMAT_BYTES);
-      break;
-    case GST_QUERY_POSITION:
-    {
-      GstFormat format;
-      gint64 cur;
-
-      /* save requested format */
-      gst_query_parse_position (query, &format, NULL);
-
-      /* try any demuxer before us first */
-      if (format == GST_FORMAT_TIME && peer && gst_pad_query (peer, query)) {
-        gst_query_parse_position (query, NULL, &cur);
-        GST_LOG_OBJECT (mad, "peer returned position %" GST_TIME_FORMAT,
-            GST_TIME_ARGS (cur));
-        break;
-      }
-
-      /* and convert to the requested format */
-      if (format != GST_FORMAT_DEFAULT) {
-        if (!gst_mad_convert_src (pad, GST_FORMAT_DEFAULT, mad->total_samples,
-                &format, &cur))
-          goto error;
-      } else {
-        cur = mad->total_samples;
-      }
-
-      gst_query_set_position (query, format, cur);
-
-      if (format == GST_FORMAT_TIME) {
-        GST_LOG ("position=%" GST_TIME_FORMAT, GST_TIME_ARGS (cur));
-      } else {
-        GST_LOG ("position=%" G_GINT64_FORMAT ", format=%u", cur, format);
-      }
-      break;
-    }
-    case GST_QUERY_DURATION:
-    {
-      GstFormat time_format = GST_FORMAT_TIME;
-      GstFormat req_format;
-      gint64 total, total_bytes;
-
-      /* save requested format */
-      gst_query_parse_duration (query, &req_format, NULL);
-
-      if (peer == NULL)
-        goto error;
-
-      /* try any demuxer before us first */
-      if (req_format == GST_FORMAT_TIME && gst_pad_query (peer, query)) {
-        gst_query_parse_duration (query, NULL, &total);
-        GST_LOG_OBJECT (mad, "peer returned duration %" GST_TIME_FORMAT,
-            GST_TIME_ARGS (total));
-        break;
-      }
-
-      /* query peer for total length in bytes */
-      if (!gst_pad_query_peer_duration (mad->sinkpad, GST_FORMAT_BYTES,
-              &total_bytes) || total_bytes <= 0) {
-        GST_LOG_OBJECT (mad, "duration query on peer pad failed");
-        goto error;
-      }
-
-      GST_LOG_OBJECT (mad, "peer pad returned total=%" G_GINT64_FORMAT
-          " bytes", total_bytes);
-
-      if (!gst_mad_convert_sink (pad, GST_FORMAT_BYTES, total_bytes,
-              &time_format, &total)) {
-        GST_DEBUG_OBJECT (mad, "conversion BYTE => TIME failed");
-        goto error;
-      }
-      if (!gst_mad_convert_src (pad, GST_FORMAT_TIME, total,
-              &req_format, &total)) {
-        GST_DEBUG_OBJECT (mad, "conversion TIME => %s failed",
-            gst_format_get_name (req_format));
-        goto error;
-      }
-
-      gst_query_set_duration (query, req_format, total);
-
-      if (req_format == GST_FORMAT_TIME) {
-        GST_LOG_OBJECT (mad, "duration=%" GST_TIME_FORMAT,
-            GST_TIME_ARGS (total));
-      } else {
-        GST_LOG_OBJECT (mad, "duration=%" G_GINT64_FORMAT " (%s)",
-            total, gst_format_get_name (req_format));
-      }
-      break;
-    }
-    case GST_QUERY_CONVERT:
-    {
-      GstFormat src_fmt, dest_fmt;
-      gint64 src_val, dest_val;
-
-      gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
-      if (!(res =
-              gst_mad_convert_src (pad, src_fmt, src_val, &dest_fmt,
-                  &dest_val)))
-        goto error;
-      gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
-      break;
-    }
-    default:
-      res = gst_pad_query_default (pad, query);
-      break;
-  }
-
-  if (peer)
-    gst_object_unref (peer);
-
-  return res;
-
-error:
-
-  GST_DEBUG ("error handling query");
-
-  if (peer)
-    gst_object_unref (peer);
-
-  return FALSE;
-}
-
-static gboolean
-index_seek (GstMad * mad, GstPad * pad, GstEvent * event)
-{
-  gdouble rate;
-  GstFormat format;
-  GstSeekFlags flags;
-  GstSeekType cur_type, stop_type;
-  gint64 cur, stop;
-  GstIndexEntry *entry = NULL;
-
-  /* since we know the exact byteoffset of the frame,
-     make sure to try bytes first */
-
-  const GstFormat try_all_formats[] = {
-    GST_FORMAT_BYTES,
-    GST_FORMAT_TIME,
-    0
-  };
-  const GstFormat *try_formats = try_all_formats;
-  const GstFormat *peer_formats;
-
-  gst_event_parse_seek (event, &rate, &format, &flags,
-      &cur_type, &cur, &stop_type, &stop);
-
-  if (rate < 0.0)
-    return FALSE;
-
-  if (format == GST_FORMAT_TIME) {
-    gst_segment_do_seek (&mad->segment, rate, format, flags, cur_type,
-        cur, stop_type, stop, NULL);
-  } else {
-    gst_segment_init (&mad->segment, GST_FORMAT_UNDEFINED);
-  }
-
-  entry = gst_index_get_assoc_entry (mad->index, mad->index_id,
-      GST_INDEX_LOOKUP_BEFORE, 0, format, cur);
-
-  GST_DEBUG ("index seek");
-
-  if (!entry)
-    return FALSE;
-
-#if 0
-  peer_formats = gst_pad_get_formats (GST_PAD_PEER (mad->sinkpad));
-#else
-  peer_formats = try_all_formats;       /* FIXME */
-#endif
-
-  while (gst_formats_contains (peer_formats, *try_formats)) {
-    gint64 value;
-    GstEvent *seek_event;
-
-    if (gst_index_entry_assoc_map (entry, *try_formats, &value)) {
-      /* lookup succeeded, create the seek */
-
-      GST_DEBUG ("index %s %" G_GINT64_FORMAT
-          " -> %s %" G_GINT64_FORMAT,
-          gst_format_get_details (format)->nick,
-          cur, gst_format_get_details (*try_formats)->nick, value);
-
-      seek_event = gst_event_new_seek (rate, *try_formats, flags,
-          cur_type, value, stop_type, stop);
-
-      if (gst_pad_send_event (GST_PAD_PEER (mad->sinkpad), seek_event)) {
-        /* seek worked, we're done, loop will exit */
-        mad->restart = TRUE;
-        g_assert (format == GST_FORMAT_TIME);
-        mad->segment_start = cur;
-        return TRUE;
-      }
-    }
-    try_formats++;
-  }
-
-  return FALSE;
-}
-
-static gboolean
-normal_seek (GstMad * mad, GstPad * pad, GstEvent * event)
-{
-  gdouble rate;
-  GstFormat format, conv;
-  GstSeekFlags flags;
-  GstSeekType cur_type, stop_type;
-  gint64 cur, stop;
-  gint64 time_cur, time_stop;
-  gint64 bytes_cur, bytes_stop;
-  gboolean flush;
-
-  /* const GstFormat *peer_formats; */
-  gboolean res;
-
-  GST_DEBUG ("normal seek");
-
-  gst_event_parse_seek (event, &rate, &format, &flags,
-      &cur_type, &cur, &stop_type, &stop);
-
-  if (rate < 0.0)
-    return FALSE;
-
-  if (format != GST_FORMAT_TIME) {
-    conv = GST_FORMAT_TIME;
-    if (!gst_mad_convert_src (pad, format, cur, &conv, &time_cur))
-      goto convert_error;
-    if (!gst_mad_convert_src (pad, format, stop, &conv, &time_stop))
-      goto convert_error;
-  } else {
-    time_cur = cur;
-    time_stop = stop;
-  }
-
-  gst_segment_do_seek (&mad->segment, rate, GST_FORMAT_TIME, flags, cur_type,
-      time_cur, stop_type, time_stop, NULL);
-
-  GST_DEBUG ("seek to time %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
-      GST_TIME_ARGS (time_cur), GST_TIME_ARGS (time_stop));
-
-  /* shave off the flush flag, we'll need it later */
-  flush = ((flags & GST_SEEK_FLAG_FLUSH) != 0);
-
-  conv = GST_FORMAT_BYTES;
-  if (!gst_mad_convert_sink (pad, GST_FORMAT_TIME, time_cur, &conv, &bytes_cur))
-    goto convert_error;
-  if (!gst_mad_convert_sink (pad, GST_FORMAT_TIME, time_stop, &conv,
-          &bytes_stop))
-    goto convert_error;
-
-  {
-    GstEvent *seek_event;
-
-    /* conversion succeeded, create the seek */
-    seek_event =
-        gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type,
-        bytes_cur, stop_type, bytes_stop);
-
-    /* do the seek */
-    res = gst_pad_push_event (mad->sinkpad, seek_event);
-
-    if (res) {
-      /* we need to break out of the processing loop on flush */
-      mad->restart = flush;
-      mad->segment_start = time_cur;
-      mad->last_ts = time_cur;
-    }
-  }
-#if 0
-  peer_formats = gst_pad_get_formats (GST_PAD_PEER (mad->sinkpad));
-  /* while we did not exhaust our seek formats without result */
-  while (peer_formats && *peer_formats && !res) {
-    gint64 desired_offset;
-
-    format = *peer_formats;
-
-    /* try to convert requested format to one we can seek with on the sinkpad */
-    if (gst_pad_convert (mad->sinkpad, GST_FORMAT_TIME, src_offset,
-            &format, &desired_offset)) {
-      GstEvent *seek_event;
-
-      /* conversion succeeded, create the seek */
-      seek_event =
-          gst_event_new_seek (format | GST_EVENT_SEEK_METHOD (event) | flush,
-          desired_offset);
-      /* do the seek */
-      if (gst_pad_send_event (GST_PAD_PEER (mad->sinkpad), seek_event)) {
-        /* seek worked, we're done, loop will exit */
-        res = TRUE;
-      }
-    }
-    /* at this point, either the seek worked or res == FALSE */
-    if (res)
-      /* we need to break out of the processing loop on flush */
-      mad->restart = flush;
-
-    peer_formats++;
-  }
-#endif
+  GstMad *mad = GST_MAD (dec);
+  guint options = 0;
+
+  GST_DEBUG_OBJECT (dec, "start");
+  mad_stream_init (&mad->stream);
+  mad_frame_init (&mad->frame);
+  mad_synth_init (&mad->synth);
+  mad->rate = 0;
+  mad->channels = 0;
+  mad->caps_set = FALSE;
+  mad->frame.header.samplerate = 0;
+  if (mad->ignore_crc)
+    options |= MAD_OPTION_IGNORECRC;
+  if (mad->half)
+    options |= MAD_OPTION_HALFSAMPLERATE;
+  mad_stream_options (&mad->stream, options);
+  mad->header.mode = -1;
+  mad->header.emphasis = -1;
 
-  return res;
+  /* call upon legacy upstream byte support (e.g. seeking) */
+  gst_base_audio_decoder_set_byte_time (dec, TRUE);
 
-  /* ERRORS */
-convert_error:
-  {
-    /* probably unsupported seek format */
-    GST_DEBUG ("failed to convert format %u into GST_FORMAT_TIME", format);
-    return FALSE;
-  }
+  return TRUE;
 }
 
 static gboolean
-gst_mad_src_event (GstPad * pad, GstEvent * event)
+gst_mad_stop (GstBaseAudioDecoder * dec)
 {
-  gboolean res = TRUE;
-  GstMad *mad;
+  GstMad *mad = GST_MAD (dec);
 
-  mad = GST_MAD (GST_PAD_PARENT (pad));
-
-  switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_SEEK:
-      /* the all-formats seek logic, ref the event, we need it later */
-      gst_event_ref (event);
-      if (!(res = gst_pad_push_event (mad->sinkpad, event))) {
-        if (mad->index)
-          res = index_seek (mad, pad, event);
-        else
-          res = normal_seek (mad, pad, event);
-      }
-      gst_event_unref (event);
-      break;
-    default:
-      res = gst_pad_push_event (mad->sinkpad, event);
-      break;
-  }
+  GST_DEBUG_OBJECT (dec, "stop");
+  mad_synth_finish (&mad->synth);
+  mad_frame_finish (&mad->frame);
+  mad_stream_finish (&mad->stream);
 
-  return res;
+  return TRUE;
 }
 
 static inline gint32
@@ -806,380 +215,13 @@ scale (mad_fixed_t sample)
   return (gint32) (sample << 3);
 }
 
-/* do we need this function? */
-static void
-gst_mad_set_property (GObject * object, guint prop_id,
-    const GValue * value, GParamSpec * pspec)
-{
-  GstMad *mad;
-
-  mad = GST_MAD (object);
-
-  switch (prop_id) {
-    case ARG_HALF:
-      mad->half = g_value_get_boolean (value);
-      break;
-    case ARG_IGNORE_CRC:
-      mad->ignore_crc = g_value_get_boolean (value);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
-}
-
-static void
-gst_mad_get_property (GObject * object, guint prop_id,
-    GValue * value, GParamSpec * pspec)
-{
-  GstMad *mad;
-
-  mad = GST_MAD (object);
-
-  switch (prop_id) {
-    case ARG_HALF:
-      g_value_set_boolean (value, mad->half);
-      break;
-    case ARG_IGNORE_CRC:
-      g_value_set_boolean (value, mad->ignore_crc);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
-}
-
-static void
-gst_mad_update_info (GstMad * mad)
-{
-  struct mad_header *header = &mad->frame.header;
-  gboolean changed = FALSE;
-  GstTagList *list = NULL;
-
-#define CHECK_HEADER(h1,str)                                    \
-G_STMT_START{                                                   \
-  if (mad->header.h1 != header->h1 || mad->new_header) {        \
-    mad->header.h1 = header->h1;                                \
-     changed = TRUE;                                            \
-  };                                                            \
-} G_STMT_END
-
-  /* update average bitrate */
-  if (mad->new_header) {
-    mad->framecount = 1;
-    mad->vbr_rate = header->bitrate;
-  } else {
-    mad->framecount++;
-    mad->vbr_rate += header->bitrate;
-  }
-  mad->vbr_average = (gint) (mad->vbr_rate / mad->framecount);
-
-  CHECK_HEADER (layer, "layer");
-  CHECK_HEADER (mode, "mode");
-  CHECK_HEADER (emphasis, "emphasis");
-  mad->new_header = FALSE;
-
-  if (changed) {
-    GEnumValue *mode;
-    GEnumValue *emphasis;
-
-    mode =
-        g_enum_get_value (g_type_class_peek (GST_TYPE_MAD_MODE),
-        mad->header.mode);
-    emphasis =
-        g_enum_get_value (g_type_class_peek (GST_TYPE_MAD_EMPHASIS),
-        mad->header.emphasis);
-    list = gst_tag_list_new (GST_TAG_LAYER, mad->header.layer,
-        GST_TAG_MODE, mode->value_nick,
-        GST_TAG_EMPHASIS, emphasis->value_nick, NULL);
-    if (!mad->framed) {
-      gchar *str;
-
-      str = g_strdup_printf ("MPEG-1 layer %d", mad->header.layer);
-      gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
-          GST_TAG_AUDIO_CODEC, str, NULL);
-      g_free (str);
-    }
-  }
-
-  changed = FALSE;
-  CHECK_HEADER (bitrate, "bitrate");
-  if (!mad->xing_found && changed) {
-    if (!list)
-      list = gst_tag_list_new_empty ();
-    gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
-        GST_TAG_BITRATE, mad->header.bitrate, NULL);
-  }
-  mad->header.bitrate = header->bitrate;
-#undef CHECK_HEADER
-
-  if (list) {
-    gst_element_post_message (GST_ELEMENT (mad),
-        gst_message_new_tag (GST_OBJECT (mad), gst_tag_list_copy (list)));
-
-    if (mad->need_newsegment)
-      mad->pending_events =
-          g_list_append (mad->pending_events, gst_event_new_tag (list));
-    else
-      gst_pad_push_event (mad->srcpad, gst_event_new_tag (list));
-  }
-}
-
-static gboolean
-gst_mad_sink_event (GstPad * pad, GstEvent * event)
-{
-  GstMad *mad = GST_MAD (GST_PAD_PARENT (pad));
-  gboolean result;
-
-  GST_DEBUG ("handling %s event", GST_EVENT_TYPE_NAME (event));
-
-  switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_SEGMENT:
-    {
-      GstSegment segment;
-
-      gst_event_copy_segment (event, &segment);
-
-      if (segment.format == GST_FORMAT_TIME) {
-        /* FIXME: is this really correct? */
-        mad->tempsize = 0;
-        result = gst_pad_push_event (mad->srcpad, event);
-        /* we don't need to restart when we get here */
-        mad->restart = FALSE;
-        mad->framed = TRUE;
-        gst_segment_copy_into (&segment, &mad->segment);
-      } else {
-        GST_DEBUG ("dropping segment event in format %s",
-            gst_format_get_name (segment.format));
-        /* on restart the chain function will generate a new
-         * newsegment event, so we can just drop this one */
-        mad->restart = TRUE;
-        gst_event_unref (event);
-        mad->tempsize = 0;
-        mad->framed = FALSE;
-        result = TRUE;
-      }
-      break;
-    }
-    case GST_EVENT_EOS:
-      if (mad->segment.rate < 0.0)
-        gst_mad_chain_reverse (mad, NULL);
-      mad->caps_set = FALSE;    /* could be a new stream */
-      result = gst_pad_push_event (mad->srcpad, event);
-      break;
-    case GST_EVENT_FLUSH_STOP:
-      /* Clear any stored data, as it won't make sense once
-       * the new data arrives */
-      mad->tempsize = 0;
-      mad_frame_mute (&mad->frame);
-      mad_synth_mute (&mad->synth);
-      gst_mad_clear_queues (mad);
-      /* fall-through */
-    case GST_EVENT_FLUSH_START:
-      result = gst_pad_event_default (pad, event);
-      break;
-    case GST_EVENT_CAPS:
-      GST_DEBUG ("dropping caps event");
-      gst_event_unref (event);
-      result = TRUE;
-      break;
-    default:
-      if (mad->restart) {
-        /* Cache all other events if we still have to send a NEWSEGMENT */
-        mad->pending_events = g_list_append (mad->pending_events, event);
-        result = TRUE;
-      } else {
-        result = gst_pad_event_default (pad, event);
-      }
-      break;
-  }
-  return result;
-}
-
-static gboolean
-gst_mad_check_restart (GstMad * mad)
-{
-  gboolean yes = mad->restart;
-
-  if (mad->restart) {
-    mad->restart = FALSE;
-    mad->tempsize = 0;
-  }
-  return yes;
-}
-
-
-/* The following code has been taken from
- * rhythmbox/metadata/monkey-media/stream-info-impl/id3-vfs/mp3bitrate.c
- * which took it from xine-lib/src/demuxers/demux_mpgaudio.c
- * This code has been kindly relicensed to LGPL by Thibaut Mattern and
- * Bastien Nocera
- */
-#define BE_32(x) GST_READ_UINT32_BE(x)
-
-#define FOURCC_TAG( ch0, ch1, ch2, ch3 )                \
-        ( (long)(unsigned char)(ch3) |                  \
-          ( (long)(unsigned char)(ch2) << 8 ) |         \
-          ( (long)(unsigned char)(ch1) << 16 ) |        \
-          ( (long)(unsigned char)(ch0) << 24 ) )
-
-/* Xing header stuff */
-#define XING_TAG FOURCC_TAG('X', 'i', 'n', 'g')
-#define XING_FRAMES_FLAG     0x0001
-#define XING_BYTES_FLAG      0x0002
-#define XING_TOC_FLAG        0x0004
-#define XING_VBR_SCALE_FLAG  0x0008
-#define XING_TOC_LENGTH      100
-
-/* check for valid "Xing" VBR header */
-static int
-is_xhead (unsigned char *buf)
-{
-  return (BE_32 (buf) == XING_TAG);
-}
-
-
-#undef LOG
-/*#define LOG*/
-#ifdef LOG
-#ifndef WIN32
-#define lprintf(x...) g_print(x)
-#else
-#define lprintf GST_DEBUG
-#endif
-#else
-#ifndef WIN32
-#define lprintf(x...)
-#else
-#define lprintf GST_DEBUG
-#endif
-#endif
-
-static int
-mpg123_parse_xing_header (struct mad_header *header,
-    const guint8 * buf, int bufsize, int *bitrate, int *time)
-{
-  int i;
-  guint8 *ptr = (guint8 *) buf;
-  double frame_duration;
-  int xflags, xframes, xbytes;
-  int abr;
-  guint8 xtoc[XING_TOC_LENGTH];
-  int lsf_bit = !(header->flags & MAD_FLAG_LSF_EXT);
-
-  xframes = xbytes = 0;
-
-  /* offset of the Xing header */
-  if (lsf_bit) {
-    if (header->mode != MAD_MODE_SINGLE_CHANNEL)
-      ptr += (32 + 4);
-    else
-      ptr += (17 + 4);
-  } else {
-    if (header->mode != MAD_MODE_SINGLE_CHANNEL)
-      ptr += (17 + 4);
-    else
-      ptr += (9 + 4);
-  }
-
-  if (ptr >= (buf + bufsize - 4))
-    return 0;
-
-  if (is_xhead (ptr)) {
-    lprintf ("Xing header found\n");
-
-    ptr += 4;
-    if (ptr >= (buf + bufsize - 4))
-      return 0;
-
-    xflags = BE_32 (ptr);
-    ptr += 4;
-
-    if (xflags & XING_FRAMES_FLAG) {
-      if (ptr >= (buf + bufsize - 4))
-        return 0;
-      xframes = BE_32 (ptr);
-      lprintf ("xframes: %d\n", xframes);
-      ptr += 4;
-    }
-    if (xflags & XING_BYTES_FLAG) {
-      if (ptr >= (buf + bufsize - 4))
-        return 0;
-      xbytes = BE_32 (ptr);
-      lprintf ("xbytes: %d\n", xbytes);
-      ptr += 4;
-    }
-    if (xflags & XING_TOC_FLAG) {
-      guchar old = 0;
-
-      lprintf ("toc found\n");
-      if (ptr >= (buf + bufsize - XING_TOC_LENGTH))
-        return 0;
-      if (*ptr != 0) {
-        lprintf ("skipping broken Xing TOC\n");
-        goto skip_toc;
-      }
-      for (i = 0; i < XING_TOC_LENGTH; i++) {
-        xtoc[i] = *(ptr + i);
-        if (old > xtoc[i]) {
-          lprintf ("skipping broken Xing TOC\n");
-          goto skip_toc;
-        }
-        lprintf ("%d ", xtoc[i]);
-      }
-      lprintf ("\n");
-    skip_toc:
-      ptr += XING_TOC_LENGTH;
-    }
-
-    if (xflags & XING_VBR_SCALE_FLAG) {
-      if (ptr >= (buf + bufsize - 4))
-        return 0;
-      lprintf ("xvbr_scale: %d\n", BE_32 (ptr));
-    }
-
-    /* 1 kbit = 1000 bits ! (and not 1024 bits) */
-    if (xflags & (XING_FRAMES_FLAG | XING_BYTES_FLAG)) {
-      if (header->layer == MAD_LAYER_I) {
-        frame_duration = 384.0 / (double) header->samplerate;
-      } else {
-        int slots_per_frame;
-
-        slots_per_frame = ((header->layer == MAD_LAYER_III)
-            && !lsf_bit) ? 72 : 144;
-        frame_duration = slots_per_frame * 8.0 / (double) header->samplerate;
-      }
-      abr = ((double) xbytes * 8.0) / ((double) xframes * frame_duration);
-      lprintf ("abr: %d bps\n", abr);
-      if (bitrate != NULL) {
-        *bitrate = abr;
-      }
-      if (time != NULL) {
-        *time = (double) xframes *frame_duration;
-
-        lprintf ("stream_length: %d s, %d min %d s\n", *time,
-            *time / 60, *time % 60);
-      }
-    } else {
-      /* it's a stupid Xing header */
-      lprintf ("not a Xing VBR file\n");
-    }
-    return 1;
-  } else {
-    lprintf ("Xing header not found\n");
-    return 0;
-  }
-}
-
-/* End of Xine code */
-
 /* internal function to check if the header has changed and thus the
  * caps need to be reset.  Only call during normal mode, not resyncing */
 static void
 gst_mad_check_caps_reset (GstMad * mad)
 {
   guint nchannels;
-  guint rate, old_rate = mad->rate;
+  guint rate;
 
   nchannels = MAD_NCHANNELS (&mad->frame.header);
 
@@ -1194,10 +236,12 @@ gst_mad_check_caps_reset (GstMad * mad)
 
   /* only set caps if they weren't already set for this continuous stream */
   if (mad->channels != nchannels || mad->rate != rate) {
+    GstCaps *caps;
+
     if (mad->caps_set) {
-      GST_DEBUG
-          ("Header changed from %d Hz/%d ch to %d Hz/%d ch, failed sync after seek ?",
-          mad->rate, mad->channels, rate, nchannels);
+      GST_DEBUG_OBJECT (mad, "Header changed from %d Hz/%d ch to %d Hz/%d ch, "
+          "failed sync after seek ?", mad->rate, mad->channels, rate,
+          nchannels);
       /* we're conservative on stream changes. However, our *initial* caps
        * might have been wrong as well - mad ain't perfect in syncing. So,
        * we count caps changes and change if we pass a limit treshold (3). */
@@ -1209,637 +253,283 @@ gst_mad_check_caps_reset (GstMad * mad)
       if (++mad->times_pending < 3)
         return;
     }
-  }
-  gst_mad_update_info (mad);
-
-  if (mad->channels != nchannels || mad->rate != rate) {
-    GstCaps *caps;
 
     if (mad->stream.options & MAD_OPTION_HALFSAMPLERATE)
       rate >>= 1;
 
-    /* FIXME see if peer can accept the caps */
-
     /* we set the caps even when the pad is not connected so they
      * can be gotten for streaminfo */
-    caps = gst_caps_new_simple ("audio/x-raw",
-        "format", G_TYPE_STRING, GST_AUDIO_NE (S32),
+    caps = gst_caps_new_simple ("audio/x-raw-int",
+        "endianness", G_TYPE_INT, G_BYTE_ORDER,
+        "signed", G_TYPE_BOOLEAN, TRUE,
+        "width", G_TYPE_INT, 32,
+        "depth", G_TYPE_INT, 32,
         "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, nchannels, NULL);
 
-    gst_pad_set_caps (mad->srcpad, caps);
+    gst_pad_set_caps (GST_BASE_AUDIO_DECODER_SRC_PAD (mad), caps);
     gst_caps_unref (caps);
 
-    mad->caps_set = TRUE;       /* set back to FALSE on discont */
+    mad->caps_set = TRUE;
     mad->channels = nchannels;
     mad->rate = rate;
-
-    /* update sample count so we don't come up with crazy timestamps */
-    if (mad->total_samples && old_rate) {
-      mad->total_samples = mad->total_samples * rate / old_rate;
-    }
   }
 }
 
-static void
-gst_mad_clear_queues (GstMad * mad)
-{
-  g_list_foreach (mad->queued, (GFunc) gst_mini_object_unref, NULL);
-  g_list_free (mad->queued);
-  mad->queued = NULL;
-  g_list_foreach (mad->gather, (GFunc) gst_mini_object_unref, NULL);
-  g_list_free (mad->gather);
-  mad->gather = NULL;
-  g_list_foreach (mad->decode, (GFunc) gst_mini_object_unref, NULL);
-  g_list_free (mad->decode);
-  mad->decode = NULL;
-}
-
 static GstFlowReturn
-gst_mad_flush_decode (GstMad * mad)
-{
-  GstFlowReturn res = GST_FLOW_OK;
-  GList *walk;
-
-  walk = mad->decode;
-
-  GST_DEBUG_OBJECT (mad, "flushing buffers to decoder");
-
-  /* clear buffer and decoder state */
-  mad->tempsize = 0;
-  mad_frame_mute (&mad->frame);
-  mad_synth_mute (&mad->synth);
-
-  mad->process = TRUE;
-  while (walk) {
-    GList *next;
-    GstBuffer *buf = GST_BUFFER_CAST (walk->data);
-
-    GST_DEBUG_OBJECT (mad, "decoding buffer %p, ts %" GST_TIME_FORMAT,
-        buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
-
-    next = g_list_next (walk);
-    /* decode buffer, resulting data prepended to output queue */
-    gst_buffer_ref (buf);
-    res = gst_mad_chain (mad->sinkpad, buf);
-
-    /* if we generated output, we can discard the buffer, else we
-     * keep it in the queue */
-    if (mad->queued) {
-      GST_DEBUG_OBJECT (mad, "decoded buffer to %p", mad->queued->data);
-      mad->decode = g_list_delete_link (mad->decode, walk);
-      gst_buffer_unref (buf);
-    } else {
-      GST_DEBUG_OBJECT (mad, "buffer did not decode, keeping");
-    }
-    walk = next;
-  }
-  mad->process = FALSE;
-
-  /* now send queued data downstream */
-  while (mad->queued) {
-    GstBuffer *buf = GST_BUFFER_CAST (mad->queued->data);
-
-    GST_DEBUG_OBJECT (mad, "pushing buffer %p of size %u, "
-        "time %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT, buf,
-        gst_buffer_get_size (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
-        GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
-    res = gst_pad_push (mad->srcpad, buf);
-
-    mad->queued = g_list_delete_link (mad->queued, mad->queued);
-  }
-
-  return res;
-}
-
-static GstFlowReturn
-gst_mad_chain_reverse (GstMad * mad, GstBuffer * buf)
-{
-  GstFlowReturn result = GST_FLOW_OK;
-
-  /* if we have a discont, move buffers to the decode list */
-  if (!buf || GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
-    GST_DEBUG_OBJECT (mad, "received discont");
-    while (mad->gather) {
-      GstBuffer *gbuf;
-
-      gbuf = GST_BUFFER_CAST (mad->gather->data);
-      /* remove from the gather list */
-      mad->gather = g_list_delete_link (mad->gather, mad->gather);
-      /* copy to decode queue */
-      mad->decode = g_list_prepend (mad->decode, gbuf);
-    }
-    /* decode stuff in the decode queue */
-    gst_mad_flush_decode (mad);
-  }
-
-  if (G_LIKELY (buf)) {
-    GST_DEBUG_OBJECT (mad, "gathering buffer %p of size %u, "
-        "time %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT, buf,
-        gst_buffer_get_size (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
-        GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
-
-    /* add buffer to gather queue */
-    mad->gather = g_list_prepend (mad->gather, buf);
-  }
-
-  return result;
-}
-
-static GstFlowReturn
-gst_mad_chain (GstPad * pad, GstBuffer * buffer)
+gst_mad_parse (GstBaseAudioDecoder * dec, GstAdapter * adapter,
+    gint * _offset, gint * len)
 {
   GstMad *mad;
-  guint8 *data, *bdata;
-  gsize bsize, size, tempsize;
-  gboolean new_pts = FALSE;
-  gboolean discont;
-  GstClockTime timestamp;
-  GstFlowReturn result = GST_FLOW_OK;
-
-  mad = GST_MAD (GST_PAD_PARENT (pad));
-
-  /* restarts happen on discontinuities, ie. seek, flush, PAUSED to PLAYING */
-  if (gst_mad_check_restart (mad)) {
-    mad->need_newsegment = TRUE;
-    GST_DEBUG ("mad restarted");
-  }
-
-  if (mad->segment.rate < 0.0) {
-    if (!mad->process)
-      return gst_mad_chain_reverse (mad, buffer);
-    /* no output discont */
-    discont = FALSE;
-  } else {
-    /* take discont flag */
-    discont = GST_BUFFER_IS_DISCONT (buffer);
-  }
-
-  timestamp = GST_BUFFER_TIMESTAMP (buffer);
-  GST_DEBUG ("mad in timestamp %" GST_TIME_FORMAT " duration:%" GST_TIME_FORMAT,
-      GST_TIME_ARGS (timestamp), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
-
-  /* handle timestamps */
-  if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
-    /* if there is nothing left to process in our temporary buffer,
-     * we can set this timestamp on the next outgoing buffer */
-    if (mad->tempsize == 0) {
-      /* we have to save the result here because we can't yet convert
-       * the timestamp to a sample offset, as the samplerate might not
-       * be known yet */
-      mad->last_ts = timestamp;
-      mad->base_byte_offset = GST_BUFFER_OFFSET (buffer);
-      mad->bytes_consumed = 0;
-    }
-    /* else we need to finish the current partial frame with the old timestamp
-     * and queue this timestamp for the next frame */
-    else {
-      new_pts = TRUE;
-    }
-  }
-  GST_DEBUG ("last_ts %" GST_TIME_FORMAT, GST_TIME_ARGS (mad->last_ts));
-
-  /* handle data */
-  bdata = gst_buffer_map (buffer, &bsize, NULL, GST_MAP_READ);
-
-  data = bdata;
-  size = bsize;
-
-  tempsize = mad->tempsize;
-
-  /* process the incoming buffer in chunks of maximum MAD_BUFFER_MDLEN bytes;
-   * this is the upper limit on processable chunk sizes set by mad */
-  while (size > 0) {
-    gint tocopy;
-    guchar *mad_input_buffer;   /* convenience pointer to tempbuffer */
-
-    if (mad->tempsize == 0 && discont) {
-      mad->discont = TRUE;
-      discont = FALSE;
-    }
-    tocopy =
-        MIN (MAD_BUFFER_MDLEN, MIN (size,
-            MAD_BUFFER_MDLEN * 3 - mad->tempsize));
-    if (tocopy == 0) {
-      GST_ELEMENT_ERROR (mad, STREAM, DECODE, (NULL),
-          ("mad claims to need more data than %u bytes, we don't have that much",
-              MAD_BUFFER_MDLEN * 3));
-      result = GST_FLOW_ERROR;
-      goto end;
+  GstFlowReturn ret = GST_FLOW_UNEXPECTED;
+  gint av, size, offset, prev_offset, consumed = 0;
+  const guint8 *data;
+
+  mad = GST_MAD (dec);
+
+  /* we basically let mad library do parsing,
+   * and translate that back to baseclass.
+   * if a frame is found (and also decoded), subsequent handle_frame
+   * only needs to synthesize it */
+
+  prev_offset = -1;
+  offset = 0;
+  av = gst_adapter_available (adapter);
+  while (offset < av) {
+    size = MIN (MAD_BUFFER_MDLEN * 3, av - offset);
+    data = gst_adapter_peek (adapter, av);
+
+    /* check for mad asking too much */
+    if (offset == prev_offset) {
+      if (G_UNLIKELY (offset + size < av)) {
+        /* mad should not do this, so really fatal */
+        GST_ELEMENT_ERROR (mad, STREAM, DECODE, (NULL),
+            ("mad claims to need more data than %u bytes", size));
+        ret = GST_FLOW_ERROR;
+        goto exit;
+      } else {
+        break;
+      }
     }
 
-    /* append the chunk to process to our internal temporary buffer */
-    GST_LOG ("tempbuffer size %ld, copying %d bytes from incoming buffer",
-        mad->tempsize, tocopy);
-    memcpy (mad->tempbuffer + mad->tempsize, data, tocopy);
-    mad->tempsize += tocopy;
-
-    /* update our incoming buffer's parameters to reflect this */
-    size -= tocopy;
-    data += tocopy;
+    /* only feed that much to mad at a time */
+    mad_stream_buffer (&mad->stream, data + offset, size);
+    prev_offset = offset;
 
-    mad_input_buffer = mad->tempbuffer;
+    while (offset - prev_offset < size) {
+      consumed = 0;
 
-    /* while we have data we can consume it */
-    while (mad->tempsize > 0) {
-      gint consumed = 0;
-      guint nsamples;
-      guint64 time_offset = GST_CLOCK_TIME_NONE;
-      guint64 time_duration = GST_CLOCK_TIME_NONE;
-      unsigned char const *before_sync, *after_sync;
-      gboolean goto_exit = FALSE;
-
-      mad->in_error = FALSE;
-
-      mad_stream_buffer (&mad->stream, mad_input_buffer, mad->tempsize);
-
-      /* added separate header decoding to catch errors earlier, also fixes
-       * some weird decoding errors... */
-      GST_LOG ("decoding the header now");
+      GST_LOG_OBJECT (mad, "decoding the header now");
       if (mad_header_decode (&mad->frame.header, &mad->stream) == -1) {
         if (mad->stream.error == MAD_ERROR_BUFLEN) {
-          GST_LOG ("not enough data in tempbuffer (%ld), breaking to get more",
-              mad->tempsize);
+          GST_LOG_OBJECT (mad,
+              "not enough data in tempbuffer (%d), breaking to get more", size);
           break;
         } else {
-          GST_WARNING ("mad_header_decode had an error: %s",
+          GST_WARNING_OBJECT (mad, "mad_header_decode had an error: %s",
               mad_stream_errorstr (&mad->stream));
         }
       }
 
-      GST_LOG ("decoding one frame now");
-
+      GST_LOG_OBJECT (mad, "parsing and decoding one frame now");
       if (mad_frame_decode (&mad->frame, &mad->stream) == -1) {
-        GST_LOG ("got error %d", mad->stream.error);
+        GST_LOG_OBJECT (mad, "got error %d", mad->stream.error);
 
         /* not enough data, need to wait for next buffer? */
         if (mad->stream.error == MAD_ERROR_BUFLEN) {
-          if (mad->stream.next_frame == mad_input_buffer) {
-            GST_LOG
-                ("not enough data in tempbuffer (%ld), breaking to get more",
-                mad->tempsize);
+          if (mad->stream.next_frame == data) {
+            GST_LOG_OBJECT (mad,
+                "not enough data in tempbuffer (%d), breaking to get more",
+                size);
             break;
           } else {
-            GST_LOG ("sync error, flushing unneeded data");
-            goto next_no_samples;
+            GST_LOG_OBJECT (mad, "sync error, flushing unneeded data");
+            goto flush;
           }
         } else if (mad->stream.error == MAD_ERROR_BADDATAPTR) {
           /* Flush data */
-          goto next_no_samples;
-        }
-        /* we are in an error state */
-        mad->in_error = TRUE;
-        GST_WARNING ("mad_frame_decode had an error: %s",
-            mad_stream_errorstr (&mad->stream));
-        if (!MAD_RECOVERABLE (mad->stream.error)) {
-          GST_ELEMENT_ERROR (mad, STREAM, DECODE, (NULL), (NULL));
-          result = GST_FLOW_ERROR;
-          goto end;
-        } else if (mad->stream.error == MAD_ERROR_LOSTSYNC) {
-          /* lost sync, force a resync */
-          GST_INFO ("recoverable lost sync error");
-        }
-
-        mad_frame_mute (&mad->frame);
-        mad_synth_mute (&mad->synth);
-        before_sync = mad->stream.ptr.byte;
-        if (mad_stream_sync (&mad->stream) != 0)
-          GST_WARNING ("mad_stream_sync failed");
-        after_sync = mad->stream.ptr.byte;
-        /* a succesful resync should make us drop bytes as consumed, so
-           calculate from the byte pointers before and after resync */
-        consumed = after_sync - before_sync;
-        GST_DEBUG ("resynchronization consumes %d bytes", consumed);
-        GST_DEBUG ("synced to data: 0x%0x 0x%0x", *mad->stream.ptr.byte,
-            *(mad->stream.ptr.byte + 1));
-
-        mad_stream_sync (&mad->stream);
-        /* recoverable errors pass */
-        goto next_no_samples;
-      }
-
-      if (mad->check_for_xing) {
-        int bitrate = 0, time = 0;
-        GstTagList *list;
-        int frame_len = mad->stream.next_frame - mad->stream.this_frame;
-
-        mad->check_for_xing = FALSE;
-
-        /* Assume Xing headers can only be the first frame in a mp3 file */
-        if (mpg123_parse_xing_header (&mad->frame.header,
-                mad->stream.this_frame, frame_len, &bitrate, &time)) {
-          mad->xing_found = TRUE;
-          list = gst_tag_list_new (GST_TAG_DURATION,
-              (gint64) time * 1000 * 1000 * 1000,
-              GST_TAG_BITRATE, bitrate, NULL);
-          gst_element_post_message (GST_ELEMENT (mad),
-              gst_message_new_tag (GST_OBJECT (mad), gst_tag_list_copy (list)));
-
-          if (mad->need_newsegment)
-            mad->pending_events =
-                g_list_append (mad->pending_events, gst_event_new_tag (list));
-          else
-            gst_pad_push_event (mad->srcpad, gst_event_new_tag (list));
-
-          goto next_no_samples;
-        }
-      }
-
-      /* if we're not resyncing/in error, check if caps need to be set again */
-      if (!mad->in_error)
-        gst_mad_check_caps_reset (mad);
-      nsamples = MAD_NSBSAMPLES (&mad->frame.header) *
-          (mad->stream.options & MAD_OPTION_HALFSAMPLERATE ? 16 : 32);
-
-      if (mad->rate == 0) {
-        g_warning ("mad->rate is 0; timestamps cannot be calculated");
-      } else {
-        /* if we have a pending timestamp, we can use it now to calculate the sample offset */
-        if (GST_CLOCK_TIME_IS_VALID (mad->last_ts)) {
-          gint64 total;
-
-          /* Convert incoming timestamp to a number of encoded samples */
-          gst_pad_query_convert (mad->srcpad, GST_FORMAT_TIME, mad->last_ts,
-              GST_FORMAT_DEFAULT, &total);
-
-          GST_DEBUG_OBJECT (mad, "calculated samples offset from ts is %"
-              G_GUINT64_FORMAT " accumulated samples offset is %"
-              G_GUINT64_FORMAT, total, mad->total_samples);
-
-          /* We are using the incoming timestamps to generate the outgoing ones
-           * if available. However some muxing formats are not precise enough
-           * to allow us to generate a perfect stream. When converting the
-           * timestamp to a number of encoded samples so far we are introducing
-           * a lot of potential error compared to our accumulated number of
-           * samples encoded. If the difference between those 2 numbers is
-           * bigger than half a frame we then use the incoming timestamp
-           * as a reference, otherwise we continue using our accumulated samples
-           * counter */
-          if (ABS (((gint64) (mad->total_samples)) - total) > nsamples / 2) {
-            GST_DEBUG_OBJECT (mad, "difference is bigger than half a frame, "
-                "using calculated samples offset %" G_GUINT64_FORMAT, total);
-            /* Override our accumulated samples counter */
-            mad->total_samples = total;
-            /* We use that timestamp directly */
-            time_offset = mad->last_ts;
+          goto flush;
+        } else {
+          GST_WARNING_OBJECT (mad, "mad_frame_decode had an error: %s",
+              mad_stream_errorstr (&mad->stream));
+          if (!MAD_RECOVERABLE (mad->stream.error)) {
+            /* well, all may be well enough bytes later on ... */
+            GST_BASE_AUDIO_DECODER_ERROR (mad, 1, STREAM, DECODE, (NULL),
+                ("mad error: %s", mad_stream_errorstr (&mad->stream)), ret);
+            /* so make sure we really move along ... */
+            if (!offset)
+              offset++;
+            goto exit;
+          } else {
+            const guint8 *before_sync, *after_sync;
+
+            mad_frame_mute (&mad->frame);
+            mad_synth_mute (&mad->synth);
+            before_sync = mad->stream.ptr.byte;
+            if (mad_stream_sync (&mad->stream) != 0)
+              GST_WARNING_OBJECT (mad, "mad_stream_sync failed");
+            after_sync = mad->stream.ptr.byte;
+            /* a succesful resync should make us drop bytes as consumed, so
+             * calculate from the byte pointers before and after resync */
+            consumed = after_sync - before_sync;
+            GST_DEBUG_OBJECT (mad, "resynchronization consumes %d bytes",
+                consumed);
+            GST_DEBUG_OBJECT (mad, "synced to data: 0x%0x 0x%0x",
+                *mad->stream.ptr.byte, *(mad->stream.ptr.byte + 1));
+
+            mad_stream_sync (&mad->stream);
+            /* recoverable errors pass */
+            goto flush;
           }
-
-          mad->last_ts = GST_CLOCK_TIME_NONE;
-        }
-
-        if (!GST_CLOCK_TIME_IS_VALID (time_offset)) {
-          time_offset = gst_util_uint64_scale_int (mad->total_samples,
-              GST_SECOND, mad->rate);
         }
-        /* Duration is next timestamp - this one to generate a continuous
-         * stream */
-        time_duration =
-            gst_util_uint64_scale_int (mad->total_samples + nsamples,
-            GST_SECOND, mad->rate) - time_offset;
+      } else {
+        /* decoding ok; found frame */
+        ret = GST_FLOW_OK;
       }
-
-      if (mad->index) {
-        guint64 x_bytes = mad->base_byte_offset + mad->bytes_consumed;
-
-        gst_index_add_association (mad->index, mad->index_id,
-            GST_ASSOCIATION_FLAG_DELTA_UNIT,
-            GST_FORMAT_BYTES, x_bytes, GST_FORMAT_TIME, time_offset, NULL);
+    flush:
+      if (consumed == 0) {
+        consumed = mad->stream.next_frame - (data + offset);
+        g_assert (consumed >= 0);
       }
 
-      if (mad->segment_start <= (time_offset ==
-              GST_CLOCK_TIME_NONE ? 0 : time_offset)) {
-
-        /* for sample accurate seeking, calculate how many samples
-           to skip and send the remaining pcm samples */
-
-        GstBuffer *outbuffer = NULL;
-        gpointer odata;
-        gsize osize;
-        mad_fixed_t const *left_ch, *right_ch;
-
-        if (mad->need_newsegment) {
-          gint64 start = time_offset;
-
-          GST_DEBUG ("Sending NEWSEGMENT event, start=%" GST_TIME_FORMAT,
-              GST_TIME_ARGS (start));
-
-          mad->segment.start = start;
-          mad->segment.stop = GST_CLOCK_TIME_NONE;
-          mad->segment.time = start;
-
-          gst_pad_push_event (mad->srcpad,
-              gst_event_new_segment (&mad->segment));
-          mad->need_newsegment = FALSE;
-        }
-
-        if (mad->pending_events) {
-          GList *l;
-
-          for (l = mad->pending_events; l != NULL; l = l->next) {
-            gst_pad_push_event (mad->srcpad, GST_EVENT (l->data));
-          }
-          g_list_free (mad->pending_events);
-          mad->pending_events = NULL;
-        }
-
-        /* will attach the caps to the buffer */
-        outbuffer = gst_buffer_new_and_alloc (nsamples * mad->channels * 4);
-
-        mad_synth_frame (&mad->synth, &mad->frame);
-        left_ch = mad->synth.pcm.samples[0];
-        right_ch = mad->synth.pcm.samples[1];
-
-        GST_DEBUG ("mad out timestamp %" GST_TIME_FORMAT " dur: %"
-            GST_TIME_FORMAT, GST_TIME_ARGS (time_offset),
-            GST_TIME_ARGS (time_duration));
-
-        GST_BUFFER_TIMESTAMP (outbuffer) = time_offset;
-        GST_BUFFER_DURATION (outbuffer) = time_duration;
-        GST_BUFFER_OFFSET (outbuffer) = mad->total_samples;
-        GST_BUFFER_OFFSET_END (outbuffer) = mad->total_samples + nsamples;
-
-        odata = gst_buffer_map (outbuffer, &osize, NULL, GST_MAP_WRITE);
-
-        /* output sample(s) in 16-bit signed native-endian PCM */
-        if (mad->channels == 1) {
-          gint count = nsamples;
-          gint32 *ptr = odata;
-
-          while (count--) {
-            *ptr++ = scale (*left_ch++) & 0xffffffff;
-          }
-        } else {
-          gint count = nsamples;
-          gint32 *ptr = odata;
-
-          while (count--) {
-            *ptr++ = scale (*left_ch++) & 0xffffffff;
-            *ptr++ = scale (*right_ch++) & 0xffffffff;
-          }
-        }
-        gst_buffer_unmap (outbuffer, odata, osize);
-
-        if ((outbuffer = gst_audio_buffer_clip (outbuffer, &mad->segment,
-                    mad->rate, 4 * mad->channels))) {
-          GST_LOG_OBJECT (mad,
-              "pushing buffer, off=%" G_GUINT64_FORMAT ", ts=%" GST_TIME_FORMAT,
-              GST_BUFFER_OFFSET (outbuffer),
-              GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuffer)));
-
-          /* apply discont */
-          if (mad->discont) {
-            GST_BUFFER_FLAG_SET (outbuffer, GST_BUFFER_FLAG_DISCONT);
-            mad->discont = FALSE;
-          }
+      if (ret == GST_FLOW_OK)
+        goto exit;
 
-          mad->segment.position = GST_BUFFER_TIMESTAMP (outbuffer);
-          if (mad->segment.rate > 0.0) {
-            result = gst_pad_push (mad->srcpad, outbuffer);
-          } else {
-            GST_LOG_OBJECT (mad, "queued buffer");
-            mad->queued = g_list_prepend (mad->queued, outbuffer);
-            result = GST_FLOW_OK;
-          }
-          if (result != GST_FLOW_OK) {
-            /* Head for the exit, dropping samples as we go */
-            goto_exit = TRUE;
-          }
-        } else {
-          GST_LOG_OBJECT (mad, "Dropping buffer");
-        }
-      }
+      offset += consumed;
+    }
+  }
 
-      mad->total_samples += nsamples;
+exit:
+  *_offset = offset;
+  *len = consumed;
 
-      /* we have a queued timestamp on the incoming buffer that we should
-       * use for the next frame */
-      if (new_pts && (mad->stream.next_frame - mad_input_buffer >= tempsize)) {
-        new_pts = FALSE;
-        mad->last_ts = timestamp;
-        mad->base_byte_offset = GST_BUFFER_OFFSET (buffer);
-        mad->bytes_consumed = 0;
-      }
-      tempsize = 0;
-      if (discont) {
-        mad->discont = TRUE;
-        discont = FALSE;
-      }
+  return ret;
+}
 
-      if (gst_mad_check_restart (mad)) {
-        goto end;
-      }
+static GstFlowReturn
+gst_mad_handle_frame (GstBaseAudioDecoder * dec, GstBuffer * buffer)
+{
+  GstMad *mad;
+  GstFlowReturn ret = GST_FLOW_UNEXPECTED;
+  GstBuffer *outbuffer;
+  guint nsamples;
+  gint32 *outdata;
+  mad_fixed_t const *left_ch, *right_ch;
+
+  mad = GST_MAD (dec);
+
+  /* no fancy draining */
+  if (G_UNLIKELY (!buffer))
+    return GST_FLOW_OK;
+
+  /* _parse prepared a frame */
+  nsamples = MAD_NSBSAMPLES (&mad->frame.header) *
+      (mad->stream.options & MAD_OPTION_HALFSAMPLERATE ? 16 : 32);
+  GST_LOG_OBJECT (mad, "mad frame with %d samples", nsamples);
+
+  /* arrange for initial caps before pushing data,
+   * and update later on if needed */
+  gst_mad_check_caps_reset (mad);
+
+  // TODO more generic check and size check ?
+  ret = gst_pad_alloc_buffer_and_set_caps (GST_BASE_AUDIO_DECODER_SRC_PAD (dec),
+      0, nsamples * mad->channels * 4,
+      GST_PAD_CAPS (GST_BASE_AUDIO_DECODER_SRC_PAD (dec)), &outbuffer);
+  if (ret != GST_FLOW_OK) {
+    /* Head for the exit, dropping samples as we go */
+    GST_LOG_OBJECT (dec,
+        "Skipping frame synthesis due to pad_alloc return value");
+    gst_base_audio_decoder_finish_frame (dec, NULL, 1);
+    goto exit;
+  }
+
+  mad_synth_frame (&mad->synth, &mad->frame);
+  left_ch = mad->synth.pcm.samples[0];
+  right_ch = mad->synth.pcm.samples[1];
+
+  outdata = (gint32 *) GST_BUFFER_DATA (outbuffer);
+
+  /* output sample(s) in 16-bit signed native-endian PCM */
+  if (mad->channels == 1) {
+    gint count = nsamples;
+
+    while (count--) {
+      *outdata++ = scale (*left_ch++) & 0xffffffff;
+    }
+  } else {
+    gint count = nsamples;
 
-    next_no_samples:
-      /* figure out how many bytes mad consumed */
-      /* if consumed is already set, it's from the resync higher up, so
-         we need to use that value instead.  Otherwise, recalculate from
-         mad's consumption */
-      if (consumed == 0)
-        consumed = mad->stream.next_frame - mad_input_buffer;
-
-      GST_LOG ("mad consumed %d bytes", consumed);
-      /* move out pointer to where mad want the next data */
-      mad_input_buffer += consumed;
-      mad->tempsize -= consumed;
-      mad->bytes_consumed += consumed;
-      if (goto_exit == TRUE)
-        goto end;
+    while (count--) {
+      *outdata++ = scale (*left_ch++) & 0xffffffff;
+      *outdata++ = scale (*right_ch++) & 0xffffffff;
     }
-    /* we only get here from breaks, tempsize never actually drops below 0 */
-    memmove (mad->tempbuffer, mad_input_buffer, mad->tempsize);
   }
-  result = GST_FLOW_OK;
 
-end:
-  gst_buffer_unmap (buffer, bdata, bsize);
-  gst_buffer_unref (buffer);
+  ret = gst_base_audio_decoder_finish_frame (dec, outbuffer, 1);
+exit:
+  return ret;
+}
+
+static void
+gst_mad_flush (GstBaseAudioDecoder * dec, gboolean hard)
+{
+  GstMad *mad;
 
-  return result;
+  mad = GST_MAD (dec);
+  if (hard) {
+    mad_frame_mute (&mad->frame);
+    mad_synth_mute (&mad->synth);
+  }
 }
 
-static GstStateChangeReturn
-gst_mad_change_state (GstElement * element, GstStateChange transition)
+static void
+gst_mad_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
 {
   GstMad *mad;
-  GstStateChangeReturn ret;
 
-  mad = GST_MAD (element);
+  mad = GST_MAD (object);
 
-  switch (transition) {
-    case GST_STATE_CHANGE_NULL_TO_READY:
-      break;
-    case GST_STATE_CHANGE_READY_TO_PAUSED:
-    {
-      guint options = 0;
-
-      mad_stream_init (&mad->stream);
-      mad_frame_init (&mad->frame);
-      mad_synth_init (&mad->synth);
-      mad->tempsize = 0;
-      mad->discont = TRUE;
-      mad->total_samples = 0;
-      mad->rate = 0;
-      mad->channels = 0;
-      mad->caps_set = FALSE;
-      mad->times_pending = mad->pending_rate = mad->pending_channels = 0;
-      mad->vbr_average = 0;
-      gst_segment_init (&mad->segment, GST_FORMAT_TIME);
-      mad->new_header = TRUE;
-      mad->framed = FALSE;
-      mad->framecount = 0;
-      mad->vbr_rate = 0;
-      mad->frame.header.samplerate = 0;
-      mad->last_ts = GST_CLOCK_TIME_NONE;
-      if (mad->ignore_crc)
-        options |= MAD_OPTION_IGNORECRC;
-      if (mad->half)
-        options |= MAD_OPTION_HALFSAMPLERATE;
-      mad_stream_options (&mad->stream, options);
+  switch (prop_id) {
+    case ARG_HALF:
+      mad->half = g_value_get_boolean (value);
       break;
-    }
-    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+    case ARG_IGNORE_CRC:
+      mad->ignore_crc = g_value_get_boolean (value);
       break;
     default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
+}
+
+static void
+gst_mad_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstMad *mad;
 
-  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+  mad = GST_MAD (object);
 
-  switch (transition) {
-    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
-      break;
-    case GST_STATE_CHANGE_PAUSED_TO_READY:
-      mad_synth_finish (&mad->synth);
-      mad_frame_finish (&mad->frame);
-      mad_stream_finish (&mad->stream);
-      mad->restart = TRUE;
-      mad->check_for_xing = TRUE;
-      if (mad->tags) {
-        gst_tag_list_free (mad->tags);
-        mad->tags = NULL;
-      }
-      gst_mad_clear_queues (mad);
+  switch (prop_id) {
+    case ARG_HALF:
+      g_value_set_boolean (value, mad->half);
       break;
-    case GST_STATE_CHANGE_READY_TO_NULL:
+    case ARG_IGNORE_CRC:
+      g_value_set_boolean (value, mad->ignore_crc);
       break;
     default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
-  return ret;
 }
 
-static gboolean
-plugin_init (GstPlugin * plugin)
+/* plugin initialisation */
+
+gboolean
+gst_mad_register (GstPlugin * plugin)
 {
-  GST_DEBUG_CATEGORY_INIT (mad_debug, "mad", 0, "mad mp3 decoding");
+  GST_DEBUG_CATEGORY_INIT (mad_debug, "bmad", 0, "mad mp3 decoding");
 
-  /* FIXME 0.11: rename to something better like madmp3dec or madmpegaudiodec
-   * or so? */
-  return gst_element_register (plugin, "mad", GST_RANK_SECONDARY,
+  return gst_element_register (plugin, "bmad", GST_RANK_NONE,
       gst_mad_get_type ());
 }
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
-    GST_VERSION_MINOR,
-    "mad",
-    "mp3 decoding based on the mad library",
-    plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
index 730911da71d572493fcb2494a46ad89ca3d73576..2da974b6c55023801035381b3485bf0f3145b44b 100644 (file)
 
 #include <gst/gst.h>
 #include <gst/tag/tag.h>
+#include <gst/audio/gstbaseaudiodecoder.h>
+
 #include <mad.h>
+#include <id3tag.h>
 
 G_BEGIN_DECLS
 
@@ -44,70 +47,34 @@ typedef struct _GstMadClass GstMadClass;
 
 struct _GstMad
 {
-  GstElement element;
-
-  /* pads */
-  GstPad *sinkpad, *srcpad;
+  GstBaseAudioDecoder element;
 
   /* state */
   struct mad_stream stream;
   struct mad_frame frame;
   struct mad_synth synth;
-  guchar *tempbuffer;           /* temporary buffer to serve to mad */
-  glong tempsize;               /* running count of temp buffer size */
-  GstClockTime last_ts;
-  guint64 base_byte_offset;
-  guint64 bytes_consumed;       /* since the base_byte_offset */
-  guint64 total_samples;        /* the number of samples since the sync point */
-
-  gboolean in_error;            /* set when mad's in an error state */
-  gboolean restart;
-  gboolean discont;
-  guint64 segment_start;
-  GstSegment segment;
-  gboolean need_newsegment;
 
   /* info */
   struct mad_header header;
-  gboolean new_header;
-  guint framecount;
-  gint vbr_average;             /* average bitrate */
-  guint64 vbr_rate;             /* average * framecount */
-
-  gboolean half;
-  gboolean ignore_crc;
-
-  GstTagList *tags;
 
   /* negotiated format */
   gint rate, pending_rate;
   gint channels, pending_channels;
   gint times_pending;
-
   gboolean caps_set;            /* used to keep track of whether to change/update caps */
-  GstIndex *index;
-  gint index_id;
-
-  gboolean check_for_xing;
-  gboolean xing_found;
-
-  gboolean framed;              /* whether there is a demuxer in front of us */
 
-  GList *pending_events;
-
-  /* reverse playback */
-  GList *decode;
-  GList *gather;
-  GList *queued;
-  gboolean process;
+  /* properties */
+  gboolean half;
+  gboolean ignore_crc;
 };
 
 struct _GstMadClass
 {
-  GstElementClass parent_class;
+  GstBaseAudioDecoderClass parent_class;
 };
 
 GType                   gst_mad_get_type (void);
+gboolean                gst_mad_register (GstPlugin * plugin);
 
 G_END_DECLS