[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