dtsdec: port to audiodecoder
authorMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Wed, 23 Nov 2011 22:29:10 +0000 (23:29 +0100)
committerMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Wed, 23 Nov 2011 22:29:10 +0000 (23:29 +0100)
ext/dts/Makefile.am
ext/dts/gstdtsdec.c
ext/dts/gstdtsdec.h

index f93e87d..f58f149 100644 (file)
@@ -1,8 +1,9 @@
 plugin_LTLIBRARIES = libgstdtsdec.la
 
 libgstdtsdec_la_SOURCES = gstdtsdec.c
-libgstdtsdec_la_CFLAGS = $(GST_CFLAGS) $(ORC_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS)
-libgstdtsdec_la_LIBADD = $(DTS_LIBS) $(ORC_LIBS) $(GST_PLUGINS_BASE_LIBS)  \
+libgstdtsdec_la_CFLAGS = -DGST_USE_UNSTABLE_API \
+       $(GST_CFLAGS) $(ORC_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS)
+libgstdtsdec_la_LIBADD = $(DTS_LIBS) $(ORC_LIBS)  $(GST_PLUGINS_BASE_LIBS)  \
        -lgstaudio-@GST_MAJORMINOR@
 libgstdtsdec_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 libgstdtsdec_la_LIBTOOLFLAGS = --tag=disable-static
index 2039c58..2a762e9 100644 (file)
@@ -127,14 +127,20 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
         "rate = (int) [ 4000, 96000 ], " "channels = (int) [ 1, 6 ]")
     );
 
-GST_BOILERPLATE (GstDtsDec, gst_dtsdec, GstElement, GST_TYPE_ELEMENT);
+GST_BOILERPLATE (GstDtsDec, gst_dtsdec, GstAudioDecoder,
+    GST_TYPE_AUDIO_DECODER);
+
+static gboolean gst_dtsdec_start (GstAudioDecoder * dec);
+static gboolean gst_dtsdec_stop (GstAudioDecoder * dec);
+static gboolean gst_dtsdec_set_format (GstAudioDecoder * bdec, GstCaps * caps);
+static gboolean gst_dtsdec_parse (GstAudioDecoder * dec, GstAdapter * adapter,
+    gint * offset, gint * length);
+static GstFlowReturn gst_dtsdec_handle_frame (GstAudioDecoder * dec,
+    GstBuffer * buffer);
+static GstFlowReturn gst_dtsdec_pre_push (GstAudioDecoder * bdec,
+    GstBuffer ** buffer);
 
-static gboolean gst_dtsdec_sink_setcaps (GstPad * pad, GstCaps * caps);
-static gboolean gst_dtsdec_sink_event (GstPad * pad, GstEvent * event);
 static GstFlowReturn gst_dtsdec_chain (GstPad * pad, GstBuffer * buf);
-static GstFlowReturn gst_dtsdec_chain_raw (GstPad * pad, GstBuffer * buf);
-static GstStateChangeReturn gst_dtsdec_change_state (GstElement * element,
-    GstStateChange transition);
 
 static void gst_dtsdec_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
