ext/faad/gstfaad.*: If we run into a decoding error, try re-opening the decoder with faacDecInit2() using fake codec ...

Original commit message from CVS:
* ext/faad/gstfaad.c: (gst_faad_init), (aac_rate_idx),
(gst_faad_setcaps), (gst_faad_chain), (gst_faad_open_decoder),
(gst_faad_close_decoder), (gst_faad_change_state):
* ext/faad/gstfaad.h:
If we run into a decoding error, try re-opening the decoder
with faacDecInit2() using fake codec data created from the
data the demuxer gave us. Should fix a whole bunch of
GStreamer-faad problems incl. 'channel coupling not
implemented', 'maximum number of scalefactor bands exceeded'
etc. (#173007, #332892).
This commit is contained in:
Tim-Philipp Müller 2006-04-28 11:04:12 +00:00
parent bf3fd4f95d
commit 2d4c661169
3 changed files with 173 additions and 92 deletions

View file

@ -1,3 +1,16 @@
2006-04-28 Tim-Philipp Müller <tim at centricular dot net>
* ext/faad/gstfaad.c: (gst_faad_init), (aac_rate_idx),
(gst_faad_setcaps), (gst_faad_chain), (gst_faad_open_decoder),
(gst_faad_close_decoder), (gst_faad_change_state):
* ext/faad/gstfaad.h:
If we run into a decoding error, try re-opening the decoder
with faacDecInit2() using fake codec data created from the
data the demuxer gave us. Should fix a whole bunch of
GStreamer-faad problems incl. 'channel coupling not
implemented', 'maximum number of scalefactor bands exceeded'
etc. (#173007, #332892).
2006-04-26 Stefan Kost <ensonic@users.sf.net> 2006-04-26 Stefan Kost <ensonic@users.sf.net>
* ext/amrwb/gstamrwbdec.c: * ext/amrwb/gstamrwbdec.c:

View file

@ -1,5 +1,6 @@
/* GStreamer FAAD (Free AAC Decoder) plugin /* GStreamer FAAD (Free AAC Decoder) plugin
* Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net> * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -121,6 +122,8 @@ static GstStateChangeReturn gst_faad_change_state (GstElement * element,
GstStateChange transition); GstStateChange transition);
static gboolean gst_faad_src_convert (GstFaad * faad, GstFormat src_format, static gboolean gst_faad_src_convert (GstFaad * faad, GstFormat src_format,
gint64 src_val, GstFormat dest_format, gint64 * dest_val); gint64 src_val, GstFormat dest_format, gint64 * dest_val);
static gboolean gst_faad_open_decoder (GstFaad * faad);
static void gst_faad_close_decoder (GstFaad * faad);
static GstElementClass *parent_class; /* NULL */ static GstElementClass *parent_class; /* NULL */
@ -190,9 +193,7 @@ gst_faad_init (GstFaad * faad)
faad->sum_dur_out = 0; faad->sum_dur_out = 0;
faad->packetised = FALSE; faad->packetised = FALSE;
faad->sinkpad = faad->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
gst_pad_new_from_template (gst_static_pad_template_get (&sink_template),
"sink");
gst_element_add_pad (GST_ELEMENT (faad), faad->sinkpad); gst_element_add_pad (GST_ELEMENT (faad), faad->sinkpad);
gst_pad_set_event_function (faad->sinkpad, gst_pad_set_event_function (faad->sinkpad,
GST_DEBUG_FUNCPTR (gst_faad_sink_event)); GST_DEBUG_FUNCPTR (gst_faad_sink_event));
@ -201,9 +202,7 @@ gst_faad_init (GstFaad * faad)
gst_pad_set_chain_function (faad->sinkpad, gst_pad_set_chain_function (faad->sinkpad,
GST_DEBUG_FUNCPTR (gst_faad_chain)); GST_DEBUG_FUNCPTR (gst_faad_chain));
faad->srcpad = faad->srcpad = gst_pad_new_from_static_template (&src_template, "src");
gst_pad_new_from_template (gst_static_pad_template_get (&src_template),
"src");
gst_pad_use_fixed_caps (faad->srcpad); gst_pad_use_fixed_caps (faad->srcpad);
gst_pad_set_getcaps_function (faad->srcpad, gst_pad_set_getcaps_function (faad->srcpad,
GST_DEBUG_FUNCPTR (gst_faad_srcgetcaps)); GST_DEBUG_FUNCPTR (gst_faad_srcgetcaps));
@ -227,6 +226,35 @@ gst_faad_send_tags (GstFaad * faad)
gst_element_found_tags (GST_ELEMENT (faad), tags); gst_element_found_tags (GST_ELEMENT (faad), tags);
} }
static gint
aac_rate_idx (gint rate)
{
if (92017 <= rate)
return 0;
else if (75132 <= rate)
return 1;
else if (55426 <= rate)
return 2;
else if (46009 <= rate)
return 3;
else if (37566 <= rate)
return 4;
else if (27713 <= rate)
return 5;
else if (23004 <= rate)
return 6;
else if (18783 <= rate)
return 7;
else if (13856 <= rate)
return 8;
else if (11502 <= rate)
return 9;
else if (9391 <= rate)
return 10;
else
return 11;
}
static gboolean static gboolean
gst_faad_setcaps (GstPad * pad, GstCaps * caps) gst_faad_setcaps (GstPad * pad, GstCaps * caps)
{ {
@ -239,8 +267,8 @@ gst_faad_setcaps (GstPad * pad, GstCaps * caps)
faad->packetised = FALSE; faad->packetised = FALSE;
if ((value = gst_structure_get_value (str, "codec_data"))) { if ((value = gst_structure_get_value (str, "codec_data"))) {
guint samplerate; guint32 samplerate;
guchar channels; guint8 channels;
/* We have codec data, means packetised stream */ /* We have codec data, means packetised stream */
faad->packetised = TRUE; faad->packetised = TRUE;
@ -253,10 +281,9 @@ gst_faad_setcaps (GstPad * pad, GstCaps * caps)
GST_DEBUG ("faacDecInit2() failed"); GST_DEBUG ("faacDecInit2() failed");
return FALSE; return FALSE;
} }
#if 0
faad->samplerate = samplerate; GST_DEBUG_OBJECT (faad, "channels=%u, rate=%u", channels, samplerate);
faad->channels = channels;
#endif
/* not updating these here, so they are updated in the /* not updating these here, so they are updated in the
* chain function, and new caps are created etc. */ * chain function, and new caps are created etc. */
faad->samplerate = 0; faad->samplerate = 0;
@ -275,6 +302,25 @@ gst_faad_setcaps (GstPad * pad, GstCaps * caps)
faad->init = FALSE; faad->init = FALSE;
} }
faad->fake_codec_data[0] = 0;
faad->fake_codec_data[1] = 0;
if (faad->packetised) {
gint rate, channels;
if (gst_structure_get_int (str, "rate", &rate) &&
gst_structure_get_int (str, "channels", &channels)) {
gint rate_idx, profile;
profile = 3; /* 0=MAIN, 1=LC, 2=SSR, 3=? */
rate_idx = aac_rate_idx (rate);
faad->fake_codec_data[0] = ((profile + 1) << 3) | ((rate_idx & 0xE) >> 1);
faad->fake_codec_data[1] = ((rate_idx & 0x1) << 7) | (channels << 3);
GST_LOG_OBJECT (faad, "created fake codec data (%u,%u)", rate, channels);
}
}
faad->need_channel_setup = TRUE; faad->need_channel_setup = TRUE;
if (!faad->packetised) if (!faad->packetised)
@ -407,51 +453,6 @@ gst_faad_chanpos_to_gst (guchar * fpos, guint num)
return pos; return pos;
} }
/*
static GstPadLinkReturn
gst_faad_sinkconnect (GstPad * pad, const GstCaps * caps)
{
GstFaad *faad = GST_FAAD (gst_pad_get_parent (pad));
GstStructure *str = gst_caps_get_structure (caps, 0);
const GValue *value;
GstBuffer *buf;
// Assume raw stream
faad->packetised = FALSE;
if ((value = gst_structure_get_value (str, "codec_data"))) {
gulong samplerate;
guchar channels;
// We have codec data, means packetised stream
faad->packetised = TRUE;
buf = g_value_get_boxed (value);
// someone forgot that char can be unsigned when writing the API
if ((gint8) faacDecInit2 (faad->handle, GST_BUFFER_DATA (buf),
GST_BUFFER_SIZE (buf), &samplerate, &channels) < 0)
return GST_PAD_LINK_REFUSED;
//faad->samplerate = samplerate;
//faad->channels = channels;
faad->init = TRUE;
if (faad->tempbuf) {
gst_buffer_unref (faad->tempbuf);
faad->tempbuf = NULL;
}
} else {
faad->init = FALSE;
}
faad->need_channel_setup = TRUE;
// if there's no decoderspecificdata, it's all fine. We cannot know
// * much more at this point...
return GST_PAD_LINK_OK;
}
*/
static GstCaps * static GstCaps *
gst_faad_srcgetcaps (GstPad * pad) gst_faad_srcgetcaps (GstPad * pad)
{ {
@ -1079,7 +1080,7 @@ gst_faad_chain (GstPad * pad, GstBuffer * buffer)
faad->next_ts = GST_BUFFER_TIMESTAMP (buffer); faad->next_ts = GST_BUFFER_TIMESTAMP (buffer);
faad->prev_ts = GST_BUFFER_TIMESTAMP (buffer); faad->prev_ts = GST_BUFFER_TIMESTAMP (buffer);
} }
GST_DEBUG ("Timestamp on incoming buffer: %" GST_TIME_FORMAT GST_LOG_OBJECT (faad, "Timestamp on incoming buffer: %" GST_TIME_FORMAT
", next_ts: %" GST_TIME_FORMAT, ", next_ts: %" GST_TIME_FORMAT,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
GST_TIME_ARGS (faad->next_ts)); GST_TIME_ARGS (faad->next_ts));
@ -1103,19 +1104,16 @@ gst_faad_chain (GstPad * pad, GstBuffer * buffer)
/* init if not already done during capsnego */ /* init if not already done during capsnego */
if (!faad->init) { if (!faad->init) {
guint32 samplerate; guint32 rate;
guchar channels; guint8 ch;
glong init_res;
init_res = faacDecInit (faad->handle, input_data, input_size, GST_DEBUG_OBJECT (faad, "initialising ...");
&samplerate, &channels); if (faacDecInit (faad->handle, input_data, input_size, &rate, &ch) < 0)
if (init_res < 0) { goto init_failed;
GST_ELEMENT_ERROR (faad, STREAM, DECODE, (NULL),
("Failed to init decoder from stream")); GST_DEBUG_OBJECT (faad, "faacDecInit() ok: rate=%u,channels=%u", rate, ch);
gst_object_unref (faad);
return GST_FLOW_UNEXPECTED; skip_bytes = 0;
}
skip_bytes = 0; /* init_res; */
faad->init = TRUE; faad->init = TRUE;
/* make sure we create new caps below */ /* make sure we create new caps below */
@ -1146,11 +1144,35 @@ gst_faad_chain (GstPad * pad, GstBuffer * buffer)
out = faacDecDecode (faad->handle, &info, input_data + skip_bytes, out = faacDecDecode (faad->handle, &info, input_data + skip_bytes,
input_size - skip_bytes); input_size - skip_bytes);
if (info.error) { if (info.error) {
GST_ELEMENT_ERROR (faad, STREAM, DECODE, (NULL), guint32 rate;
("Failed to decode buffer: %s", faacDecGetErrorMessage (info.error))); guint8 ch;
ret = GST_FLOW_ERROR;
goto out; if (!faad->packetised)
goto decode_error;
/* decode error? try again using faacDecInit2
* fabricated private codec data from sink caps */
gst_faad_close_decoder (faad);
if (!gst_faad_open_decoder (faad))
goto init2_failed;
GST_DEBUG_OBJECT (faad, "decoding error, reopening with faacDecInit2()");
if ((gint8) faacDecInit2 (faad->handle, faad->fake_codec_data, 2,
&rate, &ch) < 0) {
goto init2_failed;
}
GST_DEBUG_OBJECT (faad, "faacDecInit2(): rate=%d,channels=%d", rate, ch);
/* let's try again */
info.error = 0;
out = faacDecDecode (faad->handle, &info, input_data + skip_bytes,
input_size - skip_bytes);
if (info.error)
goto decode_error;
} }
if (info.bytesconsumed > input_size) if (info.bytesconsumed > input_size)
@ -1184,7 +1206,7 @@ gst_faad_chain (GstPad * pad, GstBuffer * buffer)
} }
/* play decoded data */ /* play decoded data */
if (info.samples > 0 && GST_PAD_PEER (faad->srcpad)) { if (info.samples > 0) {
guint bufsize = info.samples * faad->bps; guint bufsize = info.samples * faad->bps;
guint num_samples = info.samples / faad->channels; guint num_samples = info.samples / faad->channels;
@ -1207,7 +1229,7 @@ gst_faad_chain (GstPad * pad, GstBuffer * buffer)
faad->sum_dur_out += GST_BUFFER_DURATION (outbuf); faad->sum_dur_out += GST_BUFFER_DURATION (outbuf);
GST_OBJECT_UNLOCK (faad); GST_OBJECT_UNLOCK (faad);
GST_DEBUG ("pushing buffer, off=%" G_GUINT64_FORMAT ", ts=%" GST_LOG_OBJECT (faad, "pushing buffer, off=%" G_GUINT64_FORMAT ", ts=%"
GST_TIME_FORMAT, GST_BUFFER_OFFSET (outbuf), GST_TIME_FORMAT, GST_BUFFER_OFFSET (outbuf),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf))); GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
if ((ret = gst_pad_push (faad->srcpad, outbuf)) != GST_FLOW_OK && if ((ret = gst_pad_push (faad->srcpad, outbuf)) != GST_FLOW_OK &&
@ -1239,6 +1261,63 @@ out:
gst_object_unref (faad); gst_object_unref (faad);
return ret; return ret;
/* ERRORS */
init_failed:
{
GST_ELEMENT_ERROR (faad, STREAM, DECODE, (NULL),
("Failed to init decoder from stream"));
ret = GST_FLOW_ERROR;
goto out;
}
init2_failed:
{
GST_ELEMENT_ERROR (faad, STREAM, DECODE, (NULL),
("%s() failed", (faad->handle) ? "faacDecInit2" : "faacDecOpen"));
ret = GST_FLOW_ERROR;
goto out;
}
decode_error:
{
GST_ELEMENT_ERROR (faad, STREAM, DECODE, (NULL),
("Failed to decode buffer: %s", faacDecGetErrorMessage (info.error)));
ret = GST_FLOW_ERROR;
goto out;
}
}
static gboolean
gst_faad_open_decoder (GstFaad * faad)
{
faacDecConfiguration *conf;
faad->handle = faacDecOpen ();
if (faad->handle == NULL) {
GST_WARNING_OBJECT (faad, "faacDecOpen() failed");
return FALSE;
}
conf = faacDecGetCurrentConfiguration (faad->handle);
conf->defObjectType = LC;
/* conf->dontUpSampleImplicitSBR = 1; */
conf->outputFormat = FAAD_FMT_16BIT;
if (faacDecSetConfiguration (faad->handle, conf) == 0) {
GST_WARNING_OBJECT (faad, "faacDecSetConfiguration() failed");
return FALSE;
}
return TRUE;
}
static void
gst_faad_close_decoder (GstFaad * faad)
{
faacDecClose (faad->handle);
faad->handle = NULL;
} }
static GstStateChangeReturn static GstStateChangeReturn
@ -1249,21 +1328,9 @@ gst_faad_change_state (GstElement * element, GstStateChange transition)
switch (transition) { switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY: case GST_STATE_CHANGE_NULL_TO_READY:
{ if (!gst_faad_open_decoder (faad))
if (!(faad->handle = faacDecOpen ()))
return GST_STATE_CHANGE_FAILURE; return GST_STATE_CHANGE_FAILURE;
else {
faacDecConfiguration *conf;
conf = faacDecGetCurrentConfiguration (faad->handle);
conf->defObjectType = LC;
/* conf->dontUpSampleImplicitSBR = 1; */
conf->outputFormat = FAAD_FMT_16BIT;
if (faacDecSetConfiguration (faad->handle, conf) == 0)
return GST_STATE_CHANGE_FAILURE;
}
break; break;
}
default: default:
break; break;
} }
@ -1285,8 +1352,7 @@ gst_faad_change_state (GstElement * element, GstStateChange transition)
faad->sum_dur_out = 0; faad->sum_dur_out = 0;
break; break;
case GST_STATE_CHANGE_READY_TO_NULL: case GST_STATE_CHANGE_READY_TO_NULL:
faacDecClose (faad->handle); gst_faad_close_decoder (faad);
faad->handle = NULL;
if (faad->tempbuf) { if (faad->tempbuf) {
gst_buffer_unref (faad->tempbuf); gst_buffer_unref (faad->tempbuf);
faad->tempbuf = NULL; faad->tempbuf = NULL;

View file

@ -33,7 +33,7 @@ G_BEGIN_DECLS
(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_FAAD, GstFaadClass)) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_FAAD, GstFaadClass))
#define GST_IS_FAAD(obj) \ #define GST_IS_FAAD(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_FAAD)) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_FAAD))
#define GST_IS_FAAD_CLASS(obj) \ #define GST_IS_FAAD_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_FAAD)) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_FAAD))
typedef struct _GstFaad { typedef struct _GstFaad {
@ -46,6 +46,8 @@ typedef struct _GstFaad {
guint channels; /* number of channels of the last frame */ guint channels; /* number of channels of the last frame */
guint bps; /* bytes per sample */ guint bps; /* bytes per sample */
guint8 fake_codec_data[2];
GstBuffer *tempbuf; /* used to keep input leftovers */ GstBuffer *tempbuf; /* used to keep input leftovers */
/* FAAD object */ /* FAAD object */