From ea5845cb2bfdb57a06cc2ad5b2561889b5fcc049 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 20 Feb 2004 00:52:07 +0000 Subject: [PATCH] ext/xine/: add first version of xine plugin wrapper. Currently only wraps the Original commit message from CVS: 2004-02-20 Benjamin Otte * ext/xine/Makefile.am: * ext/xine/gstxine.h: * ext/xine/xine.c: * ext/xine/xineaudiodec.c: * ext/xine/xinecaps.c: add first version of xine plugin wrapper. Currently only wraps the QDM2 win32 DLL, and even that only in proof-of-concept quality. * configure.ac: * ext/Makefile.am: add xine plugin wrapper, disabled by default. Use --enable-xine to build. Note that it'll segfault on gst-register if you don't remove the goom and tvtime post plugins from xine. * gst/qtdemux/qtdemux.c: (gst_qtdemux_handle_sink_event), (qtdemux_parse), (qtdemux_parse_trak), (qtdemux_audio_caps): add extradata parsing for QDM2. change around debugging prints. --- ChangeLog | 19 ++ configure.ac | 9 + ext/Makefile.am | 8 + ext/xine/Makefile.am | 11 + ext/xine/gstxine.h | 82 ++++++ ext/xine/xine.c | 169 ++++++++++++ ext/xine/xineaudiodec.c | 556 ++++++++++++++++++++++++++++++++++++++++ ext/xine/xinecaps.c | 119 +++++++++ gst/qtdemux/qtdemux.c | 39 +-- 9 files changed, 997 insertions(+), 15 deletions(-) create mode 100644 ext/xine/Makefile.am create mode 100644 ext/xine/gstxine.h create mode 100644 ext/xine/xine.c create mode 100644 ext/xine/xineaudiodec.c create mode 100644 ext/xine/xinecaps.c diff --git a/ChangeLog b/ChangeLog index 3fae7f22cd..76a1f9ce4d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2004-02-20 Benjamin Otte + + * ext/xine/Makefile.am: + * ext/xine/gstxine.h: + * ext/xine/xine.c: + * ext/xine/xineaudiodec.c: + * ext/xine/xinecaps.c: + add first version of xine plugin wrapper. Currently only wraps the + QDM2 win32 DLL, and even that only in proof-of-concept quality. + * configure.ac: + * ext/Makefile.am: + add xine plugin wrapper, disabled by default. Use --enable-xine to + build. Note that it'll segfault on gst-register if you don't remove + the goom and tvtime post plugins from xine. + * gst/qtdemux/qtdemux.c: (gst_qtdemux_handle_sink_event), + (qtdemux_parse), (qtdemux_parse_trak), (qtdemux_audio_caps): + add extradata parsing for QDM2. + change around debugging prints. + 2004-02-19 Benjamin Otte * ext/lame/gstlame.c: (gst_lame_chain): diff --git a/configure.ac b/configure.ac index 3626b82b0d..4631797a26 100644 --- a/configure.ac +++ b/configure.ac @@ -1320,6 +1320,14 @@ vorbis_synthesis_restart (v); CFLAGS="$ac_cflags_save" fi +dnl *** xine *** +translit(dnm, m, l) AM_CONDITIONAL(USE_XINE, true) +GST_CHECK_FEATURE(XINE, [xine wrapper], xine, [ + PKG_CHECK_MODULES(XINE, libxine >= 1.0.0, HAVE_XINE=yes, HAVE_XINE=no) + AC_SUBST(XINE_CFLAGS) + AC_SUBST(XINE_LIBS) +],disabled) + dnl *** XVID *** translit(dnm, m, l) AM_CONDITIONAL(USE_XVID, true) GST_CHECK_FEATURE(XVID, [xvid plugins], xvid, [ @@ -1664,6 +1672,7 @@ ext/swfdec/Makefile ext/tarkin/Makefile ext/theora/Makefile ext/vorbis/Makefile +ext/xine/Makefile ext/xvid/Makefile gst-libs/Makefile gst-libs/gst/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index 4865588c0c..2f51a46706 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -310,6 +310,12 @@ else SPEEX_DIR= endif +if USE_XINE +XINE_DIR=xine +else +XINE_DIR= +endif + SUBDIRS=\ $(A52DEC_DIR) \ $(AALIB_DIR) \ @@ -361,6 +367,7 @@ SUBDIRS=\ $(THEORA_DIR) \ $(IVORBIS_DIR) \ $(VORBIS_DIR) \ + $(XINE_DIR) \ $(XVID_DIR) DIST_SUBDIRS=\ @@ -415,4 +422,5 @@ DIST_SUBDIRS=\ tarkin \ theora \ vorbis \ + xine \ xvid diff --git a/ext/xine/Makefile.am b/ext/xine/Makefile.am new file mode 100644 index 0000000000..b67889c750 --- /dev/null +++ b/ext/xine/Makefile.am @@ -0,0 +1,11 @@ +plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ + +plugin_LTLIBRARIES = libgstxine.la + +libgstxine_la_SOURCES = xine.c xinecaps.c xineaudiodec.c +libgstxine_la_CFLAGS = $(GST_CFLAGS) $(XINE_CFLAGS) +libgstxine_la_LIBADD = $(XINE_LIBS) +libgstxine_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +noinst_HEADERS = gstxine.h + diff --git a/ext/xine/gstxine.h b/ext/xine/gstxine.h new file mode 100644 index 0000000000..9fdc658811 --- /dev/null +++ b/ext/xine/gstxine.h @@ -0,0 +1,82 @@ +/* GStreamer + * Copyright (C) 2004 Benjamin Otte + * + * 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_XINE_H__ +#define __GST_XINE_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_XINE \ + (gst_xine_get_type()) +#define GST_XINE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_XINE,GstXine)) +#define GST_XINE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XINE, GstXineClass)) +#define GST_XINE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_XINE,GstXineClass)) +#define GST_IS_XINE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_XINE)) +#define GST_IS_XINE_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_XINE)) + +typedef struct _GstXine GstXine; +typedef struct _GstXineClass GstXineClass; + +struct _GstXine +{ + GstElement element; + + xine_stream_t * stream; + xine_ao_driver_t * audio_driver; + xine_vo_driver_t * video_driver; +}; + +struct _GstXineClass +{ + GstElementClass parent_class; + + xine_t * xine; + + xine_ao_driver_t * (* create_audio_driver) (GstXine * xine); + xine_vo_driver_t * (* create_video_driver) (GstXine * xine); +}; + +GType gst_xine_get_type (void); + +xine_stream_t * gst_xine_get_stream (GstXine *xine); +void gst_xine_free_stream (GstXine *xine); + +void gst_buffer_to_xine_buffer (buf_element_t *element, GstBuffer *buffer); + +/* conversion functions from xinecaps.c */ + +const gchar * gst_xine_get_caps_for_format (guint32 format); +guint32 gst_xine_get_format_for_caps (const GstCaps *caps); + +/* init functions for the plugins */ + +gboolean gst_xine_audio_dec_init_plugin (GstPlugin *plugin); + +G_END_DECLS + +#endif /* __GST_XINE_H__ */ diff --git a/ext/xine/xine.c b/ext/xine/xine.c new file mode 100644 index 0000000000..f3bfb5ab2f --- /dev/null +++ b/ext/xine/xine.c @@ -0,0 +1,169 @@ +/* GStreamer + * Copyright (C) 2004 Benjamin Otte + * + * 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 "gstxine.h" + +GST_BOILERPLATE (GstXine, gst_xine, GstElement, GST_TYPE_ELEMENT) + +static GstElementStateReturn gst_xine_change_state (GstElement * element); + +static xine_ao_driver_t * _xine_create_audio_driver (GstXine * xine); +static xine_vo_driver_t * _xine_create_video_driver (GstXine * xine); + + +static void +gst_xine_base_init (gpointer klass) +{ +} + +static void +gst_xine_class_init (GstXineClass *klass) +{ + GstElementClass *element = GST_ELEMENT_CLASS (klass); + + klass->xine = xine_new (); + xine_init (klass->xine); + + klass->create_audio_driver = _xine_create_audio_driver; + klass->create_video_driver = _xine_create_video_driver; + + element->change_state = gst_xine_change_state; +} + +static void +gst_xine_init (GstXine *filter) +{ +} + +static GstElementStateReturn +gst_xine_change_state (GstElement *element) +{ + GstXine *xine = GST_XINE (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_NULL_TO_READY: + break; + case GST_STATE_READY_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_PLAYING: + break; + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + break; + case GST_STATE_READY_TO_NULL: + if (xine->stream != NULL) + gst_xine_free_stream (xine); + break; + default: + GST_ERROR_OBJECT (element, "invalid state change"); + break; + } + + return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, (element), GST_STATE_SUCCESS); +} + +static xine_ao_driver_t * +_xine_create_audio_driver (GstXine *xine) +{ + return xine_open_audio_driver (GST_XINE_GET_CLASS (xine)->xine, "none", NULL); +} + +static xine_vo_driver_t * +_xine_create_video_driver (GstXine *xine) +{ + return xine_open_video_driver (GST_XINE_GET_CLASS (xine)->xine, "none", XINE_VISUAL_TYPE_NONE, NULL); +} + +xine_stream_t * +gst_xine_get_stream (GstXine *xine) +{ + if (!xine->stream) { + GstXineClass *klass = GST_XINE_GET_CLASS (xine); + g_assert (xine->video_driver == NULL); + g_assert (xine->audio_driver == NULL); + xine->audio_driver = klass->create_audio_driver (xine); + xine->video_driver = klass->create_video_driver (xine); + xine->stream = xine_stream_new (klass->xine, xine->audio_driver, xine->video_driver); + + /* FIXME: fail gracefully */ + g_assert (xine->stream); + } + + return xine->stream; +} + +void +gst_xine_free_stream (GstXine *xine) +{ + g_return_if_fail (xine->stream != NULL); + g_assert (xine->video_driver != NULL); + g_assert (xine->audio_driver != NULL); + + xine_dispose (xine->stream); + xine->stream = NULL; + xine_close_video_driver (GST_XINE_GET_CLASS (xine)->xine, xine->video_driver); + xine->video_driver = NULL; + xine_close_audio_driver (GST_XINE_GET_CLASS (xine)->xine, xine->audio_driver); + xine->audio_driver = NULL; +} + +static void +_free_xine_buf_element (buf_element_t *buffer) +{ + gst_buffer_unref (GST_BUFFER (buffer->source)); +} + +void +gst_buffer_to_xine_buffer (buf_element_t *ret, GstBuffer *buffer) +{ + g_return_if_fail (ret != NULL); + g_return_if_fail (buffer != NULL); + + /* FIXME: what's the difference? */ + ret->mem = GST_BUFFER_DATA (buffer); + ret->content = GST_BUFFER_DATA (buffer); + ret->size = GST_BUFFER_SIZE (buffer); + ret->max_size = GST_BUFFER_MAXSIZE (buffer); + /* FIXME: add more */ + ret->free_buffer = _free_xine_buf_element; + ret->source = buffer; +} + +static gboolean +plugin_init (GstPlugin *plugin) +{ + return gst_xine_audio_dec_init_plugin (plugin); +} + +GST_PLUGIN_DEFINE ( + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "xine", + "wrapper for libxine (version "XINE_VERSION") plugins", + plugin_init, + VERSION, + "GPL", + GST_PACKAGE, + GST_ORIGIN +) diff --git a/ext/xine/xineaudiodec.c b/ext/xine/xineaudiodec.c new file mode 100644 index 0000000000..3d31ef9a65 --- /dev/null +++ b/ext/xine/xineaudiodec.c @@ -0,0 +1,556 @@ +/* GStreamer + * Copyright (C) 2004 Benjamin Otte + * + * 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 "gstxine.h" +#include +#include + +#define GST_TYPE_XINE_AUDIO_DEC \ + (gst_xine_audio_dec_get_type()) +#define GST_XINE_AUDIO_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_XINE_AUDIO_DEC,GstXineAudioDec)) +#define GST_XINE_AUDIO_DEC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XINE_AUDIO_DEC, GstXineAudioDecClass)) +#define GST_XINE_AUDIO_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_XINE_AUDIO_DEC,GstXineAudioDecClass)) +#define GST_IS_XINE_AUDIO_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_XINE_AUDIO_DEC)) +#define GST_IS_XINE_AUDIO_DEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_XINE_AUDIO_DEC)) + +GType gst_xine_audio_dec_get_type (void); + +typedef struct _GstXineAudioDec GstXineAudioDec; +typedef struct _GstXineAudioDecClass GstXineAudioDecClass; + +struct _GstXineAudioDec +{ + GstXine parent; + + GstPad * sinkpad; + GstPad * srcpad; + + audio_decoder_t * decoder; + guint32 format; + xine_waveformatex wave; + gboolean setup; +}; + +struct _GstXineAudioDecClass +{ + GstXineClass parent_class; + + plugin_node_t * plugin_node; +}; + +/*** xine audio driver wrapper ************************************************/ + +typedef struct { + xine_ao_driver_t driver; + GstXineAudioDec * xine; + gboolean open; +} GstXineAudioDriver; + +static guint32 +_driver_get_capabilities (xine_ao_driver_t *driver) +{ + /* FIXME: add more when gst handles more than 2 channels */ + return AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO | AO_CAP_8BITS; +} + +static gint +_driver_get_property (xine_ao_driver_t *driver, int property) +{ + return 0; +} + +static gint +_driver_set_property (xine_ao_driver_t * driver, int property, int value) +{ + return ~value; +} + +static gint +_driver_open (xine_ao_driver_t *driver, xine_stream_t *stream, guint32 bits, guint32 rate, int mode) +{ + GstCaps *caps; + GstXineAudioDriver *xine = ((GstXineAudioDriver *) driver); + + caps = gst_caps_new_simple ("audio/x-raw-int", + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "width", G_TYPE_INT, (gint) bits, + "depth", G_TYPE_INT, (gint) bits, + "signed", G_TYPE_BOOLEAN, (bits == 8) ? FALSE : TRUE, + "channels", G_TYPE_INT, (mode | AO_CAP_MODE_STEREO) ? 2 : 1, + "rate", G_TYPE_INT, rate, + NULL); + + if (!gst_pad_set_explicit_caps (xine->xine->srcpad, caps)) { + gst_caps_free (caps); + driver->open = FALSE; + return -1; + } + + xine->open = TRUE; + gst_caps_free (caps); + return rate; +} + +static void +_driver_close (xine_ao_driver_t *driver, xine_stream_t *stream) +{ + /* FIXME: unset explicit caps here? And how? */ + driver->open = FALSE; +} + +static void +_driver_exit (xine_ao_driver_t *driver) +{ + g_free (driver); +} + +static int +_driver_control (xine_ao_driver_t *driver, int cmd, ...) +{ + return 0; +} + +static void +_driver_flush (xine_ao_driver_t *driver) +{ +} + +static int +_driver_status (xine_ao_driver_t *driver, xine_stream_t *stream, uint32_t *bits, uint32_t *rate, int *mode) +{ + const GstCaps *caps; + GstStructure *structure; + gint temp; + GstXineAudioDriver *xine = (GstXineAudioDriver *) driver; + + if (xine->open == FALSE || !(caps = gst_pad_get_negotiated_caps (xine->xine->srcpad))) + return 0; + + structure = gst_caps_get_structure (caps, 0); + *bits = 0; /* FIXME */ + if (!gst_structure_get_int (structure, "rate", &temp)) { + g_assert_not_reached (); /* may never happen with negotiated caps */ + return 0; + } + *rate = temp; + if (!gst_structure_get_int (structure, "channels", &temp)) { + g_assert_not_reached (); /* may never happen with negotiated caps */ + return 0; + } + *mode = (temp == 2) ? AO_CAP_MODE_STEREO : AO_CAP_MODE_MONO; + if (!gst_structure_get_int (structure, "width", &temp)) { + g_assert_not_reached (); /* may never happen with negotiated caps */ + return 0; + } + if (temp == 8) + *mode |= AO_CAP_8BITS; + + return 1; +} + +#define _DRIVER_BUFFER_SIZE 4096 +static audio_buffer_t * +_driver_get_buffer (xine_ao_driver_t *driver) +{ + GstXineAudioDriver *xine = (GstXineAudioDriver *) driver; + audio_buffer_t *audio = g_new0 (audio_buffer_t, 1); + + audio->mem = g_malloc (_DRIVER_BUFFER_SIZE); + audio->mem_size = _DRIVER_BUFFER_SIZE; + audio->stream = gst_xine_get_stream (GST_XINE (xine->xine)); + /* FIXME: fill more fields */ + + return audio; +} + +static void +_driver_put_buffer (xine_ao_driver_t *driver, audio_buffer_t *audio, xine_stream_t *stream) +{ + GstXineAudioDriver *xine = (GstXineAudioDriver *) driver; + GstBuffer *buffer; + + buffer = gst_buffer_new (); + GST_BUFFER_DATA (buffer) = (guint8 *) audio->mem; + GST_BUFFER_SIZE (buffer) = audio->mem_size; + GST_BUFFER_MAXSIZE (buffer) = audio->mem_size; + /* FIXME: fill more fields */ + g_free (audio); + gst_pad_push (xine->xine->srcpad, GST_DATA (buffer)); +} + +static xine_ao_driver_t * +_gst_xine_audio_dec_create_audio_driver (GstXine *xine) +{ + GstXineAudioDriver *driver = g_new (GstXineAudioDriver, 1); + + driver->xine = GST_XINE_AUDIO_DEC (xine); + driver->open = FALSE; + + driver->driver.get_buffer = _driver_get_buffer; + driver->driver.put_buffer = _driver_put_buffer; + driver->driver.get_capabilities = _driver_get_capabilities; + driver->driver.get_property = _driver_get_property; + driver->driver.set_property = _driver_set_property; + driver->driver.open = _driver_open; + driver->driver.close = _driver_close; + driver->driver.exit = _driver_exit; + driver->driver.control = _driver_control; + driver->driver.flush = _driver_flush; + driver->driver.status = _driver_status; + + return (xine_ao_driver_t *) driver; +} + +/** GstXineAudioDec ***********************************************************/ + +GST_BOILERPLATE (GstXineAudioDec, gst_xine_audio_dec, GstXine, GST_TYPE_XINE) + +static void gst_xine_audio_dec_chain (GstPad *pad, GstData *in); +static GstElementStateReturn + gst_xine_audio_dec_change_state (GstElement *element); + +/* this function handles the link with other plug-ins */ +static GstPadLinkReturn +gst_xine_audio_dec_sink_link (GstPad *pad, const GstCaps *caps) +{ + guint temp; + GstStructure *structure; + GstXineAudioDec *xine = GST_XINE_AUDIO_DEC (gst_object_get_parent (GST_OBJECT (pad))); + + xine->format = gst_xine_get_format_for_caps (caps); + if (xine->format == 0) return GST_PAD_LINK_REFUSED; + + /* get setup data */ + xine->setup = FALSE; + structure = gst_caps_get_structure (caps, 0); + if (gst_structure_get_int (structure, "channels", &temp)) + xine->wave.nChannels = temp; + if (gst_structure_get_int (structure, "rate", &temp)) + xine->wave.nSamplesPerSec = temp; + xine->wave.wBitsPerSample = 16; /* FIXME: how do we figure this thing out? */ + /* FIXME: fill wave header better */ + + return GST_PAD_LINK_OK; +} + +static void +gst_xine_audio_dec_base_init (gpointer g_class) +{ +} + +static void +gst_xine_audio_dec_class_init (GstXineAudioDecClass *klass) +{ + GstXineClass *xine = GST_XINE_CLASS (klass); + GstElementClass *element = GST_ELEMENT_CLASS (klass); + + element->change_state = gst_xine_audio_dec_change_state; + + xine->create_audio_driver = _gst_xine_audio_dec_create_audio_driver; +} + +static void +gst_xine_audio_dec_init (GstXineAudioDec *xine) +{ + xine->setup = FALSE; +} + +static void +gst_xine_audio_dec_event (GstXineAudioDec *xine, GstEvent *event) +{ + gst_pad_event_default (xine->sinkpad, event); +} + +static void +gst_xine_audio_dec_chain (GstPad *pad, GstData *in) +{ + G_GNUC_UNUSED buf_element_t buffer = { 0, }; + GstXineAudioDec *xine = GST_XINE_AUDIO_DEC (gst_object_get_parent (GST_OBJECT (pad))); + + if (GST_IS_EVENT (in)) { + gst_xine_audio_dec_event (xine, GST_EVENT (in)); + return; + } + + if (xine->format == 0) { + /* no caps yet */ + GST_ELEMENT_ERROR (xine, CORE, NEGOTIATION, (NULL), ("buffer sent before doing caps nego")); + gst_data_unref (in); + return; + } + + if (!xine->setup) { + buf_element_t element = { 0, }; + guint8 stsd[150] = { 0, }; + guint temp; + GstStructure *structure; + /* sent setup header */ + element.type = xine->format; + element.decoder_flags = BUF_FLAG_HEADER; + element.decoder_info[0] = 0; + element.decoder_info[1] = xine->wave.nSamplesPerSec; + element.decoder_info[2] = xine->wave.wBitsPerSample; + element.decoder_info[3] = xine->wave.nChannels; + element.content = (guchar *) &xine->wave; + element.size = sizeof (xine_waveformatex); + xine->decoder->decode_data (xine->decoder, &element); + /* send stsd emulation to the decoder */ + /* FIXME: qdm2 only right now */ + g_assert (gst_pad_get_negotiated_caps (xine->sinkpad)); + structure = gst_caps_get_structure (gst_pad_get_negotiated_caps (xine->sinkpad), 0); + *((guint32 *) &stsd[56]) = GUINT32_TO_BE (12); + memcpy (&stsd[60], "frmaQDM2", 8); + *((guint32 *) &stsd[68]) = GUINT32_TO_BE (36); + memcpy (&stsd[72], "QDCA", 4); + *((guint32 *) &stsd[76]) = GUINT32_TO_BE (1); + if (!gst_structure_get_int (structure, "channels", &temp)) + temp = 0; + *((guint32 *) &stsd[80]) = GUINT32_TO_BE (temp); + if (!gst_structure_get_int (structure, "rate", &temp)) + temp = 0; + *((guint32 *) &stsd[84]) = GUINT32_TO_BE (temp); + if (!gst_structure_get_int (structure, "bitrate", &temp)) + temp = 0; + *((guint32 *) &stsd[88]) = GUINT32_TO_BE (temp); + if (!gst_structure_get_int (structure, "blocksize", &temp)) + temp = 0; + *((guint32 *) &stsd[92]) = GUINT32_TO_BE (temp); + *((guint32 *) &stsd[96]) = GUINT32_TO_BE (256); + if (!gst_structure_get_int (structure, "framesize", &temp)) + temp = 0; + *((guint32 *) &stsd[100]) = GUINT32_TO_BE (temp); + *((guint32 *) &stsd[104]) = GUINT32_TO_BE (28); + memcpy (&stsd[108], "QDCP", 4); + *((guint32 *) &stsd[112]) = GUINT32_TO_BE (1065353216); + *((guint32 *) &stsd[116]) = GUINT32_TO_BE (0); + *((guint32 *) &stsd[120]) = GUINT32_TO_BE (1065353216); + *((guint32 *) &stsd[124]) = GUINT32_TO_BE (1065353216); + *((guint32 *) &stsd[128]) = GUINT32_TO_BE (27); + *((guint32 *) &stsd[132]) = GUINT32_TO_BE (8); + *((guint32 *) &stsd[136]) = GUINT32_TO_BE (0); + *((guint32 *) &stsd[140]) = GUINT32_TO_BE (24); + gst_util_dump_mem (stsd, 144); + element.decoder_flags = BUF_FLAG_SPECIAL; + element.decoder_info[1] = BUF_SPECIAL_STSD_ATOM; + element.decoder_info[2] = 144; + element.decoder_info[3] = 0; + element.decoder_info_ptr[2] = stsd; + element.size = 0; + element.content = 0; + xine->decoder->decode_data (xine->decoder, &element); + + xine->setup = TRUE; + } + + gst_buffer_to_xine_buffer (&buffer, GST_BUFFER (in)); + buffer.type = xine->format; + xine->decoder->decode_data (xine->decoder, &buffer); + gst_data_unref (in); +} + +static audio_decoder_t * +_load_decoder (GstXineAudioDec* dec) +{ + xine_stream_t *stream = gst_xine_get_stream (GST_XINE (dec)); + plugin_catalog_t *catalog = stream->xine->plugin_catalog; + plugin_node_t *node = GST_XINE_AUDIO_DEC_GET_CLASS (dec)->plugin_node; + audio_decoder_t *ret; + + /* FIXME: this is really hacky, but how to force xine to load a plugin? */ + /* how it works: xine can load a plugin for a particular stream type. + * We just take one type, which should not have plugins attached to it, + * attach our plugin and load it */ + g_assert (catalog->audio_decoder_map[DECODER_MAX - 1][0] == NULL); + catalog->audio_decoder_map[DECODER_MAX - 1][0] = node; + ret = _x_get_audio_decoder (stream, DECODER_MAX - 1); + catalog->audio_decoder_map[DECODER_MAX - 1][0] = NULL; + + return ret; +} + +static GstElementStateReturn +gst_xine_audio_dec_change_state (GstElement *element) +{ + GstXineAudioDec *xine = GST_XINE_AUDIO_DEC (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_NULL_TO_READY: + xine->decoder = _load_decoder (xine); + if (!xine->decoder) + return GST_STATE_FAILURE; + break; + case GST_STATE_READY_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_PLAYING: + break; + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + break; + case GST_STATE_READY_TO_NULL: + xine->setup = FALSE; + _x_free_audio_decoder (gst_xine_get_stream (GST_XINE (xine)), xine->decoder); + break; + default: + GST_ERROR_OBJECT (element, "invalid state change"); + break; + } + + return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, (element), GST_STATE_SUCCESS); +} + +/** GstXineAudioDec subclasses ************************************************/ + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ( + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ( + "audio/x-raw-int, " + "endianness = (int) BYTE_ORDER, " + "signed = (boolean) TRUE, " + "width = (int) 16, " + "depth = (int) 16, " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, 2 ]; " + "audio/x-raw-int, " + "signed = (boolean) FALSE, " + "width = (int) 8, " + "depth = (int) 8, " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, 2 ]" + ) +); + +static void +gst_xine_audio_dec_subclass_init (gpointer g_class, gpointer class_data) +{ + GstXineAudioDecClass *xine_class = GST_XINE_AUDIO_DEC_CLASS (g_class); + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstElementDetails details = GST_ELEMENT_DETAILS ( + NULL, + "Filter/Decoder/Audio", + NULL, + "Benjamin Otte " + ); + GstPadTemplate *template; + guint i = 0; + GstCaps *caps = gst_caps_new_empty (); + decoder_info_t *dec; + + xine_class->plugin_node = class_data; + dec = xine_class->plugin_node->info->special_info; + details.longname = g_strdup_printf ("%s xine audio decoder", xine_class->plugin_node->info->id); + details.description = g_strdup_printf ("decodes audio using the xine '%s' plugin", xine_class->plugin_node->info->id); + gst_element_class_set_details (element_class, &details); + g_free (details.longname); + g_free (details.description); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + + while (dec->supported_types[i] != 0) { + const gchar *type_str = gst_xine_get_caps_for_format (dec->supported_types[i]); + if (type_str) { + gst_caps_append (caps, gst_caps_from_string (type_str)); + } + i++; + } + template = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps); + gst_element_class_add_pad_template (element_class, template); +} + +static void +gst_xine_audio_dec_sub_init (GTypeInstance *instance, gpointer g_class) +{ + GstElementClass *klass = GST_ELEMENT_GET_CLASS (instance); + GstXineAudioDec *xine = GST_XINE_AUDIO_DEC (instance); + + xine->sinkpad = gst_pad_new_from_template ( + gst_element_class_get_pad_template (klass, "sink"), "sink"); + gst_pad_set_link_function (xine->sinkpad, gst_xine_audio_dec_sink_link); + gst_pad_set_chain_function (xine->sinkpad, gst_xine_audio_dec_chain); + gst_element_add_pad (GST_ELEMENT (xine), xine->sinkpad); + + xine->srcpad = gst_pad_new_from_template ( + gst_element_class_get_pad_template (klass, "src"), "src"); + gst_pad_use_explicit_caps (xine->srcpad); + gst_element_add_pad (GST_ELEMENT (xine), xine->srcpad); +} + +gboolean +gst_xine_audio_dec_init_plugin (GstPlugin *plugin) +{ + GTypeInfo plugin_info = + { + sizeof (GstXineAudioDecClass), + NULL, + NULL, + gst_xine_audio_dec_subclass_init, + NULL, + NULL, + sizeof (GstXineAudioDec), + 0, + gst_xine_audio_dec_sub_init, + }; + xine_node_t *list; + GstXineClass *klass; + + klass = g_type_class_ref (GST_TYPE_XINE); + + list = klass->xine->plugin_catalog->audio->first; + while (list) { + plugin_node_t *node = list->content; + decoder_info_t *dec; + guint format = 0; + + list = list->next; + if (!node) continue; + + dec = node->info->special_info; + while (dec->supported_types[format] != 0) { + const gchar *caps = gst_xine_get_caps_for_format (dec->supported_types[format]); + if (caps) { + gchar *plugin_name = g_strdup_printf ("xineaudiodec_%s", node->info->id); + gchar *type_name = g_strdup_printf ("GstXineAudioDec%s", node->info->id); + GType type; + plugin_info.class_data = node; + type = g_type_register_static (GST_TYPE_XINE_AUDIO_DEC, type_name, &plugin_info, 0); + g_free (type_name); + if (!gst_element_register (plugin, plugin_name, + MAX (GST_RANK_MARGINAL, GST_RANK_MARGINAL * dec->priority / 10 + 1), type)) { + g_free (plugin_name); + return FALSE; + } + g_free (plugin_name); + } + format++; + } + } + + g_type_class_unref (klass); + return TRUE; +} + diff --git a/ext/xine/xinecaps.c b/ext/xine/xinecaps.c new file mode 100644 index 0000000000..d243f72f21 --- /dev/null +++ b/ext/xine/xinecaps.c @@ -0,0 +1,119 @@ +/* GStreamer + * Copyright (C) 2004 Benjamin Otte + * + * 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 "gstxine.h" +#include + +typedef struct { + guint32 xine; + gchar * caps; +} GstXineCapsMap; + +static GstXineCapsMap _gst_xine_caps_map[] = { + { BUF_AUDIO_QDESIGN2, "audio/x-qdm2" }, +/* FIXME: +#define BUF_AUDIO_A52 0x03000000 +#define BUF_AUDIO_MPEG 0x03010000 +#define BUF_AUDIO_LPCM_BE 0x03020000 +#define BUF_AUDIO_LPCM_LE 0x03030000 +#define BUF_AUDIO_WMAV1 0x03040000 +#define BUF_AUDIO_DTS 0x03050000 +#define BUF_AUDIO_MSADPCM 0x03060000 +#define BUF_AUDIO_MSIMAADPCM 0x03070000 +#define BUF_AUDIO_MSGSM 0x03080000 +#define BUF_AUDIO_VORBIS 0x03090000 +#define BUF_AUDIO_IMC 0x030a0000 +#define BUF_AUDIO_LH 0x030b0000 +#define BUF_AUDIO_VOXWARE 0x030c0000 +#define BUF_AUDIO_ACELPNET 0x030d0000 +#define BUF_AUDIO_AAC 0x030e0000 +#define BUF_AUDIO_DNET 0x030f0000 +#define BUF_AUDIO_VIVOG723 0x03100000 +#define BUF_AUDIO_DK3ADPCM 0x03110000 +#define BUF_AUDIO_DK4ADPCM 0x03120000 +#define BUF_AUDIO_ROQ 0x03130000 +#define BUF_AUDIO_QTIMAADPCM 0x03140000 +#define BUF_AUDIO_MAC3 0x03150000 +#define BUF_AUDIO_MAC6 0x03160000 +#define BUF_AUDIO_QDESIGN1 0x03170000 +#define BUF_AUDIO_QDESIGN2 0x03180000 +#define BUF_AUDIO_QCLP 0x03190000 +#define BUF_AUDIO_SMJPEG_IMA 0x031A0000 +#define BUF_AUDIO_VQA_IMA 0x031B0000 +#define BUF_AUDIO_MULAW 0x031C0000 +#define BUF_AUDIO_ALAW 0x031D0000 +#define BUF_AUDIO_GSM610 0x031E0000 +#define BUF_AUDIO_EA_ADPCM 0x031F0000 +#define BUF_AUDIO_WMAV2 0x03200000 +#define BUF_AUDIO_COOK 0x03210000 +#define BUF_AUDIO_ATRK 0x03220000 +#define BUF_AUDIO_14_4 0x03230000 +#define BUF_AUDIO_28_8 0x03240000 +#define BUF_AUDIO_SIPRO 0x03250000 +#define BUF_AUDIO_WMAV3 0x03260000 +#define BUF_AUDIO_INTERPLAY 0x03270000 +#define BUF_AUDIO_XA_ADPCM 0x03280000 +#define BUF_AUDIO_WESTWOOD 0x03290000 +#define BUF_AUDIO_DIALOGIC_IMA 0x032A0000 +#define BUF_AUDIO_NSF 0x032B0000 +#define BUF_AUDIO_FLAC 0x032C0000 +#define BUF_AUDIO_DV 0x032D0000 +#define BUF_AUDIO_WMAV 0x032E0000 +#define BUF_AUDIO_SPEEX 0x032F0000 +#define BUF_AUDIO_RAWPCM 0x03300000 +#define BUF_AUDIO_4X_ADPCM 0x03310000 +*/ + { 0, NULL } +}; + +const gchar * +gst_xine_get_caps_for_format (guint32 format) +{ + guint i = 0; + + while (_gst_xine_caps_map[i].xine != 0) { + if (_gst_xine_caps_map[i].xine == format) + return _gst_xine_caps_map[i].caps; + i++; + } + + return NULL; +} + +guint32 +gst_xine_get_format_for_caps (const GstCaps *caps) +{ + guint i = 0; + GstCaps *compare, *intersect; + + while (_gst_xine_caps_map[i].xine != 0) { + compare = gst_caps_from_string (_gst_xine_caps_map[i].caps); + intersect = gst_caps_intersect (caps, compare); + gst_caps_free (compare); + if (!gst_caps_is_empty (intersect)) { + gst_caps_free (intersect); + return _gst_xine_caps_map[i].xine; + } + gst_caps_free (intersect); + i++; + } + + return 0; +} + diff --git a/gst/qtdemux/qtdemux.c b/gst/qtdemux/qtdemux.c index 96c1b4f6aa..7cb84f1e3a 100644 --- a/gst/qtdemux/qtdemux.c +++ b/gst/qtdemux/qtdemux.c @@ -153,7 +153,7 @@ static QtNodeType *qtdemux_type_get(guint32 fourcc); static void qtdemux_node_dump(GstQTDemux *qtdemux, GNode *node); static void qtdemux_parse_tree(GstQTDemux *qtdemux); static GstCaps *qtdemux_video_caps(GstQTDemux *qtdemux, guint32 fourcc, const guint8 *stsd_data); -static GstCaps *qtdemux_audio_caps(GstQTDemux *qtdemux, guint32 fourcc); +static GstCaps *qtdemux_audio_caps(GstQTDemux *qtdemux, guint32 fourcc, const guint8 *data); static GType gst_qtdemux_get_type (void) { @@ -284,7 +284,7 @@ static gboolean gst_qtdemux_handle_sink_event (GstQTDemux *qtdemux) gst_pad_event_default(qtdemux->sinkpad, event); return FALSE; case GST_EVENT_FLUSH: - g_warning("flush event"); + //g_warning("flush event"); break; case GST_EVENT_DISCONTINUOUS: GST_DEBUG ("discontinuous event\n"); @@ -780,15 +780,15 @@ static void qtdemux_parse(GstQTDemux *qtdemux, GNode *node, void *buffer, int le QtNodeType *type; void *end; - //g_print("qtdemux_parse %p %d\n",buffer, length); + g_print("qtdemux_parse %p %d\n",buffer, length); node_length = QTDEMUX_GUINT32_GET(buffer); fourcc = QTDEMUX_FOURCC_GET(buffer+4); type = qtdemux_type_get(fourcc); - /*g_print("parsing '" GST_FOURCC_FORMAT "', length=%d\n", - GST_FOURCC_ARGS(fourcc), node_length);*/ + g_print("parsing '" GST_FOURCC_FORMAT "', length=%d\n", + GST_FOURCC_ARGS(fourcc), node_length); if(type->flags & QT_CONTAINER){ void *buf; @@ -1301,20 +1301,22 @@ static void qtdemux_parse_trak(GstQTDemux *qtdemux, GNode *trak) g_print("sample rate: %g\n", QTDEMUX_FP32_GET(stsd->data+offset + 16)); stream->rate = QTDEMUX_FP32_GET(stsd->data+offset + 16); + offset = 52; if(version == 0x00010000){ - g_print("samples/packet: %d\n", QTDEMUX_GUINT32_GET(stsd->data+offset + 20)); - stream->samples_per_packet = QTDEMUX_GUINT32_GET(stsd->data+offset + 20); - g_print("bytes/packet: %d\n", QTDEMUX_GUINT32_GET(stsd->data+offset + 24)); - g_print("bytes/frame: %d\n", QTDEMUX_GUINT32_GET(stsd->data+offset + 28)); - stream->bytes_per_frame = QTDEMUX_GUINT32_GET(stsd->data+offset + 28); - g_print("bytes/sample: %d\n", QTDEMUX_GUINT32_GET(stsd->data+offset + 32)); + g_print("samples/packet: %d\n", QTDEMUX_GUINT32_GET(stsd->data+offset)); + stream->samples_per_packet = QTDEMUX_GUINT32_GET(stsd->data+offset); + g_print("bytes/packet: %d\n", QTDEMUX_GUINT32_GET(stsd->data+offset + 4)); + g_print("bytes/frame: %d\n", QTDEMUX_GUINT32_GET(stsd->data+offset + 8)); + stream->bytes_per_frame = QTDEMUX_GUINT32_GET(stsd->data+offset + 8); + g_print("bytes/sample: %d\n", QTDEMUX_GUINT32_GET(stsd->data+offset + 12)); + offset = 68; } else { stream->bytes_per_frame = stream->n_channels * QTDEMUX_GUINT16_GET(stsd->data+offset + 10); stream->samples_per_packet = 1; } stream->caps = qtdemux_audio_caps(qtdemux, - QTDEMUX_FOURCC_GET(stsd->data+16+4)); + QTDEMUX_FOURCC_GET(stsd->data+16+4), (QTDEMUX_GUINT32_GET(stsd->data) > offset) ? stsd->data + offset : NULL); g_print("caps %s\n",gst_caps_to_string(stream->caps)); }else{ g_print("unknown subtype\n"); @@ -1597,7 +1599,7 @@ static GstCaps *qtdemux_video_caps(GstQTDemux *qtdemux, guint32 fourcc, const gu } } -static GstCaps *qtdemux_audio_caps(GstQTDemux *qtdemux, guint32 fourcc) +static GstCaps *qtdemux_audio_caps(GstQTDemux *qtdemux, guint32 fourcc, const guint8 *data) { switch(fourcc){ case GST_MAKE_FOURCC('N','O','N','E'): @@ -1685,8 +1687,15 @@ static GstCaps *qtdemux_audio_caps(GstQTDemux *qtdemux, guint32 fourcc) return gst_caps_from_string ("audio/mpeg, " "mpegversion = (int) 4"); case GST_MAKE_FOURCC('Q','D','M','2'): - /* QDesign music version 2 (no constant) */ - return gst_caps_from_string ("audio/x-qdm2"); + /* FIXME: QDesign music version 2 (no constant) */ + if (QTDEMUX_GUINT32_GET (data) <= 100) { + gst_util_dump_mem ((guint8*)data, 100); + return gst_caps_new_simple ("audio/x-qdm2", + "framesize", G_TYPE_INT, QTDEMUX_GUINT32_GET (data + 52), + "bitrate", G_TYPE_INT, QTDEMUX_GUINT32_GET (data + 40), + "blocksize", G_TYPE_INT, QTDEMUX_GUINT32_GET (data + 44), + NULL); + } case GST_MAKE_FOURCC('q','t','v','r'): /* ? */ case GST_MAKE_FOURCC('Q','D','M','C'):