@@ -164,16 +170,21 @@ static void
 gst_dtsdec_class_init (GstDtsDecClass * klass)
 {
   GObjectClass *gobject_class;
-  GstElementClass *gstelement_class;
+  GstAudioDecoderClass *gstbase_class;
   guint cpuflags;
 
   gobject_class = (GObjectClass *) klass;
-  gstelement_class = (GstElementClass *) klass;
+  gstbase_class = (GstAudioDecoderClass *) klass;
 
   gobject_class->set_property = gst_dtsdec_set_property;
   gobject_class->get_property = gst_dtsdec_get_property;
 
-  gstelement_class->change_state = gst_dtsdec_change_state;
+  gstbase_class->start = GST_DEBUG_FUNCPTR (gst_dtsdec_start);
+  gstbase_class->stop = GST_DEBUG_FUNCPTR (gst_dtsdec_stop);
+  gstbase_class->set_format = GST_DEBUG_FUNCPTR (gst_dtsdec_set_format);
+  gstbase_class->parse = GST_DEBUG_FUNCPTR (gst_dtsdec_parse);
+  gstbase_class->handle_frame = GST_DEBUG_FUNCPTR (gst_dtsdec_handle_frame);
+  gstbase_class->pre_push = GST_DEBUG_FUNCPTR (gst_dtsdec_pre_push);
 
   /**
    * GstDtsDec::drc
@@ -209,23 +220,104 @@ gst_dtsdec_class_init (GstDtsDecClass * klass)
 static void
 gst_dtsdec_init (GstDtsDec * dtsdec, GstDtsDecClass * g_class)
 {
-  /* create the sink and src pads */
-  dtsdec->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
-  gst_pad_set_setcaps_function (dtsdec->sinkpad,
-      GST_DEBUG_FUNCPTR (gst_dtsdec_sink_setcaps));
-  gst_pad_set_chain_function (dtsdec->sinkpad,
+  dtsdec->request_channels = DCA_CHANNEL;
+  dtsdec->dynamic_range_compression = FALSE;
+
+  /* retrieve and intercept base class chain.
+   * Quite HACKish, but that's dvd specs for you,
+   * since one buffer needs to be split into 2 frames */
+  dtsdec->base_chain = GST_PAD_CHAINFUNC (GST_AUDIO_DECODER_SINK_PAD (dtsdec));
+  gst_pad_set_chain_function (GST_AUDIO_DECODER_SINK_PAD (dtsdec),
       GST_DEBUG_FUNCPTR (gst_dtsdec_chain));
-  gst_pad_set_event_function (dtsdec->sinkpad,
-      GST_DEBUG_FUNCPTR (gst_dtsdec_sink_event));
-  gst_element_add_pad (GST_ELEMENT (dtsdec), dtsdec->sinkpad);
+}
 
-  dtsdec->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
-  gst_element_add_pad (GST_ELEMENT (dtsdec), dtsdec->srcpad);
+static gboolean
+gst_dtsdec_start (GstAudioDecoder * dec)
+{
+  GstDtsDec *dts = GST_DTSDEC (dec);
+  GstDtsDecClass *klass;
+
+  GST_DEBUG_OBJECT (dec, "start");
+
+  klass = GST_DTSDEC_CLASS (G_OBJECT_GET_CLASS (dts));
+  dts->state = dca_init (klass->dts_cpuflags);
+  dts->samples = dca_samples (dts->state);
+  dts->bit_rate = -1;
+  dts->sample_rate = -1;
+  dts->stream_channels = DCA_CHANNEL;
+  dts->using_channels = DCA_CHANNEL;
+  dts->level = 1;
+  dts->bias = 0;
+  dts->flag_update = TRUE;
 
-  dtsdec->request_channels = DCA_CHANNEL;
-  dtsdec->dynamic_range_compression = FALSE;
+  /* call upon legacy upstream byte support (e.g. seeking) */
+  gst_audio_decoder_set_byte_time (dec, TRUE);
 
-  gst_segment_init (&dtsdec->segment, GST_FORMAT_UNDEFINED);
+  return TRUE;
+}
+
+static gboolean
+gst_dtsdec_stop (GstAudioDecoder * dec)
+{
+  GstDtsDec *dts = GST_DTSDEC (dec);
+
+  GST_DEBUG_OBJECT (dec, "stop");
+
+  dts->samples = NULL;
+  if (dts->state) {
+    dca_free (dts->state);
+    dts->state = NULL;
+  }
+  if (dts->pending_tags) {
+    gst_tag_list_free (dts->pending_tags);
+    dts->pending_tags = NULL;
+  }
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_dtsdec_parse (GstAudioDecoder * bdec, GstAdapter * adapter,
+    gint * _offset, gint * len)
+{
+  GstDtsDec *dts;
+  guint8 *data;
+  gint av, size;
+  gint length = 0, flags, sample_rate, bit_rate, frame_length;
+  GstFlowReturn result = GST_FLOW_UNEXPECTED;
+
+  dts = GST_DTSDEC (bdec);
+
+  size = av = gst_adapter_available (adapter);
+  data = (guint8 *) gst_adapter_peek (adapter, av);
+
+  /* find and read header */
+  bit_rate = dts->bit_rate;
+  sample_rate = dts->sample_rate;
+  flags = 0;
+  while (av >= 7) {
+    length = dca_syncinfo (dts->state, data, &flags,
+        &sample_rate, &bit_rate, &frame_length);
+
+    if (length == 0) {
+      /* shift window to re-find sync */
+      data++;
+      size--;
+    } else if (length <= size) {
+      GST_LOG_OBJECT (dts, "Sync: frame size %d", length);
+      result = GST_FLOW_OK;
+      break;
+    } else {
+      GST_LOG_OBJECT (dts, "Not enough data available (needed %d had %d)",
+          length, size);
+      break;
+    }
+  }
+
+  *_offset = av - size;
+  *len = length;
+
+  return result;
 }
 
 static gint
@@ -327,105 +419,6 @@ gst_dtsdec_channels (uint32_t flags, GstAudioChannelPosition ** pos)
   return chans;
 }
 
-static void
-clear_queued (GstDtsDec * dec)
-{
-  g_list_foreach (dec->queued, (GFunc) gst_mini_object_unref, NULL);
-  g_list_free (dec->queued);
-  dec->queued = NULL;
-}
-
-static GstFlowReturn
-flush_queued (GstDtsDec * dec)
-{
-  GstFlowReturn ret = GST_FLOW_OK;
-
-  while (dec->queued) {
-    GstBuffer *buf = GST_BUFFER_CAST (dec->queued->data);
-
-    GST_LOG_OBJECT (dec, "pushing buffer %p, timestamp %"
-        GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, buf,
-        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
-        GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
-
-    /* iterate ouput queue an push downstream */
-    ret = gst_pad_push (dec->srcpad, buf);
-
-    dec->queued = g_list_delete_link (dec->queued, dec->queued);
-  }
-  return ret;
-}
-
-static GstFlowReturn
-gst_dtsdec_drain (GstDtsDec * dec)
-{
-  GstFlowReturn ret = GST_FLOW_OK;
-
-  if (dec->segment.rate < 0.0) {
-    /* if we have some queued frames for reverse playback, flush
-     * them now */
-    ret = flush_queued (dec);
-  }
-  return ret;
-}
-
-static GstFlowReturn
-gst_dtsdec_push (GstDtsDec * dtsdec,
-    GstPad * srcpad, int flags, sample_t * samples, GstClockTime timestamp)
-{
-  GstBuffer *buf;
-  int chans, n, c;
-  GstFlowReturn result;
-
-  flags &= (DCA_CHANNEL_MASK | DCA_LFE);
-  chans = gst_dtsdec_channels (flags, NULL);
-  if (!chans) {
-    GST_ELEMENT_ERROR (GST_ELEMENT (dtsdec), STREAM, DECODE, (NULL),
-        ("Invalid channel flags: %d", flags));
-    return GST_FLOW_ERROR;
-  }
-
-  result =
-      gst_pad_alloc_buffer_and_set_caps (srcpad, 0,
-      256 * chans * (SAMPLE_WIDTH / 8), GST_PAD_CAPS (srcpad), &buf);
-  if (result != GST_FLOW_OK)
-    return result;
-
-  for (n = 0; n < 256; n++) {
-    for (c = 0; c < chans; c++) {
-      ((sample_t *) GST_BUFFER_DATA (buf))[n * chans + c] =
-          samples[c * 256 + n];
-    }
-  }
-  GST_BUFFER_TIMESTAMP (buf) = timestamp;
-  GST_BUFFER_DURATION (buf) = 256 * GST_SECOND / dtsdec->sample_rate;
-
-  result = GST_FLOW_OK;
-  if ((buf = gst_audio_buffer_clip (buf, &dtsdec->segment,
-              dtsdec->sample_rate, (SAMPLE_WIDTH / 8) * chans))) {
-    /* set discont when needed */
-    if (dtsdec->discont) {
-      GST_LOG_OBJECT (dtsdec, "marking DISCONT");
-      GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
-      dtsdec->discont = FALSE;
-    }
-
-    if (dtsdec->segment.rate > 0.0) {
-      GST_DEBUG_OBJECT (dtsdec,
-          "Pushing buffer with ts %" GST_TIME_FORMAT " duration %"
-          GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
-          GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
-
-      result = gst_pad_push (srcpad, buf);
-    } else {
-      /* reverse playback, queue frame till later when we get a discont. */
-      GST_DEBUG_OBJECT (dtsdec, "queued frame");
-      dtsdec->queued = g_list_prepend (dtsdec->queued, buf);
-    }
-  }
-  return result;
-}
-
 static gboolean
 gst_dtsdec_renegotiate (GstDtsDec * dts)
 {
@@ -446,7 +439,7 @@ gst_dtsdec_renegotiate (GstDtsDec * dts)
   gst_audio_set_channel_positions (gst_caps_get_structure (caps, 0), pos);
   g_free (pos);
 
-  if (!gst_pad_set_caps (dts->srcpad, caps))
+  if (!gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dts), caps))
     goto done;
 
   result = TRUE;
@@ -458,100 +451,70 @@ done:
   return result;
 }
 
-static gboolean
-gst_dtsdec_sink_event (GstPad * pad, GstEvent * event)
-{
-  GstDtsDec *dtsdec = GST_DTSDEC (gst_pad_get_parent (pad));
-  gboolean ret = FALSE;
-
-  GST_LOG_OBJECT (dtsdec, "%s event", GST_EVENT_TYPE_NAME (event));
-
-  switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_NEWSEGMENT:{
-      GstFormat format;
-      gboolean update;
-      gint64 start, end, pos;
-      gdouble rate;
-
-      gst_event_parse_new_segment (event, &update, &rate, &format, &start, &end,
-          &pos);
-
-      /* drain queued buffers before activating the segment so that we can clip
-       * against the old segment first */
-      gst_dtsdec_drain (dtsdec);
-
-      if (format != GST_FORMAT_TIME || !GST_CLOCK_TIME_IS_VALID (start)) {
-        GST_WARNING ("No time in newsegment event %p (format is %s)",
-            event, gst_format_get_name (format));
-        gst_event_unref (event);
-        dtsdec->sent_segment = FALSE;
-        /* set some dummy values, FIXME: do proper conversion */
-        dtsdec->time = start = pos = 0;
-        format = GST_FORMAT_TIME;
-        end = -1;
-      } else {
-        dtsdec->time = start;
-        dtsdec->sent_segment = TRUE;
-        ret = gst_pad_push_event (dtsdec->srcpad, event);
-      }
-
-      gst_segment_set_newsegment (&dtsdec->segment, update, rate, format, start,
-          end, pos);
-      break;
-    }
-    case GST_EVENT_TAG:
-      ret = gst_pad_push_event (dtsdec->srcpad, event);
-      break;
-    case GST_EVENT_EOS:
-      gst_dtsdec_drain (dtsdec);
-      ret = gst_pad_push_event (dtsdec->srcpad, event);
-      break;
-    case GST_EVENT_FLUSH_START:
-      ret = gst_pad_push_event (dtsdec->srcpad, event);
-      break;
-    case GST_EVENT_FLUSH_STOP:
-      if (dtsdec->cache) {
-        gst_buffer_unref (dtsdec->cache);
-        dtsdec->cache = NULL;
-      }
-      clear_queued (dtsdec);
-      gst_segment_init (&dtsdec->segment, GST_FORMAT_UNDEFINED);
-      ret = gst_pad_push_event (dtsdec->srcpad, event);
-      break;
-    default:
-      ret = gst_pad_push_event (dtsdec->srcpad, event);
-      break;
-  }
-
-  gst_object_unref (dtsdec);
-  return ret;
-}
-
 static void
 gst_dtsdec_update_streaminfo (GstDtsDec * dts)
 {
   GstTagList *taglist;
 
-  taglist = gst_tag_list_new ();
-
-  gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND,
-      GST_TAG_AUDIO_CODEC, "DTS DCA", NULL);
-
   if (dts->bit_rate > 3) {
+    taglist = gst_tag_list_new ();
     /* 1 => open bitrate, 2 => variable bitrate, 3 => lossless */
     gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_BITRATE,
         (guint) dts->bit_rate, NULL);
