mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-10 11:29:55 +00:00
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:
parent
695f8923b3
commit
788acc4917
21 changed files with 2048 additions and 131 deletions
24
configure.ac
24
configure.ac
|
@ -12,7 +12,7 @@ AM_MAINTAINER_MODE
|
|||
|
||||
dnl when going to/from release please set the nano (fourth number) right !
|
||||
dnl releases only do Wall, cvs and prerelease does Werror too
|
||||
AS_VERSION(gst-plugins, GST_PLUGINS_VERSION, 0, 7, 1, 1, GST_CVS="no", GST_CVS="yes")
|
||||
AS_VERSION(gst-plugins, GST_PLUGINS_VERSION, 0, 7, 2, 1, GST_CVS="no", GST_CVS="yes")
|
||||
|
||||
dnl add a suffix to apps
|
||||
if test x$program_suffix = xNONE ; then
|
||||
|
@ -189,7 +189,6 @@ fi
|
|||
|
||||
AC_SUBST(GST_CONTROL_LIBS)
|
||||
|
||||
|
||||
dnl Set up conditionals for (target) architecture:
|
||||
dnl ==============================================
|
||||
|
||||
|
@ -241,6 +240,8 @@ if test "x$HAVE_GTK_22" = "xyes"; then
|
|||
AC_SUBST(GTK_VERSION)
|
||||
GTK_PREFIX=`$PKG_CONFIG --variable=prefix gdk-pixbuf-2.0`
|
||||
GTK_SYSCONFDIR=`$PKG_CONFIG --variable=prefix gdk-pixbuf-2.0`
|
||||
GDK_PIXBUF_LIBDIR=`$PKG_CONFIG --variable=libdir gdk-pixbuf-2.0`
|
||||
GDK_PIXBUF_PREFIXDIR=`$PKG_CONFIG --variable=prefix gdk-pixbuf-2.0`
|
||||
AC_SUBST(GTK_BASE_DIR)
|
||||
else
|
||||
PKG_CHECK_MODULES(GTK2, gtk+-2.0, HAVE_GTK_20=yes, HAVE_GTK_20=no)
|
||||
|
@ -255,7 +256,7 @@ AC_SUBST(GTK_CFLAGS)
|
|||
AC_SUBST(HAVE_GTK)
|
||||
AM_CONDITIONAL(HAVE_GDK_LOADERS, test "x$HAVE_GTK_22" = "xyes")
|
||||
|
||||
GDK_PIXBUF_LOADER_DIR="\$(libdir)/gtk-2.0/\$(GTK_VERSION)/\loaders"
|
||||
GDK_PIXBUF_LOADER_DIR="$GDK_PIXBUF_LIBDIR/gtk-2.0/\$(GTK_VERSION)/loaders"
|
||||
AC_ARG_WITH(gdk-pixbuf-loader-dir,
|
||||
AC_HELP_STRING([--with-gdk-pixbuf-loader-dir],
|
||||
[directory to install the gdk_pixbuf loader]),
|
||||
|
@ -265,7 +266,7 @@ AC_ARG_WITH(gdk-pixbuf-loader-dir,
|
|||
])
|
||||
AC_SUBST(GDK_PIXBUF_LOADER_DIR)
|
||||
|
||||
GDK_PIXBUF_CONFFILE="\$(sysconfdir)/gtk-2.0/gdk-pixbuf.loaders"
|
||||
GDK_PIXBUF_CONFFILE="$GDK_PIXBUF_PREFIXDIR/gtk-2.0/gdk-pixbuf.loaders"
|
||||
AC_ARG_WITH(gdk-pixbuf-conffile,
|
||||
AC_HELP_STRING([--with-gdk-pixbuf-conffile],
|
||||
[path to the gdk_pixbuf config file]),
|
||||
|
@ -334,6 +335,7 @@ GST_PLUGINS_ALL="\
|
|||
speed \
|
||||
stereo \
|
||||
synaesthesia \
|
||||
tags \
|
||||
tcp \
|
||||
typefind \
|
||||
udp \
|
||||
|
@ -970,16 +972,15 @@ dnl FIXME: we could use header checks here as well IMO
|
|||
translit(dnm, m, l) AM_CONDITIONAL(USE_MAD, true)
|
||||
GST_CHECK_FEATURE(MAD, [mad mp3 decoder], mad, [
|
||||
dnl check with pkg-config first
|
||||
PKG_CHECK_MODULES(MAD, mad id3tag, HAVE_MAD="yes", HAVE_MAD="no")
|
||||
PKG_CHECK_MODULES(MAD, mad >= 0.15 id3tag >= 0.15, HAVE_MAD="yes", HAVE_MAD="no")
|
||||
if test "x$HAVE_MAD" = "xno"; then
|
||||
dnl fall back to oldskool detection
|
||||
AC_CHECK_LIB(mad, mad_decoder_finish, HAVE_MAD="yes" MAD_LIBS="-lmad")
|
||||
if test "x$HAVE_MAD" = "xyes"; then
|
||||
# installed with mad >= 0.14
|
||||
HAVE_MAD="no"
|
||||
save_libs=$LIBS
|
||||
LIBS="-lz"
|
||||
AC_CHECK_LIB(id3tag, id3_tag_query, HAVE_MAD="yes" MAD_LIBS="$MAD_LIBS -lid3tag")
|
||||
AC_CHECK_LIB(id3tag, id3_tag_options, HAVE_MAD="yes" MAD_LIBS="-lmad -lid3tag -lz")
|
||||
LIBS=$save_LIBS
|
||||
fi
|
||||
fi
|
||||
|
@ -1096,6 +1097,13 @@ GST_CHECK_FEATURE(TARKIN, [tarkinenc tarkindec], tarkin, [
|
|||
HAVE_TARKIN="yes"
|
||||
])
|
||||
|
||||
dnl *** ogg ***
|
||||
translit(dnm, m, l) AM_CONDITIONAL(USE_OGG, true)
|
||||
GST_CHECK_FEATURE(OGG, [ogg de/encoder], oggdemux oggmux, [
|
||||
XIPH_PATH_OGG(HAVE_OGG=yes, HAVE_OGG=no)
|
||||
AS_SCRUB_INCLUDE(OGG_CFLAGS)
|
||||
])
|
||||
|
||||
dnl *** vorbis ***
|
||||
dnl AM_PATH_VORBIS only takes two options
|
||||
translit(dnm, m, l) AM_CONDITIONAL(USE_VORBIS, true)
|
||||
|
@ -1365,6 +1373,7 @@ gst/spectrum/Makefile
|
|||
gst/speed/Makefile
|
||||
gst/stereo/Makefile
|
||||
gst/synaesthesia/Makefile
|
||||
gst/tags/Makefile
|
||||
gst/tcp/Makefile
|
||||
gst/typefind/Makefile
|
||||
gst/udp/Makefile
|
||||
|
@ -1427,6 +1436,7 @@ ext/mas/Makefile
|
|||
ext/mikmod/Makefile
|
||||
ext/mpeg2dec/Makefile
|
||||
ext/mplex/Makefile
|
||||
ext/ogg/Makefile
|
||||
ext/pango/Makefile
|
||||
ext/raw1394/Makefile
|
||||
ext/sdl/Makefile
|
||||
|
|
|
@ -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
9
ext/ogg/Makefile.am
Normal 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
587
ext/ogg/gstoggdemux.c
Normal 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
|
||||
)
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
13
gst/tags/Makefile.am
Normal 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
331
gst/tags/gstmp3tag.c
Normal 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
52
gst/tags/gsttagediting.c
Normal 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
55
gst/tags/gsttagediting.h
Normal 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__ */
|
41
gst/tags/gsttageditingprivate.h
Normal file
41
gst/tags/gsttageditingprivate.h
Normal 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
563
gst/tags/gstvorbistag.c
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
102
m4/ogg.m4
Normal 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
|
||||
])
|
|
@ -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
|
||||
|
|
1
tests/old/testsuite/alsa/.gitignore
vendored
1
tests/old/testsuite/alsa/.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
formats
|
||||
state
|
||||
|
|
1
testsuite/alsa/.gitignore
vendored
1
testsuite/alsa/.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
formats
|
||||
state
|
||||
|
|
4
testsuite/autoplug/.gitignore
vendored
Normal file
4
testsuite/autoplug/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
autoplug
|
||||
autoplug2
|
||||
autoplug3
|
||||
autoplug4
|
Loading…
Reference in a new issue