diff --git a/configure.ac b/configure.ac index 70f87633c8..b64ac1c404 100644 --- a/configure.ac +++ b/configure.ac @@ -1048,7 +1048,7 @@ sys/vcd/Makefile sys/vga/Makefile sys/xvideo/Makefile ext/Makefile -ext/a52/Makefile +ext/a52dec/Makefile ext/aalib/Makefile ext/alsa/Makefile ext/arts/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index b889fb382a..62b9636705 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -1,7 +1,7 @@ -if USE_A52 -A52_DIR=a52 +if USE_A52DEC +A52DEC_DIR=a52dec else -A52_DIR= +A52DEC_DIR= endif if USE_AALIB @@ -178,7 +178,7 @@ else XMMS_DIR= endif -SUBDIRS=$(A52_DIR) $(AALIB_DIR) $(ALSA_DIR) \ +SUBDIRS=$(A52DEC_DIR) $(AALIB_DIR) $(ALSA_DIR) \ $(ARTS_DIR) $(ARTSC_DIR) $(AUDIOFILE_DIR) \ $(AVIFILE_DIR) $(CDPARANOIA_DIR) $(DV_DIR) $(DVDREAD_DIR) $(ESD_DIR) \ $(FESTIVAL_DIR) $(FLAC_DIR) $(GNOMEVFS_DIR) $(GSM_DIR) $(HERMES_DIR) \ @@ -186,7 +186,7 @@ SUBDIRS=$(A52_DIR) $(AALIB_DIR) $(ALSA_DIR) \ $(OPENQUICKTIME_DIR) $(RTP_DIR) $(SDL_DIR) $(SHOUT_DIR) \ $(SMOOTHWAVE_DIR) $(VORBIS_DIR) $(XMMS_DIR) -DIST_SUBDIRS=a52 aalib alsa arts artsd avifile audiofile cdparanoia \ +DIST_SUBDIRS=a52dec aalib alsa arts artsd avifile audiofile cdparanoia \ dv dvdread esd \ festival flac gnomevfs gsm hermes http jpeg lame mad mikmod mpeg2dec \ openquicktime rtp sdl shout smoothwave vorbis xmms diff --git a/ext/a52dec/Makefile.am b/ext/a52dec/Makefile.am new file mode 100644 index 0000000000..24baaacdf3 --- /dev/null +++ b/ext/a52dec/Makefile.am @@ -0,0 +1,16 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgsta52dec.la + +libgsta52dec_la_SOURCES = gsta52dec.c +libgsta52dec_la_LIBADD = -la52 -lm +libgsta52dec_la_CFLAGS = -O3 $(ARCHFLAGS) $(FOMIT_FRAME_POINTER) $(GLIB_CFLAGS) $(XML_CFLAGS) $(GST_CFLAGS) + +noinst_HEADERS = gsta52dec.h + +if HAVE_CPU_I386 +ARCHFLAGS = -m486 +else +ARCHFLAGS = +endif + diff --git a/ext/a52dec/gsta52dec.c b/ext/a52dec/gsta52dec.c new file mode 100644 index 0000000000..924528ed8a --- /dev/null +++ b/ext/a52dec/gsta52dec.c @@ -0,0 +1,569 @@ +/* GStreamer + * Copyright (C) <2001> David I. Lehn + * + * 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. + */ + +#include + +#include +#include +#include +#include +#include +#include "gsta52dec.h" + +/* elementfactory information */ +static GstElementDetails gst_a52dec_details = { + "ATSC A/52 audio decoder", + "Filter/Decoder/Audio", + "Decodes ATSC A/52 encoded audio streams", + VERSION, + "David I. Lehn ", + "(C) 2001", +}; + + +/* A52Dec signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + ARG_0, + ARG_DRC +}; + +/* + * "audio/a52" and "audio/ac3" are the same format. The name + * "ac3" is now deprecated and should not be used in new code. + */ +GST_PADTEMPLATE_FACTORY (sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "a52dec_sink", + "audio/a52", + NULL + ), + GST_CAPS_NEW ( + "ac3dec_sink", + "audio/ac3", + NULL + ) +); + +GST_PADTEMPLATE_FACTORY (src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "a52dec_src", + "audio/raw", + "format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "signed", GST_PROPS_BOOLEAN (TRUE), + "width", GST_PROPS_INT (16), + "depth", GST_PROPS_INT (16), + "rate", GST_PROPS_INT_RANGE (4000, 48000), + "channels", GST_PROPS_INT_RANGE (1, 6) + ) +); + +static void gst_a52dec_class_init (GstA52DecClass * klass); +static void gst_a52dec_init (GstA52Dec * a52dec); + +static void gst_a52dec_loop (GstElement * element); +static GstElementStateReturn + gst_a52dec_change_state (GstElement * element); + +static void gst_a52dec_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_a52dec_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GstElementClass *parent_class = NULL; +/* static guint gst_a52dec_signals[LAST_SIGNAL] = { 0 }; */ + +GType +gst_a52dec_get_type (void) +{ + static GType a52dec_type = 0; + + if (!a52dec_type) { + static const GTypeInfo a52dec_info = { + sizeof (GstA52DecClass), NULL, NULL, (GClassInitFunc) gst_a52dec_class_init, + NULL, + NULL, + sizeof (GstA52Dec), + 0, + (GInstanceInitFunc) gst_a52dec_init, + }; + + a52dec_type = g_type_register_static (GST_TYPE_ELEMENT, "GstA52Dec", &a52dec_info, 0); + } + return a52dec_type; +} + +static void +gst_a52dec_class_init (GstA52DecClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_ref (GST_TYPE_ELEMENT); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DRC, + g_param_spec_boolean ("drc", "Dynamic Range Compression", + "Use Dynamic Range Compression", FALSE, + G_PARAM_READWRITE)); + + gobject_class->set_property = gst_a52dec_set_property; + gobject_class->get_property = gst_a52dec_get_property; + + gstelement_class->change_state = gst_a52dec_change_state; +} + +static void +gst_a52dec_init (GstA52Dec * a52dec) +{ + /* create the sink and src pads */ + a52dec->sinkpad = gst_pad_new_from_template (GST_PADTEMPLATE_GET (sink_factory), "sink"); + gst_element_add_pad (GST_ELEMENT (a52dec), a52dec->sinkpad); + gst_pad_set_caps (a52dec->sinkpad, gst_pad_get_padtemplate_caps (a52dec->sinkpad)); + gst_element_set_loop_function ((GstElement *) a52dec, gst_a52dec_loop); + + a52dec->srcpad = gst_pad_new_from_template (GST_PADTEMPLATE_GET (src_factory), "src"); + gst_element_add_pad (GST_ELEMENT (a52dec), a52dec->srcpad); + + a52dec->dynamic_range_compression = FALSE; +} + +/* BEGIN modified a52dec conversion code */ + +static inline int16_t +convert (int32_t i) +{ + if (i > 0x43c07fff) + return 32767; + else if (i < 0x43bf8000) + return -32768; + else + return i - 0x43c00000; +} + +static inline void +float_to_int (float *_f, int16_t * s16, int flags) +{ + int i; + int32_t *f = (int32_t *) _f; + + switch (flags) { + case A52_MONO: + for (i = 0; i < 256; i++) { + s16[5 * i] = s16[5 * i + 1] = s16[5 * i + 2] = s16[5 * i + 3] = 0; + s16[5 * i + 4] = convert (f[i]); + } + break; + case A52_CHANNEL: + case A52_STEREO: + case A52_DOLBY: + for (i = 0; i < 256; i++) { + s16[2 * i] = convert (f[i]); + s16[2 * i + 1] = convert (f[i + 256]); + } + break; + case A52_3F: + for (i = 0; i < 256; i++) { + s16[5 * i] = convert (f[i]); + s16[5 * i + 1] = convert (f[i + 512]); + s16[5 * i + 2] = s16[5 * i + 3] = 0; + s16[5 * i + 4] = convert (f[i + 256]); + } + break; + case A52_2F2R: + for (i = 0; i < 256; i++) { + s16[4 * i] = convert (f[i]); + s16[4 * i + 1] = convert (f[i + 256]); + s16[4 * i + 2] = convert (f[i + 512]); + s16[4 * i + 3] = convert (f[i + 768]); + } + break; + case A52_3F2R: + for (i = 0; i < 256; i++) { + s16[5 * i] = convert (f[i]); + s16[5 * i + 1] = convert (f[i + 512]); + s16[5 * i + 2] = convert (f[i + 768]); + s16[5 * i + 3] = convert (f[i + 1024]); + s16[5 * i + 4] = convert (f[i + 256]); + } + break; + case A52_MONO | A52_LFE: + for (i = 0; i < 256; i++) { + s16[6 * i] = s16[6 * i + 1] = s16[6 * i + 2] = s16[6 * i + 3] = 0; + s16[6 * i + 4] = convert (f[i + 256]); + s16[6 * i + 5] = convert (f[i]); + } + break; + case A52_CHANNEL | A52_LFE: + case A52_STEREO | A52_LFE: + case A52_DOLBY | A52_LFE: + for (i = 0; i < 256; i++) { + s16[6 * i] = convert (f[i + 256]); + s16[6 * i + 1] = convert (f[i + 512]); + s16[6 * i + 2] = s16[6 * i + 3] = s16[6 * i + 4] = 0; + s16[6 * i + 5] = convert (f[i]); + } + break; + case A52_3F | A52_LFE: + for (i = 0; i < 256; i++) { + s16[6 * i] = convert (f[i + 256]); + s16[6 * i + 1] = convert (f[i + 768]); + s16[6 * i + 2] = s16[6 * i + 3] = 0; + s16[6 * i + 4] = convert (f[i + 512]); + s16[6 * i + 5] = convert (f[i]); + } + break; + case A52_2F2R | A52_LFE: + for (i = 0; i < 256; i++) { + s16[6 * i] = convert (f[i + 256]); + s16[6 * i + 1] = convert (f[i + 512]); + s16[6 * i + 2] = convert (f[i + 768]); + s16[6 * i + 3] = convert (f[i + 1024]); + s16[6 * i + 4] = 0; + s16[6 * i + 5] = convert (f[i]); + } + break; + case A52_3F2R | A52_LFE: + for (i = 0; i < 256; i++) { + s16[6 * i] = convert (f[i + 256]); + s16[6 * i + 1] = convert (f[i + 768]); + s16[6 * i + 2] = convert (f[i + 1024]); + s16[6 * i + 3] = convert (f[i + 1280]); + s16[6 * i + 4] = convert (f[i + 512]); + s16[6 * i + 5] = convert (f[i]); + } + break; + } +} + +static int +gst_a52dec_channels (int flags) +{ + int chans = 0; + + if (flags & A52_LFE) { + chans += 1; + } + flags &= A52_CHANNEL_MASK; + switch (flags) { + case A52_3F2R: + chans += 5; + break; + case A52_2F2R: + case A52_3F1R: + chans += 4; + break; + case A52_2F1R: + case A52_3F: + chans += 3; + case A52_CHANNEL: + case A52_STEREO: + case A52_DOLBY: + chans += 2; + break; + default: + /* error */ + fprintf (stderr, "a52dec invalid flags?"); + return 0; + } + return chans; +} + +static int +gst_a52dec_push (GstPad * srcpad, int flags, sample_t * _samples, gint64 timestamp) +{ + GstBuffer *buf; + int chans; + +#ifdef LIBA52_DOUBLE + float samples[256 * 6]; + int i; + + for (i = 0; i < 256 * 6; i++) + samples[i] = _samples[i]; +#else + float *samples = _samples; +#endif + + flags &= A52_CHANNEL_MASK | A52_LFE; + + chans = gst_a52dec_channels (flags); + if (!chans) { + return 1; + } + + buf = gst_buffer_new (); + GST_BUFFER_SIZE (buf) = sizeof (int16_t) * 256 * chans; + GST_BUFFER_DATA (buf) = g_malloc (GST_BUFFER_SIZE (buf)); + GST_BUFFER_TIMESTAMP (buf) = timestamp; + float_to_int (samples, (int16_t *) GST_BUFFER_DATA (buf), flags); + + gst_pad_push (srcpad, buf); + + return 0; +} + +/* END modified a52dec conversion code */ + +static void +gst_a52dec_reneg (GstPad * pad, int channels, int rate) +{ + GST_INFO (GST_CAT_PLUGIN_INFO, "a52dec: reneg channels:%d rate:%d\n", channels, rate); + + gst_pad_set_caps (pad, gst_caps_new ("a52dec_src_caps", + "audio/raw", + gst_props_new ("format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "signed", GST_PROPS_BOOLEAN (TRUE), + "width", GST_PROPS_INT (16), + "depth", GST_PROPS_INT (16), + "channels", GST_PROPS_INT (channels), + "rate", GST_PROPS_INT (rate), NULL) + )); +} + +static void +gst_a52dec_handle_event (GstA52Dec *a52dec) +{ + guint32 remaining; + GstEvent *event; + + gst_bytestream_get_status (a52dec->bs, &remaining, &event); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_DISCONTINUOUS: + gst_bytestream_flush_fast (a52dec->bs, remaining); + default: + gst_pad_event_default (a52dec->sinkpad, event); + break; + } +} + +static void +gst_a52dec_loop (GstElement *element) +{ + GstA52Dec *a52dec; + guint8 *data; + int i, length, flags, sample_rate, bit_rate; + int stream_channels; + GstBuffer *buf; + + a52dec = GST_A52DEC (element); + + /* find and read header */ + while (1) { + data = gst_bytestream_peek_bytes (a52dec->bs, 7); + if (!data) { + gst_a52dec_handle_event (a52dec); + return; + } + length = a52_syncinfo (data, &flags, &sample_rate, &bit_rate); + if (length == 0) { + /* slide window to next 7 bytesa */ + gst_bytestream_flush_fast (a52dec->bs, 1); + } + else + break; + + /* FIXME this can potentially be an infinite loop, we have to + * insert a yield operations here */ + } + + /* check if params have changed since last call */ + stream_channels = flags & A52_CHANNEL_MASK; + + if ((sample_rate != a52dec->sample_rate) || + (bit_rate != a52dec->bit_rate) || + (stream_channels != a52dec->stream_channels)) + { + a52dec->sample_rate = sample_rate; + a52dec->bit_rate = bit_rate; + a52dec->stream_channels = stream_channels; + /* FIXME force stereo for now */ + a52dec->req_channels = A52_STEREO; + gst_a52dec_reneg (a52dec->srcpad, + gst_a52dec_channels (a52dec->req_channels), a52dec->sample_rate); + } + + /* read the header + rest of frame */ + buf = gst_bytestream_read (a52dec->bs, length); + if (!buf) { + gst_a52dec_handle_event (a52dec); + return; + } + data = GST_BUFFER_DATA (buf); + + /* process */ + flags = a52dec->req_channels | A52_ADJUST_LEVEL; + a52dec->level = 1; + + memset (a52dec->state, 0, sizeof (a52_state_t)); + + if (a52_frame (a52dec->state, data, &flags, &a52dec->level, a52dec->bias)) { + fprintf (stderr, "a52dec a52_frame error\n"); + goto end; + } + + if ((flags & A52_CHANNEL_MASK) != a52dec->req_channels) { + gst_a52dec_reneg (a52dec->srcpad, gst_a52dec_channels (flags), a52dec->sample_rate); + } + + if (a52dec->dynamic_range_compression == FALSE) { + a52_dynrng (a52dec->state, NULL, NULL); + } + + for (i = 0; i < 6; i++) { + if (a52_block (a52dec->state, a52dec->samples)) { + gst_element_info (element, "a52dec a52_block error %d\n", i); + continue; + } + /* push on */ + gst_a52dec_push (a52dec->srcpad, a52dec->req_channels, a52dec->samples, GST_BUFFER_TIMESTAMP (buf)); + } + +end: + /* FIXME, this is a possible leak */ + gst_buffer_unref (buf); +} + +static GstElementStateReturn +gst_a52dec_change_state (GstElement * element) +{ + GstA52Dec *a52dec = GST_A52DEC (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_NULL_TO_READY: + break; + case GST_STATE_READY_TO_PAUSED: + a52dec->bs = gst_bytestream_new (a52dec->sinkpad); + a52dec->samples = a52_init (0); /* mm_accel()); */ + /* FIXME this is 0ed only untill a liba52 bug is fixed */ + a52dec->state = g_new (a52_state_t, 1); + a52dec->bit_rate = -1; + a52dec->sample_rate = -1; + a52dec->stream_channels = -1; + a52dec->req_channels = -1; + a52dec->level = 1; + a52dec->bias = 384; + break; + case GST_STATE_PAUSED_TO_PLAYING: + break; + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + gst_bytestream_destroy (a52dec->bs); + a52dec->bs = NULL; + g_free (a52dec->state); + g_free (a52dec->samples); + break; + case GST_STATE_READY_TO_NULL: + break; + default: + break; + + } + + GST_ELEMENT_CLASS (parent_class)->change_state (element); + + return GST_STATE_SUCCESS; +} + +static void +gst_a52dec_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstA52Dec *src; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_A52DEC (object)); + src = GST_A52DEC (object); + + switch (prop_id) { + case ARG_DRC: + src->dynamic_range_compression = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_a52dec_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstA52Dec *src; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_A52DEC (object)); + src = GST_A52DEC (object); + + switch (prop_id) { + case ARG_DRC: + g_value_set_int (value, src->dynamic_range_compression); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GModule * module, GstPlugin * plugin) +{ + GstElementFactory *factory; + + /* this filter needs the bytestream package */ + if (!gst_library_load ("gstbytestream")) { + gst_info ("a52dec:: could not load support library: 'gstbytestream'\n"); + return FALSE; + } + + /* create an elementfactory for the a52dec element */ + factory = gst_elementfactory_new ("a52dec", GST_TYPE_A52DEC, &gst_a52dec_details); + g_return_val_if_fail (factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (src_factory)); + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (sink_factory)); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "a52dec", + plugin_init +}; diff --git a/ext/a52dec/gsta52dec.h b/ext/a52dec/gsta52dec.h new file mode 100644 index 0000000000..d201f9e48a --- /dev/null +++ b/ext/a52dec/gsta52dec.h @@ -0,0 +1,74 @@ +/* GStreamer + * Copyright (C) <2001> David I. Lehn + * + * 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. + */ + + +#ifndef __GST_A52DEC_H__ +#define __GST_A52DEC_H__ + + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_A52DEC \ + (gst_a52dec_get_type()) +#define GST_A52DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_A52DEC,GstA52Dec)) +#define GST_A52DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_A52DEC,GstA52Dec)) +#define GST_IS_A52DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_A52DEC)) +#define GST_IS_A52DEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A52DEC)) + +typedef struct _GstA52Dec GstA52Dec; +typedef struct _GstA52DecClass GstA52DecClass; + +struct _GstA52Dec { + GstElement element; + + /* pads */ + GstPad *sinkpad,*srcpad; + int bit_rate; + int sample_rate; + int stream_channels; + int req_channels; + sample_t level; + sample_t bias; + gboolean dynamic_range_compression; + sample_t *samples; + a52_state_t *state; + GstByteStream *bs; +}; + +struct _GstA52DecClass { + GstElementClass parent_class; +}; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_A52DEC_H__ */