+
+    if (dts->pending_tags) {
+      gst_tag_list_free (dts->pending_tags);
+      dts->pending_tags = NULL;
+    }
+
+    dts->pending_tags = taglist;
   }
+}
 
-  gst_element_found_tags_for_pad (GST_ELEMENT (dts), dts->srcpad, taglist);
+static GstFlowReturn
+gst_dtsdec_pre_push (GstAudioDecoder * bdec, GstBuffer ** buffer)
+{
+  GstDtsDec *dts = GST_DTSDEC (bdec);
+
+  if (G_UNLIKELY (dts->pending_tags)) {
+    gst_element_found_tags_for_pad (GST_ELEMENT (dts),
+        GST_AUDIO_DECODER_SRC_PAD (dts), dts->pending_tags);
+    dts->pending_tags = NULL;
+  }
+
+  return GST_FLOW_OK;
 }
 
 static GstFlowReturn
-gst_dtsdec_handle_frame (GstDtsDec * dts, guint8 * data,
-    guint length, gint flags, gint sample_rate, gint bit_rate)
+gst_dtsdec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buffer)
 {
+  GstDtsDec *dts;
   gint channels, i, num_blocks;
   gboolean need_renegotiation = FALSE;
+  guint8 *data;
+  gint size, chans;
+  gint length = 0, flags, sample_rate, bit_rate, frame_length;
+  GstFlowReturn result = GST_FLOW_UNEXPECTED;
+  GstBuffer *outbuf;
+
+  dts = GST_DTSDEC (bdec);
+
+  /* parsed stuff already, so this should work out fine */
+  data = GST_BUFFER_DATA (buffer);
+  size = GST_BUFFER_SIZE (buffer);
+  g_assert (size >= 7);
+
+  bit_rate = dts->bit_rate;
+  sample_rate = dts->sample_rate;
+  flags = 0;
+  length = dca_syncinfo (dts->state, data, &flags, &sample_rate, &bit_rate,
+      &frame_length);
+  g_assert (length == size);
+
+  if (flags != dts->prev_flags) {
+    dts->prev_flags = flags;
+    dts->flag_update = TRUE;
+  }
 
   /* go over stream properties, renegotiate or update streaminfo if needed */
   if (dts->sample_rate != sample_rate) {
@@ -581,7 +544,7 @@ gst_dtsdec_handle_frame (GstDtsDec * dts, guint8 * data,
 
     dts->flag_update = FALSE;
 
-    caps = gst_pad_get_allowed_caps (dts->srcpad);
+    caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (dts));
     if (caps && gst_caps_get_size (caps) > 0) {
       GstCaps *copy = gst_caps_copy_nth (caps, 0);
       GstStructure *structure = gst_caps_get_structure (copy, 0);
@@ -618,14 +581,16 @@ gst_dtsdec_handle_frame (GstDtsDec * dts, guint8 * data,
   } else {
     flags = dts->using_channels;
   }
+
   /* process */
   flags |= DCA_ADJUST_LEVEL;
   dts->level = 1;
   if (dca_frame (dts->state, data, &flags, &dts->level, dts->bias)) {
-    GST_WARNING_OBJECT (dts, "dts_frame error");
-    dts->discont = TRUE;
-    return GST_FLOW_OK;
+    GST_AUDIO_DECODER_ERROR (dts, 1, STREAM, DECODE, (NULL),
+        ("dts_frame error"), result);
+    goto exit;
   }
+
   channels = flags & (DCA_CHANNEL_MASK | DCA_LFE);
   if (dts->using_channels != channels) {
     need_renegotiation = TRUE;
@@ -636,42 +601,71 @@ gst_dtsdec_handle_frame (GstDtsDec * dts, guint8 * data,
   if (need_renegotiation) {
     GST_DEBUG ("dtsdec: sample_rate:%d stream_chans:0x%x using_chans:0x%x",
         dts->sample_rate, dts->stream_channels, dts->using_channels);
-    if (!gst_dtsdec_renegotiate (dts)) {
-      GST_ELEMENT_ERROR (dts, CORE, NEGOTIATION, (NULL), (NULL));
-      return GST_FLOW_ERROR;
-    }
+    if (!gst_dtsdec_renegotiate (dts))
+      goto failed_negotiation;
   }
 
   if (dts->dynamic_range_compression == FALSE) {
     dca_dynrng (dts->state, NULL, NULL);
   }
 
+  flags &= (DCA_CHANNEL_MASK | DCA_LFE);
+  chans = gst_dtsdec_channels (flags, NULL);
+  if (!chans)
+    goto invalid_flags;
+
   /* handle decoded data, one block is 256 samples */
   num_blocks = dca_blocks_num (dts->state);
+  result =
+      gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (dts), 0,
+      256 * chans * (SAMPLE_WIDTH / 8) * num_blocks,
+      GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (dts)), &outbuf);
+  if (result != GST_FLOW_OK)
+    goto exit;
+
+  data = GST_BUFFER_DATA (outbuf);
   for (i = 0; i < num_blocks; i++) {
     if (dca_block (dts->state)) {
-      /* Ignore errors, but mark a discont */
-      GST_WARNING_OBJECT (dts, "dts_block error %d", i);
-      dts->discont = TRUE;
+      /* also marks discont */
+      GST_AUDIO_DECODER_ERROR (dts, 1, STREAM, DECODE, (NULL),
+          ("error decoding block %d", i), result);
+      if (result != GST_FLOW_OK)
+        goto exit;
     } else {
-      GstFlowReturn ret;
+      gint n, c;
 
-      /* push on */
-      ret = gst_dtsdec_push (dts, dts->srcpad, dts->using_channels,
-          dts->samples, dts->time);
-      if (ret != GST_FLOW_OK)
-        return ret;
+      for (n = 0; n < 256; n++) {
+        for (c = 0; c < chans; c++) {
+          ((sample_t *) data)[n * chans + c] = dts->samples[c * 256 + n];
+        }
+      }
     }
-    dts->time += GST_SECOND * 256 / dts->sample_rate;
+    data += 256 * chans * (SAMPLE_WIDTH / 8);
   }
 
-  return GST_FLOW_OK;
+  result = gst_audio_decoder_finish_frame (bdec, outbuf, 1);
+
+exit:
+  return result;
+
+  /* ERRORS */
+failed_negotiation:
+  {
+    GST_ELEMENT_ERROR (dts, CORE, NEGOTIATION, (NULL), (NULL));
+    return GST_FLOW_ERROR;
+  }
+invalid_flags:
+  {
+    GST_ELEMENT_ERROR (GST_ELEMENT (dts), STREAM, DECODE, (NULL),
+        ("Invalid channel flags: %d", flags));
+    return GST_FLOW_ERROR;
+  }
 }
 
 static gboolean
-gst_dtsdec_sink_setcaps (GstPad * pad, GstCaps * caps)
+gst_dtsdec_set_format (GstAudioDecoder * bdec, GstCaps * caps)
 {
-  GstDtsDec *dts = GST_DTSDEC (gst_pad_get_parent (pad));
+  GstDtsDec *dts = GST_DTSDEC (bdec);
   GstStructure *structure;
 
   structure = gst_caps_get_structure (caps, 0);
@@ -681,8 +675,6 @@ gst_dtsdec_sink_setcaps (GstPad * pad, GstCaps * caps)
   else
     dts->dvdmode = FALSE;
 
-  gst_object_unref (dts);
-
   return TRUE;
 }
 
@@ -693,17 +685,6 @@ gst_dtsdec_chain (GstPad * pad, GstBuffer * buf)
   GstDtsDec *dts = GST_DTSDEC (GST_PAD_PARENT (pad));
   gint first_access;
 
-  if (GST_BUFFER_IS_DISCONT (buf)) {
-    GST_LOG_OBJECT (dts, "received DISCONT");
-    gst_dtsdec_drain (dts);
-    /* clear cache on discont and mark a discont in the element */
-    if (dts->cache) {
-      gst_buffer_unref (dts->cache);
-      dts->cache = NULL;
-    }
-    dts->discont = TRUE;
-  }
-
   if (dts->dvdmode) {
     gint size = GST_BUFFER_SIZE (buf);
     guint8 *data = GST_BUFFER_DATA (buf);
@@ -726,33 +707,38 @@ gst_dtsdec_chain (GstPad * pad, GstBuffer * buf)
         goto bad_first_access_parameter;
 
       subbuf = gst_buffer_create_sub (buf, offset, len);
+      gst_buffer_copy_metadata (subbuf, buf, GST_BUFFER_COPY_ALL);
       GST_BUFFER_TIMESTAMP (subbuf) = GST_CLOCK_TIME_NONE;
-      ret = gst_dtsdec_chain_raw (pad, subbuf);
-      if (ret != GST_FLOW_OK)
+      ret = dts->base_chain (pad, subbuf);
+      if (ret != GST_FLOW_OK) {
+        gst_buffer_unref (buf);
         goto done;
+      }
 
       offset += len;
       len = size - offset;
 
       if (len > 0) {
         subbuf = gst_buffer_create_sub (buf, offset, len);
+        gst_buffer_copy_metadata (subbuf, buf, GST_BUFFER_COPY_ALL);
         GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf);
 
-        ret = gst_dtsdec_chain_raw (pad, subbuf);
+        ret = dts->base_chain (pad, subbuf);
       }
+      gst_buffer_unref (buf);
     } else {
       /* first_access = 0 or 1, so if there's a timestamp it applies to the first byte */
       subbuf = gst_buffer_create_sub (buf, offset, size - offset);
+      gst_buffer_copy_metadata (subbuf, buf, GST_BUFFER_COPY_ALL);
       GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf);
-      ret = gst_dtsdec_chain_raw (pad, subbuf);
+      ret = dts->base_chain (pad, subbuf);
+      gst_buffer_unref (buf);
     }
   } else {
-    gst_buffer_ref (buf);
-    ret = gst_dtsdec_chain_raw (pad, buf);
+    ret = dts->base_chain (pad, buf);
   }
 
 done:
