diff --git a/configure.ac b/configure.ac index ee981e25c4..14f10d689d 100644 --- a/configure.ac +++ b/configure.ac @@ -255,12 +255,14 @@ AG_GST_CHECK_FEATURE(A52DEC, [a52dec], a52dec, [ dnl *** amr-nb *** translit(dnm, m, l) AM_CONDITIONAL(USE_AMRNB, true) AG_GST_CHECK_FEATURE(AMRNB, [amrnb library], amrnb, [ - PKG_CHECK_MODULES(AMRNB, opencore-amrnb, HAVE_AMRNB="yes", - [ AG_GST_CHECK_LIBHEADER(AMRNB, opencore-amrnb, - Decoder_Interface_init, $LIBM, - opencore-amrnb/interf_dec.h, - AMRNB_LIBS="-lopencore-amrnb") - ]) + PKG_CHECK_MODULES(AMRNB, opencore-amrnb, [ + if $PKG_CONFIG --atleast-version=0.1.3 opencore-amrnb; then + AC_DEFINE(HAVE_OPENCORE_AMRNB_0_1_3_OR_LATER, 1, [Defined for newer opencore-amrnb]) + fi + HAVE_AMRNB="yes" + ], [ + HAVE_AMRNB="no" + ]) AC_SUBST(AMRNB_CFLAGS) AC_SUBST(AMRNB_LIBS) ]) @@ -268,12 +270,14 @@ AG_GST_CHECK_FEATURE(AMRNB, [amrnb library], amrnb, [ dnl *** amr-wb dec *** translit(dnm, m, l) AM_CONDITIONAL(USE_AMRWB, true) AG_GST_CHECK_FEATURE(AMRWB, [amrwb library], amrwbdec, [ - PKG_CHECK_MODULES(AMRWB, opencore-amrwb, HAVE_AMRWB="yes", - [ AG_GST_CHECK_LIBHEADER(AMRWB, opencore-amrwb, - D_IF_decode, , - opencore-amrwb/dec_if.h, - AMRWB_LIBS="-lopencore-amrwb") - ]) + PKG_CHECK_MODULES(AMRWB, opencore-amrwb, [ + if $PKG_CONFIG --atleast-version=0.1.3 opencore-amrwb; then + AC_DEFINE(HAVE_OPENCORE_AMRWB_0_1_3_OR_LATER, 1, [Defined for newer opencore-amrwb]) + fi + HAVE_AMRWB="yes" + ], [ + HAVE_AMRWB="no" + ]) AC_SUBST(AMRWB_CFLAGS) AC_SUBST(AMRWB_LIBS) ]) diff --git a/ext/a52dec/gsta52dec.c b/ext/a52dec/gsta52dec.c index f20a07ea0c..1fcdc3ddd7 100644 --- a/ext/a52dec/gsta52dec.c +++ b/ext/a52dec/gsta52dec.c @@ -99,8 +99,6 @@ static gboolean gst_a52dec_parse (GstAudioDecoder * dec, GstAdapter * adapter, gint * offset, gint * length); static GstFlowReturn gst_a52dec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer); -static GstFlowReturn gst_a52dec_pre_push (GstAudioDecoder * bdec, - GstBuffer ** buffer); static GstFlowReturn gst_a52dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer); @@ -152,7 +150,6 @@ gst_a52dec_class_init (GstA52DecClass * klass) gstbase_class->set_format = GST_DEBUG_FUNCPTR (gst_a52dec_set_format); gstbase_class->parse = GST_DEBUG_FUNCPTR (gst_a52dec_parse); gstbase_class->handle_frame = GST_DEBUG_FUNCPTR (gst_a52dec_handle_frame); - gstbase_class->pre_push = GST_DEBUG_FUNCPTR (gst_a52dec_pre_push); /** * GstA52Dec::drc @@ -283,10 +280,6 @@ gst_a52dec_stop (GstAudioDecoder * dec) a52_free (a52dec->state); a52dec->state = NULL; } - if (a52dec->pending_tags) { - gst_tag_list_free (a52dec->pending_tags); - a52dec->pending_tags = NULL; - } return TRUE; } @@ -458,26 +451,9 @@ gst_a52dec_update_streaminfo (GstA52Dec * a52dec) gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_BITRATE, (guint) a52dec->bit_rate, NULL); - if (a52dec->pending_tags) { - gst_tag_list_free (a52dec->pending_tags); - a52dec->pending_tags = NULL; - } - - a52dec->pending_tags = taglist; -} - -static GstFlowReturn -gst_a52dec_pre_push (GstAudioDecoder * bdec, GstBuffer ** buffer) -{ - GstA52Dec *a52dec = GST_A52DEC (bdec); - - if (G_UNLIKELY (a52dec->pending_tags)) { - gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (a52dec), - gst_event_new_tag (a52dec->pending_tags)); - a52dec->pending_tags = NULL; - } - - return GST_FLOW_OK; + gst_audio_decoder_merge_tags (GST_AUDIO_DECODER (a52dec), taglist, + GST_TAG_MERGE_REPLACE); + gst_tag_list_free (taglist); } static GstFlowReturn diff --git a/ext/a52dec/gsta52dec.h b/ext/a52dec/gsta52dec.h index dd272c14cb..3bb81315b9 100644 --- a/ext/a52dec/gsta52dec.h +++ b/ext/a52dec/gsta52dec.h @@ -64,8 +64,6 @@ struct _GstA52Dec { gboolean dynamic_range_compression; sample_t *samples; a52_state_t *state; - - GstTagList *pending_tags; }; struct _GstA52DecClass { diff --git a/ext/amrnb/amrnbdec.h b/ext/amrnb/amrnbdec.h index 1e818395b9..5fe698290c 100644 --- a/ext/amrnb/amrnbdec.h +++ b/ext/amrnb/amrnbdec.h @@ -22,7 +22,12 @@ #include #include + +#ifdef HAVE_OPENCORE_AMRNB_0_1_3_OR_LATER +#include +#else #include +#endif G_BEGIN_DECLS diff --git a/ext/amrnb/amrnbenc.h b/ext/amrnb/amrnbenc.h index 7f673ac69a..48a8fe8c62 100644 --- a/ext/amrnb/amrnbenc.h +++ b/ext/amrnb/amrnbenc.h @@ -21,9 +21,14 @@ #define __GST_AMRNBENC_H__ #include -#include #include +#ifdef HAVE_OPENCORE_AMRNB_0_1_3_OR_LATER +#include +#else +#include +#endif + G_BEGIN_DECLS #define GST_TYPE_AMRNBENC \ diff --git a/ext/amrwbdec/amrwbdec.h b/ext/amrwbdec/amrwbdec.h index c3528fca00..6b82ae9920 100644 --- a/ext/amrwbdec/amrwbdec.h +++ b/ext/amrwbdec/amrwbdec.h @@ -22,8 +22,14 @@ #include #include + +#ifdef HAVE_OPENCORE_AMRWB_0_1_3_OR_LATER +#include +#include +#else #include #include +#endif G_BEGIN_DECLS diff --git a/ext/dvdread/dvdreadsrc.c b/ext/dvdread/dvdreadsrc.c index 59c8c1eba3..7f5536a1a9 100644 --- a/ext/dvdread/dvdreadsrc.c +++ b/ext/dvdread/dvdreadsrc.c @@ -1082,7 +1082,7 @@ gst_dvd_read_src_get_size (GstDvdReadSrc * src, gint64 * size) gboolean ret = FALSE; if (src->dvd_title) { - gsize blocks; + gssize blocks; blocks = DVDFileSize (src->dvd_title); if (blocks >= 0) { diff --git a/ext/mad/gstmad.c b/ext/mad/gstmad.c index 0dbf1b57fb..491ba462e3 100644 --- a/ext/mad/gstmad.c +++ b/ext/mad/gstmad.c @@ -278,9 +278,9 @@ gst_mad_parse (GstAudioDecoder * dec, GstAdapter * adapter, { GstMad *mad; GstFlowReturn ret = GST_FLOW_EOS; - gint av, size, offset, prev_offset, consumed = 0; + gint av, size, offset; const guint8 *data; - gboolean eos; + gboolean eos, sync; guint8 *guard = NULL; mad = GST_MAD (dec); @@ -288,7 +288,9 @@ gst_mad_parse (GstAudioDecoder * dec, GstAdapter * adapter, av = gst_adapter_available (adapter); data = gst_adapter_map (adapter, av); - gst_audio_decoder_get_parse_state (dec, NULL, &eos); + gst_audio_decoder_get_parse_state (dec, &sync, &eos); + GST_LOG_OBJECT (mad, "parse state sync %d, eos %d", sync, eos); + if (eos) { /* This is one streaming hack right there. * mad will not decode the last frame if it is not followed by @@ -312,125 +314,94 @@ gst_mad_parse (GstAudioDecoder * dec, GstAdapter * adapter, * if a frame is found (and also decoded), subsequent handle_frame * only needs to synthesize it */ - prev_offset = -1; offset = 0; - while (offset < av) { - size = MIN (MAD_BUFFER_MDLEN * 3, av - offset); + size = 0; - /* check for mad asking too much */ - if (offset == prev_offset) { - if (G_UNLIKELY (offset + size < av)) { - /* mad should not do this, so really fatal */ - GST_ELEMENT_ERROR (mad, STREAM, DECODE, (NULL), - ("mad claims to need more data than %u bytes", size)); - ret = GST_FLOW_ERROR; - goto exit; - } else { +resume: + if (G_UNLIKELY (offset + MAD_BUFFER_GUARD > av)) + goto exit; + + GST_LOG_OBJECT (mad, "setup mad stream at offset %d (of av %d)", offset, av); + mad_stream_buffer (&mad->stream, data + offset, av - offset); + /* convey sync idea to mad */ + mad->stream.sync = sync; + /* if we get back here, lost sync anyway */ + sync = FALSE; + + while (TRUE) { + GST_LOG_OBJECT (mad, "decoding the header now"); + if (mad_header_decode (&mad->frame.header, &mad->stream) == -1) { + /* HACK it seems mad reports wrong error when it is trying to determine + * free bitrate and scanning for next header */ + if (mad->stream.error == MAD_ERROR_LOSTSYNC) { + const guint8 *ptr = mad->stream.this_frame; + guint32 header; + + if (ptr >= data && ptr < data + av) { + header = GST_READ_UINT32_BE (ptr); + /* looks like possible freeform header with not much data */ + if (((header & 0xFFE00000) == 0xFFE00000) && + (((header >> 12) & 0xF) == 0x0) && (av < 4096)) { + GST_DEBUG_OBJECT (mad, "overriding freeform LOST_SYNC to BUFLEN"); + mad->stream.error = MAD_ERROR_BUFLEN; + } + } + } + if (mad->stream.error == MAD_ERROR_BUFLEN) { + GST_LOG_OBJECT (mad, "not enough data, getting more"); + offset = mad->stream.next_frame - data; break; - } - } - - /* only feed that much to mad at a time */ - mad_stream_buffer (&mad->stream, data + offset, size); - prev_offset = offset; - - while (offset - prev_offset < size) { - consumed = 0; - - GST_LOG_OBJECT (mad, "decoding the header now"); - if (mad_header_decode (&mad->frame.header, &mad->stream) == -1) { - /* HACK it seems mad reports wrong error when it is trying to determine - * free bitrate and scanning for next header */ - if (mad->stream.error == MAD_ERROR_LOSTSYNC) { - const guint8 *ptr = mad->stream.this_frame; - guint32 header; - - if (ptr >= data && ptr < data + av) { - header = GST_READ_UINT32_BE (ptr); - /* looks like possible freeform header with not much data */ - if (((header & 0xFFE00000) == 0xFFE00000) && - (((header >> 12) & 0xF) == 0x0) && (av < 4096)) { - GST_DEBUG_OBJECT (mad, "overriding freeform LOST_SYNC to BUFLEN"); - mad->stream.error = MAD_ERROR_BUFLEN; - } - } - } - if (mad->stream.error == MAD_ERROR_BUFLEN) { - GST_LOG_OBJECT (mad, - "not enough data in tempbuffer (%d), breaking to get more", size); - break; - } else { - GST_WARNING_OBJECT (mad, "mad_header_decode had an error: %s", - mad_stream_errorstr (&mad->stream)); - } - } - - GST_LOG_OBJECT (mad, "parsing and decoding one frame now"); - if (mad_frame_decode (&mad->frame, &mad->stream) == -1) { - GST_LOG_OBJECT (mad, "got error %d", mad->stream.error); - - /* not enough data, need to wait for next buffer? */ - if (mad->stream.error == MAD_ERROR_BUFLEN) { - if (mad->stream.next_frame == data) { - GST_LOG_OBJECT (mad, - "not enough data in tempbuffer (%d), breaking to get more", - size); - break; - } else { - GST_LOG_OBJECT (mad, "sync error, flushing unneeded data"); - goto flush; - } - } else if (mad->stream.error == MAD_ERROR_BADDATAPTR) { - /* Flush data */ - goto flush; - } else { - GST_WARNING_OBJECT (mad, "mad_frame_decode had an error: %s", - mad_stream_errorstr (&mad->stream)); - if (!MAD_RECOVERABLE (mad->stream.error)) { - /* well, all may be well enough bytes later on ... */ - GST_AUDIO_DECODER_ERROR (mad, 1, STREAM, DECODE, (NULL), - ("mad error: %s", mad_stream_errorstr (&mad->stream)), ret); - /* so make sure we really move along ... */ - if (!offset) - offset++; - goto exit; - } else { - const guint8 *before_sync, *after_sync; - - mad_frame_mute (&mad->frame); - mad_synth_mute (&mad->synth); - before_sync = mad->stream.ptr.byte; - if (mad_stream_sync (&mad->stream) != 0) - GST_WARNING_OBJECT (mad, "mad_stream_sync failed"); - after_sync = mad->stream.ptr.byte; - /* a succesful resync should make us drop bytes as consumed, so - * calculate from the byte pointers before and after resync */ - consumed = after_sync - before_sync; - GST_DEBUG_OBJECT (mad, "resynchronization consumes %d bytes", - consumed); - GST_DEBUG_OBJECT (mad, "synced to data: 0x%0x 0x%0x", - *mad->stream.ptr.byte, *(mad->stream.ptr.byte + 1)); - - mad_stream_sync (&mad->stream); - /* recoverable errors pass */ - goto flush; - } - } + } else if (mad->stream.error == MAD_ERROR_LOSTSYNC) { + GST_LOG_OBJECT (mad, "lost sync"); + continue; } else { - /* decoding ok; found frame */ - ret = GST_FLOW_OK; + /* probably some bogus header, basically also lost sync */ + GST_DEBUG_OBJECT (mad, "mad_header_decode had an error: %s", + mad_stream_errorstr (&mad->stream)); + continue; } - flush: - if (consumed == 0) { - consumed = mad->stream.next_frame - (data + offset); - g_assert (consumed >= 0); - } - - if (ret == GST_FLOW_OK) - goto exit; - - offset += consumed; } + + /* could have a frame now, subsequent will confirm */ + offset = mad->stream.this_frame - data; + size = mad->stream.next_frame - mad->stream.this_frame; + g_assert (size); + + GST_LOG_OBJECT (mad, "parsing and decoding one frame now " + "(offset %d, size %d)", offset, size); + if (mad_frame_decode (&mad->frame, &mad->stream) == -1) { + GST_LOG_OBJECT (mad, "got error %d", mad->stream.error); + + /* not enough data, need to wait for next buffer? */ + if (mad->stream.error == MAD_ERROR_BUFLEN) { + /* not really expect this error at this stage anymore + * assume bogus frame and bad sync and move along a bit */ + GST_WARNING_OBJECT (mad, "not enough data (unexpected), moving along"); + offset++; + goto resume; + } else if (mad->stream.error == MAD_ERROR_BADDATAPTR) { + GST_DEBUG_OBJECT (mad, "bad data ptr, skipping presumed frame"); + /* flush past presumed frame */ + offset += size; + goto resume; + } else { + GST_WARNING_OBJECT (mad, "mad_frame_decode had an error: %s", + mad_stream_errorstr (&mad->stream)); + if (!MAD_RECOVERABLE (mad->stream.error)) { + /* well, all may be well enough bytes later on ... */ + GST_AUDIO_DECODER_ERROR (mad, 1, STREAM, DECODE, (NULL), + ("mad error: %s", mad_stream_errorstr (&mad->stream)), ret); + } + /* move along and try again */ + offset++; + goto resume; + } + g_assert_not_reached (); + } + + /* so decoded ok, got a frame now */ + ret = GST_FLOW_OK; + break; } exit: @@ -438,7 +409,7 @@ exit: gst_adapter_unmap (adapter); *_offset = offset; - *len = consumed; + *len = size; /* ensure that if we added some dummy guard bytes above, we don't claim to have used them as they're unknown to the caller. */ diff --git a/ext/mad/gstmad.c.orig b/ext/mad/gstmad.c.orig new file mode 100644 index 0000000000..20149f177a --- /dev/null +++ b/ext/mad/gstmad.c.orig @@ -0,0 +1,567 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-mad + * @see_also: lame + * + * MP3 audio decoder. + * + * + * Example pipelines + * |[ + * gst-launch filesrc location=music.mp3 ! mpegaudioparse ! mad ! audioconvert ! audioresample ! autoaudiosink + * ]| Decode and play the mp3 file + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "gstmad.h" +#include + +enum +{ + ARG_0, + ARG_HALF, + ARG_IGNORE_CRC +}; + +GST_DEBUG_CATEGORY_STATIC (mad_debug); +#define GST_CAT_DEFAULT mad_debug + +static GstStaticPadTemplate mad_src_template_factory = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) " GST_AUDIO_NE (S32) ", " + "layout = (string) interleaved, " + "rate = (int) { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }, " + "channels = (int) [ 1, 2 ]") + ); + +/* FIXME: make three caps, for mpegversion 1, 2 and 2.5 */ +static GstStaticPadTemplate mad_sink_template_factory = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/mpeg, " + "mpegversion = (int) 1, " + "layer = (int) [ 1, 3 ], " + "rate = (int) { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }, " + "channels = (int) [ 1, 2 ]") + ); + + +static gboolean gst_mad_start (GstAudioDecoder * dec); +static gboolean gst_mad_stop (GstAudioDecoder * dec); +static gboolean gst_mad_parse (GstAudioDecoder * dec, GstAdapter * adapter, + gint * offset, gint * length); +static GstFlowReturn gst_mad_handle_frame (GstAudioDecoder * dec, + GstBuffer * buffer); +static void gst_mad_flush (GstAudioDecoder * dec, gboolean hard); + +static void gst_mad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_mad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +#define parent_class gst_mad_parent_class +G_DEFINE_TYPE (GstMad, gst_mad, GST_TYPE_AUDIO_DECODER); + +static void +gst_mad_class_init (GstMadClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *element_class = (GstElementClass *) klass; + GstAudioDecoderClass *base_class = (GstAudioDecoderClass *) klass; + + base_class->start = GST_DEBUG_FUNCPTR (gst_mad_start); + base_class->stop = GST_DEBUG_FUNCPTR (gst_mad_stop); + base_class->parse = GST_DEBUG_FUNCPTR (gst_mad_parse); + base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_mad_handle_frame); + base_class->flush = GST_DEBUG_FUNCPTR (gst_mad_flush); + + base_class->start = GST_DEBUG_FUNCPTR (gst_mad_start); + base_class->stop = GST_DEBUG_FUNCPTR (gst_mad_stop); + base_class->parse = GST_DEBUG_FUNCPTR (gst_mad_parse); + base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_mad_handle_frame); + base_class->flush = GST_DEBUG_FUNCPTR (gst_mad_flush); + + gobject_class->set_property = gst_mad_set_property; + gobject_class->get_property = gst_mad_get_property; + + /* init properties */ + /* currently, string representations are used, we might want to change that */ + /* FIXME: descriptions need to be more technical, + * default values and ranges need to be selected right */ + g_object_class_install_property (gobject_class, ARG_HALF, + g_param_spec_boolean ("half", "Half", "Generate PCM at 1/2 sample rate", + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, ARG_IGNORE_CRC, + g_param_spec_boolean ("ignore-crc", "Ignore CRC", "Ignore CRC errors", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&mad_sink_template_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&mad_src_template_factory)); + + gst_element_class_set_details_simple (element_class, "mad mp3 decoder", + "Codec/Decoder/Audio", + "Uses mad code to decode mp3 streams", "Wim Taymans "); +} + +static void +gst_mad_init (GstMad * mad) +{ + GstAudioDecoder *dec; + + dec = GST_AUDIO_DECODER (mad); + gst_audio_decoder_set_tolerance (dec, 20 * GST_MSECOND); + + mad->half = FALSE; + mad->ignore_crc = TRUE; +} + +static gboolean +gst_mad_start (GstAudioDecoder * dec) +{ + GstMad *mad = GST_MAD (dec); + guint options = 0; + + GST_DEBUG_OBJECT (dec, "start"); + mad_stream_init (&mad->stream); + mad_frame_init (&mad->frame); + mad_synth_init (&mad->synth); + mad->rate = 0; + mad->channels = 0; + mad->caps_set = FALSE; + mad->frame.header.samplerate = 0; + if (mad->ignore_crc) + options |= MAD_OPTION_IGNORECRC; + if (mad->half) + options |= MAD_OPTION_HALFSAMPLERATE; + mad_stream_options (&mad->stream, options); + mad->header.mode = -1; + mad->header.emphasis = -1; + mad->eos = FALSE; + + /* call upon legacy upstream byte support (e.g. seeking) */ + gst_audio_decoder_set_byte_time (dec, TRUE); + + return TRUE; +} + +static gboolean +gst_mad_stop (GstAudioDecoder * dec) +{ + GstMad *mad = GST_MAD (dec); + + GST_DEBUG_OBJECT (dec, "stop"); + mad_synth_finish (&mad->synth); + mad_frame_finish (&mad->frame); + mad_stream_finish (&mad->stream); + + return TRUE; +} + +static inline gint32 +scale (mad_fixed_t sample) +{ +#if MAD_F_FRACBITS < 28 + /* round */ + sample += (1L << (28 - MAD_F_FRACBITS - 1)); +#endif + + /* clip */ + if (sample >= MAD_F_ONE) + sample = MAD_F_ONE - 1; + else if (sample < -MAD_F_ONE) + sample = -MAD_F_ONE; + +#if MAD_F_FRACBITS < 28 + /* quantize */ + sample >>= (28 - MAD_F_FRACBITS); +#endif + + /* convert from 29 bits to 32 bits */ + return (gint32) (sample << 3); +} + +/* internal function to check if the header has changed and thus the + * caps need to be reset. Only call during normal mode, not resyncing */ +static void +gst_mad_check_caps_reset (GstMad * mad) +{ + guint nchannels; + guint rate; + + nchannels = MAD_NCHANNELS (&mad->frame.header); + +#if MAD_VERSION_MINOR <= 12 + rate = mad->header.sfreq; +#else + rate = mad->frame.header.samplerate; +#endif + + /* rate and channels are not supposed to change in a continuous stream, + * so check this first before doing anything */ + + /* only set caps if they weren't already set for this continuous stream */ + if (!gst_pad_has_current_caps (GST_AUDIO_DECODER_SRC_PAD (mad)) + || mad->channels != nchannels || mad->rate != rate) { + GstAudioInfo info; + static const GstAudioChannelPosition chan_pos[2][2] = { + {GST_AUDIO_CHANNEL_POSITION_MONO}, + {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT} + }; + + if (mad->caps_set) { + GST_DEBUG_OBJECT (mad, "Header changed from %d Hz/%d ch to %d Hz/%d ch, " + "failed sync after seek ?", mad->rate, mad->channels, rate, + nchannels); + /* we're conservative on stream changes. However, our *initial* caps + * might have been wrong as well - mad ain't perfect in syncing. So, + * we count caps changes and change if we pass a limit treshold (3). */ + if (nchannels != mad->pending_channels || rate != mad->pending_rate) { + mad->times_pending = 0; + mad->pending_channels = nchannels; + mad->pending_rate = rate; + } + if (++mad->times_pending < 3) + return; + } + + if (mad->stream.options & MAD_OPTION_HALFSAMPLERATE) + rate >>= 1; + + /* we set the caps even when the pad is not connected so they + * can be gotten for streaminfo */ + gst_audio_info_init (&info); + gst_audio_info_set_format (&info, + GST_AUDIO_FORMAT_S32, rate, nchannels, chan_pos[nchannels - 1]); + + gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (mad), &info); + + mad->caps_set = TRUE; + mad->channels = nchannels; + mad->rate = rate; + } +} + +static GstFlowReturn +gst_mad_parse (GstAudioDecoder * dec, GstAdapter * adapter, + gint * _offset, gint * len) +{ + GstMad *mad; +<<<<<<< HEAD + GstFlowReturn ret = GST_FLOW_EOS; + gint av, size, offset, prev_offset, consumed = 0; + const guint8 *data; + gboolean eos; + guint8 *guard = NULL; +======= + GstFlowReturn ret = GST_FLOW_UNEXPECTED; + gint av, size, offset; + const guint8 *data; + gboolean eos, sync; + GstBuffer *guard = NULL; +>>>>>>> origin/master + + mad = GST_MAD (dec); + + av = gst_adapter_available (adapter); + data = gst_adapter_map (adapter, av); + + gst_audio_decoder_get_parse_state (dec, &sync, &eos); + GST_LOG_OBJECT (mad, "parse state sync %d, eos %d", sync, eos); + + if (eos) { + /* This is one streaming hack right there. + * mad will not decode the last frame if it is not followed by + * a number of 0 bytes, due to some buffer overflow, which can + * not be fixed for reasons I did not inquire into, see + * http://www.mars.org/mailman/public/mad-dev/2001-May/000262.html + */ + guard = g_malloc (av + MAD_BUFFER_GUARD); + /* let's be nice and not mess with baseclass state and keep hacks local */ + memcpy (guard, data, av); + memset (guard + av, 0, MAD_BUFFER_GUARD); + GST_DEBUG_OBJECT (mad, "Added %u zero guard bytes in the adapter; " + "using fallback buffer of size %u", + MAD_BUFFER_GUARD, av + MAD_BUFFER_GUARD); + data = guard; + av = av + MAD_BUFFER_GUARD; + } + + /* we basically let mad library do parsing, + * and translate that back to baseclass. + * if a frame is found (and also decoded), subsequent handle_frame + * only needs to synthesize it */ + + offset = 0; + size = 0; + +resume: + if (G_UNLIKELY (offset + MAD_BUFFER_GUARD > av)) + goto exit; + + GST_LOG_OBJECT (mad, "setup mad stream at offset %d (of av %d)", offset, av); + mad_stream_buffer (&mad->stream, data + offset, av - offset); + /* convey sync idea to mad */ + mad->stream.sync = sync; + /* if we get back here, lost sync anyway */ + sync = FALSE; + + while (TRUE) { + GST_LOG_OBJECT (mad, "decoding the header now"); + if (mad_header_decode (&mad->frame.header, &mad->stream) == -1) { + /* HACK it seems mad reports wrong error when it is trying to determine + * free bitrate and scanning for next header */ + if (mad->stream.error == MAD_ERROR_LOSTSYNC) { + const guint8 *ptr = mad->stream.this_frame; + guint32 header; + + if (ptr >= data && ptr < data + av) { + header = GST_READ_UINT32_BE (ptr); + /* looks like possible freeform header with not much data */ + if (((header & 0xFFE00000) == 0xFFE00000) && + (((header >> 12) & 0xF) == 0x0) && (av < 4096)) { + GST_DEBUG_OBJECT (mad, "overriding freeform LOST_SYNC to BUFLEN"); + mad->stream.error = MAD_ERROR_BUFLEN; + } + } + } + if (mad->stream.error == MAD_ERROR_BUFLEN) { + GST_LOG_OBJECT (mad, "not enough data, getting more"); + offset = mad->stream.next_frame - data; + break; + } else if (mad->stream.error == MAD_ERROR_LOSTSYNC) { + GST_LOG_OBJECT (mad, "lost sync"); + continue; + } else { + /* probably some bogus header, basically also lost sync */ + GST_DEBUG_OBJECT (mad, "mad_header_decode had an error: %s", + mad_stream_errorstr (&mad->stream)); + continue; + } + } + + /* could have a frame now, subsequent will confirm */ + offset = mad->stream.this_frame - data; + size = mad->stream.next_frame - mad->stream.this_frame; + g_assert (size); + + GST_LOG_OBJECT (mad, "parsing and decoding one frame now " + "(offset %d, size %d)", offset, size); + if (mad_frame_decode (&mad->frame, &mad->stream) == -1) { + GST_LOG_OBJECT (mad, "got error %d", mad->stream.error); + + /* not enough data, need to wait for next buffer? */ + if (mad->stream.error == MAD_ERROR_BUFLEN) { + /* not really expect this error at this stage anymore + * assume bogus frame and bad sync and move along a bit */ + GST_WARNING_OBJECT (mad, "not enough data (unexpected), moving along"); + offset++; + goto resume; + } else if (mad->stream.error == MAD_ERROR_BADDATAPTR) { + GST_DEBUG_OBJECT (mad, "bad data ptr, skipping presumed frame"); + /* flush past presumed frame */ + offset += size; + goto resume; + } else { + GST_WARNING_OBJECT (mad, "mad_frame_decode had an error: %s", + mad_stream_errorstr (&mad->stream)); + if (!MAD_RECOVERABLE (mad->stream.error)) { + /* well, all may be well enough bytes later on ... */ + GST_AUDIO_DECODER_ERROR (mad, 1, STREAM, DECODE, (NULL), + ("mad error: %s", mad_stream_errorstr (&mad->stream)), ret); + } + /* move along and try again */ + offset++; + goto resume; + } + g_assert_not_reached (); + } + + /* so decoded ok, got a frame now */ + ret = GST_FLOW_OK; + break; + } + +exit: + + gst_adapter_unmap (adapter); + + *_offset = offset; + *len = size; + + /* ensure that if we added some dummy guard bytes above, we don't claim + to have used them as they're unknown to the caller. */ + if (eos) { + g_assert (av >= MAD_BUFFER_GUARD); + av -= MAD_BUFFER_GUARD; + if (*_offset > av) + *_offset = av; + if (*len > av) + *len = av; + g_assert (guard); + g_free (guard); + } + + return ret; +} + +static GstFlowReturn +gst_mad_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer) +{ + GstMad *mad; + GstFlowReturn ret = GST_FLOW_EOS; + GstBuffer *outbuffer; + guint nsamples; + GstMapInfo outmap; + gint32 *outdata; + mad_fixed_t const *left_ch, *right_ch; + + mad = GST_MAD (dec); + + /* no fancy draining */ + if (G_UNLIKELY (!buffer)) + return GST_FLOW_OK; + + /* _parse prepared a frame */ + nsamples = MAD_NSBSAMPLES (&mad->frame.header) * + (mad->stream.options & MAD_OPTION_HALFSAMPLERATE ? 16 : 32); + GST_LOG_OBJECT (mad, "mad frame with %d samples", nsamples); + + /* arrange for initial caps before pushing data, + * and update later on if needed */ + gst_mad_check_caps_reset (mad); + + mad_synth_frame (&mad->synth, &mad->frame); + left_ch = mad->synth.pcm.samples[0]; + right_ch = mad->synth.pcm.samples[1]; + + outbuffer = gst_buffer_new_and_alloc (nsamples * mad->channels * 4); + + gst_buffer_map (outbuffer, &outmap, GST_MAP_WRITE); + outdata = (gint32 *) outmap.data; + + /* output sample(s) in 16-bit signed native-endian PCM */ + if (mad->channels == 1) { + gint count = nsamples; + + while (count--) { + *outdata++ = scale (*left_ch++) & 0xffffffff; + } + } else { + gint count = nsamples; + + while (count--) { + *outdata++ = scale (*left_ch++) & 0xffffffff; + *outdata++ = scale (*right_ch++) & 0xffffffff; + } + } + + gst_buffer_unmap (outbuffer, &outmap); + + ret = gst_audio_decoder_finish_frame (dec, outbuffer, 1); + + return ret; +} + +static void +gst_mad_flush (GstAudioDecoder * dec, gboolean hard) +{ + GstMad *mad; + + mad = GST_MAD (dec); + if (hard) { + mad_frame_mute (&mad->frame); + mad_synth_mute (&mad->synth); + } +} + +static void +gst_mad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstMad *mad; + + mad = GST_MAD (object); + + switch (prop_id) { + case ARG_HALF: + mad->half = g_value_get_boolean (value); + break; + case ARG_IGNORE_CRC: + mad->ignore_crc = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_mad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstMad *mad; + + mad = GST_MAD (object); + + switch (prop_id) { + case ARG_HALF: + g_value_set_boolean (value, mad->half); + break; + case ARG_IGNORE_CRC: + g_value_set_boolean (value, mad->ignore_crc); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* plugin initialisation */ + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (mad_debug, "mad", 0, "mad mp3 decoding"); + + /* FIXME 0.11: rename to something better like madmp3dec or madmpegaudiodec + * or so? */ + return gst_element_register (plugin, "mad", GST_RANK_SECONDARY, + gst_mad_get_type ()); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "mad", + "mp3 decoding based on the mad library", + plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/ext/mpeg2dec/gstmpeg2dec.c b/ext/mpeg2dec/gstmpeg2dec.c index 659e6d29b6..5b1716eaee 100644 --- a/ext/mpeg2dec/gstmpeg2dec.c +++ b/ext/mpeg2dec/gstmpeg2dec.c @@ -269,10 +269,20 @@ gst_mpeg2dec_crop_buffer (GstMpeg2dec * dec, GstBuffer ** buf) gst_video_format_get_component_height (dec->format, c, dec->height); c_width = gst_video_format_get_component_width (dec->format, c, dec->width); - for (line = 0; line < c_height; line++) { - memcpy (dest, src, c_width); - dest += stride_out; - src += stride_in; + GST_DEBUG ("stride_in:%d _out:%d c_width:%d c_height:%d", + stride_in, stride_out, c_width, c_height); + + if (stride_in == stride_out && stride_in == c_width) { + /* FAST PATH */ + memcpy (dest, src, c_height * stride_out); + dest += stride_out * c_height; + src += stride_out * c_height; + } else { + for (line = 0; line < c_height; line++) { + memcpy (dest, src, c_width); + dest += stride_out; + src += stride_in; + } } }