mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-20 00:31:13 +00:00
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.
This commit is contained in:
parent
3fb997111f
commit
b6e891bbda
2 changed files with 353 additions and 178 deletions
|
@ -1,5 +1,6 @@
|
|||
/* GStreamer DTS decoder plugin based on libdtsdec
|
||||
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
||||
* Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
|
||||
*
|
||||
* 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 <liboil/liboilcpu.h>
|
||||
#include <liboil/liboilfunction.h>
|
||||
|
||||
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 <thaytan@noraisin.net>\n"
|
||||
"Ronald Bultje <rbultje@ronald.bitfreak.net>");
|
||||
|
||||
#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;
|
||||
gboolean update;
|
||||
gint64 start, end, pos;
|
||||
gdouble rate;
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
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 */
|
||||
result = gst_pad_push (dts->srcpad, out);
|
||||
|
||||
if (result != GST_FLOW_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_dtsdec_chain_raw (GstPad * pad, GstBuffer * buf)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
/* Ignore errors, but mark a discont */
|
||||
GST_WARNING_OBJECT (dts, "dts_block error %d", i);
|
||||
dts->discont = TRUE;
|
||||
} else {
|
||||
GST_LOG ("Not enough data available (needed %d had %d)", length, size);
|
||||
break;
|
||||
GstFlowReturn ret;
|
||||
|
||||
/* push on */
|
||||
ret = gst_dtsdec_push (dts, dts->srcpad, dts->using_channels,
|
||||
dts->samples, dts->time);
|
||||
if (ret != GST_FLOW_OK)
|
||||
return ret;
|
||||
}
|
||||
dts->time += GST_SECOND * 256 / dts->sample_rate;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
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 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);
|
||||
|
|
|
@ -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;
|
||||
GstBuffer *cache;
|
||||
GstClockTime time;
|
||||
|
||||
/* keep track of time */
|
||||
GstClockTime current_ts;
|
||||
/* reverse playback */
|
||||
GList *queued;
|
||||
};
|
||||
|
||||
struct _GstDtsDecClass {
|
||||
|
|
Loading…
Reference in a new issue