-  gst_buffer_unref (buf);
   return ret;
 
 /* ERRORS */
@@ -772,154 +758,6 @@ bad_first_access_parameter:
   }
 }
 
-static GstFlowReturn
-gst_dtsdec_chain_raw (GstPad * pad, GstBuffer * buf)
-{
-  GstDtsDec *dts;
-  guint8 *data;
-  gint size;
-  gint length = 0, flags, sample_rate, bit_rate, frame_length;
-  GstFlowReturn result = GST_FLOW_OK;
-
-  dts = GST_DTSDEC (GST_PAD_PARENT (pad));
-
-  if (!dts->sent_segment) {
-    GstSegment segment;
-
-    /* Create a basic segment. Usually, we'll get a new-segment sent by 
-     * another element that will know more information (a demuxer). If we're
-     * just looking at a raw AC3 stream, we won't - so we need to send one
-     * here, but we don't know much info, so just send a minimal TIME 
-     * new-segment event
-     */
-    gst_segment_init (&segment, GST_FORMAT_TIME);
-    gst_pad_push_event (dts->srcpad, gst_event_new_new_segment (FALSE,
-            segment.rate, segment.format, segment.start,
-            segment.duration, segment.start));
-    dts->sent_segment = TRUE;
-  }
-
-  /* merge with cache, if any. Also make sure timestamps match */
-  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
-    dts->time = GST_BUFFER_TIMESTAMP (buf);
-    GST_DEBUG_OBJECT (dts,
-        "Received buffer with ts %" GST_TIME_FORMAT " duration %"
-        GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
-        GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
-  }
-
-  if (dts->cache) {
-    buf = gst_buffer_join (dts->cache, buf);
-    dts->cache = NULL;
-  }
-  data = GST_BUFFER_DATA (buf);
-  size = GST_BUFFER_SIZE (buf);
-
-  /* find and read header */
-  bit_rate = dts->bit_rate;
-  sample_rate = dts->sample_rate;
-  flags = 0;
-  while (size >= 7) {
-    length = dca_syncinfo (dts->state, data, &flags,
-        &sample_rate, &bit_rate, &frame_length);
-
-    if (length == 0) {
-      /* shift window to re-find sync */
-      data++;
-      size--;
-    } else if (length <= size) {
-      GST_DEBUG ("Sync: frame size %d", length);
-
-      if (flags != dts->prev_flags)
-        dts->flag_update = TRUE;
-      dts->prev_flags = flags;
-
-      result = gst_dtsdec_handle_frame (dts, data, length,
-          flags, sample_rate, bit_rate);
-      if (result != GST_FLOW_OK) {
-        size = 0;
-        break;
-      }
-      size -= length;
-      data += length;
-    } else {
-      GST_LOG ("Not enough data available (needed %d had %d)", length, size);
-      break;
-    }
-  }
-
-  /* keep cache */
-  if (length == 0) {
-    GST_LOG ("No sync found");
-  }
-
-  if (size > 0) {
-    dts->cache = gst_buffer_create_sub (buf,
-        GST_BUFFER_SIZE (buf) - size, size);
-  }
-
-  gst_buffer_unref (buf);
-
-  return result;
-}
-
-static GstStateChangeReturn
-gst_dtsdec_change_state (GstElement * element, GstStateChange transition)
-{
-  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
-  GstDtsDec *dts = GST_DTSDEC (element);
-
-  switch (transition) {
-    case GST_STATE_CHANGE_NULL_TO_READY:{
-      GstDtsDecClass *klass;
-
-      klass = GST_DTSDEC_CLASS (G_OBJECT_GET_CLASS (dts));
-      dts->state = dca_init (klass->dts_cpuflags);
-      break;
-    }
-    case GST_STATE_CHANGE_READY_TO_PAUSED:
-      dts->samples = dca_samples (dts->state);
-      dts->bit_rate = -1;
-      dts->sample_rate = -1;
-      dts->stream_channels = DCA_CHANNEL;
-      dts->using_channels = DCA_CHANNEL;
-      dts->level = 1;
-      dts->bias = 0;
-      dts->time = 0;
-      dts->sent_segment = FALSE;
-      dts->flag_update = TRUE;
-      gst_segment_init (&dts->segment, GST_FORMAT_UNDEFINED);
-      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:
-      dts->samples = NULL;
-      if (dts->cache) {
-        gst_buffer_unref (dts->cache);
-        dts->cache = NULL;
-      }
-      clear_queued (dts);
-      break;
-    case GST_STATE_CHANGE_READY_TO_NULL:
-      dca_free (dts->state);
-      dts->state = NULL;
-      break;
-    default:
-      break;
-  }
-
-  return ret;
-}
-
 static void
 gst_dtsdec_set_property (GObject * object, guint prop_id, const GValue * value,
     GParamSpec * pspec)
