From 788acc4917c03a2e09891cc4d5c58f8b63dce42c Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Mon, 24 Nov 2003 04:08:48 +0000 Subject: [PATCH] tagging stuff and build fixes. In detail: Original commit message from CVS: tagging stuff and build fixes. In detail: - make gdk-pixbuf loader work when distchecking - fix invalid syntax in ffmpeg Makefile. wildcards for EXTRA_DIST are not allowed. This broke builds where distdir != srcdir - fix ffmpeg cvs grabbing when srcdir != distdir - new id3tag plugin for id3 tag reading/writing (uses mad's libid3tag) - mad and libid3tag require mad/libid3tag v0.15. Fixed configure to require that - added ogg demuxer in ext/ogg. The demuxer does not handle events yet. Especially getting seeking right will require some effort or code copying from libvorbis. - added raw vorbis detection to typefinding. oggdemux requires a typefind function to detect its contents. - tags plugin in gst/tags. Provides API in . API includes tag matching GStreamer <=> ID3 and GStreamer <=> vorbis and writing/reading vorbiscomments or ID3v1 tags. Also included is a simple vorbiscomment reader/writer. Writing will not really work though until someone writes oggmux. - various build fixes. Mostly missing (DIST)CLEANFILES. - vorbisenc handles tag writing. Now it's YOUR turn to fix and write more plugins that handle writing/reading of tags. :) --- configure.ac | 24 +- ext/Makefile.am | 8 + ext/ogg/Makefile.am | 9 + ext/ogg/gstoggdemux.c | 587 ++++++++++++++++++++++++++++++++++++ ext/vorbis/Makefile.am | 4 +- ext/vorbis/vorbisenc.c | 203 ++++++++----- ext/vorbis/vorbisenc.h | 4 +- gst-libs/ext/ffmpeg/Makefile.am | 131 +++++--- gst/tags/Makefile.am | 13 + gst/tags/gstmp3tag.c | 331 ++++++++++++++++++++ gst/tags/gsttagediting.c | 52 ++++ gst/tags/gsttagediting.h | 55 ++++ gst/tags/gsttageditingprivate.h | 41 +++ gst/tags/gstvorbistag.c | 563 ++++++++++++++++++++++++++++++++++ gst/typefind/gsttypefindfunctions.c | 37 +++ m4/as-slurp-ffmpeg.m4 | 5 +- m4/ogg.m4 | 102 +++++++ pkgconfig/Makefile.am | 2 +- tests/old/testsuite/alsa/.gitignore | 1 + testsuite/alsa/.gitignore | 1 + testsuite/autoplug/.gitignore | 4 + 21 files changed, 2047 insertions(+), 130 deletions(-) create mode 100644 ext/ogg/Makefile.am create mode 100644 ext/ogg/gstoggdemux.c create mode 100644 gst/tags/Makefile.am create mode 100644 gst/tags/gstmp3tag.c create mode 100644 gst/tags/gsttagediting.c create mode 100644 gst/tags/gsttagediting.h create mode 100644 gst/tags/gsttageditingprivate.h create mode 100644 gst/tags/gstvorbistag.c create mode 100644 m4/ogg.m4 create mode 100644 testsuite/autoplug/.gitignore diff --git a/configure.ac b/configure.ac index b1070ff..6889940 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ AM_MAINTAINER_MODE dnl when going to/from release please set the nano (fourth number) right ! dnl releases only do Wall, cvs and prerelease does Werror too -AS_VERSION(gst-plugins, GST_PLUGINS_VERSION, 0, 7, 1, 1, GST_CVS="no", GST_CVS="yes") +AS_VERSION(gst-plugins, GST_PLUGINS_VERSION, 0, 7, 2, 1, GST_CVS="no", GST_CVS="yes") dnl add a suffix to apps if test x$program_suffix = xNONE ; then @@ -189,7 +189,6 @@ fi AC_SUBST(GST_CONTROL_LIBS) - dnl Set up conditionals for (target) architecture: dnl ============================================== @@ -241,6 +240,8 @@ if test "x$HAVE_GTK_22" = "xyes"; then AC_SUBST(GTK_VERSION) GTK_PREFIX=`$PKG_CONFIG --variable=prefix gdk-pixbuf-2.0` GTK_SYSCONFDIR=`$PKG_CONFIG --variable=prefix gdk-pixbuf-2.0` + GDK_PIXBUF_LIBDIR=`$PKG_CONFIG --variable=libdir gdk-pixbuf-2.0` + GDK_PIXBUF_PREFIXDIR=`$PKG_CONFIG --variable=prefix gdk-pixbuf-2.0` AC_SUBST(GTK_BASE_DIR) else PKG_CHECK_MODULES(GTK2, gtk+-2.0, HAVE_GTK_20=yes, HAVE_GTK_20=no) @@ -255,7 +256,7 @@ AC_SUBST(GTK_CFLAGS) AC_SUBST(HAVE_GTK) AM_CONDITIONAL(HAVE_GDK_LOADERS, test "x$HAVE_GTK_22" = "xyes") -GDK_PIXBUF_LOADER_DIR="\$(libdir)/gtk-2.0/\$(GTK_VERSION)/\loaders" +GDK_PIXBUF_LOADER_DIR="$GDK_PIXBUF_LIBDIR/gtk-2.0/\$(GTK_VERSION)/loaders" AC_ARG_WITH(gdk-pixbuf-loader-dir, AC_HELP_STRING([--with-gdk-pixbuf-loader-dir], [directory to install the gdk_pixbuf loader]), @@ -265,7 +266,7 @@ AC_ARG_WITH(gdk-pixbuf-loader-dir, ]) AC_SUBST(GDK_PIXBUF_LOADER_DIR) -GDK_PIXBUF_CONFFILE="\$(sysconfdir)/gtk-2.0/gdk-pixbuf.loaders" +GDK_PIXBUF_CONFFILE="$GDK_PIXBUF_PREFIXDIR/gtk-2.0/gdk-pixbuf.loaders" AC_ARG_WITH(gdk-pixbuf-conffile, AC_HELP_STRING([--with-gdk-pixbuf-conffile], [path to the gdk_pixbuf config file]), @@ -334,6 +335,7 @@ GST_PLUGINS_ALL="\ speed \ stereo \ synaesthesia \ + tags \ tcp \ typefind \ udp \ @@ -970,16 +972,15 @@ dnl FIXME: we could use header checks here as well IMO translit(dnm, m, l) AM_CONDITIONAL(USE_MAD, true) GST_CHECK_FEATURE(MAD, [mad mp3 decoder], mad, [ dnl check with pkg-config first - PKG_CHECK_MODULES(MAD, mad id3tag, HAVE_MAD="yes", HAVE_MAD="no") + PKG_CHECK_MODULES(MAD, mad >= 0.15 id3tag >= 0.15, HAVE_MAD="yes", HAVE_MAD="no") if test "x$HAVE_MAD" = "xno"; then dnl fall back to oldskool detection AC_CHECK_LIB(mad, mad_decoder_finish, HAVE_MAD="yes" MAD_LIBS="-lmad") if test "x$HAVE_MAD" = "xyes"; then - # installed with mad >= 0.14 HAVE_MAD="no" save_libs=$LIBS LIBS="-lz" - AC_CHECK_LIB(id3tag, id3_tag_query, HAVE_MAD="yes" MAD_LIBS="$MAD_LIBS -lid3tag") + AC_CHECK_LIB(id3tag, id3_tag_options, HAVE_MAD="yes" MAD_LIBS="-lmad -lid3tag -lz") LIBS=$save_LIBS fi fi @@ -1096,6 +1097,13 @@ GST_CHECK_FEATURE(TARKIN, [tarkinenc tarkindec], tarkin, [ HAVE_TARKIN="yes" ]) +dnl *** ogg *** +translit(dnm, m, l) AM_CONDITIONAL(USE_OGG, true) +GST_CHECK_FEATURE(OGG, [ogg de/encoder], oggdemux oggmux, [ + XIPH_PATH_OGG(HAVE_OGG=yes, HAVE_OGG=no) + AS_SCRUB_INCLUDE(OGG_CFLAGS) +]) + dnl *** vorbis *** dnl AM_PATH_VORBIS only takes two options translit(dnm, m, l) AM_CONDITIONAL(USE_VORBIS, true) @@ -1365,6 +1373,7 @@ gst/spectrum/Makefile gst/speed/Makefile gst/stereo/Makefile gst/synaesthesia/Makefile +gst/tags/Makefile gst/tcp/Makefile gst/typefind/Makefile gst/udp/Makefile @@ -1427,6 +1436,7 @@ ext/mas/Makefile ext/mikmod/Makefile ext/mpeg2dec/Makefile ext/mplex/Makefile +ext/ogg/Makefile ext/pango/Makefile ext/raw1394/Makefile ext/sdl/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index d70a37e..a26660d 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -196,6 +196,12 @@ else MPLEX_DIR= endif +if USE_OGG +OGG_DIR=ogg +else +OGG_DIR= +endif + if USE_PANGO PANGO_DIR=pango else @@ -313,6 +319,7 @@ SUBDIRS=\ $(MIKMOD_DIR) \ $(MPEG2DEC_DIR) \ $(MPLEX_DIR) \ + $(OGG_DIR) \ $(PANGO_DIR) \ $(RAW1394_DIR) \ $(SDL_DIR) \ @@ -361,6 +368,7 @@ DIST_SUBDIRS=\ mikmod \ mpeg2dec \ mplex \ + ogg \ pango \ raw1394 \ sdl \ diff --git a/ext/ogg/Makefile.am b/ext/ogg/Makefile.am new file mode 100644 index 0000000..e0230ec --- /dev/null +++ b/ext/ogg/Makefile.am @@ -0,0 +1,9 @@ +plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ + +plugin_LTLIBRARIES = libgstogg.la + +libgstogg_la_SOURCES = gstoggdemux.c +libgstogg_la_CFLAGS = $(GST_CFLAGS) $(OGG_CFLAGS) +libgstogg_la_LIBADD = $(OGG_LIBS) +libgstogg_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c new file mode 100644 index 0000000..067d4de --- /dev/null +++ b/ext/ogg/gstoggdemux.c @@ -0,0 +1,587 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * gstid3tagsetter.c: plugin for reading / modifying id3 tags + * + * 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 +#include +/* memcpy - if someone knows a way to get rid of it, please speak up */ +#include + +GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug); +#define GST_CAT_DEFAULT gst_ogg_demux_debug + +#define GST_TYPE_OGG_DEMUX (gst_ogg_demux_get_type()) +#define GST_OGG_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_DEMUX, GstOggDemux)) +#define GST_OGG_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_DEMUX, GstOggDemux)) +#define GST_IS_OGG_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_DEMUX)) +#define GST_IS_OGG_DEMUX_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_DEMUX)) + +typedef struct _GstOggDemux GstOggDemux; +typedef struct _GstOggDemuxClass GstOggDemuxClass; + +typedef struct { + GstPad * pad; /* reference for this pad is held by element we belong to */ + + int serial; + ogg_stream_state stream; + guint64 offset; /* end offset of last buffer */ +} GstOggPad; + +struct _GstOggDemux { + GstElement element; + + /* pads */ + GstPad * sinkpad; + GSList * srcpads; /* list of GstOggPad */ + + /* ogg stuff */ + ogg_sync_state sync; +}; + +struct _GstOggDemuxClass { + GstElementClass parent_class; +}; + +/* elementfactory information */ +static GstElementDetails gst_ogg_demux_details = GST_ELEMENT_DETAILS ( + "ogg demuxer", + "Codec/Demuxer", + "demux ogg streams (info about ogg: http://xiph.org)", + "Benjamin Otte " +); + + +/* signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + /* FILL ME */ +}; + +GST_PAD_TEMPLATE_FACTORY (ogg_demux_src_template_factory, + "src", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + NULL +) +GST_PAD_TEMPLATE_FACTORY (ogg_demux_sink_template_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "ogg_demux_sink", + "application/ogg", + NULL + ) +) + + +static void gst_ogg_demux_base_init (gpointer g_class); +static void gst_ogg_demux_class_init (gpointer g_class, + gpointer class_data); +static void gst_ogg_demux_init (GTypeInstance * instance, + gpointer g_class); +static void gst_ogg_demux_dispose (GObject * object); + +static gboolean gst_ogg_demux_src_event (GstPad * pad, + GstEvent * event); +static const GstEventMask* gst_ogg_demux_get_event_masks (GstPad * pad); +static const GstQueryType* gst_ogg_demux_get_query_types (GstPad * pad); + +static gboolean gst_ogg_demux_src_query (GstPad * pad, + GstQueryType type, + GstFormat * format, + gint64 * value); + +static void gst_ogg_demux_chain (GstPad * pad, + GstData * buffer); + +static GstElementStateReturn gst_ogg_demux_change_state (GstElement * element); + +static GstOggPad * gst_ogg_pad_new (GstOggDemux * ogg, + int serial_no); +static void gst_ogg_pad_remove (GstOggDemux * ogg, + GstOggPad * ogg_pad); +static void gst_ogg_pad_reset (GstOggDemux * ogg, + GstOggPad * pad); +static void gst_ogg_demux_push (GstOggDemux * ogg, + ogg_page * page); +static void gst_ogg_pad_push (GstOggDemux * ogg, + GstOggPad * ogg_pad); + +static GstCaps * gst_ogg_type_find (ogg_packet * packet); + + +static GstElementClass *parent_class = NULL; +/* static guint gst_ogg_demux_signals[LAST_SIGNAL] = { 0 }; */ + +GType +gst_ogg_demux_get_type (void) +{ + static GType ogg_demux_type = 0; + + if (!ogg_demux_type) { + static const GTypeInfo ogg_demux_info = { + sizeof (GstOggDemuxClass), + gst_ogg_demux_base_init, + NULL, + gst_ogg_demux_class_init, + NULL, + NULL, + sizeof (GstOggDemux), + 0, + gst_ogg_demux_init, + }; + + ogg_demux_type = g_type_register_static(GST_TYPE_ELEMENT, "GstOggDemux", &ogg_demux_info, 0); + } + return ogg_demux_type; +} + +static void +gst_ogg_demux_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details (element_class, &gst_ogg_demux_details); + + gst_element_class_add_pad_template (element_class, + GST_PAD_TEMPLATE_GET (ogg_demux_sink_template_factory)); + gst_element_class_add_pad_template (element_class, + GST_PAD_TEMPLATE_GET (ogg_demux_src_template_factory)); +} +static void +gst_ogg_demux_class_init (gpointer g_class, gpointer class_data) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); + GObjectClass *gobject_class = G_OBJECT_CLASS (g_class); + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + gstelement_class->change_state = gst_ogg_demux_change_state; + + gobject_class->dispose = gst_ogg_demux_dispose; +} +static void +gst_ogg_demux_init (GTypeInstance *instance, gpointer g_class) +{ + GstOggDemux *ogg = GST_OGG_DEMUX (instance); + + /* create the sink pad */ + ogg->sinkpad = gst_pad_new_from_template( + GST_PAD_TEMPLATE_GET (ogg_demux_sink_template_factory), "sink"); + gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad); + gst_pad_set_chain_function (ogg->sinkpad, GST_DEBUG_FUNCPTR (gst_ogg_demux_chain)); + + /* initalize variables */ + ogg->srcpads = NULL; + + GST_FLAG_SET (ogg, GST_ELEMENT_EVENT_AWARE); +} +static void +gst_ogg_demux_dispose (GObject *object) +{ + GstOggDemux *ogg; + + ogg = GST_OGG_DEMUX (object); + + /* srcpads are removed when going to READY */ + g_assert (ogg->srcpads == NULL); +} + +static const GstEventMask* +gst_ogg_demux_get_event_masks (GstPad *pad) +{ + static const GstEventMask gst_ogg_demux_src_event_masks[] = { + { GST_EVENT_SEEK, GST_SEEK_METHOD_SET | + GST_SEEK_FLAG_FLUSH }, + { 0, } + }; + return gst_ogg_demux_src_event_masks; +} +static const GstQueryType* +gst_ogg_demux_get_query_types (GstPad *pad) +{ + static const GstQueryType gst_ogg_demux_src_query_types[] = { + GST_QUERY_TOTAL, + GST_QUERY_POSITION, + 0 + }; + return gst_ogg_demux_src_query_types; +} + +static gboolean +gst_ogg_demux_src_query (GstPad *pad, GstQueryType type, + GstFormat *format, gint64 *value) +{ + gboolean res = FALSE; + GstOggDemux *ogg; + + ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); + + switch (type) { + case GST_QUERY_TOTAL: { + break; + } + case GST_QUERY_POSITION: + break; + default: + break; + } + return res; +} + +static gboolean +gst_ogg_demux_src_event (GstPad *pad, GstEvent *event) +{ + GstOggDemux *ogg; + + ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + break; + default: + break; + } + + gst_event_unref (event); + return FALSE; +} +static void +gst_ogg_demux_handle_event (GstPad *pad, GstEvent *event) +{ + GstOggDemux *ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); + + ogg=ogg; + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_DISCONTINUOUS: + break; + case GST_EVENT_EOS: + if (ogg->srcpads) + GST_WARNING_OBJECT (ogg, "got EOS in unfinished ogg stream"); + while (ogg->srcpads) { + gst_ogg_pad_remove (ogg, (GstOggPad *) ogg->srcpads->data); + } + gst_element_set_eos (GST_ELEMENT (ogg)); + break; + default: + gst_pad_event_default (pad, event); + break; + } + return; +} +static void +gst_ogg_demux_chain (GstPad *pad, GstData *buffer) +{ + GstOggDemux *ogg; + guint8 *data; + int pageout_ret = 1; + + /* handle events */ + if (GST_IS_EVENT (buffer)) { + gst_ogg_demux_handle_event (pad, GST_EVENT (buffer)); + return; + } + + ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad)); + + data = (guint8 *) ogg_sync_buffer(&ogg->sync, GST_BUFFER_SIZE (buffer)); + memcpy (data, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer)); + if (ogg_sync_wrote (&ogg->sync, GST_BUFFER_SIZE (buffer)) != 0) { + gst_data_unref (buffer); + gst_element_error (GST_ELEMENT (ogg), "ogg_sync_wrote failed"); + return; + } + gst_data_unref (buffer); + while (pageout_ret != 0) { + ogg_page page; + + pageout_ret = ogg_sync_pageout (&ogg->sync, &page); + switch (pageout_ret) { + case -1: + /* FIXME: need some kind of discont here, we don't know any values */ + break; + case 0: + break; + case 1: + gst_ogg_demux_push (ogg, &page); + break; + default: + GST_WARNING_OBJECT (ogg, "unknown return value %d from ogg_sync_pageout", pageout_ret); + pageout_ret = 0; + break; + } + } + return; +} +static GstOggPad * +gst_ogg_pad_new (GstOggDemux *ogg, int serial) +{ + GstOggPad *ret = g_new (GstOggPad, 1); + + ret->serial = serial; + if (ogg_stream_init (&ret->stream, serial) != 0) { + GST_ERROR_OBJECT (ogg, "Could not initialize ogg_stream struct for serial %d.", serial); + g_free (ret); + return NULL; + } + ret->pad = NULL; + ret->offset = 0; + GST_LOG_OBJECT (ogg, "created new ogg src %p for stream with serial %d", ret, serial); + + return ret; +} +static void +gst_ogg_pad_remove (GstOggDemux *ogg, GstOggPad *pad) +{ + ogg->srcpads = g_slist_remove (ogg->srcpads, pad); + if (pad->pad) { + gst_pad_push (pad->pad, GST_DATA (gst_event_new (GST_EVENT_EOS))); + gst_element_remove_pad (GST_ELEMENT (ogg), pad->pad); + } + if (ogg_stream_clear (&pad->stream) != 0) + GST_ERROR_OBJECT (ogg, "ogg_stream_clear (serial %d) did not return 0, ignoring this error", pad->serial); + GST_LOG_OBJECT (ogg, "free ogg src %p for stream with serial %d", pad, pad->serial); + g_free (pad); +} +static void +gst_ogg_demux_push (GstOggDemux *ogg, ogg_page* page) +{ + GSList *walk;; + GstOggPad *cur; + + /* find the stream */ + walk = ogg->srcpads; + while (walk) { + cur = (GstOggPad *) walk->data; + if (cur->serial == ogg_page_serialno (page)) { + goto br; + } + walk = g_slist_next (walk); + } + cur = NULL; +br: + /* now we either have a stream (cur) or not */ + if (ogg_page_bos (page)) { + if (cur) { + /* huh? */ + GST_WARNING_OBJECT (ogg, "Ignoring error: ogg page declared as BOS while stream already existed."); + } else { + /* FIXME: monitor if we are still in creation stage? */ + cur = gst_ogg_pad_new (ogg, ogg_page_serialno (page)); + if (!cur) { + gst_element_error (GST_ELEMENT (ogg), "Creating ogg_stream struct failed."); + } + ogg->srcpads = g_slist_prepend (ogg->srcpads, cur); + } + } + if (cur == NULL) { + gst_element_error (GST_ELEMENT (ogg), "invalid ogg stream serial no"); + return; + } + if (ogg_stream_pagein (&cur->stream, page) != 0) { + GST_WARNING_OBJECT (ogg, "ogg stream choked on page (serial %d), resetting stream", cur->serial); + gst_ogg_pad_reset (ogg, cur); + return; + } + gst_ogg_pad_push (ogg, cur); +#if 0 + /* Removing pads while PLAYING doesn't work with current schedulers */ + /* remove from list, as this will never be called again */ + if (ogg_page_eos (page)) { + gst_ogg_pad_remove (ogg, cur); + } +#endif +} +static void +gst_ogg_pad_push (GstOggDemux *ogg, GstOggPad *pad) +{ + ogg_packet packet; + int ret; + + while (TRUE) { + ret = ogg_stream_packetout (&pad->stream, &packet); + switch (ret) { + case 0: + return; + case -1: + gst_ogg_pad_reset (ogg, pad); + break; + case 1: { + if (!pad->pad) { + GstCaps *caps = gst_ogg_type_find (&packet); + gchar *name = g_strdup_printf ("serial %d", pad->serial); + + pad->pad = gst_pad_new_from_template (ogg_demux_src_template_factory(), name); + g_free (name); + gst_pad_set_event_function (pad->pad, GST_DEBUG_FUNCPTR (gst_ogg_demux_src_event)); + gst_pad_set_event_mask_function (pad->pad, GST_DEBUG_FUNCPTR (gst_ogg_demux_get_event_masks)); + gst_pad_set_query_function (pad->pad, GST_DEBUG_FUNCPTR (gst_ogg_demux_src_query)); + gst_pad_set_query_type_function (pad->pad, GST_DEBUG_FUNCPTR (gst_ogg_demux_get_query_types)); + if (caps) + g_assert (gst_pad_try_set_caps (pad->pad, caps) >= GST_PAD_LINK_OK); + gst_pad_set_active (pad->pad, TRUE); + gst_element_add_pad (GST_ELEMENT (ogg), pad->pad); + } + /* optimization: use a bufferpool containing the ogg packet */ + GstBuffer *buf = gst_buffer_new_and_alloc (packet.bytes); + memcpy (buf->data, packet.packet, packet.bytes); + GST_BUFFER_OFFSET (buf) = pad->offset; + pad->offset = GST_BUFFER_OFFSET_END (buf) = packet.granulepos; + gst_pad_push (pad->pad, GST_DATA (buf)); + break; + } + default: + GST_ERROR_OBJECT (ogg, "invalid return value %d for ogg_stream_packetout, resetting stream", ret); + gst_ogg_pad_reset (ogg, pad); + break; + } + } +} +static void +gst_ogg_pad_reset (GstOggDemux *ogg, GstOggPad *pad) +{ + ogg_stream_reset (&pad->stream); + pad->offset = GST_BUFFER_OFFSET_NONE; + /* FIXME: need a discont here */ +} +static GstElementStateReturn +gst_ogg_demux_change_state (GstElement *element) +{ + GstOggDemux *ogg; + + ogg = GST_OGG_DEMUX (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_NULL_TO_READY: + ogg_sync_init (&ogg->sync); + break; + case GST_STATE_READY_TO_PAUSED: + ogg_sync_reset (&ogg->sync); + break; + case GST_STATE_PAUSED_TO_PLAYING: + break; + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + while (ogg->srcpads) { + gst_ogg_pad_remove (ogg, (GstOggPad *) ogg->srcpads->data); + } + break; + case GST_STATE_READY_TO_NULL: + ogg_sync_clear (&ogg->sync); + break; + default: + g_assert_not_reached (); + break; + } + + return parent_class->change_state (element); +} + +/*** typefinding **************************************************************/ +/* ogg supports its own typefinding because the ogg spec defines that the first + * packet of an ogg stream must identify the stream. Therefore ogg can use a + * simplified approach at typefinding. + */ +typedef struct { + ogg_packet * packet; + guint best_probability; + GstCaps * caps; +} OggTypeFind; +static guint8 * +ogg_find_peek (gpointer data, gint64 offset, guint size) +{ + OggTypeFind *find = (OggTypeFind *) data; + + if (offset + size <= find->packet->bytes) { + return ((guint8 *) find->packet->packet) + offset; + } else { + return NULL; + } +} +static void +ogg_find_suggest (gpointer data, guint probability, GstCaps *caps) +{ + OggTypeFind *find = (OggTypeFind *) data; + + if (probability > find->best_probability) { + gst_caps_replace_sink (&find->caps, caps); + find->best_probability = probability; + } +} +static GstCaps * +gst_ogg_type_find (ogg_packet *packet) +{ + GstTypeFind gst_find; + OggTypeFind find; + GList *walk, *type_list = NULL; + + walk = type_list = gst_type_find_factory_get_list (); + + find.packet = packet; + find.best_probability = 0; + find.caps = NULL; + gst_find.data = &find; + gst_find.peek = ogg_find_peek; + gst_find.suggest = ogg_find_suggest; + + while (walk) { + GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (walk->data); + + gst_type_find_factory_call_function (factory, &gst_find); + if (find.best_probability >= GST_TYPE_FIND_MAXIMUM) + break; + walk = g_list_next (walk); + } + + if (find.best_probability > 0) + return find.caps; + + return NULL; +} + +static gboolean +plugin_init (GstPlugin *plugin) +{ + GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer"); + + return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY, GST_TYPE_OGG_DEMUX); +} +GST_PLUGIN_DEFINE ( + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "ogg", + "ogg stream manipulation (info about ogg: http://xiph.org)", + plugin_init, + VERSION, + GST_LICENSE, + GST_COPYRIGHT, + GST_PACKAGE, + GST_ORIGIN +) diff --git a/ext/vorbis/Makefile.am b/ext/vorbis/Makefile.am index 11e1b53..da2d134 100644 --- a/ext/vorbis/Makefile.am +++ b/ext/vorbis/Makefile.am @@ -2,9 +2,9 @@ plugin_LTLIBRARIES = libgstvorbis.la libgstvorbis_la_SOURCES = vorbis.c vorbisenc.c vorbisfile.c -libgstvorbis_la_CFLAGS = $(GST_CFLAGS) $(VORBIS_CFLAGS) +libgstvorbis_la_CFLAGS = $(GST_CFLAGS) $(VORBIS_CFLAGS) ## AM_PATH_VORBIS also sets VORBISENC_LIBS -libgstvorbis_la_LIBADD = $(VORBIS_LIBS) $(VORBISENC_LIBS) $(VORBISFILE_LIBS) +libgstvorbis_la_LIBADD = $(VORBIS_LIBS) $(VORBISENC_LIBS) $(VORBISFILE_LIBS) libgstvorbis_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) noinst_HEADERS = vorbisenc.h diff --git a/ext/vorbis/vorbisenc.c b/ext/vorbis/vorbisenc.c index f4f576b..9b37db3 100644 --- a/ext/vorbis/vorbisenc.c +++ b/ext/vorbis/vorbisenc.c @@ -26,6 +26,7 @@ #include #include +#include #include "vorbisenc.h" static GstPadTemplate *gst_vorbisenc_src_template, *gst_vorbisenc_sink_template; @@ -116,8 +117,15 @@ vorbisenc_get_type (void) 0, (GInstanceInitFunc) gst_vorbisenc_init, }; - + static const GInterfaceInfo tag_setter_info = { + NULL, + NULL, + NULL + }; + vorbisenc_type = g_type_register_static (GST_TYPE_ELEMENT, "VorbisEnc", &vorbisenc_info, 0); + + g_type_add_interface_static (vorbisenc_type, GST_TYPE_TAG_SETTER, &tag_setter_info); } return vorbisenc_type; } @@ -197,9 +205,6 @@ gst_vorbisenc_class_init (VorbisEncClass * klass) g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SERIAL, g_param_spec_int ("serial", "Serial", "Specify a serial number for the stream. (-1 is random)", -1, G_MAXINT, -1, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_METADATA, - g_param_spec_boxed ("metadata", "Metadata", "Metadata to add to the stream,", - GST_TYPE_CAPS, G_PARAM_READWRITE)); g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MANAGED, g_param_spec_boolean ("managed", "Managed", "Enable bitrate management engine", FALSE, G_PARAM_READWRITE)); @@ -458,52 +463,106 @@ gst_vorbisenc_init (VorbisEnc * vorbisenc) vorbisenc->setup = FALSE; vorbisenc->eos = FALSE; + vorbisenc->header_sent = FALSE; - vorbisenc->metadata = GST_CAPS_NEW ( - "vorbisenc_metadata", - "application/x-gst-metadata", - "DESCRIPTION", GST_PROPS_STRING ("Track encoded with GStreamer"), - "DATE", GST_PROPS_STRING (""), - "TRACKNUMBER", GST_PROPS_STRING (""), - "TITLE", GST_PROPS_STRING (""), - "ARTIST", GST_PROPS_STRING (""), - "ALBUM", GST_PROPS_STRING (""), - "GENRE", GST_PROPS_STRING ("") - ); - - + vorbisenc->tags = gst_tag_list_new (); + /* we're chained and we can deal with events */ GST_FLAG_SET (vorbisenc, GST_ELEMENT_EVENT_AWARE); } -static void -gst_vorbisenc_add_metadata (VorbisEnc *vorbisenc, GstCaps *caps) +static void +gst_vorbisenc_metadata_set1 (const GstTagList *list, const gchar *tag, gpointer vorbisenc) { - GList *props; - GstPropsEntry *prop; + gchar *vorbistag, *vorbisvalue; + guint i, count; + VorbisEnc *enc = GST_VORBISENC (vorbisenc); + + count = gst_tag_list_get_tag_size (list, tag); + for (i = 0; i < count; i++) { + /* get tag name right */ + if (strcmp (tag, GST_TAG_TITLE) == 0) { + vorbistag = g_strdup ("TITLE"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else if (strcmp (tag, GST_TAG_VERSION) == 0) { + vorbistag = g_strdup ("VERSION"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else if (strcmp (tag, GST_TAG_ALBUM) == 0) { + vorbistag = g_strdup ("ALBUM"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else if (strcmp (tag, GST_TAG_TRACK_NUMBER) == 0) { + vorbistag = g_strdup ("TRACKNUMBER"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else if (strcmp (tag, GST_TAG_ARTIST) == 0) { + vorbistag = g_strdup ("ARTIST"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else if (strcmp (tag, GST_TAG_PERFORMER) == 0) { + vorbistag = g_strdup ("PERFORMER"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else if (strcmp (tag, GST_TAG_COPYRIGHT) == 0) { + vorbistag = g_strdup ("COPYRIGHT"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else if (strcmp (tag, GST_TAG_LICENSE) == 0) { + vorbistag = g_strdup ("LICENSE"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else if (strcmp (tag, GST_TAG_ORGANIZATION) == 0) { + vorbistag = g_strdup ("ORGANIZATION"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else if (strcmp (tag, GST_TAG_DESCRIPTION) == 0) { + vorbistag = g_strdup ("DESCRIPTION"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else if (strcmp (tag, GST_TAG_GENRE) == 0) { + vorbistag = g_strdup ("GENRE"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else if (strcmp (tag, GST_TAG_DATE) == 0) { + /* FIXME: how are dates represented in vorbis files? */ + GDate *date; + guint u; + + vorbistag = g_strdup ("DATE"); + g_assert (gst_tag_list_get_uint_index (list, tag, i, &u)); + date = g_date_new_julian (u); + vorbisvalue = g_strdup_printf ("%04d-%02d-%02d", (gint) g_date_get_year (date), + (gint) g_date_get_month (date), (gint) g_date_get_day (date)); + g_date_free (date); + /* NOTE: GST_TAG_LOCATION != vorbis' location + } else if (strcmp (tag, PLACE) == 0) { + vorbistag = g_strdup ("LOCATION"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + */ + } else if (strcmp (tag, GST_TAG_CONTACT) == 0) { + vorbistag = g_strdup ("CONTACT"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else if (strcmp (tag, GST_TAG_ISRC) == 0) { + vorbistag = g_strdup ("ISRC"); + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else { + vorbistag = g_ascii_strup (tag, -1); + if (gst_tag_get_type (tag) == G_TYPE_STRING) { + g_assert (gst_tag_list_get_string_index (list, tag, i, &vorbisvalue)); + } else { + const GValue *value = gst_tag_list_get_value_index (list, tag, i); + vorbisvalue = g_strdup_value_contents (value); + } + } + } - if (caps == NULL) + vorbis_comment_add_tag (&enc->vc, vorbistag, vorbisvalue); +} +static void +gst_vorbisenc_set_metadata (VorbisEnc *vorbisenc) +{ + GstTagList *copy; + const GstTagList *user_tags; + + user_tags = gst_tag_setter_get_list (GST_TAG_SETTER (vorbisenc)); + if (!(vorbisenc->tags || user_tags)) return; + copy = gst_tag_list_merge (user_tags, vorbisenc->tags, gst_tag_setter_get_merge_mode (GST_TAG_SETTER (vorbisenc))); vorbis_comment_init (&vorbisenc->vc); - - props = gst_caps_get_props (caps)->properties; - while (props) { - prop = (GstPropsEntry*)(props->data); - props = g_list_next(props); - - if (gst_props_entry_get_props_type (prop) == GST_PROPS_STRING_TYPE) { - const gchar *name = gst_props_entry_get_name (prop); - const gchar *value; - - gst_props_entry_get_string (prop, &value); - - if (!value || strlen (value) == 0) - continue; - - vorbis_comment_add_tag (&vorbisenc->vc, g_strdup (name), g_strdup (value)); - } - } + gst_tag_list_foreach (copy, gst_vorbisenc_metadata_set1, vorbisenc); + gst_tag_list_free (copy); } static gchar* @@ -635,8 +694,6 @@ gst_vorbisenc_setup (VorbisEnc *vorbisenc) } vorbis_encode_setup_init(&vorbisenc->vi); - gst_vorbisenc_add_metadata (vorbisenc, vorbisenc->metadata); - /* set up the analysis state and auxiliary encoding storage */ vorbis_analysis_init (&vorbisenc->vd, &vorbisenc->vi); vorbis_block_init (&vorbisenc->vd, &vorbisenc->vb); @@ -654,28 +711,6 @@ gst_vorbisenc_setup (VorbisEnc *vorbisenc) ogg_stream_init (&vorbisenc->os, serial); - /* Vorbis streams begin with three headers; the initial header (with - most of the codec setup parameters) which is mandated by the Ogg - bitstream spec. The second header holds any comment fields. The - third header holds the bitstream codebook. We merely need to - make the headers, then pass them to libvorbis one at a time; - libvorbis handles the additional Ogg bitstream constraints */ - - { - ogg_packet header; - ogg_packet header_comm; - ogg_packet header_code; - - vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header, &header_comm, &header_code); - ogg_stream_packetin (&vorbisenc->os, &header); /* automatically placed in its own - page */ - ogg_stream_packetin (&vorbisenc->os, &header_comm); - ogg_stream_packetin (&vorbisenc->os, &header_code); - - /* no need to write out here. We'll get to that in the main loop */ - vorbisenc->flush_header = TRUE; - } - vorbisenc->setup = TRUE; return TRUE; @@ -732,6 +767,15 @@ gst_vorbisenc_chain (GstPad * pad, GstData *_data) vorbis_analysis_wrote (&vorbisenc->vd, 0); gst_event_unref (event); break; + case GST_EVENT_TAG: + if (vorbisenc->tags) { + gst_tag_list_merge (vorbisenc->tags, gst_event_tag_get_list (event), + gst_tag_setter_get_merge_mode (GST_TAG_SETTER (vorbisenc))); + } else { + g_assert_not_reached (); + } + gst_pad_event_default (pad, event); + return; default: gst_pad_event_default (pad, event); return; @@ -749,16 +793,28 @@ gst_vorbisenc_chain (GstPad * pad, GstData *_data) return; } - if (vorbisenc->flush_header) { + if (!vorbisenc->header_sent) { gint result; + /* Vorbis streams begin with three headers; the initial header (with + most of the codec setup parameters) which is mandated by the Ogg + bitstream spec. The second header holds any comment fields. The + third header holds the bitstream codebook. We merely need to + make the headers, then pass them to libvorbis one at a time; + libvorbis handles the additional Ogg bitstream constraints */ + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + gst_vorbisenc_set_metadata (vorbisenc); + vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header, &header_comm, &header_code); + ogg_stream_packetin (&vorbisenc->os, &header); /* automatically placed in its own page */ + ogg_stream_packetin (&vorbisenc->os, &header_comm); + ogg_stream_packetin (&vorbisenc->os, &header_code); while ((result = ogg_stream_flush(&vorbisenc->os, &vorbisenc->og))) { - if (!result) - break; - gst_vorbisenc_write_page (vorbisenc, &vorbisenc->og); } - vorbisenc->flush_header = FALSE; + vorbisenc->header_sent = TRUE; } /* data to encode */ @@ -852,9 +908,6 @@ gst_vorbisenc_get_property (GObject * object, guint prop_id, GValue * value, GPa case ARG_SERIAL: g_value_set_int (value, vorbisenc->serial); break; - case ARG_METADATA: - g_value_set_static_boxed (value, vorbisenc->metadata); - break; case ARG_MANAGED: g_value_set_boolean (value, vorbisenc->managed); break; @@ -920,9 +973,6 @@ gst_vorbisenc_set_property (GObject * object, guint prop_id, const GValue * valu case ARG_SERIAL: vorbisenc->serial = g_value_get_int (value); break; - case ARG_METADATA: - vorbisenc->metadata = g_value_get_boxed (value); - break; case ARG_MANAGED: vorbisenc->managed = g_value_get_boolean (value); break; @@ -947,6 +997,9 @@ gst_vorbisenc_change_state (GstElement *element) break; case GST_STATE_PAUSED_TO_READY: vorbisenc->setup = FALSE; + vorbisenc->header_sent = FALSE; + gst_tag_list_free (vorbisenc->tags); + vorbisenc->tags = gst_tag_list_new (); break; case GST_STATE_READY_TO_NULL: default: diff --git a/ext/vorbis/vorbisenc.h b/ext/vorbis/vorbisenc.h index c0eb35c..72932c7 100644 --- a/ext/vorbis/vorbisenc.h +++ b/ext/vorbis/vorbisenc.h @@ -78,10 +78,10 @@ struct _VorbisEnc { guint64 samples_in; guint64 bytes_out; - GstCaps *metadata; + GstTagList * tags; gboolean setup; - gboolean flush_header; + gboolean header_sent; gchar *last_message; }; diff --git a/gst-libs/ext/ffmpeg/Makefile.am b/gst-libs/ext/ffmpeg/Makefile.am index 0a7a076..eaa23fc 100644 --- a/gst-libs/ext/ffmpeg/Makefile.am +++ b/gst-libs/ext/ffmpeg/Makefile.am @@ -25,43 +25,6 @@ patches: SUBDIRS = -EXTRA_DIST = \ - $(PATCHES) \ - Tag \ - ffmpeg/CREDITS \ - ffmpeg/INSTALL \ - ffmpeg/README \ - ffmpeg/config.mak \ - ffmpeg/configure \ - ffmpeg/cygwin_inttypes.h \ - ffmpeg/ffserver.h \ - ffmpeg/ffmpeg.c \ - ffmpeg/ffserver.c \ - ffmpeg/berrno.h \ - ffmpeg/config.h \ - ffmpeg/libavcodec/alpha/*.c \ - ffmpeg/libavcodec/alpha/*.h \ - ffmpeg/libavcodec/alpha/*.S \ - ffmpeg/libavcodec/armv4l/*.c \ - ffmpeg/libavcodec/armv4l/*.S \ - ffmpeg/libavcodec/i386/*.c \ - ffmpeg/libavcodec/i386/*.h \ - ffmpeg/libavcodec/liba52/*.c \ - ffmpeg/libavcodec/liba52/*.h \ - ffmpeg/libavcodec/ppc/*.c \ - ffmpeg/libavcodec/ppc/*.h \ - ffmpeg/libavcodec/ps2/*.c \ - ffmpeg/libavcodec/ps2/*.h \ - ffmpeg/libavcodec/*.c \ - ffmpeg/libavcodec/*.h \ - ffmpeg/libavformat/*.c \ - ffmpeg/libavformat/*.h \ - ffmpeg/tests/*.c \ - ffmpeg/tests/*.ref \ - ffmpeg/tests/*.sh \ - ffmpeg/tests/test.conf \ - ffmpeg/vhook/*.c - checkout: cvs -d:pserver:anonymous@mplayerhq.hu:/cvsroot/ffmpeg co ffmpeg @@ -164,9 +127,7 @@ libavformat_la_CFLAGS = $(defs) -Wall -I$(srcdir) -I$(srcdir)/ffmpeg \ -I$(top_srcdir)/gst-libs/ext/linux/ -DHAVE_AV_CONFIG_H \ -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_GNU_SOURCE - -if HAVE_CPU_I386 -sources_i386 = \ +files_i386 = \ ffmpeg/libavcodec/i386/cputest.c \ ffmpeg/libavcodec/i386/dsputil_mmx.c \ ffmpeg/libavcodec/i386/fdct_mmx.c \ @@ -175,12 +136,13 @@ sources_i386 = \ ffmpeg/libavcodec/i386/motion_est_mmx.c \ ffmpeg/libavcodec/i386/mpegvideo_mmx.c \ ffmpeg/libavcodec/i386/simple_idct_mmx.c +if HAVE_CPU_I386 +sources_i386 = $(files_i386) else sources_i386 = endif -if HAVE_CPU_PPC -sources_powerpc = \ +files_powerpc = \ ffmpeg/libavcodec/ppc/dsputil_ppc.c \ ffmpeg/libavcodec/ppc/mpegvideo_ppc.c # disabled Altivec support for now until someone shows up that make them compile conditionally @@ -189,6 +151,8 @@ sources_powerpc = \ # ffmpeg/libavcodec/ppc/gmc_altivec.c \ # ffmpeg/libavcodec/ppc/idct_altivec.c \ # ffmpeg/libavcodec/ppc/mpegvideo_altivec.c +if HAVE_CPU_PPC +sources_powerpc = $(files_powerpc) else sources_powerpc = endif @@ -270,3 +234,86 @@ libavcodec_la_SOURCES = \ $(sources_i386) \ $(sources_powerpc) +more_libavcodec_files = \ + ffmpeg/libavcodec/mdec.c \ + ffmpeg/libavcodec/motion_est_template.c \ + ffmpeg/libavcodec/svq3.c \ + ffmpeg/libavcodec/wmv2.c + + +all_headers = \ + ffmpeg/berrno.h \ + ffmpeg/cmdutils.h \ + ffmpeg/cygwin_inttypes.h \ + ffmpeg/ffserver.h \ + ffmpeg/xvmc_render.h \ + ffmpeg/libavcodec/ac3.h \ + ffmpeg/libavcodec/ac3tab.h \ + ffmpeg/libavcodec/avcodec.h \ + ffmpeg/libavcodec/bswap.h \ + ffmpeg/libavcodec/cabac.h \ + ffmpeg/libavcodec/common.h \ + ffmpeg/libavcodec/dsputil.h \ + ffmpeg/libavcodec/dvdata.h \ + ffmpeg/libavcodec/faandct.h \ + ffmpeg/libavcodec/fastmemcpy.h \ + ffmpeg/libavcodec/golomb.h \ + ffmpeg/libavcodec/h263data.h \ + ffmpeg/libavcodec/h264data.h \ + ffmpeg/libavcodec/imgconvert_template.h \ + ffmpeg/libavcodec/indeo3data.h \ + ffmpeg/libavcodec/mpeg12data.h \ + ffmpeg/libavcodec/mpeg4data.h \ + ffmpeg/libavcodec/mpegaudio.h \ + ffmpeg/libavcodec/mpegaudiodectab.h \ + ffmpeg/libavcodec/mpegaudiotab.h \ + ffmpeg/libavcodec/mpegvideo.h \ + ffmpeg/libavcodec/msmpeg4data.h \ + ffmpeg/libavcodec/oggvorbis.h \ + ffmpeg/libavcodec/ra144.h \ + ffmpeg/libavcodec/ra288.h \ + ffmpeg/libavcodec/rational.h \ + ffmpeg/libavcodec/simple_idct.h \ + ffmpeg/libavcodec/sp5x.h \ + ffmpeg/libavcodec/svq1_cb.h \ + ffmpeg/libavcodec/svq1_vlc.h \ + ffmpeg/libavcodec/vp3data.h \ + ffmpeg/libavcodec/wmadata.h \ + ffmpeg/libavcodec/i386/dsputil_mmx_avg.h \ + ffmpeg/libavcodec/i386/dsputil_mmx_rnd.h \ + ffmpeg/libavcodec/i386/mmx.h \ + ffmpeg/libavcodec/ppc/dsputil_altivec.h \ + ffmpeg/libavcodec/ppc/dsputil_ppc.h \ + ffmpeg/libavcodec/ppc/gcc_fixes.h \ + ffmpeg/libavformat/avformat.h \ + ffmpeg/libavformat/avi.h \ + ffmpeg/libavformat/avio.h \ + ffmpeg/libavformat/barpainet.h \ + ffmpeg/libavformat/dv.h \ + ffmpeg/libavformat/dv1394.h \ + ffmpeg/libavformat/framehook.h \ + ffmpeg/libavformat/mpegts.h \ + ffmpeg/libavformat/os_support.h \ + ffmpeg/libavformat/rtp.h \ + ffmpeg/libavformat/rtsp.h \ + ffmpeg/libavformat/rtspcodes.h + +DISTCLEANFILES = \ + ffmpeg/config.h \ + ffmpeg/config.mak + +EXTRA_DIST = \ + $(PATCHES) \ + Tag \ + ffmpeg/CREDITS \ + ffmpeg/INSTALL \ + ffmpeg/README \ + ffmpeg/configure \ + ffmpeg/ffmpeg.c \ + ffmpeg/ffserver.c \ + $(libavcodec_la_SOURCES) \ + $(more_libavcodec_files) \ + $(libavformat_la_SOURCES) \ + $(files_i386) \ + $(files_powerpc) \ + $(all_headers) diff --git a/gst/tags/Makefile.am b/gst/tags/Makefile.am new file mode 100644 index 0000000..c8c4916 --- /dev/null +++ b/gst/tags/Makefile.am @@ -0,0 +1,13 @@ + +plugin_LTLIBRARIES = libgsttagediting.la + +libgsttagediting_la_SOURCES = gstvorbistag.c gsttagediting.c gstmp3tag.c +libgsttagediting_la_CFLAGS = $(GST_CFLAGS) +libgsttagediting_la_LIBADD = $(GST_LIBS) +libgsttagediting_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +libgsttageditingincludedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/tags +libgsttageditinginclude_HEADERS = gsttagediting.h + +noinst_HEADERS = gsttageditingprivate.h + diff --git a/gst/tags/gstmp3tag.c b/gst/tags/gstmp3tag.c new file mode 100644 index 0000000..0c410fa --- /dev/null +++ b/gst/tags/gstmp3tag.c @@ -0,0 +1,331 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * gstvorbistagsetter.c: plugin for reading / modifying vorbis tags + * + * 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 "gsttageditingprivate.h" +#include + +static const gchar *genres[] = { + "Blues", + "Classic Rock", + "Country", + "Dance", + "Disco", + "Funk", + "Grunge", + "Hip-Hop", + "Jazz", + "Metal", + "New Age", + "Oldies", + "Other", + "Pop", + "R&B", + "Rap", + "Reggae", + "Rock", + "Techno", + "Industrial", + "Alternative", + "Ska", + "Death Metal", + "Pranks", + "Soundtrack", + "Euro-Techno", + "Ambient", + "Trip-Hop", + "Vocal", + "Jazz+Funk", + "Fusion", + "Trance", + "Classical", + "Instrumental", + "Acid", + "House", + "Game", + "Sound Clip", + "Gospel", + "Noise", + "Alternative Rock", + "Bass", + "Soul", + "Punk", + "Space", + "Meditative", + "Instrumental Pop", + "Instrumental Rock", + "Ethnic", + "Gothic", + "Darkwave", + "Techno-Industrial", + "Electronic", + "Pop-Folk", + "Eurodance", + "Dream", + "Southern Rock", + "Comedy", + "Cult", + "Gangsta", + "Top 40", + "Christian Rap", + "Pop/Funk", + "Jungle", + "Native American", + "Cabaret", + "New Wave", + "Psychadelic", + "Rave", + "Showtunes", + "Trailer", + "Lo-Fi", + "Tribal", + "Acid Punk", + "Acid Jazz", + "Polka", + "Retro", + "Musical", + "Rock & Roll", + "Hard Rock", + "Folk", + "Folk/Rock", + "National Folk", + "Swing", + "Fusion", + "Bebob", + "Latin", + "Revival", + "Celtic", + "Bluegrass", + "Avantgarde", + "Gothic Rock", + "Progressive Rock", + "Psychadelic Rock", + "Symphonic Rock", + "Slow Rock", + "Big Band", + "Chorus", + "Easy Listening", + "Acoustic", + "Humour", + "Speech", + "Chanson", + "Opera", + "Chamber Music", + "Sonata", + "Symphony", + "Booty Bass", + "Primus", + "Porn Groove", + "Satire", + "Slow Jam", + "Club", + "Tango", + "Samba", + "Folklore", + "Ballad", + "Power Ballad", + "Rhythmic Soul", + "Freestyle", + "Duet", + "Punk Rock", + "Drum Solo", + "A Capella", + "Euro-House", + "Dance Hall", + "Goa", + "Drum & Bass", + "Club-House", + "Hardcore", + "Terror", + "Indie", + "BritPop", + "Negerpunk", + "Polsk Punk", + "Beat", + "Christian Gangsta Rap", + "Heavy Metal", + "Black Metal", + "Crossover", + "Contemporary Christian", + "Christian Rock", + "Merengue", + "Salsa", + "Thrash Metal", + "Anime", + "Jpop", + "Synthpop" +}; + +static GstTagEntryMatch tag_matches[] = { + { GST_TAG_TITLE, "TIT2" }, + { GST_TAG_ALBUM, "TALB" }, + { GST_TAG_TRACK_NUMBER, "TRCK" }, + { GST_TAG_ARTIST, "TPE1" }, + { GST_TAG_COPYRIGHT, "TCOP" }, + { GST_TAG_GENRE, "TCON" }, + { GST_TAG_DATE, "TDRC" }, + { GST_TAG_COMMENT, "COMM" }, + { NULL, NULL } +}; +/** +* gst_tag_from_id3_tag: +* @id3_tag: ID3v2 tag to convert to GStreamer tag +* +* Looks up the GStreamer tag for a ID3v2 tag. +* +* Returns: The corresponding GStreamer tag or NULL if none exists. +*/ +G_CONST_RETURN gchar * +gst_tag_from_id3_tag (const gchar *id3_tag) +{ + int i = 0; + + g_return_val_if_fail (id3_tag != NULL, NULL); + + while (tag_matches[i].gstreamer_tag != NULL) { + if (strcmp (id3_tag, tag_matches[i].original_tag) == 0) { + break; + } + i++; + } + return tag_matches[i].gstreamer_tag; +} +/** +* gst_tag_to_id3_tag: +* @gst_tag: GStreamer tag to convert to vorbiscomment tag +* +* Looks up the ID3v2 tag for a GStreamer tag. +* +* Returns: The corresponding ID3v2 tag or NULL if none exists. +*/ +G_CONST_RETURN gchar * +gst_tag_to_id3_tag (const gchar *gst_tag) +{ + int i = 0; + + g_return_val_if_fail (gst_tag != NULL, NULL); + + while (tag_matches[i].gstreamer_tag != NULL) { + if (strcmp (gst_tag, tag_matches[i].gstreamer_tag) == 0) { + return tag_matches[i].original_tag; + } + i++; + } +return NULL; +} +static void +gst_tag_extract (GstTagList *list, const gchar *tag, const gchar *start, const guint size) +{ + gsize bytes_read; + gchar *conv; + + /* FIXME: better charset detection? */ + if (g_utf8_validate (start, size, NULL)) { + conv = g_strchomp (g_strndup (start, size)); + } else { + conv = g_locale_to_utf8 (start, size, &bytes_read, NULL, NULL); + if (bytes_read != size) { + g_free (conv); + conv = g_convert (start, size, "UTF-8", "ISO-8859-1", &bytes_read, NULL, NULL); + if (bytes_read != size) { + g_free (conv); + return; + } + } + } + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, tag, conv, NULL); + g_free (conv); +} +/** +* gst_tag_list_new_from_id3v1: +* @data: 128 bytes of data containing the ID3v1 tag +* +* Parses the data containing an ID3v1 tag and returns a #GstTagList from the +* parsed data. +* +* Returns: A new tag list or NULL if the data was not an ID3v1 tag. +*/ +GstTagList * +gst_tag_list_new_from_id3v1 (const guint8 *data) +{ + guint year; + gchar *ystr; + GstTagList *list; + + g_return_val_if_fail (data != NULL, NULL); + + if (data[0] == 'T' && data[1] == 'A' && data[2] == 'G') return NULL; + list = gst_tag_list_new (); + gst_tag_extract (list, GST_TAG_TITLE, &data[3], 30); + gst_tag_extract (list, GST_TAG_ARTIST, &data[33], 30); + gst_tag_extract (list, GST_TAG_ALBUM, &data[63], 30); + ystr = g_strndup (&data[93], 4); + year = strtoul (ystr, NULL, 10); + g_free (ystr); + if (year > 0) { + GDate *date = g_date_new_dmy (1, 1, year); + year = g_date_get_julian (date); + g_date_free (date); + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, year, NULL); + } + if (data[125] == 0) { + gst_tag_extract (list, GST_TAG_ALBUM, &data[97], 28); + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_NUMBER, (guint) data[126], NULL); + } else { + gst_tag_extract (list, GST_TAG_ALBUM, &data[97], 30); + } + if (data[127] < gst_tag_id3_genre_count ()) { + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_NUMBER, gst_tag_id3_genre_get (data[126]), NULL); + } + + return list; +} +/** + * gst_tag_id3_genre_count: + * + * Gets the number of ID3v1 genres that can be identified. Winamp genres are + * included. + * + * Returns: the number of ID3v1 genres that can be identified + */ +guint +gst_tag_id3_genre_count (void) +{ + return G_N_ELEMENTS (genres); +} +/** + * gst_tag_id3_genre_get: + * @id: ID of genre to query + * + * Gets the ID3v1 genre name for a given ID. + * + * Returns: the genre or NULL if no genre is associated with that ID. + */ +G_CONST_RETURN gchar * +gst_tag_id3_genre_get (const guint id) +{ + if (id >= G_N_ELEMENTS (genres)) return NULL; + return genres[id]; +} + diff --git a/gst/tags/gsttagediting.c b/gst/tags/gsttagediting.c new file mode 100644 index 0000000..00de407 --- /dev/null +++ b/gst/tags/gsttagediting.c @@ -0,0 +1,52 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * gstoggplugins.c: register ogg plugins + * + * 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 "gsttageditingprivate.h" +#include + +static gboolean +plugin_init(GstPlugin *plugin) +{ + if (!gst_element_register (plugin, "vorbistag", + GST_RANK_PRIMARY, gst_vorbis_tag_get_type())) { + return FALSE; + } + + return TRUE; +} + +GST_PLUGIN_DEFINE ( + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "gsttags", + "elements for manipulating data from ogg streams", + plugin_init, + VERSION, + GST_LICENSE, + GST_COPYRIGHT, + GST_PACKAGE, + GST_ORIGIN +) + diff --git a/gst/tags/gsttagediting.h b/gst/tags/gsttagediting.h new file mode 100644 index 0000000..8a0cc28 --- /dev/null +++ b/gst/tags/gsttagediting.h @@ -0,0 +1,55 @@ +/* GStreamer + * Copyright (C) 2003 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_TAG_EDITING_H__ +#define __GST_TAG_EDITING_H__ + +#include + +G_BEGIN_DECLS + + +/* functions for vorbis comment manipulation */ + +G_CONST_RETURN gchar * gst_tag_from_vorbis_tag (const gchar * vorbis_tag); +G_CONST_RETURN gchar * gst_tag_to_vorbis_tag (const gchar * gst_tag); +/* functions to convert GstBuffers with vorbiscomment contents to GstTagLists and back */ +GstTagList * gst_tag_list_from_vorbiscomment_buffer (const GstBuffer * buffer, + const guint8 * id_data, + const guint id_data_length, + gchar ** vendor_string); +GstBuffer * gst_tag_list_to_vorbiscomment_buffer (const GstTagList * list, + const guint8 * id_data, + const guint id_data_length, + const gchar * vendor_string); + +/* functions for ID3 tag manipulation */ + +guint gst_tag_id3_genre_count (void); +G_CONST_RETURN gchar * gst_tag_id3_genre_get (const guint id); +GstTagList * gst_tag_list_new_from_id3v1 (const guint8 * data); + +G_CONST_RETURN gchar * gst_tag_from_id3_tag (const gchar * vorbis_tag); +G_CONST_RETURN gchar * gst_tag_to_id3_tag (const gchar * gst_tag); + + +G_END_DECLS + +#endif /* __GST_TAG_EDITING_H__ */ diff --git a/gst/tags/gsttageditingprivate.h b/gst/tags/gsttageditingprivate.h new file mode 100644 index 0000000..a3ffcaf --- /dev/null +++ b/gst/tags/gsttageditingprivate.h @@ -0,0 +1,41 @@ +/* GStreamer + * Copyright (C) 2003 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_OGG_PLUGINS_H__ +#define __GST_OGG_PLUGINS_H__ + +#include + +G_BEGIN_DECLS + + +typedef struct _GstTagEntryMatch GstTagEntryMatch; +struct _GstTagEntryMatch { + gchar * gstreamer_tag; + gchar * original_tag; +}; + + +GType gst_vorbis_tag_get_type (void); + + +G_END_DECLS + +#endif /* __GST_OGG_PLUGINS_H__ */ diff --git a/gst/tags/gstvorbistag.c b/gst/tags/gstvorbistag.c new file mode 100644 index 0000000..8762765 --- /dev/null +++ b/gst/tags/gstvorbistag.c @@ -0,0 +1,563 @@ +/* GStreamer + * Copyright (C) 2003 Benjamin Otte + * + * gstvorbistagsetter.c: plugin for reading / modifying vorbis tags + * + * 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 +#include "gsttageditingprivate.h" +#include + +GST_DEBUG_CATEGORY_STATIC (gst_vorbis_tag_debug); +#define GST_CAT_DEFAULT gst_vorbis_tag_debug + +#define GST_TYPE_VORBIS_TAG (gst_vorbis_tag_get_type()) +#define GST_VORBIS_TAG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VORBIS_TAG, GstVorbisTag)) +#define GST_VORBIS_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VORBIS_TAG, GstVorbisTag)) +#define GST_IS_VORBIS_TAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VORBIS_TAG)) +#define GST_IS_VORBIS_TAG_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VORBIS_TAG)) + +typedef struct _GstVorbisTag GstVorbisTag; +typedef struct _GstVorbisTagClass GstVorbisTagClass; + +struct _GstVorbisTag { + GstElement element; + + /* pads */ + GstPad * sinkpad; + GstPad * srcpad; + + /* output mode */ + guint output; +}; + +struct _GstVorbisTagClass { + GstElementClass parent_class; +}; + +/* elementfactory information */ +static GstElementDetails gst_vorbis_tag_details = GST_ELEMENT_DETAILS ( + "vorbis tag extractor", + "Tag", + "Extract tagging information from vorbis streams", + "Benjamin Otte " +); + +enum { + OUTPUT_UNKNOWN, + OUTPUT_TAGS, + OUTPUT_DATA +}; + +/* signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +GST_PAD_TEMPLATE_FACTORY (vorbis_tag_src_template_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "vorbis_tag_data_src", + "application/x-vorbis", + NULL + ), + GST_CAPS_NEW ( + "vorbis_tag_tag_src", + "application/x-gst-tags", + NULL + ) +) + +GST_PAD_TEMPLATE_FACTORY (vorbis_tag_sink_template_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "vorbis_tag_data_sink", + "application/x-vorbis", + NULL + ) +) + + +static void gst_vorbis_tag_base_init (gpointer g_class); +static void gst_vorbis_tag_class_init (gpointer g_class, + gpointer class_data); +static void gst_vorbis_tag_init (GTypeInstance * instance, + gpointer g_class); + +static void gst_vorbis_tag_chain (GstPad * pad, + GstData * data); + +static GstElementStateReturn gst_vorbis_tag_change_state (GstElement * element); + +static GstElementClass *parent_class = NULL; +/* static guint gst_vorbis_tag_signals[LAST_SIGNAL] = { 0 }; */ + +GType +gst_vorbis_tag_get_type (void) +{ + static GType vorbis_tag_type = 0; + + if (!vorbis_tag_type) { + static const GTypeInfo vorbis_tag_info = { + sizeof (GstVorbisTagClass), + gst_vorbis_tag_base_init, + NULL, + gst_vorbis_tag_class_init, + NULL, + NULL, + sizeof (GstVorbisTag), + 0, + gst_vorbis_tag_init, + }; + static const GInterfaceInfo tag_setter_info = { + NULL, + NULL, + NULL + }; + + vorbis_tag_type = g_type_register_static(GST_TYPE_ELEMENT, "GstVorbisTag", &vorbis_tag_info, 0); + + g_type_add_interface_static (vorbis_tag_type, GST_TYPE_TAG_SETTER, &tag_setter_info); + + GST_DEBUG_CATEGORY_INIT (gst_vorbis_tag_debug, "vorbistag", 0, "vorbis tagging element"); + } + return vorbis_tag_type; +} +static void +gst_vorbis_tag_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details (element_class, &gst_vorbis_tag_details); + + gst_element_class_add_pad_template (element_class, + GST_PAD_TEMPLATE_GET (vorbis_tag_sink_template_factory)); + gst_element_class_add_pad_template (element_class, + GST_PAD_TEMPLATE_GET (vorbis_tag_src_template_factory)); +} +static void +gst_vorbis_tag_class_init (gpointer g_class, gpointer class_data) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); + + parent_class = g_type_class_peek_parent (g_class); + + gstelement_class->change_state = gst_vorbis_tag_change_state; +} +static void +gst_vorbis_tag_init (GTypeInstance *instance, gpointer g_class) +{ + GstVorbisTag *tag = GST_VORBIS_TAG (instance); + + /* create the sink and src pads */ + tag->sinkpad = gst_pad_new_from_template( + GST_PAD_TEMPLATE_GET (vorbis_tag_sink_template_factory), "sink"); + gst_element_add_pad (GST_ELEMENT (tag), tag->sinkpad); + gst_pad_set_chain_function (tag->sinkpad, GST_DEBUG_FUNCPTR (gst_vorbis_tag_chain)); + + tag->srcpad = gst_pad_new_from_template( + GST_PAD_TEMPLATE_GET (vorbis_tag_src_template_factory), "src"); + gst_element_add_pad (GST_ELEMENT (tag), tag->srcpad); +} +static GstTagEntryMatch tag_matches[] = { + { GST_TAG_TITLE, "TITLE" }, + { GST_TAG_VERSION, "VERSION" }, + { GST_TAG_ALBUM, "ALBUM" }, + { GST_TAG_TRACK_NUMBER,"TRACKNUMBER" }, + { GST_TAG_ARTIST, "ARTIST" }, + { GST_TAG_PERFORMER, "PERFORMER" }, + { GST_TAG_COPYRIGHT, "COPYRIGHT" }, + { GST_TAG_LICENSE, "LICENSE" }, + { GST_TAG_ORGANIZATION,"ORGANIZATION" }, + { GST_TAG_DESCRIPTION,"DESCRIPTION" }, + { GST_TAG_GENRE, "GENRE" }, + { GST_TAG_DATE, "DATE" }, + { GST_TAG_CONTACT, "CONTACT" }, + { GST_TAG_ISRC, "ISRC" }, + { GST_TAG_COMMENT, "COMMENT" }, + { NULL, NULL } +}; +/** + * gst_tag_from_vorbis_tag: + * @vorbis_tag: vorbiscomment tag to convert to GStreamer tag + * + * Looks up the GStreamer tag for a vorbiscommment tag. + * + * Returns: The corresponding GStreamer tag or NULL if none exists. + */ +G_CONST_RETURN gchar * +gst_tag_from_vorbis_tag (const gchar *vorbis_tag) +{ + int i = 0; + gchar *real_vorbis_tag; + + g_return_val_if_fail (vorbis_tag != NULL, NULL); + + real_vorbis_tag = g_ascii_strup (vorbis_tag, -1); + while (tag_matches[i].gstreamer_tag != NULL) { + if (strcmp (real_vorbis_tag, tag_matches[i].original_tag) == 0) { + break; + } + i++; + } + g_free (real_vorbis_tag); + return tag_matches[i].gstreamer_tag; +} +/** + * gst_tag_to_vorbis_tag: + * @gst_tag: GStreamer tag to convert to vorbiscomment tag + * + * Looks up the vorbiscommment tag for a GStreamer tag. + * + * Returns: The corresponding vorbiscommment tag or NULL if none exists. + */ +G_CONST_RETURN gchar * +gst_tag_to_vorbis_tag (const gchar *gst_tag) +{ + int i = 0; + + g_return_val_if_fail (gst_tag != NULL, NULL); + + while (tag_matches[i].gstreamer_tag != NULL) { + if (strcmp (gst_tag, tag_matches[i].gstreamer_tag) == 0) { + return tag_matches[i].original_tag; + } + i++; + } + return NULL; +} +static void +gst_tag_add (GstTagList *list, const gchar *tag, const gchar *value) +{ + const gchar *gst_tag = gst_tag_from_vorbis_tag (tag); + + if (gst_tag == NULL) return; + + switch (gst_tag_get_type (gst_tag)) { + case G_TYPE_UINT: + if (strcmp (gst_tag, GST_TAG_DATE) == 0) { + GDate *date; + guint y, d = 1, m = 1; + gchar *check = (gchar *) value; + y = strtoul (check, &check, 10); + if (*check == '-') { + check++; + m = strtoul (check, &check, 10); + if (*check == '-') { + check++; + d = strtoul (check, &check, 10); + } + } + if (*check != '\0') break; + if (y == 0) break; + date = g_date_new_dmy (d, m, y); + y = g_date_get_julian (date); + g_date_free (date); + gst_tag_list_add (list, GST_TAG_MERGE_APPEND, gst_tag, y, NULL); + break; + } else { + guint tmp; + gchar *check; + tmp = strtoul (value, &check, 10); + if (*check == '/' && strcmp (gst_tag, GST_TAG_TRACK_NUMBER) == 0) { + guint count; + check++; + count = strtoul (check, &check, 10); + if (*check != '\0' || count == 0) break; + gst_tag_list_add (list, GST_TAG_MERGE_APPEND, GST_TAG_TRACK_COUNT, count, NULL); + } + if (*check != '\0') break; + gst_tag_list_add (list, GST_TAG_MERGE_APPEND, gst_tag, tmp, NULL); + } + break; + case G_TYPE_STRING: + gst_tag_list_add (list, GST_TAG_MERGE_APPEND, gst_tag, value, NULL); + break; + default: + break; + } +} +/** + * gst_tag_list_from_vorbiscomment_buffer: + * @buffer: buffer to convert + * @id_data: identification data at start of stream + * @id_data_length: length of identification data + * @ vendor_string: pointer to a string that should take the vendor string of this + * vorbis comment or NULL if you don't need it. + * + * Creates a new tag list that contains the information parsed out of a + * vorbiscomment packet. + * + * Returns: A new #GstTagList with all tags that could be extracted from the + * given vorbiscomment buffer or NULL on error. + */ +GstTagList * +gst_tag_list_from_vorbiscomment_buffer (const GstBuffer *buffer, const guint8 *id_data, + const guint id_data_length, gchar **vendor_string) +{ +#define ADVANCE(x) G_STMT_START{ \ + data += x; \ + size -= x; \ + if (size < 4) goto error; \ + cur_size = GUINT32_FROM_LE (*((guint32 *) data)); \ + data += 4; \ + size -= 4; \ + if (cur_size >= size) goto error; \ + cur = data; \ +}G_STMT_END + gchar *cur, *value; + guint cur_size; + guint iterations; + guint8 *data; + guint size; + GstTagList *list; + + g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); + g_return_val_if_fail (id_data != NULL, NULL); + g_return_val_if_fail (id_data_length > 0, NULL); + + data = GST_BUFFER_DATA (buffer); + size = GST_BUFFER_SIZE (buffer); + list = gst_tag_list_new (); + + if (size < 11) goto error; + if (memcmp (data, id_data, id_data_length) != 0) goto error; + ADVANCE (id_data_length); + if (vendor_string) *vendor_string = g_strndup (cur, cur_size); + ADVANCE (cur_size); + iterations = cur_size; + cur_size = 0; + while (iterations) { + ADVANCE (cur_size); + iterations--; + cur = g_strndup (cur, cur_size); + value = strchr(cur, '='); + if (value == NULL) { + g_free (cur); + continue; + } + *value = '\0'; + value++; + if (!g_utf8_validate (value, -1, NULL)) { + g_free (cur); + continue; + } + gst_tag_add (list, cur, value); + g_free (cur); + } + + return list; + +error: + gst_tag_list_free (list); + return NULL; +} +typedef struct { + guint count; + guint data_count; + GList *entries; +} MyForEach; +static void +write_one_tag (const GstTagList *list, const gchar *tag, gpointer user_data) +{ + gchar *result; + guint i; + const gchar *vorbis_tag = gst_tag_to_vorbis_tag (tag); + MyForEach *data = (MyForEach *) user_data; + + if (!vorbis_tag) return; + for (i = 0; i < gst_tag_list_get_tag_size (list, tag); i++) { + switch (gst_tag_get_type (tag)) { + case G_TYPE_UINT: + if (strcmp (tag, GST_TAG_DATE) == 0) { + GDate *date; + guint u; + + g_assert (gst_tag_list_get_uint_index (list, tag, i, &u)); + date = g_date_new_julian (u); + /* vorbis suggests using ISO date formats */ + result = g_strdup_printf ("%s=%04d-%02d-%02d", vorbis_tag, (gint) g_date_get_year (date), + (gint) g_date_get_month (date), (gint) g_date_get_day (date)); + g_date_free (date); + } else { + guint u; + + g_assert (gst_tag_list_get_uint_index (list, tag, i, &u)); + result = g_strdup_printf ("%s=%u", vorbis_tag, u); + } + break; + case G_TYPE_STRING: { + gchar *str; + g_assert (gst_tag_list_get_string_index (list, tag, i, &str)); + result = g_strdup_printf ("%s=%s", vorbis_tag, str); + break; + } + default: + GST_DEBUG ("Couldn't write tag %s", tag); + return; + } + data->count ++; + data->data_count += strlen (result); + data->entries = g_list_prepend (data->entries, result); + } +} +/** + * gst_tag_list_to_vorbiscomment_buffer: + * @buffer: tag list to convert + * @id_data: identification data at start of stream + * @id_data_length: length of identification data + * @ vendor_string: string that describes the vendor string or NULL + * + * Creates a new vorbiscomment buffer from a tag list. + * + * Returns: A new #GstBuffer containing a vorbiscomment buffer with all tags that + * could be converted from the given tag list. + */ +GstBuffer * +gst_tag_list_to_vorbiscomment_buffer (const GstTagList *list, const guint8 *id_data, + const guint id_data_length, const gchar *vendor_string) +{ + GstBuffer *buffer; + guint8 *data; + guint i; + GList *l; + MyForEach my_data = { 0, 0, NULL }; + guint vendor_len; + int required_size; + + g_return_val_if_fail (GST_IS_TAG_LIST (list), NULL); + g_return_val_if_fail (id_data != NULL, NULL); + g_return_val_if_fail (id_data_length > 0, NULL); + + if (vendor_string == NULL) + vendor_string = "GStreamer encoded vorbiscomment"; + vendor_len = strlen (vendor_string); + required_size = id_data_length + 4 + vendor_len + 4 + 1; + gst_tag_list_foreach ((GstTagList *) list, write_one_tag, &my_data); + required_size += 4 * my_data.count + my_data.data_count; + buffer = gst_buffer_new_and_alloc (required_size); + data = GST_BUFFER_DATA (buffer); + memcpy (data, id_data, id_data_length); + data += id_data_length; + *((guint32 *) data) = GUINT32_TO_LE (vendor_len); + data += 4; + memcpy (data, vendor_string, vendor_len); + data += vendor_len; + l = my_data.entries = g_list_reverse (my_data.entries); + *((guint32 *) data) = GUINT32_TO_LE (my_data.count); + data += 4; + for (i = 0; i < my_data.count; i++) { + g_assert (l != NULL); + gchar *cur = l->data; + l = g_list_next (l); + guint size = strlen (cur); + *((guint32 *) data) = GUINT32_TO_LE (size); + data += 4; + memcpy (data, cur, size); + data += size; + } + g_list_free (my_data.entries); + *data = 1; + + return buffer; +} +static void +gst_vorbis_tag_chain (GstPad *pad, GstData *data) +{ + GstVorbisTag *tag; + GstBuffer *buffer; + + buffer = GST_BUFFER (data); + tag = GST_VORBIS_TAG (gst_pad_get_parent (pad)); + + if (tag->output == OUTPUT_UNKNOWN) { + /* caps nego */ + do { + if (gst_pad_try_set_caps (tag->srcpad, GST_CAPS_NEW ("vorbis_tag_data_src", "application/x-vorbis", NULL)) >= 0) { + tag->output = OUTPUT_DATA; + } else if (gst_pad_try_set_caps (tag->srcpad, GST_CAPS_NEW ("vorbis_tag_tag_src", "application/x-gst-tags", NULL)) >= 0) { + tag->output = OUTPUT_TAGS; + } else { + GstCaps *caps = gst_pad_template_get_caps (GST_PAD_TEMPLATE_GET (vorbis_tag_src_template_factory)); + if (gst_pad_recover_caps_error (tag->srcpad, caps)) + continue; + return; + } + } while (FALSE); + } + + if (GST_BUFFER_SIZE (buffer) == 0) + gst_element_error (GST_ELEMENT (tag), "empty buffers are not allowed in vorbis data"); + + if (GST_BUFFER_DATA (buffer)[0] == 3) { + gchar *vendor; + GstTagList *list = gst_tag_list_from_vorbiscomment_buffer (buffer, "\003vorbis", 7, &vendor); + + gst_data_unref (data); + if (list == NULL) { + gst_element_error (GST_ELEMENT (tag), "invalid data in vorbis comments"); + return; + } + gst_element_found_tags_for_pad (GST_ELEMENT (tag), tag->srcpad, 0, + gst_tag_list_copy (list)); + gst_tag_list_merge (list, gst_tag_setter_get_list (GST_TAG_SETTER (tag)), gst_tag_setter_get_merge_mode (GST_TAG_SETTER (tag))); + data = GST_DATA (gst_tag_list_to_vorbiscomment_buffer (list, "\003vorbis", 7, vendor)); + gst_tag_list_free (list); + g_free (vendor); + } + + if (tag->output == OUTPUT_DATA) { + gst_pad_push (tag->srcpad, data); + } else { + gst_data_unref (data); + } +} +static GstElementStateReturn +gst_vorbis_tag_change_state (GstElement *element) +{ + GstVorbisTag *tag; + + tag = GST_VORBIS_TAG (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: + /* do something to get out of the chain function faster */ + break; + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + tag->output = OUTPUT_UNKNOWN; + break; + case GST_STATE_READY_TO_NULL: + break; + } + + return parent_class->change_state (element); +} diff --git a/gst/typefind/gsttypefindfunctions.c b/gst/typefind/gsttypefindfunctions.c index 1b70cf3..b89b9f4 100644 --- a/gst/typefind/gsttypefindfunctions.c +++ b/gst/typefind/gsttypefindfunctions.c @@ -860,6 +860,41 @@ dv_type_find (GstTypeFind *tf, gpointer private) } } +/*** application/x-vorbis *****************************************************/ + +#define VORBIS_CAPS GST_CAPS_NEW ("vorbis_type_find", "application/x-vorbis", NULL) +static void +vorbis_type_find (GstTypeFind *tf, gpointer private) +{ + guint8 *data = gst_type_find_peek (tf, 0, 30); + + if (data) { + guint blocksize_0; + guint blocksize_1; + /* 1 byte packet type (identification=0x01) + 6 byte string "vorbis" + 4 byte vorbis version */ + if (memcmp (data, "\001vorbis\000\000\000\000", 11) != 0) return; + data += 11; + /* 1 byte channels must be != 0 */ + if (data[0] == 0) return; + data++; + /* 4 byte samplerate must be != 0 */ + if (*((guint32 *) data) == 0) return; + data += 16; + /* blocksize checks */ + blocksize_0 = data[0] & 0x0F; + blocksize_1 = (data[0] & 0xF0) >> 4; + if (blocksize_0 > blocksize_1) return; + if (blocksize_0 < 6 || blocksize_0 > 13) return; + if (blocksize_1 < 6 || blocksize_1 > 13) return; + data++; + /* framing bit */ + if ((data[0] & 0x01) != 1) return; + gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, VORBIS_CAPS); + } +} + /*** generic typefind for streams that have some data at a specific position***/ typedef struct { @@ -1046,6 +1081,8 @@ plugin_init (GstPlugin *plugin) zip_exts, "PK\003\004", 4, GST_TYPE_FIND_LIKELY); TYPE_FIND_REGISTER_START_WITH (plugin, "application/x-compress", GST_RANK_SECONDARY, compress_exts, "\037\235", 2, GST_TYPE_FIND_LIKELY); + TYPE_FIND_REGISTER (plugin, "application/x-vorbis", GST_RANK_PRIMARY, + vorbis_type_find, NULL, VORBIS_CAPS, NULL); return TRUE; } diff --git a/m4/as-slurp-ffmpeg.m4 b/m4/as-slurp-ffmpeg.m4 index a339842..0df909c 100644 --- a/m4/as-slurp-ffmpeg.m4 +++ b/m4/as-slurp-ffmpeg.m4 @@ -22,7 +22,10 @@ AC_DEFUN(AS_SLURP_FFMPEG, DIRECTORY=`pwd` # get/update cvs if test ! -d $1; then mkdir -p $1; fi - cd $1 + dnl we need to check $srcdir/$1 or it will always checkout ffmpeg even if it is there + dnl at least when top_srcdir != top_builddir. + dnl FIXME: unfortunately this makes the checkout go into top_srcdir + cd $srcdir/$1 if test ! -e ffmpeg/README; then # check out cvs code diff --git a/m4/ogg.m4 b/m4/ogg.m4 new file mode 100644 index 0000000..c73bab5 --- /dev/null +++ b/m4/ogg.m4 @@ -0,0 +1,102 @@ +# Configure paths for libogg +# Jack Moffitt 10-21-2000 +# Shamelessly stolen from Owen Taylor and Manish Singh + +dnl XIPH_PATH_OGG([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl Test for libogg, and define OGG_CFLAGS and OGG_LIBS +dnl +AC_DEFUN(XIPH_PATH_OGG, +[dnl +dnl Get the cflags and libraries +dnl +AC_ARG_WITH(ogg,[ --with-ogg=PFX Prefix where libogg is installed (optional)], ogg_prefix="$withval", ogg_prefix="") +AC_ARG_WITH(ogg-libraries,[ --with-ogg-libraries=DIR Directory where libogg library is installed (optional)], ogg_libraries="$withval", ogg_libraries="") +AC_ARG_WITH(ogg-includes,[ --with-ogg-includes=DIR Directory where libogg header files are installed (optional)], ogg_includes="$withval", ogg_includes="") +AC_ARG_ENABLE(oggtest, [ --disable-oggtest Do not try to compile and run a test Ogg program],, enable_oggtest=yes) + + if test "x$ogg_libraries" != "x" ; then + OGG_LIBS="-L$ogg_libraries" + elif test "x$ogg_prefix" != "x" ; then + OGG_LIBS="-L$ogg_prefix/lib" + elif test "x$prefix" != "xNONE" ; then + OGG_LIBS="-L$prefix/lib" + fi + + OGG_LIBS="$OGG_LIBS -logg" + + if test "x$ogg_includes" != "x" ; then + OGG_CFLAGS="-I$ogg_includes" + elif test "x$ogg_prefix" != "x" ; then + OGG_CFLAGS="-I$ogg_prefix/include" + elif test "$prefix" != "xNONE"; then + OGG_CFLAGS="-I$prefix/include" + fi + + AC_MSG_CHECKING(for Ogg) + no_ogg="" + + + if test "x$enable_oggtest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $OGG_CFLAGS" + LIBS="$LIBS $OGG_LIBS" +dnl +dnl Now check if the installed Ogg is sufficiently new. +dnl + rm -f conf.oggtest + AC_TRY_RUN([ +#include +#include +#include +#include + +int main () +{ + system("touch conf.oggtest"); + return 0; +} + +],, no_ogg=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + + if test "x$no_ogg" = "x" ; then + AC_MSG_RESULT(yes) + ifelse([$1], , :, [$1]) + else + AC_MSG_RESULT(no) + if test -f conf.oggtest ; then + : + else + echo "*** Could not run Ogg test program, checking why..." + CFLAGS="$CFLAGS $OGG_CFLAGS" + LIBS="$LIBS $OGG_LIBS" + AC_TRY_LINK([ +#include +#include +], [ return 0; ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding Ogg or finding the wrong" + echo "*** version of Ogg. If it is not finding Ogg, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means Ogg was incorrectly installed" + echo "*** or that you have moved Ogg since it was installed." ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + OGG_CFLAGS="" + OGG_LIBS="" + ifelse([$2], , :, [$2]) + fi + AC_SUBST(OGG_CFLAGS) + AC_SUBST(OGG_LIBS) + rm -f conf.oggtest +]) diff --git a/pkgconfig/Makefile.am b/pkgconfig/Makefile.am index bd89d68..808545a 100644 --- a/pkgconfig/Makefile.am +++ b/pkgconfig/Makefile.am @@ -42,7 +42,7 @@ pkgconfig_DATA = \ gstreamer-play-@GST_MAJORMINOR@.pc \ gstreamer-interfaces-@GST_MAJORMINOR@.pc -CLEANFILES = $(pcfiles) $(pcfiles_uninstalled) +CLEANFILES = $(pcfiles) $(pcfiles_uninstalled) $(GCONF_PC) $(GCONF_PC_UNINSTALLED) EXTRA_DIST= \ gstreamer-libs.pc.in gstreamer-libs-uninstalled.pc.in \ gstreamer-play.pc.in gstreamer-play-uninstalled.pc.in diff --git a/tests/old/testsuite/alsa/.gitignore b/tests/old/testsuite/alsa/.gitignore index f40838d..03acce2 100644 --- a/tests/old/testsuite/alsa/.gitignore +++ b/tests/old/testsuite/alsa/.gitignore @@ -1 +1,2 @@ formats +state diff --git a/testsuite/alsa/.gitignore b/testsuite/alsa/.gitignore index f40838d..03acce2 100644 --- a/testsuite/alsa/.gitignore +++ b/testsuite/alsa/.gitignore @@ -1 +1,2 @@ formats +state diff --git a/testsuite/autoplug/.gitignore b/testsuite/autoplug/.gitignore new file mode 100644 index 0000000..46e2b37 --- /dev/null +++ b/testsuite/autoplug/.gitignore @@ -0,0 +1,4 @@ +autoplug +autoplug2 +autoplug3 +autoplug4 -- 2.7.4