tagging stuff and build fixes. In detail:
authorBenjamin Otte <otte@gnome.org>
Mon, 24 Nov 2003 04:08:48 +0000 (04:08 +0000)
committerBenjamin Otte <otte@gnome.org>
Mon, 24 Nov 2003 04:08:48 +0000 (04:08 +0000)
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 <gst/tags/gsttagediting.h>. 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. :)

21 files changed:
configure.ac
ext/Makefile.am
ext/ogg/Makefile.am [new file with mode: 0644]
ext/ogg/gstoggdemux.c [new file with mode: 0644]
ext/vorbis/Makefile.am
ext/vorbis/vorbisenc.c
ext/vorbis/vorbisenc.h
gst-libs/ext/ffmpeg/Makefile.am
gst/tags/Makefile.am [new file with mode: 0644]
gst/tags/gstmp3tag.c [new file with mode: 0644]
gst/tags/gsttagediting.c [new file with mode: 0644]
gst/tags/gsttagediting.h [new file with mode: 0644]
gst/tags/gsttageditingprivate.h [new file with mode: 0644]
gst/tags/gstvorbistag.c [new file with mode: 0644]
gst/typefind/gsttypefindfunctions.c
m4/as-slurp-ffmpeg.m4
m4/ogg.m4 [new file with mode: 0644]
pkgconfig/Makefile.am
tests/old/testsuite/alsa/.gitignore
testsuite/alsa/.gitignore
testsuite/autoplug/.gitignore [new file with mode: 0644]

index b1070ff..6889940 100644 (file)
@@ -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
index d70a37e..a26660d 100644 (file)
@@ -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 (file)
index 0000000..e0230ec
--- /dev/null
@@ -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 (file)
index 0000000..067d4de
--- /dev/null
@@ -0,0 +1,587 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * 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 <gst/gst.h>
+#include <ogg/ogg.h>
+/* memcpy - if someone knows a way to get rid of it, please speak up */
+#include <string.h> 
+
+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 <in7y118@public.uni-hamburg.de>"
+);
+
+
+/* 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
+)
index 11e1b53..da2d134 100644 (file)
@@ -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
index f4f576b..9b37db3 100644 (file)
@@ -26,6 +26,7 @@
 #include <time.h>
 #include <vorbis/vorbisenc.h>
 
+#include <gst/gsttaginterface.h>
 #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:
index c0eb35c..72932c7 100644 (file)
@@ -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;
 };
 
index 0a7a076..eaa23fc 100644 (file)
@@ -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 (file)
index 0000000..c8c4916
--- /dev/null
@@ -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 (file)
index 0000000..0c410fa
--- /dev/null
@@ -0,0 +1,331 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * 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 <string.h>
+
+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 (file)
index 0000000..00de407
--- /dev/null
@@ -0,0 +1,52 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * 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 <gst/gst.h>
+
+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 (file)
index 0000000..8a0cc28
--- /dev/null
@@ -0,0 +1,55 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * 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 <gst/gst.h>
+
+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 (file)
index 0000000..a3ffcaf
--- /dev/null
@@ -0,0 +1,41 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * 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 <gst/tags/gsttagediting.h>
+
+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 (file)
index 0000000..8762765
--- /dev/null
@@ -0,0 +1,563 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * 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 <gst/gsttaginterface.h>
+#include "gsttageditingprivate.h"
+#include <string.h>
+
+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 <in7y118@public.uni-hamburg.de>"
+);
+
+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);
+}
index 1b70cf3..b89b9f4 100644 (file)
@@ -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;
 }
index a339842..0df909c 100644 (file)
@@ -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 (file)
index 0000000..c73bab5
--- /dev/null
+++ b/m4/ogg.m4
@@ -0,0 +1,102 @@
+# Configure paths for libogg
+# Jack Moffitt <jack@icecast.org> 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ogg/ogg.h>
+
+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 <stdio.h>
+#include <ogg/ogg.h>
+],     [ 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
+])
index bd89d68..808545a 100644 (file)
@@ -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/testsuite/autoplug/.gitignore b/testsuite/autoplug/.gitignore
new file mode 100644 (file)
index 0000000..46e2b37
--- /dev/null
@@ -0,0 +1,4 @@
+autoplug
+autoplug2
+autoplug3
+autoplug4