From b6e891bbdad221105daecdef83098a2585d0f039 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 18 May 2009 23:38:59 +0100 Subject: dtsdec: Reconcile element code with a52dec changes Re-work the dtsdec element code to unify it with changes made it a52dec, including support for reverse playback and dynamic channel negotiation on the source pad. --- ext/dts/gstdtsdec.c | 489 +++++++++++++++++++++++++++++++++++----------------- ext/dts/gstdtsdec.h | 32 ++-- 2 files changed, 348 insertions(+), 173 deletions(-) (limited to 'ext/dts') diff --git a/ext/dts/gstdtsdec.c b/ext/dts/gstdtsdec.c index 5b85a803..08695936 100644 --- a/ext/dts/gstdtsdec.c +++ b/ext/dts/gstdtsdec.c @@ -1,5 +1,6 @@ /* GStreamer DTS decoder plugin based on libdtsdec * Copyright (C) 2004 Ronald Bultje + * Copyright (C) 2009 Jan Schmidt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -84,43 +85,46 @@ typedef struct dts_state_s dca_state_t; #include #include -GST_DEBUG_CATEGORY_STATIC (dtsdec_debug); -#define GST_CAT_DEFAULT (dtsdec_debug) - static const GstElementDetails gst_dtsdec_details = GST_ELEMENT_DETAILS ("DTS audio decoder", "Codec/Decoder/Audio", "Decodes DTS audio streams", + "Jan Schmidt \n" "Ronald Bultje "); +#if defined(LIBDTS_FIXED) || defined(LIBDCA_FIXED) +#define SAMPLE_WIDTH 16 +#elif defined (LIBDTS_DOUBLE) || defined(LIBDCA_DOUBLE) +#define SAMPLE_WIDTH 64 +#else +#define SAMPLE_WIDTH 32 +#endif + +GST_DEBUG_CATEGORY_STATIC (dtsdec_debug); +#define GST_CAT_DEFAULT (dtsdec_debug) + enum { ARG_0, ARG_DRC }; + static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-dts;" "audio/x-private1-dts") + GST_STATIC_CAPS ("audio/x-dts; audio/x-private1-dts") ); #if defined(LIBDTS_FIXED) || defined(LIBDCA_FIXED) #define DTS_CAPS "audio/x-raw-int, " \ "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", " \ "signed = (boolean) true, " \ - "width = (int) 16, " \ + "width = (int) " G_STRINGIFY (SAMPLE_WIDTH) ", " \ "depth = (int) 16" -#define SAMPLE_WIDTH 16 -#elif defined(LIBDTS_DOUBLE) || defined(LIBDCA_DOUBLE) -#define DTS_CAPS "audio/x-raw-float, " \ - "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", " \ - "width = (int) 64" -#define SAMPLE_WIDTH 64 #else #define DTS_CAPS "audio/x-raw-float, " \ "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", " \ - "width = (int) 32" -#define SAMPLE_WIDTH 32 + "width = (int) " G_STRINGIFY (SAMPLE_WIDTH) #endif static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", @@ -135,6 +139,7 @@ GST_BOILERPLATE (GstDtsDec, gst_dtsdec, GstElement, GST_TYPE_ELEMENT); 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); @@ -155,7 +160,7 @@ gst_dtsdec_base_init (gpointer g_class) gst_static_pad_template_get (&src_factory)); gst_element_class_set_details (element_class, &gst_dtsdec_details); - GST_DEBUG_CATEGORY_INIT (dtsdec_debug, "dtsdec", 0, "DTS audio decoder"); + GST_DEBUG_CATEGORY_INIT (dtsdec_debug, "dtsdec", 0, "DTS/DCA audio decoder"); } static void @@ -202,6 +207,7 @@ 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)); @@ -212,10 +218,12 @@ gst_dtsdec_init (GstDtsDec * dtsdec, GstDtsDecClass * g_class) gst_element_add_pad (GST_ELEMENT (dtsdec), dtsdec->sinkpad); dtsdec->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); - gst_pad_use_fixed_caps (dtsdec->srcpad); gst_element_add_pad (GST_ELEMENT (dtsdec), dtsdec->srcpad); + dtsdec->request_channels = DCA_CHANNEL; dtsdec->dynamic_range_compression = FALSE; + + gst_segment_init (&dtsdec->segment, GST_FORMAT_UNDEFINED); } static gint @@ -317,6 +325,105 @@ 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) { @@ -360,32 +467,57 @@ gst_dtsdec_sink_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_NEWSEGMENT:{ GstFormat format; - gint64 val; - - gst_event_parse_new_segment (event, NULL, NULL, &format, &val, NULL, - NULL); - if (format != GST_FORMAT_TIME || !GST_CLOCK_TIME_IS_VALID (val)) { - GST_WARNING ("No time in newsegment event %p", event); + 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->current_ts = val; + dtsdec->time = start; + dtsdec->sent_segment = TRUE; + ret = gst_pad_push_event (dtsdec->srcpad, event); } - if (dtsdec->cache) { - gst_buffer_unref (dtsdec->cache); - dtsdec->cache = NULL; - } - ret = gst_pad_event_default (pad, 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; } - ret = gst_pad_event_default (pad, event); + clear_queued (dtsdec); + gst_segment_init (&dtsdec->segment, GST_FORMAT_UNDEFINED); + ret = gst_pad_push_event (dtsdec->srcpad, event); break; default: - ret = gst_pad_event_default (pad, event); + ret = gst_pad_push_event (dtsdec->srcpad, event); break; } @@ -393,24 +525,6 @@ gst_dtsdec_sink_event (GstPad * pad, GstEvent * event) return ret; } -static gboolean -gst_dtsdec_sink_setcaps (GstPad * pad, GstCaps * caps) -{ - GstDtsDec *dts = GST_DTSDEC (gst_pad_get_parent (pad)); - GstStructure *structure; - - structure = gst_caps_get_structure (caps, 0); - - if (structure && gst_structure_has_name (structure, "audio/x-private1-dts")) - dts->dvdmode = TRUE; - else - dts->dvdmode = FALSE; - - gst_object_unref (dts); - - return TRUE; -} - static void gst_dtsdec_update_streaminfo (GstDtsDec * dts) { @@ -419,6 +533,7 @@ gst_dtsdec_update_streaminfo (GstDtsDec * dts) taglist = gst_tag_list_new (); gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, + GST_TAG_AUDIO_CODEC, "DTS DCA", GST_TAG_BITRATE, (guint) dts->bit_rate, NULL); gst_element_found_tags_for_pad (GST_ELEMENT (dts), dts->srcpad, taglist); @@ -428,29 +543,37 @@ static GstFlowReturn gst_dtsdec_handle_frame (GstDtsDec * dts, guint8 * data, guint length, gint flags, gint sample_rate, gint bit_rate) { + gint channels, i, num_blocks; gboolean need_renegotiation = FALSE; - gint channels, num_blocks; - GstBuffer *out; - gint i, s, c, num_c; - sample_t *samples; - GstFlowReturn result = GST_FLOW_OK; - /* go over stream properties, update caps/streaminfo if needed */ + /* go over stream properties, renegotiate or update streaminfo if needed */ if (dts->sample_rate != sample_rate) { need_renegotiation = TRUE; dts->sample_rate = sample_rate; } - dts->stream_channels = flags; + if (flags) { + dts->stream_channels = flags & (DCA_CHANNEL_MASK | DCA_LFE); + } if (bit_rate != dts->bit_rate) { dts->bit_rate = bit_rate; gst_dtsdec_update_streaminfo (dts); } - if (dts->request_channels == DCA_CHANNEL) { + /* If we haven't had an explicit number of channels chosen through properties + * at this point, choose what to downmix to now, based on what the peer will + * accept - this allows a52dec to do downmixing in preference to a + * downstream element such as audioconvert. + * FIXME: Add the property back in for forcing output channels. + */ + if (dts->request_channels != DCA_CHANNEL) { + flags = dts->request_channels; + } else if (dts->flag_update) { GstCaps *caps; + dts->flag_update = FALSE; + caps = gst_pad_get_allowed_caps (dts->srcpad); if (caps && gst_caps_get_size (caps) > 0) { GstCaps *copy = gst_caps_copy_nth (caps, 0); @@ -472,38 +595,38 @@ gst_dtsdec_handle_frame (GstDtsDec * dts, guint8 * data, flags ? gst_dtsdec_channels (flags, NULL) : 6); gst_structure_get_int (structure, "channels", &channels); if (channels <= 6) - dts->request_channels = dts_channels[channels - 1]; + flags = dts_channels[channels - 1]; else - dts->request_channels = dts_channels[5]; + flags = dts_channels[5]; gst_caps_unref (copy); } else if (flags) { - dts->request_channels = dts->stream_channels; + flags = dts->stream_channels; } else { - dts->request_channels = DCA_3F2R | DCA_LFE; + flags = DCA_3F2R | DCA_LFE; } if (caps) gst_caps_unref (caps); + } else { + flags = dts->using_channels; } - /* process */ - flags = dts->request_channels | DCA_ADJUST_LEVEL; + flags |= DCA_ADJUST_LEVEL; dts->level = 1; - if (dca_frame (dts->state, data, &flags, &dts->level, dts->bias)) { - GST_WARNING ("dts_frame error"); + GST_WARNING_OBJECT (dts, "dts_frame error"); + dts->discont = TRUE; return GST_FLOW_OK; } - channels = flags & (DCA_CHANNEL_MASK | DCA_LFE); - if (dts->using_channels != channels) { need_renegotiation = TRUE; dts->using_channels = channels; } - if (need_renegotiation == TRUE) { + /* negotiate if required */ + 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)) { @@ -520,107 +643,60 @@ gst_dtsdec_handle_frame (GstDtsDec * dts, guint8 * data, num_blocks = dca_blocks_num (dts->state); for (i = 0; i < num_blocks; i++) { if (dca_block (dts->state)) { - GST_WARNING ("dts_block error %d", i); - continue; - } - - samples = dca_samples (dts->state); - num_c = gst_dtsdec_channels (dts->using_channels, NULL); - - result = gst_pad_alloc_buffer_and_set_caps (dts->srcpad, 0, - (SAMPLE_WIDTH / 8) * 256 * num_c, GST_PAD_CAPS (dts->srcpad), &out); - - if (result != GST_FLOW_OK) - break; + /* Ignore errors, but mark a discont */ + GST_WARNING_OBJECT (dts, "dts_block error %d", i); + dts->discont = TRUE; + } else { + GstFlowReturn ret; - GST_BUFFER_TIMESTAMP (out) = dts->current_ts; - GST_BUFFER_DURATION (out) = GST_SECOND * 256 / dts->sample_rate; - dts->current_ts += GST_BUFFER_DURATION (out); - - /* libdts returns buffers in 256-sample-blocks per channel, - * we want interleaved. And we need to copy anyway... */ - data = GST_BUFFER_DATA (out); - for (s = 0; s < 256; s++) { - for (c = 0; c < num_c; c++) { - *(sample_t *) data = samples[s + c * 256]; - data += (SAMPLE_WIDTH / 8); - } + /* push on */ + ret = gst_dtsdec_push (dts, dts->srcpad, dts->using_channels, + dts->samples, dts->time); + if (ret != GST_FLOW_OK) + return ret; } - - /* push on */ - result = gst_pad_push (dts->srcpad, out); - - if (result != GST_FLOW_OK) - break; + dts->time += GST_SECOND * 256 / dts->sample_rate; } - return result; + return GST_FLOW_OK; } -static GstFlowReturn -gst_dtsdec_chain_raw (GstPad * pad, GstBuffer * buf) +static gboolean +gst_dtsdec_sink_setcaps (GstPad * pad, GstCaps * caps) { - GstDtsDec *dts; - guint8 *data; - gint size; - gint length, flags, sample_rate, bit_rate, frame_length; - GstFlowReturn result = GST_FLOW_OK; - - dts = GST_DTSDEC (GST_PAD_PARENT (pad)); - - if (dts->cache) { - buf = gst_buffer_join (dts->cache, buf); - dts->cache = NULL; - } + GstDtsDec *dts = GST_DTSDEC (gst_pad_get_parent (pad)); + GstStructure *structure; - data = GST_BUFFER_DATA (buf); - size = GST_BUFFER_SIZE (buf); - length = 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); - 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; - } - } + structure = gst_caps_get_structure (caps, 0); - /* 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); - } + if (structure && gst_structure_has_name (structure, "audio/x-private1-dts")) + dts->dvdmode = TRUE; + else + dts->dvdmode = FALSE; - gst_buffer_unref (buf); + gst_object_unref (dts); - return result; + return TRUE; } - static GstFlowReturn gst_dtsdec_chain (GstPad * pad, GstBuffer * buf) { - GstFlowReturn res = GST_FLOW_OK; + GstFlowReturn ret = GST_FLOW_OK; 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); @@ -644,8 +720,8 @@ gst_dtsdec_chain (GstPad * pad, GstBuffer * buf) subbuf = gst_buffer_create_sub (buf, offset, len); GST_BUFFER_TIMESTAMP (subbuf) = GST_CLOCK_TIME_NONE; - res = gst_dtsdec_chain_raw (pad, subbuf); - if (res != GST_FLOW_OK) + ret = gst_dtsdec_chain_raw (pad, subbuf); + if (ret != GST_FLOW_OK) goto done; offset += len; @@ -655,21 +731,20 @@ gst_dtsdec_chain (GstPad * pad, GstBuffer * buf) subbuf = gst_buffer_create_sub (buf, offset, len); GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf); - res = gst_dtsdec_chain_raw (pad, subbuf); + ret = gst_dtsdec_chain_raw (pad, subbuf); } } else { - /* first_access = 0 or 1, so if there's a timestamp it applies - * to the first byte */ + /* 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_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf); - res = gst_dtsdec_chain_raw (pad, subbuf); + ret = gst_dtsdec_chain_raw (pad, subbuf); } } else { - res = gst_dtsdec_chain_raw (pad, buf); + ret = gst_dtsdec_chain_raw (pad, buf); } done: - return res; + return ret; /* ERRORS */ not_enough_data: @@ -684,7 +759,97 @@ bad_first_access_parameter: ("Bad first_access parameter (%d) in buffer", first_access)); return GST_FLOW_ERROR; } +} + +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 @@ -705,13 +870,14 @@ gst_dtsdec_change_state (GstElement * element, GstStateChange transition) dts->samples = dca_samples (dts->state); dts->bit_rate = -1; dts->sample_rate = -1; - dts->stream_channels = 0; - /* FIXME force stereo for now */ - dts->request_channels = DCA_CHANNEL; - dts->using_channels = 0; + dts->stream_channels = DCA_CHANNEL; + dts->using_channels = DCA_CHANNEL; dts->level = 1; dts->bias = 0; - dts->current_ts = 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; @@ -730,6 +896,7 @@ gst_dtsdec_change_state (GstElement * element, GstStateChange transition) gst_buffer_unref (dts->cache); dts->cache = NULL; } + clear_queued (dts); break; case GST_STATE_CHANGE_READY_TO_NULL: dca_free (dts->state); diff --git a/ext/dts/gstdtsdec.h b/ext/dts/gstdtsdec.h index 5222c687..a7c8f718 100644 --- a/ext/dts/gstdtsdec.h +++ b/ext/dts/gstdtsdec.h @@ -43,15 +43,22 @@ struct _GstDtsDec { GstElement element; /* pads */ - GstPad *sinkpad; - GstPad *srcpad; + GstPad *sinkpad; + GstPad *srcpad; + GstSegment segment; + + gboolean dvdmode; + gboolean sent_segment; + gboolean discont; + gboolean flag_update; + gboolean prev_flags; /* stream properties */ - gint bit_rate; - gint sample_rate; - gint stream_channels; - gint request_channels; - gint using_channels; + gint bit_rate; + gint sample_rate; + gint stream_channels; + gint request_channels; + gint using_channels; /* decoding properties */ sample_t level; @@ -63,13 +70,14 @@ struct _GstDtsDec { #else dts_state_t *state; #endif - gboolean dvdmode; + /* Data left over from the previous buffer */ - GstBuffer *cache; - - /* keep track of time */ - GstClockTime current_ts; + GstBuffer *cache; + GstClockTime time; + + /* reverse playback */ + GList *queued; }; struct _GstDtsDecClass { -- cgit v1.2.1