index a7c8f71..be6005a 100644 (file)
@@ -22,6 +22,7 @@
 #define __GST_DTSDEC_H__
 
 #include <gst/gst.h>
+#include <gst/audio/gstaudiodecoder.h>
 
 G_BEGIN_DECLS
 
@@ -40,16 +41,11 @@ typedef struct _GstDtsDec GstDtsDec;
 typedef struct _GstDtsDecClass GstDtsDecClass;
 
 struct _GstDtsDec {
-  GstElement    element;
+  GstAudioDecoder       element;
 
-  /* pads */
-  GstPad        *sinkpad;
-  GstPad        *srcpad;
-  GstSegment     segment;
+  GstPadChainFunction base_chain;
 
   gboolean       dvdmode;
-  gboolean       sent_segment;
-  gboolean       discont;
   gboolean       flag_update;
   gboolean       prev_flags;
 
@@ -71,17 +67,11 @@ struct _GstDtsDec {
   dts_state_t  *state;
 #endif
 
-
-  /* Data left over from the previous buffer */
-  GstBuffer            *cache;
-  GstClockTime time;
-
-  /* reverse playback */
-  GList *queued;
+  GstTagList    *pending_tags;
 };
 
 struct _GstDtsDecClass {
-  GstElementClass parent_class;
+  GstAudioDecoderClass parent_class;
 
   guint32 dts_cpuflags;
 };