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
AC_SUBST(GST_CONTROL_LIBS)
-
dnl Set up conditionals for (target) architecture:
dnl ==============================================
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)
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]),
])
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]),
speed \
stereo \
synaesthesia \
+ tags \
tcp \
typefind \
udp \
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
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)
gst/speed/Makefile
gst/stereo/Makefile
gst/synaesthesia/Makefile
+gst/tags/Makefile
gst/tcp/Makefile
gst/typefind/Makefile
gst/udp/Makefile
ext/mikmod/Makefile
ext/mpeg2dec/Makefile
ext/mplex/Makefile
+ext/ogg/Makefile
ext/pango/Makefile
ext/raw1394/Makefile
ext/sdl/Makefile
MPLEX_DIR=
endif
+if USE_OGG
+OGG_DIR=ogg
+else
+OGG_DIR=
+endif
+
if USE_PANGO
PANGO_DIR=pango
else
$(MIKMOD_DIR) \
$(MPEG2DEC_DIR) \
$(MPLEX_DIR) \
+ $(OGG_DIR) \
$(PANGO_DIR) \
$(RAW1394_DIR) \
$(SDL_DIR) \
mikmod \
mpeg2dec \
mplex \
+ ogg \
pango \
raw1394 \
sdl \
--- /dev/null
+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)
+
--- /dev/null
+/* 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
+)
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
#include <time.h>
#include <vorbis/vorbisenc.h>
+#include <gst/gsttaginterface.h>
#include "vorbisenc.h"
static GstPadTemplate *gst_vorbisenc_src_template, *gst_vorbisenc_sink_template;
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;
}
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));
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*
}
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);
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;
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;
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 */
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;
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;
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:
guint64 samples_in;
guint64 bytes_out;
- GstCaps *metadata;
+ GstTagList * tags;
gboolean setup;
- gboolean flush_header;
+ gboolean header_sent;
gchar *last_message;
};
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
-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 \
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
# 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
$(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)
--- /dev/null
+
+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
+
--- /dev/null
+/* 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];
+}
+
--- /dev/null
+/* 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
+)
+
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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);
+}
}
}
+/*** 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 {
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;
}
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
--- /dev/null
+# 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
+])
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
--- /dev/null
+autoplug
+autoplug2
+autoplug3
+autoplug4