diff options
author | Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> | 2011-11-23 23:29:10 +0100 |
---|---|---|
committer | Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> | 2011-11-23 23:29:10 +0100 |
commit | 8b5fbcaedd42ea4f2c49193323c24bb8a462e128 (patch) | |
tree | b380d07ce3b375e1813abdfdbf844e17d29546bf /ext/dts | |
parent | da43e59aabfbb7e684bea27ce34734c4ee62fda1 (diff) | |
download | gstreamer-plugins-bad-8b5fbcaedd42ea4f2c49193323c24bb8a462e128.tar.gz |
dtsdec: port to audiodecoder
Diffstat (limited to 'ext/dts')
-rw-r--r-- | ext/dts/Makefile.am | 5 | ||||
-rw-r--r-- | ext/dts/gstdtsdec.c | 614 | ||||
-rw-r--r-- | ext/dts/gstdtsdec.h | 20 |
3 files changed, 234 insertions, 405 deletions
diff --git a/ext/dts/Makefile.am b/ext/dts/Makefile.am index f93e87dea..f58f14972 100644 --- a/ext/dts/Makefile.am +++ b/ext/dts/Makefile.am @@ -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 diff --git a/ext/dts/gstdtsdec.c b/ext/dts/gstdtsdec.c index 2039c58ba..2a762e903 100644 --- a/ext/dts/gstdtsdec.c +++ b/ext/dts/gstdtsdec.c @@ -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) diff --git a/ext/dts/gstdtsdec.h b/ext/dts/gstdtsdec.h index a7c8f7180..be6005a0a 100644 --- a/ext/dts/gstdtsdec.h +++ b/ext/dts/gstdtsdec.h @@ -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; }; |