From 584acf39528aa7faac911cee528b6f09d9d2855c Mon Sep 17 00:00:00 2001 From: Thomas Vander Stichele Date: Fri, 25 Nov 2005 18:15:51 +0000 Subject: ext/wavpack/: put back wavpack - still needs porting Original commit message from CVS: * ext/wavpack/gstwavpackcommon.h: * ext/wavpack/gstwavpackdec.c: (gst_wavpack_dec_link), (gst_wavpack_dec_wvclink), (gst_wavpack_dec_get_type), (gst_wavpack_dec_base_init), (gst_wavpack_dec_dispose), (gst_wavpack_dec_class_init), (gst_wavpack_dec_src_query), (gst_wavpack_dec_init), (gst_wavpack_dec_setup_context), (gst_wavpack_dec_format_samples), (gst_wavpack_dec_loop), (gst_wavpack_dec_plugin_init): * ext/wavpack/gstwavpackdec.h: * ext/wavpack/gstwavpackparse.c: (gst_wavpack_parse_get_type), (gst_wavpack_parse_base_init), (gst_wavpack_parse_dispose), (gst_wavpack_parse_class_init), (gst_wavpack_parse_src_query), (gst_wavpack_parse_src_event), (find_header), (find_sample), (gst_wavpack_parse_seek), (gst_wavpack_parse_init), (gst_wavpack_parse_handle_event), (gst_wavpack_parse_loop), (gst_wavpack_parse_change_state), (gst_wavpack_parse_plugin_init): * ext/wavpack/gstwavpackparse.h: put back wavpack - still needs porting --- ext/wavpack/Makefile.am | 13 + ext/wavpack/gstwavpack.c | 40 ++++ ext/wavpack/gstwavpackcommon.c | 14 ++ ext/wavpack/gstwavpackcommon.h | 4 + ext/wavpack/gstwavpackdec.c | 406 +++++++++++++++++++++++++++++++ ext/wavpack/gstwavpackdec.h | 79 ++++++ ext/wavpack/gstwavpackparse.c | 529 +++++++++++++++++++++++++++++++++++++++++ ext/wavpack/gstwavpackparse.h | 75 ++++++ 8 files changed, 1160 insertions(+) create mode 100644 ext/wavpack/Makefile.am create mode 100644 ext/wavpack/gstwavpack.c create mode 100644 ext/wavpack/gstwavpackcommon.c create mode 100644 ext/wavpack/gstwavpackcommon.h create mode 100644 ext/wavpack/gstwavpackdec.c create mode 100644 ext/wavpack/gstwavpackdec.h create mode 100644 ext/wavpack/gstwavpackparse.c create mode 100644 ext/wavpack/gstwavpackparse.h (limited to 'ext') diff --git a/ext/wavpack/Makefile.am b/ext/wavpack/Makefile.am new file mode 100644 index 00000000..b2ca835e --- /dev/null +++ b/ext/wavpack/Makefile.am @@ -0,0 +1,13 @@ + +plugin_LTLIBRARIES = libgstwavpack.la + +libgstwavpack_la_SOURCES = gstwavpack.c \ + gstwavpackcommon.c \ + gstwavpackparse.c \ + gstwavpackdec.c + +libgstwavpack_la_CFLAGS = $(GST_CFLAGS) $(WAVPACK_CFLAGS) +libgstwavpack_la_LIBADD = $(WAVPACK_LIBS) +libgstwavpack_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = gstwavpackparse.h gstwavpackdec.h gstwavpackcommon.h diff --git a/ext/wavpack/gstwavpack.c b/ext/wavpack/gstwavpack.c new file mode 100644 index 00000000..5a734220 --- /dev/null +++ b/ext/wavpack/gstwavpack.c @@ -0,0 +1,40 @@ +/* GStreamer wavpack plugin + * (c) 2004 Arwed v. Merkatz + * + * gstwavpack.c: plugin loader + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstwavpackparse.h" +#include "gstwavpackdec.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return (gst_wavpack_parse_plugin_init (plugin) + && gst_wavpack_dec_plugin_init (plugin)); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "wavpack", + "Wavpack lossless/lossy audio format handling", + plugin_init, VERSION, "LGPL", "gst-wavpack", "http://www.wavpack.com") diff --git a/ext/wavpack/gstwavpackcommon.c b/ext/wavpack/gstwavpackcommon.c new file mode 100644 index 00000000..61c8add0 --- /dev/null +++ b/ext/wavpack/gstwavpackcommon.c @@ -0,0 +1,14 @@ +#include "gstwavpackcommon.h" +#include + +gboolean +gst_wavpack_read_header (WavpackHeader * header, guint8 * buf) +{ + g_memmove (header, buf, sizeof (WavpackHeader)); + little_endian_to_native (header, WavpackHeaderFormat); + + if (strncmp (header->ckID, "wvpk", 4)) + return FALSE; + else + return TRUE; +} diff --git a/ext/wavpack/gstwavpackcommon.h b/ext/wavpack/gstwavpackcommon.h new file mode 100644 index 00000000..fbd07cf7 --- /dev/null +++ b/ext/wavpack/gstwavpackcommon.h @@ -0,0 +1,4 @@ +#include +#include + +gboolean gst_wavpack_read_header (WavpackHeader *header, guint8 *buf); diff --git a/ext/wavpack/gstwavpackdec.c b/ext/wavpack/gstwavpackdec.c new file mode 100644 index 00000000..d9fa074c --- /dev/null +++ b/ext/wavpack/gstwavpackdec.c @@ -0,0 +1,406 @@ +/* GStreamer Wavpack plugin + * (c) 2005 Arwed v. Merkatz + * + * gstwavpackdec.c: raw Wavpack bitstream decoder + * + * 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 "gstwavpackdec.h" +#include "gstwavpackcommon.h" + +/* Filter signals and args */ +enum +{ + LAST_SIGNAL +}; + +enum +{ + ARG_0 +}; + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-wavpack, " + "width = (int) { 8, 16, 24, 32 }, " + "channels = (int) { 1, 2 }, " + "rate = (int) [ 8000, 96000 ], " "framed = (boolean) true") + ); + +static GstStaticPadTemplate wvc_sink_factory = +GST_STATIC_PAD_TEMPLATE ("wvcsink", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("audio/x-wavpack-correction, " "framed = (boolean) true") + ); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "width = (int) { 8, 16, 24 }, " + "depth = (int) { 8, 16, 24 }, " + "channels = (int) { 1, 2 }, " + "rate = (int) [ 8000, 96000 ], " + "endianness = (int) LITTLE_ENDIAN, " + "signed = (boolean) true;" + "audio/x-raw-float, " + "width = (int) 32, " + "channels = (int) { 1, 2 }, " + "rate = (int) [ 8000, 96000 ], " + "endianness = (int) LITTLE_ENDIAN, " "buffer-frames = (int) 0") + ); + +static void gst_wavpack_dec_class_init (GstWavpackDecClass * klass); +static void gst_wavpack_dec_base_init (GstWavpackDecClass * klass); +static void gst_wavpack_dec_init (GstWavpackDec * wavpackdec); + +static void gst_wavpack_dec_loop (GstElement * element); + +static GstElementClass *parent = NULL; + +static GstPadLinkReturn +gst_wavpack_dec_link (GstPad * pad, const GstCaps * caps) +{ + GstWavpackDec *wavpackdec = GST_WAVPACK_DEC (gst_pad_get_parent (pad)); + GstStructure *structure = gst_caps_get_structure (caps, 0); + GstCaps *srccaps; + gint bits; + + if (!gst_caps_is_fixed (caps)) + return GST_PAD_LINK_DELAYED; + + gst_structure_get_int (structure, "rate", + (gint32 *) & wavpackdec->samplerate); + gst_structure_get_int (structure, "channels", + (gint *) & wavpackdec->channels); + gst_structure_get_int (structure, "width", &bits); + wavpackdec->width = bits; + + if (bits != 32) { + srccaps = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, wavpackdec->samplerate, + "channels", G_TYPE_INT, wavpackdec->channels, + "depth", G_TYPE_INT, bits, + "width", G_TYPE_INT, bits, + "endianness", G_TYPE_INT, LITTLE_ENDIAN, + "signed", G_TYPE_BOOLEAN, TRUE, NULL); + } else { + srccaps = gst_caps_new_simple ("audio/x-raw-float", + "rate", G_TYPE_INT, wavpackdec->samplerate, + "channels", G_TYPE_INT, wavpackdec->channels, + "width", G_TYPE_INT, 32, + "endianness", G_TYPE_INT, LITTLE_ENDIAN, + "buffer-frames", G_TYPE_INT, 0, NULL); + } + + gst_pad_set_explicit_caps (wavpackdec->srcpad, srccaps); + + // blocks are usually 0.5 seconds long, assume that's always the case for now + wavpackdec->decodebuf = + (int32_t *) g_malloc ((wavpackdec->samplerate / 2) * + wavpackdec->channels * sizeof (int32_t)); + return GST_PAD_LINK_OK; +} + +static GstPadLinkReturn +gst_wavpack_dec_wvclink (GstPad * pad, const GstCaps * caps) +{ + if (!gst_caps_is_fixed (caps)) + return GST_PAD_LINK_DELAYED; + + return GST_PAD_LINK_OK; +} + +GType +gst_wavpack_dec_get_type (void) +{ + static GType plugin_type = 0; + + if (!plugin_type) { + static const GTypeInfo plugin_info = { + sizeof (GstWavpackDecClass), + (GBaseInitFunc) gst_wavpack_dec_base_init, + NULL, + (GClassInitFunc) gst_wavpack_dec_class_init, + NULL, + NULL, + sizeof (GstWavpackDec), + 0, + (GInstanceInitFunc) gst_wavpack_dec_init, + }; + plugin_type = g_type_register_static (GST_TYPE_ELEMENT, + "GstWavpackDec", &plugin_info, 0); + } + return plugin_type; +} + +static void +gst_wavpack_dec_base_init (GstWavpackDecClass * klass) +{ + static GstElementDetails plugin_details = { + "WAVPACK decoder", + "Codec/Decoder/Audio", + "Decode Wavpack audio data", + "Arwed v. Merkatz " + }; + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&wvc_sink_factory)); + gst_element_class_set_details (element_class, &plugin_details); +} + +static void +gst_wavpack_dec_dispose (GObject * object) +{ + GstWavpackDec *wavpackdec = GST_WAVPACK_DEC (object); + + g_free (wavpackdec->decodebuf); + + G_OBJECT_CLASS (parent)->dispose (object); +} + +static void +gst_wavpack_dec_class_init (GstWavpackDecClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent = g_type_class_ref (GST_TYPE_ELEMENT); + + gobject_class->dispose = gst_wavpack_dec_dispose; +} + +static gboolean +gst_wavpack_dec_src_query (GstPad * pad, GstQueryType type, + GstFormat * format, gint64 * value) +{ + return gst_pad_query_default (pad, type, format, value); +} + +static void +gst_wavpack_dec_init (GstWavpackDec * wavpackdec) +{ + GstElementClass *klass = GST_ELEMENT_GET_CLASS (wavpackdec); + + wavpackdec->sinkpad = + gst_pad_new_from_template (gst_element_class_get_pad_template (klass, + "sink"), "sink"); + gst_pad_set_link_function (wavpackdec->sinkpad, gst_wavpack_dec_link); + + wavpackdec->wvcsinkpad = + gst_pad_new_from_template (gst_element_class_get_pad_template (klass, + "wvcsink"), "wvcsink"); + gst_pad_set_link_function (wavpackdec->wvcsinkpad, gst_wavpack_dec_wvclink); + + wavpackdec->srcpad = + gst_pad_new_from_template (gst_element_class_get_pad_template (klass, + "src"), "src"); + gst_pad_use_explicit_caps (wavpackdec->srcpad); + + gst_pad_set_query_function (wavpackdec->srcpad, gst_wavpack_dec_src_query); + + gst_element_add_pad (GST_ELEMENT (wavpackdec), wavpackdec->sinkpad); + gst_element_add_pad (GST_ELEMENT (wavpackdec), wavpackdec->srcpad); + gst_element_add_pad (GST_ELEMENT (wavpackdec), wavpackdec->wvcsinkpad); + gst_element_set_loop_function (GST_ELEMENT (wavpackdec), + gst_wavpack_dec_loop); + + wavpackdec->decodebuf = NULL; + wavpackdec->stream = (WavpackStream *) g_malloc0 (sizeof (WavpackStream)); + wavpackdec->context = (WavpackContext *) g_malloc0 (sizeof (WavpackContext)); +} + +static void +gst_wavpack_dec_setup_context (GstWavpackDec * wavpackdec, guchar * data, + guchar * cdata) +{ + WavpackContext *context = wavpackdec->context; + WavpackStream *stream = wavpackdec->stream; + + memset (context, 0, sizeof (context)); + + context->open_flags = 0; + context->current_stream = 0; + context->num_streams = 1; + + memset (stream, 0, sizeof (stream)); + context->streams[0] = stream; + + gst_wavpack_read_header (&stream->wphdr, data); + stream->blockbuff = data; + + if (cdata) { + context->wvc_flag = TRUE; + gst_wavpack_read_header (&stream->wphdr, cdata); + stream->block2buff = cdata; + } else { + context->wvc_flag = FALSE; + } + + unpack_init (context); +} + +static GstBuffer * +gst_wavpack_dec_format_samples (GstWavpackDec * wavpackdec, int32_t * samples, + guint num_samples) +{ + GstBuffer *buf; + gint i; + guint8 *dst; + int32_t temp; + + buf = + gst_buffer_new_and_alloc (num_samples * wavpackdec->width / 8 * + wavpackdec->channels); + dst = (guint8 *) GST_BUFFER_DATA (buf); + + switch (wavpackdec->width) { + case 8: + for (i = 0; i < num_samples * wavpackdec->channels; ++i) + *dst++ = *samples++ + 128; + break; + case 16: + for (i = 0; i < num_samples * wavpackdec->channels; ++i) { + *dst++ = (guint8) (temp = *samples++); + *dst++ = (guint8) (temp >> 8); + } + break; + case 24: + for (i = 0; i < num_samples * wavpackdec->channels; ++i) { + *dst++ = (guint8) (temp = *samples++); + *dst++ = (guint8) (temp >> 8); + *dst++ = (guint8) (temp >> 16); + } + break; + case 32: + for (i = 0; i < num_samples * wavpackdec->channels; ++i) { + *dst++ = (guint8) (temp = *samples++); + *dst++ = (guint8) (temp >> 8); + *dst++ = (guint8) (temp >> 16); + *dst++ = (guint8) (temp >> 24); + } + break; + default: + break; + } + + return buf; +} + + +static void +gst_wavpack_dec_loop (GstElement * element) +{ + GstWavpackDec *wavpackdec = GST_WAVPACK_DEC (element); + GstData *data, *cdata = NULL; + GstBuffer *buf, *outbuf, *cbuf = NULL; + + gboolean got_event = FALSE; + + if (!gst_pad_is_linked (wavpackdec->sinkpad)) { + return; + } + + /* + if (!gst_pad_is_linked (wavpackdec->wvcsinkpad)) { + return; + } + */ + + data = gst_pad_pull (wavpackdec->sinkpad); + + if (GST_IS_EVENT (data)) { + GstEvent *event = GST_EVENT (data); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + gst_event_unref (event); + gst_pad_push (wavpackdec->srcpad, + GST_DATA (gst_event_new (GST_EVENT_EOS))); + gst_element_set_eos (element); + break; + default: + gst_pad_event_default (wavpackdec->srcpad, event); + break; + } + got_event = TRUE; + } + + if (gst_pad_is_linked (wavpackdec->wvcsinkpad)) { + cdata = gst_pad_pull (wavpackdec->wvcsinkpad); + if (GST_IS_EVENT (cdata)) { + GstEvent *event = GST_EVENT (cdata); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + gst_event_unref (event); + gst_pad_push (wavpackdec->srcpad, + GST_DATA (gst_event_new (GST_EVENT_EOS))); + gst_element_set_eos (element); + break; + default: + gst_pad_event_default (wavpackdec->srcpad, event); + break; + } + got_event = TRUE; + } else { + cbuf = GST_BUFFER (cdata); + } + } + + if (got_event) + return; + + buf = GST_BUFFER (data); + + gst_wavpack_dec_setup_context (wavpackdec, GST_BUFFER_DATA (buf), + cbuf ? GST_BUFFER_DATA (cbuf) : NULL); + unpack_samples (wavpackdec->context, wavpackdec->decodebuf, + wavpackdec->context->streams[0]->wphdr.block_samples); + outbuf = + gst_wavpack_dec_format_samples (wavpackdec, wavpackdec->decodebuf, + wavpackdec->context->streams[0]->wphdr.block_samples); + + GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf); + GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf); + gst_buffer_unref (buf); + + gst_pad_push (wavpackdec->srcpad, GST_DATA (outbuf)); +} + +gboolean +gst_wavpack_dec_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "wavpackdec", + GST_RANK_PRIMARY, GST_TYPE_WAVPACK_DEC); +} diff --git a/ext/wavpack/gstwavpackdec.h b/ext/wavpack/gstwavpackdec.h new file mode 100644 index 00000000..32fc48d1 --- /dev/null +++ b/ext/wavpack/gstwavpackdec.h @@ -0,0 +1,79 @@ +/* GStreamer Wavpack plugin + * (c) 2004 Arwed v. Merkatz + * + * gstwavpackdec.h: raw Wavpack bitstream decoder + * + * 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_WAVPACK_DEC_H__ +#define __GST_WAVPACK_DEC_H__ + +#include + +#include + +G_BEGIN_DECLS + +/* #define's don't like whitespacey bits */ +#define GST_TYPE_WAVPACK_DEC \ + (gst_wavpack_dec_get_type()) +#define GST_WAVPACK_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WAVPACK_DEC,GstWavpackDec)) +#define GST_WAVPACK_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_WAVPACK_DEC,GstWavpackDec)) +#define GST_IS_WAVPACK_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WAVPACK_DEC)) +#define GST_IS_WAVPACK_DEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_WAVPACK_DEC)) + +typedef struct _GstWavpackDec GstWavpackDec; +typedef struct _GstWavpackDecClass GstWavpackDecClass; + +struct _GstWavpackDec +{ + GstElement element; + + GstPad *sinkpad, *srcpad; + GstPad *wvcsinkpad; + + WavpackContext *context; + + int32_t *decodebuf; + + WavpackStream *stream; + + guint32 samplerate; + guint channels; + guint width; + long frame_length; + + guchar *decdata; + long *cache; +}; + +struct _GstWavpackDecClass +{ + GstElementClass parent; +}; + +GType gst_wavpack_dec_get_type (void); + +gboolean gst_wavpack_dec_plugin_init (GstPlugin *plugin); + +G_END_DECLS + +#endif /* __GST_WAVPACK_DEC_H__ */ diff --git a/ext/wavpack/gstwavpackparse.c b/ext/wavpack/gstwavpackparse.c new file mode 100644 index 00000000..31468e26 --- /dev/null +++ b/ext/wavpack/gstwavpackparse.c @@ -0,0 +1,529 @@ +/* GStreamer wavpack plugin + * (c) 2005 Arwed v. Merkatz + * + * gstwavpackparse.c: wavpack file parser + * + * 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 "gstwavpackparse.h" +#include "gstwavpackcommon.h" + +GST_DEBUG_CATEGORY_STATIC (gst_wavpack_parse_debug); +#define GST_CAT_DEFAULT gst_wavpack_parse_debug + +/* Filter signals and args */ +enum +{ + LAST_SIGNAL +}; + +enum +{ + ARG_0 +}; + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-wavpack, " + "framed = (boolean) false; " + "audio/x-wavpack-correction, " "framed = (boolean) false") + ); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS ("audio/x-wavpack, " + "width = (int) { 8, 16, 24 }, " + "channels = (int) { 1, 2 }, " + "rate = (int) [ 8000, 96000 ], " "framed = (boolean) true") + ); + +static GstStaticPadTemplate wvc_src_factory = GST_STATIC_PAD_TEMPLATE ("wvcsrc", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS ("audio/x-wavpack-correction, " "framed = (boolean) true") + ); + +static void gst_wavpack_parse_class_init (GstWavpackParseClass * klass); +static void gst_wavpack_parse_base_init (GstWavpackParseClass * klass); +static void gst_wavpack_parse_init (GstWavpackParse * wavpackparse); + +static void gst_wavpack_parse_handle_event (GstElement * element); +static void gst_wavpack_parse_loop (GstElement * element); +static GstElementStateReturn gst_wavpack_parse_change_state (GstElement * + element); + +static GstElementClass *parent = NULL; + +GType +gst_wavpack_parse_get_type (void) +{ + static GType plugin_type = 0; + + if (!plugin_type) { + static const GTypeInfo plugin_info = { + sizeof (GstWavpackParseClass), + (GBaseInitFunc) gst_wavpack_parse_base_init, + NULL, + (GClassInitFunc) gst_wavpack_parse_class_init, + NULL, + NULL, + sizeof (GstWavpackParse), + 0, + (GInstanceInitFunc) gst_wavpack_parse_init, + }; + plugin_type = g_type_register_static (GST_TYPE_ELEMENT, + "GstWavpackParse", &plugin_info, 0); + } + return plugin_type; +} + +static void +gst_wavpack_parse_base_init (GstWavpackParseClass * klass) +{ + static GstElementDetails plugin_details = { + "Wavpack file parser", + "Codec/Demuxer/Audio", + "Parses Wavpack files", + "Arwed v. Merkatz " + }; + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&wvc_src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + gst_element_class_set_details (element_class, &plugin_details); +} + +static void +gst_wavpack_parse_dispose (GObject * object) +{ + G_OBJECT_CLASS (parent)->dispose (object); +} + +static void +gst_wavpack_parse_class_init (GstWavpackParseClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent = g_type_class_ref (GST_TYPE_ELEMENT); + + gobject_class->dispose = gst_wavpack_parse_dispose; + gstelement_class->change_state = gst_wavpack_parse_change_state; +} + +static gboolean +gst_wavpack_parse_src_query (GstPad * pad, GstQueryType type, + GstFormat * format, gint64 * value) +{ + GstWavpackParse *wavpackparse = GST_WAVPACK_PARSE (gst_pad_get_parent (pad)); + + if ((type == GST_QUERY_TOTAL) && (*format == GST_FORMAT_TIME)) { + if (wavpackparse->total_samples == 0) { + *value = 0; + return FALSE; + } + *value = + ((gdouble) wavpackparse->total_samples / + (gdouble) wavpackparse->samplerate) * GST_SECOND; + return TRUE; + } else if ((type == GST_QUERY_POSITION) && (*format == GST_FORMAT_TIME)) { + *value = wavpackparse->timestamp; + return TRUE; + } else { + return gst_pad_query_default (pad, type, format, value); + } + return FALSE; +} + +static gboolean +gst_wavpack_parse_src_event (GstPad * pad, GstEvent * event) +{ + GstWavpackParse *wavpackparse; + GstSeekType method; + GstFormat format; + gboolean need_flush; + gint64 offset, dest; + + wavpackparse = GST_WAVPACK_PARSE (gst_pad_get_parent (pad)); + + if (GST_EVENT_TYPE (event) != GST_EVENT_SEEK) { + return gst_pad_send_event (GST_PAD_PEER (wavpackparse->sinkpad), event); + } + + format = GST_EVENT_SEEK_FORMAT (event); + offset = GST_EVENT_SEEK_OFFSET (event); + method = GST_EVENT_SEEK_METHOD (event); + need_flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH; + + gst_data_unref (GST_DATA (event)); + event = NULL; + + if (offset < 0 || method != GST_SEEK_METHOD_SET) + return FALSE; + + if (format == GST_FORMAT_TIME) { + dest = offset * wavpackparse->samplerate / GST_SECOND; + } else if (format == GST_FORMAT_DEFAULT) { + dest = offset; + } else { + return FALSE; + } + + wavpackparse->need_discont = TRUE; + wavpackparse->need_flush = need_flush; + + wavpackparse->seek_pending = TRUE; + wavpackparse->seek_offset = dest; + + return TRUE; +} + +#define BUFSIZE 4096 + +static guint64 +find_header (GstByteStream * bs, guint64 filepos, WavpackHeader * wphdr) +{ + guint64 pos = filepos; + + gst_bytestream_seek (bs, filepos, GST_SEEK_METHOD_SET); + + while (1) { + guint8 *data, *cur; + gint read; + + read = gst_bytestream_peek_bytes (bs, &data, BUFSIZE); + while (read != BUFSIZE) { + guint remaining; + GstEvent *event = NULL; + + gst_bytestream_get_status (bs, &remaining, &event); + if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { + gst_event_unref (event); + return gst_bytestream_length (bs); + } + gst_event_unref (event); + read = gst_bytestream_peek_bytes (bs, &data, BUFSIZE); + continue; + } + cur = data; + do { + if (cur[0] == 'w' && cur[1] == 'v' && cur[2] == 'p' && cur[3] == 'k') { + gst_wavpack_read_header (wphdr, cur); + return pos + (cur - data); + } + cur++; + } while ((cur - data) < (BUFSIZE - sizeof (WavpackHeader))); + gst_bytestream_flush_fast (bs, BUFSIZE - sizeof (WavpackHeader)); + pos += BUFSIZE - sizeof (WavpackHeader); + } + + /* never reached */ + return gst_bytestream_length (bs); +} + +/* find the position of sample in the input bytestream, adapted from the + * original wavpack find_sample function */ +static guint64 +find_sample (GstWavpackParse * wavpackparse, guint32 sample) +{ + WavpackHeader wphdr; + guint64 file_pos1 = 0, file_pos2 = gst_bytestream_length (wavpackparse->bs); + guint64 sample_pos1 = 0, sample_pos2 = wavpackparse->total_samples; + double ratio = 0.96; + int file_skip = 0; + + if (sample >= wavpackparse->total_samples) { + return gst_bytestream_length (wavpackparse->bs); + } + + while (1) { + double bytes_per_sample; + guint64 seek_pos; + + bytes_per_sample = file_pos2 - file_pos1; + bytes_per_sample /= sample_pos2 - sample_pos1; + seek_pos = file_pos1 + (file_skip ? 32 : 0); + seek_pos += (guint64) (bytes_per_sample * (sample - sample_pos1) * ratio); + seek_pos = find_header (wavpackparse->bs, seek_pos, &wphdr); + + if (seek_pos == gst_bytestream_length (wavpackparse->bs) + || seek_pos >= file_pos2) { + if (ratio > 0.0) { + if ((ratio -= 0.24) < 0.0) + ratio = 0.0; + } else { + return gst_bytestream_length (wavpackparse->bs); + } + } else if (wphdr.block_index > sample) { + sample_pos2 = wphdr.block_index; + file_pos2 = seek_pos; + } else if (wphdr.block_index + wphdr.block_samples <= sample) { + if (seek_pos == file_pos1) + file_skip = 1; + else { + sample_pos1 = wphdr.block_index; + file_pos1 = seek_pos; + } + } else { + return seek_pos; + } + } +} + + +static void +gst_wavpack_parse_seek (GstWavpackParse * wavpackparse) +{ + guint8 *data; + gint num; + guint remaining; + WavpackHeader *header = g_malloc (sizeof (WavpackHeader)); + + guint64 offset = find_sample (wavpackparse, wavpackparse->seek_offset); + + gst_bytestream_seek (wavpackparse->bs, offset, GST_SEEK_METHOD_SET); + + if (offset == gst_bytestream_length (wavpackparse->bs)) { + /* seek failed or went beyond the end, go EOS */ + wavpackparse->timestamp = + ((gdouble) wavpackparse->total_samples / + (gdouble) wavpackparse->samplerate) * GST_SECOND; + return; + } + + num = + gst_bytestream_peek_bytes (wavpackparse->bs, &data, + sizeof (WavpackHeader)); + while (num != sizeof (WavpackHeader)) { + GstEvent *event = NULL; + + gst_bytestream_get_status (wavpackparse->bs, &remaining, &event); + if (!event) { + return; + } + gst_event_unref (event); + num = + gst_bytestream_peek_bytes (wavpackparse->bs, &data, + sizeof (WavpackHeader)); + continue; + } + gst_wavpack_read_header (header, data); + + if (wavpackparse->need_flush) { + GstEvent *flush = gst_event_new (GST_EVENT_FLUSH); + + gst_pad_push (wavpackparse->srcpad, GST_DATA (flush)); + wavpackparse->need_flush = FALSE; + } + + wavpackparse->need_discont = TRUE; + wavpackparse->timestamp = + ((gdouble) header->block_index / (gdouble) wavpackparse->samplerate) * + GST_SECOND; +} + +static void +gst_wavpack_parse_init (GstWavpackParse * wavpackparse) +{ + GstElementClass *klass = GST_ELEMENT_GET_CLASS (wavpackparse); + + wavpackparse->sinkpad = + gst_pad_new_from_template (gst_element_class_get_pad_template (klass, + "sink"), "sink"); + + gst_element_add_pad (GST_ELEMENT (wavpackparse), wavpackparse->sinkpad); + gst_element_set_loop_function (GST_ELEMENT (wavpackparse), + gst_wavpack_parse_loop); + + GST_FLAG_SET (wavpackparse, GST_ELEMENT_EVENT_AWARE); +} + +static void +gst_wavpack_parse_handle_event (GstElement * element) +{ + GstWavpackParse *wavpackparse = GST_WAVPACK_PARSE (element); + GstEvent *event = NULL; + guint remaining; + + gst_bytestream_get_status (wavpackparse->bs, &remaining, &event); + if (event) { + if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { + if (GST_IS_PAD (wavpackparse->srcpad) + && gst_pad_is_active (wavpackparse->srcpad)) + gst_pad_push (wavpackparse->srcpad, GST_DATA (event)); + gst_element_set_eos (element); + } else if (GST_EVENT_TYPE (event) == GST_EVENT_DISCONTINUOUS) { + if (GST_IS_PAD (wavpackparse->srcpad) + && gst_pad_is_active (wavpackparse->srcpad)) + gst_pad_event_default (wavpackparse->srcpad, event); + } else { + gst_event_unref (event); + } + } else { + GST_ELEMENT_ERROR (element, STREAM, DEMUX, ("couldn't read wavpack header"), + (NULL)); + } +} + +static void +gst_wavpack_parse_loop (GstElement * element) +{ + GstWavpackParse *wavpackparse = GST_WAVPACK_PARSE (element); + guint8 *data; + gint num; + GstBuffer *buf; + WavpackHeader *header = g_malloc (sizeof (WavpackHeader)); + + if (wavpackparse->seek_pending) { + gst_wavpack_parse_seek (wavpackparse); + wavpackparse->need_discont = TRUE; + wavpackparse->seek_pending = FALSE; + } + + if (wavpackparse->need_discont) { + if (GST_IS_PAD (wavpackparse->srcpad) + && gst_pad_is_active (wavpackparse->srcpad)) + gst_pad_push (wavpackparse->srcpad, + GST_DATA (gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, + wavpackparse->timestamp, GST_FORMAT_UNDEFINED))); + wavpackparse->need_discont = FALSE; + } + + num = + gst_bytestream_peek_bytes (wavpackparse->bs, &data, + sizeof (WavpackHeader)); + if (num != sizeof (WavpackHeader)) { + gst_wavpack_parse_handle_event (element); + return; + } + gst_wavpack_read_header (header, data); + + num = gst_bytestream_peek_bytes (wavpackparse->bs, &data, header->ckSize + 8); + if (num != header->ckSize + 8) { + gst_wavpack_parse_handle_event (element); + return; + } + + if (!GST_IS_PAD (wavpackparse->srcpad)) { + guchar *bufptr = data + sizeof (WavpackHeader); + GstCaps *caps = NULL; + WavpackMetadata meta; + + while (read_metadata_buff (&meta, data, &bufptr)) { + if (meta.id == ID_WVC_BITSTREAM) { + caps = gst_caps_new_simple ("audio/x-wavpack-correction", + "framed", G_TYPE_BOOLEAN, TRUE, NULL); + wavpackparse->srcpad = + gst_pad_new_from_template (gst_element_class_get_pad_template + (GST_ELEMENT_GET_CLASS (wavpackparse), "wvcsrc"), "wvcsrc"); + } else if (meta.id == ID_RIFF_HEADER) { + WaveHeader *wheader = g_malloc (sizeof (WaveHeader)); + + // skip RiffChunkHeader and ChunkHeader + g_memmove (wheader, meta.data + 20, sizeof (WaveHeader)); + little_endian_to_native (wheader, WaveHeaderFormat); + wavpackparse->samplerate = wheader->SampleRate; + wavpackparse->channels = wheader->NumChannels; + wavpackparse->total_samples = header->total_samples; + caps = gst_caps_new_simple ("audio/x-wavpack", + "width", G_TYPE_INT, wheader->BitsPerSample, + "channels", G_TYPE_INT, wavpackparse->channels, + "rate", G_TYPE_INT, wavpackparse->samplerate, + "framed", G_TYPE_BOOLEAN, TRUE, NULL); + wavpackparse->srcpad = + gst_pad_new_from_template (gst_element_class_get_pad_template + (GST_ELEMENT_GET_CLASS (wavpackparse), "src"), "src"); + } + } + gst_pad_use_explicit_caps (wavpackparse->srcpad); + gst_pad_set_query_function (wavpackparse->srcpad, + gst_wavpack_parse_src_query); + gst_pad_set_event_function (wavpackparse->srcpad, + gst_wavpack_parse_src_event); + gst_element_add_pad (GST_ELEMENT (wavpackparse), wavpackparse->srcpad); + + gst_pad_set_explicit_caps (wavpackparse->srcpad, caps); + } + + buf = gst_buffer_new_and_alloc (header->ckSize + 8); + memcpy (GST_BUFFER_DATA (buf), data, header->ckSize + 8); + gst_bytestream_flush_fast (wavpackparse->bs, header->ckSize + 8); + wavpackparse->timestamp = + ((gdouble) header->block_index / (gdouble) wavpackparse->samplerate) * + GST_SECOND; + GST_BUFFER_TIMESTAMP (buf) = wavpackparse->timestamp; + GST_BUFFER_DURATION (buf) = + ((gdouble) header->block_samples / (gdouble) wavpackparse->samplerate) * + GST_SECOND; + gst_pad_push (wavpackparse->srcpad, GST_DATA (buf)); +} + +static GstElementStateReturn +gst_wavpack_parse_change_state (GstElement * element) +{ + GstWavpackParse *wavpackparse = GST_WAVPACK_PARSE (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_READY_TO_PAUSED: + wavpackparse->need_discont = TRUE; + wavpackparse->bs = gst_bytestream_new (wavpackparse->sinkpad); + break; + case GST_STATE_PAUSED_TO_READY: + gst_bytestream_destroy (wavpackparse->bs); + wavpackparse->seek_pending = FALSE; + break; + default: + break; + } + + if (GST_ELEMENT_CLASS (parent)->change_state) + return GST_ELEMENT_CLASS (parent)->change_state (element); + + return GST_STATE_SUCCESS; +} + +gboolean +gst_wavpack_parse_plugin_init (GstPlugin * plugin) +{ + if (!gst_library_load ("gstbytestream")) + return FALSE; + + if (!gst_element_register (plugin, "wavpackparse", + GST_RANK_PRIMARY, GST_TYPE_WAVPACK_PARSE)) { + return FALSE; + } + + GST_DEBUG_CATEGORY_INIT (gst_wavpack_parse_debug, "wavpackparse", 0, + "wavpack file parser"); + + return TRUE; +} diff --git a/ext/wavpack/gstwavpackparse.h b/ext/wavpack/gstwavpackparse.h new file mode 100644 index 00000000..ef76ba0f --- /dev/null +++ b/ext/wavpack/gstwavpackparse.h @@ -0,0 +1,75 @@ +/* GStreamer wavpack plugin + * (c) 2005 Arwed v. Merkatz + * + * gstwavpackparse.h: wavpack file parser + * + * 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_WAVPACK_PARSE_H__ +#define __GST_WAVPACK_PARSE_H__ + +#include +#include + +G_BEGIN_DECLS + +/* #define's don't like whitespacey bits */ +#define GST_TYPE_WAVPACK_PARSE \ + (gst_wavpack_parse_get_type()) +#define GST_WAVPACK_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WAVPACK_PARSE,GstWavpackParse)) +#define GST_WAVPACK_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_WAVPACK_PARSE,GstWavpackParse)) +#define GST_IS_WAVPACK_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WAVPACK_PARSE)) +#define GST_IS_WAVPACK_PARSE_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_WAVPACK_PARSE)) + +typedef struct _GstWavpackParse GstWavpackParse; +typedef struct _GstWavpackParseClass GstWavpackParseClass; + +struct _GstWavpackParse +{ + GstElement element; + + GstPad *sinkpad, *srcpad; + + GstByteStream* bs; + + guint32 samplerate; + guint32 channels; + guint32 total_samples; + guint64 timestamp; + + guint64 seek_offset; + gboolean seek_pending; + gboolean need_discont; + gboolean need_flush; +}; + +struct _GstWavpackParseClass +{ + GstElementClass parent; +}; + +GType gst_wavpack_parse_get_type (void); + +gboolean gst_wavpack_parse_plugin_init (GstPlugin *plugin); + +G_END_DECLS + +#endif /* __GST_WAVPACK_PARSE_H__ */ -- cgit v1.2.1