[gst-devel] next patch needs approval
Benjamin Otte
in7y118 at public.uni-hamburg.de
Fri Feb 8 11:04:12 CET 2002
This patch adds metadata to streams.
It's a bit tricky to explain, I hope the attached doc/patch does that.
Attached is the diff and two new files (How do I add them to a "cvs
diff", when they're not in the repository?) and a diff for mad to test
it out. You need mad >=0.14 for that patch to work.
As always, the patch is fully binary compatible.
Cheers,
Benjamin
-------------- next part --------------
A non-text attachment was scrubbed...
Name: metadata.doc
Type: application/msword
Size: 4190 bytes
Desc:
URL: <http://lists.freedesktop.org/archives/gstreamer-devel/attachments/20020208/fbb6a590/attachment.doc>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: gstmetadata.c
Type: text/x-c
Size: 11992 bytes
Desc:
URL: <http://lists.freedesktop.org/archives/gstreamer-devel/attachments/20020208/fbb6a590/attachment.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: gstmetadata.h
Type: text/x-c
Size: 2322 bytes
Desc:
URL: <http://lists.freedesktop.org/archives/gstreamer-devel/attachments/20020208/fbb6a590/attachment-0001.bin>
-------------- next part --------------
Index: Makefile.am
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/Makefile.am,v
retrieving revision 1.93
diff -u -r1.93 Makefile.am
--- Makefile.am 6 Feb 2002 18:53:33 -0000 1.93
+++ Makefile.am 8 Feb 2002 18:46:10 -0000
@@ -79,6 +79,7 @@
$(GST_TYPEFIND_SRC) \
gstutils.c \
gsttimecache.c \
+ gstmetadata.c \
$(GST_PARSE_SRC) \
$(GSTARCH_SRCS) \
$(GST_LOADSAVE_SRC)
@@ -140,6 +141,7 @@
gsttypefind.h \
gstutils.h \
gsttimecache.h \
+ gstmetadata.h \
gstparse.h \
gstversion.h \
gstxml.h
Index: gst.h
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gst.h,v
retrieving revision 1.29
diff -u -r1.29 gst.h
--- gst.h 3 Feb 2002 20:07:09 -0000 1.29
+++ gst.h 8 Feb 2002 18:46:10 -0000
@@ -49,6 +49,7 @@
#include <gst/gstxml.h>
#include <gst/gstscheduler.h>
#include <gst/gsttimecache.h>
+#include <gst/gstmetadata.h>
#include <gst/gstevent.h>
#include <gst/gstparse.h>
Index: gstelement.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gstelement.c,v
retrieving revision 1.112
diff -u -r1.112 gstelement.c
--- gstelement.c 6 Feb 2002 21:12:52 -0000 1.112
+++ gstelement.c 8 Feb 2002 18:46:14 -0000
@@ -62,6 +62,7 @@
static GstElementStateReturn gst_element_change_state (GstElement *element);
static void gst_element_error_func (GstElement* element, GstElement *source, gchar *errormsg);
+static GstMetadata * gst_element_get_metadata_func (GstElement *element, GstPad *pad);
#ifndef GST_DISABLE_LOADSAVE
static xmlNodePtr gst_element_save_thyself (GstObject *object, xmlNodePtr parent);
@@ -140,6 +141,7 @@
gstobject_class->restore_thyself = GST_DEBUG_FUNCPTR (gst_element_restore_thyself);
#endif
+ klass->get_metadata = GST_DEBUG_FUNCPTR (gst_element_get_metadata_func);
klass->change_state = GST_DEBUG_FUNCPTR (gst_element_change_state);
klass->error = GST_DEBUG_FUNCPTR (gst_element_error_func);
klass->elementfactory = NULL;
@@ -1099,7 +1101,84 @@
{
g_warning ("The function gst_element_send_event is gone. Use g_object_notify instead.");
}
+/* find a sink pad. If we have one, try to get metadata from there.
+ * repeat until metadata found or no more sink pads.
+ */
+static GstMetadata *
+gst_element_get_metadata_func (GstElement *element, GstPad *pad)
+{
+ GList *padlist = gst_element_get_pad_list(element);
+
+ while (padlist)
+ {
+ GstPad *pad = (GstPad *) GST_PAD_REALIZE (GST_PAD(padlist->data));
+ if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK)
+ {
+ GstMetadata *meta = gst_pad_get_metadata (pad);
+ if (meta != NULL)
+ return meta;
+ }
+ padlist = g_list_next (padlist);
+ }
+ return NULL;
+}
+/**
+ * gst_element_get_metadata:
+ * @element: element to get metadata of
+ * @pad: pad to get metadata of
+ *
+ * Gets the metadata object of a pad or element.
+ *
+ * Returns: The #GstMetaData object belonging to the specified
+ * pad or element or NULL, if none was found.
+ */
+GstMetadata *
+gst_element_get_metadata (GstElement *element, GstPad *pad)
+{
+ /* checks */
+ g_return_val_if_fail (element != NULL, NULL);
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+ if (pad != NULL)
+ {
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+ g_return_val_if_fail (GST_OBJECT_PARENT (pad) == GST_OBJECT (element), NULL);
+ }
+
+ /* call the get_metadata function of the element's class */
+ if ((GST_ELEMENT_CLASS (G_OBJECT_GET_CLASS (element)))->get_metadata)
+ {
+ return (GST_ELEMENT_CLASS (G_OBJECT_GET_CLASS (element)))->get_metadata (element, pad);
+ }
+
+ return NULL;
+}
+/**
+ * gst_element_provide_metadata:
+ * @element: element providing the metadata
+ * @pad: pad to provide the metadata to or NULL
+ * @first_property_name: NULL-terminated property name / value pairs
+ *
+ * Updates the Metadata Object belonging to the pad or element with the
+ * provided information.
+ */
+void
+gst_element_provide_metadata (GstElement *element, GstPad *pad, const gchar *first_property_name, ...)
+{
+ GstMetadata *meta;
+ va_list var_args;
+
+ g_return_if_fail (element != NULL);
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ meta = gst_element_get_metadata (element, pad);
+ if (meta != NULL)
+ {
+ va_start (var_args, first_property_name);
+ g_object_set_valist (G_OBJECT (meta), first_property_name, var_args);
+ va_end (var_args);
+ }
+}
/**
* gst_element_get_state:
* @element: element to get state of
Index: gstelement.h
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gstelement.h,v
retrieving revision 1.80
diff -u -r1.80 gstelement.h
--- gstelement.h 6 Feb 2002 19:05:19 -0000 1.80
+++ gstelement.h 8 Feb 2002 18:46:14 -0000
@@ -31,6 +31,7 @@
#include <gst/gstpad.h>
#include <gst/gstclock.h>
#include <gst/gstpluginfeature.h>
+#include <gst/gstmetadata.h>
#ifdef __cplusplus
extern "C" {
@@ -171,7 +172,7 @@
GstElementStateReturn (*change_state) (GstElement *element);
/* request a new pad */
GstPad* (*request_new_pad) (GstElement *element, GstPadTemplate *templ, const gchar* name);
- void (*send_event) (GstElement *element, GstEvent *event);
+ GstMetadata * (*get_metadata) (GstElement *element, GstPad *pad);
};
void gst_element_class_add_padtemplate (GstElementClass *klass, GstPadTemplate *templ);
@@ -228,6 +229,9 @@
void gst_element_send_event (GstElement *element, GstEvent *event);
+/* metadata functions */
+GstMetadata * gst_element_get_metadata (GstElement *element, GstPad *pad);
+void gst_element_provide_metadata (GstElement *element, GstPad *pad, const gchar *first_property_name, ...);
GstElementState gst_element_get_state (GstElement *element);
gint gst_element_set_state (GstElement *element, GstElementState state);
Index: gstpad.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gstpad.c,v
retrieving revision 1.145
diff -u -r1.145 gstpad.c
--- gstpad.c 2 Feb 2002 13:34:43 -0000 1.145
+++ gstpad.c 8 Feb 2002 18:46:18 -0000
@@ -1875,7 +1875,50 @@
return result;
}
+/**
+ * gst_pad_get_metadata:
+ * @pad: The pad to get the metadata from
+ *
+ * look for metadata.
+ *
+ * Returns: The #GstMetadata object belonging to the specified pad
+ * or NULL, if none was found.
+ */
+GstMetadata *
+gst_pad_get_metadata (GstPad *pad)
+{
+ GstRealPad *rpad;
+ /* checks */
+ g_return_val_if_fail (pad != NULL, NULL);
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
+ rpad = GST_PAD_REALIZE (pad);
+ g_assert (rpad != NULL);
+
+ switch (GST_PAD_DIRECTION (pad))
+ {
+ case GST_PAD_SINK:
+ if (GST_PAD_PEER (rpad) != NULL)
+ {
+ return gst_pad_get_metadata ((GstPad *) GST_PAD_PEER (rpad));
+ }
+ break;
+ case GST_PAD_SRC:
+ if (GST_PAD_PARENT (rpad) != NULL)
+ {
+ GstElement *element = GST_ELEMENT (GST_PAD_PARENT (rpad));
+ if (GST_IS_ELEMENT (element))
+ {
+ return gst_element_get_metadata (element, (GstPad *) rpad);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return NULL;
+}
/************************************************************************
*
* templates
Index: gstpad.h
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gstpad.h,v
retrieving revision 1.79
diff -u -r1.79 gstpad.h
--- gstpad.h 28 Jan 2002 00:51:39 -0000 1.79
+++ gstpad.h 8 Feb 2002 18:46:19 -0000
@@ -30,6 +30,7 @@
#include <gst/gstbuffer.h>
#include <gst/gstcaps.h>
#include <gst/gstevent.h>
+#include <gst/gstmetadata.h>
#ifdef __cplusplus
@@ -384,6 +385,8 @@
GstPad* gst_pad_get_peer (GstPad *pad);
GstBufferPool* gst_pad_get_bufferpool (GstPad *pad);
+
+GstMetadata * gst_pad_get_metadata (GstPad *pad);
gboolean gst_pad_can_connect (GstPad *srcpad, GstPad *sinkpad);
gboolean gst_pad_can_connect_filtered (GstPad *srcpad, GstPad *sinkpad, GstCaps *filtercaps);
Index: elements/gstfilesrc.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/elements/gstfilesrc.c,v
retrieving revision 1.22
diff -u -r1.22 gstfilesrc.c
--- elements/gstfilesrc.c 5 Feb 2002 21:11:43 -0000 1.22
+++ elements/gstfilesrc.c 8 Feb 2002 18:46:22 -0000
@@ -112,6 +112,7 @@
static gboolean gst_filesrc_srcpad_event (GstPad *pad, GstEvent *event);
static GstElementStateReturn gst_filesrc_change_state (GstElement *element);
+static GstMetadata * gst_filesrc_get_metadata (GstElement *element, GstPad *pad);
static GstElementClass *parent_class = NULL;
@@ -165,6 +166,7 @@
gobject_class->get_property = gst_filesrc_get_property;
gstelement_class->change_state = gst_filesrc_change_state;
+ gstelement_class->get_metadata = gst_filesrc_get_metadata;
}
static gint
@@ -205,6 +207,8 @@
src->map_regions_lock = g_mutex_new();
src->seek_happened = FALSE;
+ src->metadata = gst_metadata_new (GST_ELEMENT (src));
+ GST_OBJECT_NAME (src->metadata) = "Filesrc Metadata Object";
}
static void
@@ -220,6 +224,8 @@
g_mutex_free (src->map_regions_lock);
if (src->filename)
g_free (src->filename);
+
+ gst_object_unparent (GST_OBJECT (src->metadata));
}
@@ -653,6 +659,12 @@
return GST_STATE_SUCCESS;
}
+static GstMetadata *
+gst_filesrc_get_metadata (GstElement *element, GstPad *pad)
+{
+ GstFileSrc *src = GST_FILESRC (element);
+ return src->metadata;
+}
static gboolean
gst_filesrc_srcpad_event (GstPad *pad, GstEvent *event)
{
Index: elements/gstfilesrc.h
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/elements/gstfilesrc.h,v
retrieving revision 1.3
diff -u -r1.3 gstfilesrc.h
--- elements/gstfilesrc.h 2 Feb 2002 13:24:25 -0000 1.3
+++ elements/gstfilesrc.h 8 Feb 2002 18:46:22 -0000
@@ -69,6 +69,8 @@
GstBuffer *mapbuf;
size_t mapsize;
+
+ GstMetadata *metadata;
GTree *map_regions;
GMutex *map_regions_lock;
-------------- next part --------------
Index: configure.ac
===================================================================
RCS file: /cvsroot/gstreamer/gst-plugins/configure.ac,v
retrieving revision 1.104
diff -u -r1.104 configure.ac
--- configure.ac 6 Feb 2002 18:29:12 -0000 1.104
+++ configure.ac 8 Feb 2002 18:58:14 -0000
@@ -469,7 +469,7 @@
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, [
- AC_CHECK_LIB(mad, mad_decoder_finish, HAVE_MAD="yes" MAD_LIBS="-lmad")
+ AC_CHECK_LIB(id3tag, id3_tag_parse, HAVE_MAD="yes" MAD_LIBS="-lmad -lid3tag")
])
AC_SUBST(MAD_LIBS)
Index: ext/mad/gstmad.c
===================================================================
RCS file: /cvsroot/gstreamer/gst-plugins/ext/mad/gstmad.c,v
retrieving revision 1.10
diff -u -r1.10 gstmad.c
--- ext/mad/gstmad.c 6 Feb 2002 18:10:35 -0000 1.10
+++ ext/mad/gstmad.c 8 Feb 2002 18:58:15 -0000
@@ -21,6 +21,8 @@
#include <string.h>
#include <mad.h>
+#include <id3tag.h>
+
#define GST_TYPE_MAD \
(gst_mad_get_type())
@@ -53,7 +55,10 @@
guint64 framestamp; /* timestamp-like, but counted in frames */
guint64 sync_point;
guint64 total_samples; /* the number of samples since the sync point */
-
+
+ /* metadata */
+ guint id3_length; /* the id3 information we're about to read */
+
/* info */
struct mad_header header;
gboolean new_header;
@@ -138,7 +143,7 @@
static GstElementClass *parent_class = NULL;
-//static guint gst_mad_signals[LAST_SIGNAL] = { 0 };
+/* static guint gst_mad_signals[LAST_SIGNAL] = { 0 }; */
GType
gst_mad_get_type (void)
@@ -184,7 +189,7 @@
/* init properties */
/* currently, string representations are used, we might want to change that */
- /* FIXME: descriptions need to be more technical, default values and ranges need to be selected right */
+ /* FIXME: descriptions need to be more technical, default values and ranges need to be selected right */
g_object_class_install_property (gobject_class, ARG_LAYER,
g_param_spec_string ("layer", "Layer", "The audio MPEG Layer this stream is encoded in",
layers[0], G_PARAM_READABLE));
@@ -227,13 +232,15 @@
mad->total_samples = 0;
mad->sync_point = 0;
mad->new_header = TRUE;
+ mad->id3_length = 0;
+
}
static void
gst_mad_dispose (GObject *object)
{
GstMad *mad = GST_MAD (object);
-
+
G_OBJECT_CLASS (parent_class)->dispose (object);
g_free (mad->tempbuffer);
@@ -310,7 +317,7 @@
gst_mad_update_info (GstMad *mad, struct mad_header const *header)
{
#define CHECK_HEADER(h1,str) \
-G_STMT_START{ \
+G_STMT_START { \
if (mad->header.h1 != header->h1 || mad->new_header) { \
mad->header.h1 = header->h1; \
g_object_notify (G_OBJECT (mad), str); \
@@ -334,6 +341,103 @@
}
static void
+gst_mad_read_id3 (GstMad *mad) {
+
+ struct id3_tag *tag = 0;
+ struct id3_frame const *frame;
+ id3_ucs4_t const *ucs4;
+ id3_latin1_t *latin1;
+ gint i;
+
+ struct {
+ gchar const *id;
+ guint const name;
+ } const info[] = {
+ { ID3_FRAME_TITLE , "title" },
+ { "TIT3" , "subtitle" }, /* Subtitle */
+ { "TCOP" , "copyright" }, /* Copyright */
+ { "TPRO" , "producer" }, /* Produced */
+ { "TCOM" , "composer" },
+ { ID3_FRAME_ARTIST , "artist" },
+ { "TPE2" , "orchestra" },
+ { "TPE3" , "conductor" },
+ { "TEXT" , "lyricist" },
+ { ID3_FRAME_ALBUM , "album" },
+ //{ ID3_FRAME_YEAR , "date" },
+ //{ ID3_FRAME_TRACK , "track" },
+ { "TPUB" , "publisher" },
+ { ID3_FRAME_GENRE , "genre" },
+ { "TRSN" , "station" },
+ { "TENC" , "encoder" }
+ };
+
+ GST_INFO (GST_PLUGIN_INFO, " %s has found a %u bytes long ID3 tag...\n", GST_ELEMENT_NAME (mad), mad->id3_length);
+ mad_stream_skip(&mad->stream, mad->id3_length - 1);
+
+ tag = id3_tag_parse(mad->stream.this_frame, mad->id3_length);
+
+ if (tag)
+ {
+ GST_DEBUG (GST_PLUGIN_INFO, " %s now handles ID3 tag...\n", GST_ELEMENT_NAME (mad));
+
+ /* text information */
+ for (i = 0; i < sizeof(info) / sizeof(info[0]); ++i) {
+ union id3_field const *field;
+ guint nstrings, j;
+
+ frame = id3_tag_findframe(tag, info[i].id, 0);
+ if (frame == 0)
+ continue;
+
+ field = &frame->fields[1];
+ nstrings = id3_field_getnstrings(field);
+
+ for (j = 0; j < nstrings; ++j) {
+ ucs4 = id3_field_getstrings(field, j);
+ g_assert (ucs4);
+
+ if (strcmp(info[i].id, ID3_FRAME_GENRE) == 0)
+ ucs4 = id3_genre_name(ucs4);
+
+ latin1 = id3_ucs4_latin1duplicate(ucs4);
+
+ if (latin1 != NULL)
+ {
+ gst_element_provide_metadata (mad, NULL, info[i].name, latin1, NULL);
+ /* g_print ("%d: %s\n", info[i].arg, g_value_get_string (&value)); */
+ free (latin1);
+ }
+ }
+ }
+
+ /* comments */
+ /* FIXME: metadata does only support one comment */
+ if ((frame = id3_tag_findframe(tag, ID3_FRAME_COMMENT, 0)))
+ {
+ ucs4 = id3_field_getstring(&frame->fields[2]);
+ g_assert(ucs4);
+
+ if (!(*ucs4))
+ {
+ ucs4 = id3_field_getfullstring(&frame->fields[3]);
+ g_assert(ucs4);
+
+ latin1 = id3_ucs4_latin1duplicate(ucs4);
+ if (latin1 != NULL)
+ {
+ gst_element_provide_metadata (mad, NULL, "comment", latin1, NULL);
+ free (latin1);
+ }
+ }
+ }
+
+ id3_tag_delete (tag);
+ }
+
+ mad->id3_length = 0;
+}
+
+static void
gst_mad_chain (GstPad *pad, GstBuffer *buffer)
{
GstMad *mad;
@@ -355,6 +459,7 @@
gint tocopy;
guchar *mad_input_buffer;
+cont:
/* cut the buffer in MDLEN pieces */
tocopy = MIN (MAD_BUFFER_MDLEN, size);
@@ -365,6 +470,15 @@
data += tocopy;
mad_input_buffer = mad->tempbuffer;
+
+ /* do we have to read id3 stuff? */
+ if (mad->id3_length > 0)
+ {
+ if (mad->id3_length > mad->tempsize)
+ continue;
+ mad_stream_buffer (&mad->stream, mad_input_buffer, mad->tempsize);
+ gst_mad_read_id3 (mad);
+ }
/* it we have enough data we can proceed */
while (mad->tempsize >= MAD_BUFFER_MDLEN) {
@@ -382,6 +496,26 @@
return;
}
else {
+ if (mad->stream.error == MAD_ERROR_LOSTSYNC)
+ {
+ mad->id3_length = id3_tag_query(mad->stream.this_frame,
+ mad->stream.bufend - mad->stream.this_frame);
+ if (mad->id3_length > MAD_BUFFER_MDLEN * 3)
+ {
+ /* we get an invalid pointer, if we skip more. Or not? */
+ mad_stream_skip(&mad->stream, MAD_BUFFER_MDLEN);
+ mad->id3_length = 0;
+ }
+ if (mad->id3_length > mad->tempsize)
+ {
+ consumed = 0;
+ goto cont;
+ }
+ if (mad->id3_length > 0)
+ {
+ gst_mad_read_id3 (mad);
+ }
+ }
goto next;
}
}
@@ -430,11 +564,7 @@
"signed", GST_PROPS_BOOLEAN (TRUE),
"width", GST_PROPS_INT (16),
"depth", GST_PROPS_INT (16),
-#if MAD_VERSION_MINOR <= 12
- "rate", GST_PROPS_INT (mad->header.sfreq),
-#else
"rate", GST_PROPS_INT (mad->header.samplerate),
-#endif
"channels", GST_PROPS_INT (nchannels),
NULL)));
}
More information about the gstreamer-devel
mailing list