tagging stuff and build fixes. In detail:

Original commit message from CVS:
tagging stuff and build fixes. In detail:
- make gdk-pixbuf loader work when distchecking
- fix invalid syntax in ffmpeg Makefile. wildcards for EXTRA_DIST are not allowed. This broke builds where distdir != srcdir
- fix ffmpeg cvs grabbing when srcdir != distdir
- new id3tag plugin for id3 tag reading/writing (uses mad's libid3tag)
- mad and libid3tag require mad/libid3tag v0.15. Fixed configure to require that
- added ogg demuxer in ext/ogg. The demuxer does not handle events yet. Especially getting seeking right will require some effort or code copying from libvorbis.
- added raw vorbis detection to typefinding. oggdemux requires a typefind function to detect its contents.
- tags plugin in gst/tags. Provides API in <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. :)
This commit is contained in:
Benjamin Otte 2003-11-24 04:08:48 +00:00
parent 695f8923b3
commit 788acc4917
21 changed files with 2048 additions and 131 deletions

View 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

View 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 \

9
ext/ogg/Makefile.am Normal file
View file

@ -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)

587
ext/ogg/gstoggdemux.c Normal file
View file

@ -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
)

View 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

View 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);
if (caps == NULL)
return;
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));
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);
}
}
}
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);
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:

View 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;
};

View 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)

13
gst/tags/Makefile.am Normal file
View file

@ -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

331
gst/tags/gstmp3tag.c Normal file
View file

@ -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];
}

52
gst/tags/gsttagediting.c Normal file
View file

@ -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
)

55
gst/tags/gsttagediting.h Normal file
View file

@ -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__ */

View file

@ -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__ */

563
gst/tags/gstvorbistag.c Normal file
View file

@ -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);
}

View 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;
}

View 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

102
m4/ogg.m4 Normal file
View file

@ -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
])

View 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

View file

@ -1 +1,2 @@
formats
state

View file

@ -1 +1,2 @@
formats
state

4
testsuite/autoplug/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
autoplug
autoplug2
autoplug3
autoplug4