merge in tagging
authorBenjamin Otte <otte@gnome.org>
Mon, 24 Nov 2003 02:09:23 +0000 (02:09 +0000)
committerBenjamin Otte <otte@gnome.org>
Mon, 24 Nov 2003 02:09:23 +0000 (02:09 +0000)
Original commit message from CVS:
merge in tagging
Includes:
- gsttag.[ch] - The definition of GstTagList and tag registering/querying
- gsttaginterface.[ch] - Interface for elements that can handle setting of tags
- updates and merges to gststructure.[ch] and gstvalue.[ch]
- testsuite/tags - some tests for tagging
- bugfixes
- updates to make make distcheck work
- updates the version number to 0.7.2.1

Does not include:
- including tagging stuff in docs
- extensive tests

66 files changed:
configure.ac
docs/.gitignore
docs/Makefile.am
docs/gst/.gitignore
docs/gst/tmpl/gstautoplug.sgml
docs/gst/tmpl/gstbin.sgml
docs/gst/tmpl/gstbuffer.sgml
docs/gst/tmpl/gstcaps.sgml
docs/gst/tmpl/gstclock.sgml
docs/gst/tmpl/gstconfig.sgml
docs/gst/tmpl/gstelement.sgml
docs/gst/tmpl/gstelementfactory.sgml
docs/gst/tmpl/gstevent.sgml
docs/gst/tmpl/gstindex.sgml
docs/gst/tmpl/gstinfo.sgml
docs/gst/tmpl/gstobject.sgml
docs/gst/tmpl/gstpadtemplate.sgml
docs/gst/tmpl/gstplugin.sgml
docs/gst/tmpl/gstreamer-unused.sgml
docs/gst/tmpl/gstthread.sgml
docs/gst/tmpl/gsttypefind.sgml
docs/gst/tmpl/gstxml.sgml
docs/libs/.gitignore
examples/Makefile.am
examples/retag/.gitignore [new file with mode: 0644]
examples/retag/Makefile.am [new file with mode: 0644]
examples/retag/retag.c [new file with mode: 0644]
examples/retag/transcode.c [new file with mode: 0644]
gst/Makefile.am
gst/gst.c
gst/gst.h
gst/gstbuffer.c
gst/gstbuffer.h
gst/gstelement.c
gst/gstelement.h
gst/gstelementfactory.c
gst/gstevent.c
gst/gstevent.h
gst/gstinfo.c
gst/gstinfo.h
gst/gststructure.c
gst/gststructure.h
gst/gsttag.c [new file with mode: 0644]
gst/gsttag.h [new file with mode: 0644]
gst/gsttaginterface.c [new file with mode: 0644]
gst/gsttaginterface.h [new file with mode: 0644]
gst/gsttaglist.c [new file with mode: 0644]
gst/gsttaglist.h [new file with mode: 0644]
gst/gsttagsetter.c [new file with mode: 0644]
gst/gsttagsetter.h [new file with mode: 0644]
gst/gstvalue.c
gst/gstvalue.h
gst/registries/gstxmlregistry.c
libs/gst/Makefile.am
pkgconfig/Makefile.am
po/.gitignore [new file with mode: 0644]
tests/old/examples/Makefile.am
tests/old/examples/retag/.gitignore [new file with mode: 0644]
tests/old/examples/retag/Makefile.am [new file with mode: 0644]
tests/old/examples/retag/retag.c [new file with mode: 0644]
tests/old/examples/retag/transcode.c [new file with mode: 0644]
tests/old/testsuite/Makefile.am
tests/old/testsuite/threads/queue.c
testsuite/Makefile.am
testsuite/threads/queue.c
tools/gst-launch.c

index baa2046..d8d65de 100644 (file)
@@ -3,7 +3,7 @@ AC_CANONICAL_TARGET([])
 
 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(gstreamer, GST_VERSION, 0, 7, 1, 1, GST_CVS="no", GST_CVS="yes")
+AS_VERSION(gstreamer, GST_VERSION, 0, 7, 2, 1, GST_CVS="no", GST_CVS="yes")
 
 if test x$program_suffix = xNONE ; then
   program_suffix=-$GST_VERSION_MAJOR.$GST_VERSION_MINOR
@@ -615,6 +615,7 @@ testsuite/indexers/Makefile
 testsuite/parse/Makefile
 testsuite/plugin/Makefile
 testsuite/refcounting/Makefile
+testsuite/tags/Makefile
 testsuite/threads/Makefile
 examples/Makefile
 examples/autoplug/Makefile
@@ -630,15 +631,19 @@ examples/queue/Makefile
 examples/queue2/Makefile
 examples/queue3/Makefile
 examples/queue4/Makefile
+examples/retag/Makefile
 examples/thread/Makefile
 examples/typefind/Makefile
 examples/xml/Makefile
 tools/Makefile
 docs/Makefile
+docs/faq/Makefile
 docs/gst/Makefile
 docs/libs/Makefile
+docs/manual/Makefile
 docs/plugins/Makefile
 docs/plugins/gstreamer-plugins.types
+docs/pwg/Makefile
 docs/xsl/Makefile
 docs/version.entities
 pkgconfig/Makefile
index 08f5ed3..2cd23a3 100644 (file)
@@ -5,3 +5,4 @@ Makefile.in
 *.la
 .deps
 .libs
+version.entities
index 9ff0084..892624a 100644 (file)
@@ -6,7 +6,7 @@ SUBDIRS_PLUGINS =
 endif
 
 if BUILD_DOCS
-SUBDIRS_DOCS = gst libs
+SUBDIRS_DOCS = faq manual pwg gst libs
 else
 SUBDIRS_DOCS =
 endif
index f692317..d785f34 100644 (file)
@@ -12,6 +12,7 @@ gstreamer-unused.txt
 gstreamer-undocumented.txt
 gstreamer-decl-list.txt
 gstreamer-decl.txt
+gstreamer-presed-scan.c
 gstreamer-scan.c
 gstreamer-scan
 gstreamer.args
index 6c5bd63..3acf7c2 100644 (file)
@@ -99,6 +99,14 @@ The autoplug object
 </para>
 
 
+<!-- ##### SIGNAL GstAutoplug::new-object ##### -->
+<para>
+
+</para>
+
+@gstautoplug: the object which received the signal.
+@arg1: 
+
 <!-- ##### ENUM GstAutoplugFlags ##### -->
 <para>
 The type of the autoplugger.
@@ -141,11 +149,3 @@ The type of the autoplugger.
 @Returns: 
 
 
-<!-- ##### SIGNAL GstAutoplug::new-object ##### -->
-<para>
-
-</para>
-
-@gstautoplug: the object which received the signal.
-@arg1: 
-
index 68c62da..8a0038e 100644 (file)
@@ -72,6 +72,34 @@ The GstBin object
 </para>
 
 
+<!-- ##### SIGNAL GstBin::element-added ##### -->
+<para>
+
+</para>
+
+@gstbin: the object which received the signal.
+@arg1: the element that was added to the bin
+
+<!-- ##### SIGNAL GstBin::element-removed ##### -->
+<para>
+
+</para>
+
+@gstbin: the object which received the signal.
+@arg1: the element that was removed from the bin
+
+<!-- ##### SIGNAL GstBin::iterate ##### -->
+<para>
+This signal is emitted when a bin iterates, either automatically or
+due to a #gst_bin_iterate() call.  The return value is used to
+determine if the object method handler processed any data.
+In most normal cases, a user-provided signal handler should return
+FALSE.
+</para>
+
+@gstbin: the object which received the signal.
+@Returns: TRUE if the state of the bin was advanced.
+
 <!-- ##### USER_FUNCTION GstBinPrePostIterateFunction ##### -->
 <para>
 The signature of the callback for the post and pre iterate function as set with
@@ -224,31 +252,3 @@ gst_bin_set_pre_iterate_function() and gst_bin_set_post_iterate_function().
 @clock: 
 
 
-<!-- ##### SIGNAL GstBin::element-added ##### -->
-<para>
-
-</para>
-
-@gstbin: the object which received the signal.
-@arg1: the element that was added to the bin
-
-<!-- ##### SIGNAL GstBin::element-removed ##### -->
-<para>
-
-</para>
-
-@gstbin: the object which received the signal.
-@arg1: the element that was removed from the bin
-
-<!-- ##### SIGNAL GstBin::iterate ##### -->
-<para>
-This signal is emitted when a bin iterates, either automatically or
-due to a #gst_bin_iterate() call.  The return value is used to
-determine if the object method handler processed any data.
-In most normal cases, a user-provided signal handler should return
-FALSE.
-</para>
-
-@gstbin: the object which received the signal.
-@Returns: TRUE if the state of the bin was advanced.
-
index 824c1e5..c9474ec 100644 (file)
@@ -262,6 +262,7 @@ The basic structure of a buffer.
 @timestamp: 
 @duration: 
 @offset: 
+@offset_end: 
 @pool: 
 @pool_private: 
 
index 65f1399..8cb1606 100644 (file)
@@ -406,24 +406,6 @@ The name used for tracing
 @Returns: 
 
 
-<!-- ##### FUNCTION gst_caps_set_type_id ##### -->
-<para>
-
-</para>
-
-@caps: 
-@type_id: 
-
-
-<!-- ##### FUNCTION gst_caps_get_type_id ##### -->
-<para>
-
-</para>
-
-@caps: 
-@Returns: 
-
-
 <!-- ##### FUNCTION gst_caps_set_mime ##### -->
 <para>
 
index da3b748..ae2cf0d 100644 (file)
@@ -235,6 +235,16 @@ The GstClock object
 </para>
 
 
+<!-- ##### ARG GstClock:max-diff ##### -->
+<para>
+Maximum allowed diff for clock sync requests against the real time.
+</para>
+
+<!-- ##### ARG GstClock:stats ##### -->
+<para>
+Boolean property to activate stat generation on the clock.
+</para>
+
 <!-- ##### FUNCTION gst_clock_set_speed ##### -->
 <para>
 
@@ -402,13 +412,3 @@ The GstClock object
 @id: 
 
 
-<!-- ##### ARG GstClock:max-diff ##### -->
-<para>
-Maximum allowed diff for clock sync requests against the real time.
-</para>
-
-<!-- ##### ARG GstClock:stats ##### -->
-<para>
-Boolean property to activate stat generation on the clock.
-</para>
-
index 26c2e33..99b8033 100644 (file)
@@ -108,13 +108,6 @@ If this is defined, the <link linkend="gstreamer-gstinfo">debugging subsystem
 
 
 
-<!-- ##### MACRO GST_DISABLE_TYPEFIND ##### -->
-<para>
-
-</para>
-
-
-
 <!-- ##### MACRO GST_DISABLE_URI ##### -->
 <para>
 
index c06cdbd..4ce8a7b 100644 (file)
@@ -71,6 +71,58 @@ The element object
 </para>
 
 
+<!-- ##### SIGNAL GstElement::eos ##### -->
+<para>
+Signal emited when the element goes to PAUSED due to an end-of-stream
+condition.
+</para>
+
+@gstelement: the object which received the signal.
+
+<!-- ##### SIGNAL GstElement::error ##### -->
+<para>
+Is triggered whenever an error occured.
+
+</para>
+
+@gstelement: the object which received the signal.
+@arg1: the error message
+@arg2: 
+
+<!-- ##### SIGNAL GstElement::found-tag ##### -->
+<para>
+
+</para>
+
+@gstelement: the object which received the signal.
+@arg1: 
+@arg2: 
+
+<!-- ##### SIGNAL GstElement::new-pad ##### -->
+<para>
+Is triggered whenever a new pad is added to an element.
+</para>
+
+@gstelement: the object which received the signal.
+@arg1: the new pad that was added
+
+<!-- ##### SIGNAL GstElement::pad-removed ##### -->
+<para>
+Is triggered whenever a pad has been removed from the element.
+</para>
+
+@gstelement: the object which received the signal.
+@arg1: The pad that was removed.
+
+<!-- ##### SIGNAL GstElement::state-change ##### -->
+<para>
+Is triggered whenever the state of an element changes.
+</para>
+
+@gstelement: the object which received the signal.
+@arg1: the new state of the object
+@arg2: 
+
 <!-- ##### MACRO gst_element_get_name ##### -->
 <para>
 Gets the name of the element.
@@ -880,14 +932,6 @@ Queries if the Element is decoupled.
 @obj: a #GstElement to query
 
 
-<!-- ##### MACRO GST_ELEMENT_IS_EOS ##### -->
-<para>
-Query wether this element is in the End Of Stream state.
-</para>
-
-@obj: a #GstElement to query
-
-
 <!-- ##### MACRO GST_ELEMENT_IS_EVENT_AWARE ##### -->
 <para>
 Query wether this element can handle events.
@@ -971,46 +1015,3 @@ Helper macro to create query type functions
 @...: list of query types.
 
 
-<!-- ##### SIGNAL GstElement::eos ##### -->
-<para>
-Signal emited when the element goes to PAUSED due to an end-of-stream
-condition.
-</para>
-
-@gstelement: the object which received the signal.
-
-<!-- ##### SIGNAL GstElement::error ##### -->
-<para>
-Is triggered whenever an error occured.
-
-</para>
-
-@gstelement: the object which received the signal.
-@arg1: the error message
-@arg2: 
-
-<!-- ##### SIGNAL GstElement::new-pad ##### -->
-<para>
-Is triggered whenever a new pad is added to an element.
-</para>
-
-@gstelement: the object which received the signal.
-@arg1: the new pad that was added
-
-<!-- ##### SIGNAL GstElement::pad-removed ##### -->
-<para>
-Is triggered whenever a pad has been removed from the element.
-</para>
-
-@gstelement: the object which received the signal.
-@arg1: The pad that was removed.
-
-<!-- ##### SIGNAL GstElement::state-change ##### -->
-<para>
-Is triggered whenever the state of an element changes.
-</para>
-
-@gstelement: the object which received the signal.
-@arg1: the new state of the object
-@arg2: 
-
index eddebe8..6e6cfe1 100644 (file)
@@ -62,22 +62,8 @@ describes the element, mostly for the benefit of editors.
 
 @longname: 
 @klass: 
-@license: 
 @description: 
-@version: 
 @author: 
-@copyright: 
-
-<!-- ##### FUNCTION gst_element_factory_new ##### -->
-<para>
-
-</para>
-
-@name: 
-@type: 
-@details: 
-@Returns: 
-
 
 <!-- ##### FUNCTION gst_element_factory_find ##### -->
 <para>
@@ -88,15 +74,6 @@ describes the element, mostly for the benefit of editors.
 @Returns: 
 
 
-<!-- ##### FUNCTION gst_element_factory_add_pad_template ##### -->
-<para>
-
-</para>
-
-@elementfactory: 
-@templ: 
-
-
 <!-- ##### FUNCTION gst_element_factory_can_src_caps ##### -->
 <para>
 
@@ -137,51 +114,3 @@ describes the element, mostly for the benefit of editors.
 @Returns: 
 
 
-<!-- ##### FUNCTION gst_element_factory_make_or_warn ##### -->
-<para>
-
-</para>
-
-@factoryname: 
-@name: 
-@Returns: 
-
-
-<!-- ##### MACRO gst_element_factory_set_rank ##### -->
-<para>
-
-</para>
-
-@factory: 
-@rank: 
-
-
-<!-- ##### MACRO GST_ELEMENT_RANK_MARGINAL ##### -->
-<para>
-The element is only marginally usefull for autoplugging
-</para>
-
-
-
-<!-- ##### MACRO GST_ELEMENT_RANK_NONE ##### -->
-<para>
-The plugin may not be used in autoplugging
-</para>
-
-
-
-<!-- ##### MACRO GST_ELEMENT_RANK_PRIMARY ##### -->
-<para>
-The plugin is well suited for autoplugging
-</para>
-
-
-
-<!-- ##### MACRO GST_ELEMENT_RANK_SECONDARY ##### -->
-<para>
-The plugin is suited for autoplugging but only as a second
-candidate.
-</para>
-
-
-
index a32604f..11467fe 100644 (file)
@@ -48,6 +48,7 @@ The different major types of events.
 @GST_EVENT_INTERRUPT: mainly used by _get based elements when they were interrupted
                       while waiting for a buffer.
 @GST_EVENT_NAVIGATION: 
+@GST_EVENT_TAG: 
 
 <!-- ##### MACRO GST_EVENT_TYPE ##### -->
 <para>
index d828fa3..cdc3cfa 100644 (file)
@@ -209,6 +209,19 @@ The GstIndex object
 </para>
 
 
+<!-- ##### SIGNAL GstIndex::entry-added ##### -->
+<para>
+Is emited when a new entry is added to the index.
+</para>
+
+@gstindex: the object which received the signal.
+@arg1: The entry added to the index.
+
+<!-- ##### ARG GstIndex:resolver ##### -->
+<para>
+
+</para>
+
 <!-- ##### FUNCTION gst_index_new ##### -->
 <para>
 
@@ -401,16 +414,3 @@ The GstIndex object
 @id: 
 
 
-<!-- ##### SIGNAL GstIndex::entry-added ##### -->
-<para>
-Is emited when a new entry is added to the index.
-</para>
-
-@gstindex: the object which received the signal.
-@arg1: The entry added to the index.
-
-<!-- ##### ARG GstIndex:resolver ##### -->
-<para>
-
-</para>
-
index e2650f4..3ae7489 100644 (file)
@@ -233,21 +233,6 @@ default. If you want to define a default category, do it like this:
 @Varargs: 
 
 
-<!-- ##### FUNCTION gst_debug_logv ##### -->
-<para>
-
-</para>
-
-@category: 
-@level: 
-@file: 
-@function: 
-@line: 
-@object: 
-@format: 
-@args: 
-
-
 <!-- ##### FUNCTION gst_debug_log_default ##### -->
 <para>
 
index 52616a2..cf69ab9 100644 (file)
@@ -40,6 +40,47 @@ The GstObject
 </para>
 
 
+<!-- ##### SIGNAL GstObject::deep-notify ##### -->
+<para>
+The deep notify signal is used to be notified of property changes.
+it is typically attached to the toplevel bin to receive notifications
+from all the elements contained in that bin.
+</para>
+
+@gstobject: the object which received the signal.
+@arg1: the object that originated the signal
+@arg2: the property that changed
+
+<!-- ##### SIGNAL GstObject::object-saved ##### -->
+<para>
+Is trigered whenever a new object is saved to XML. You can connect to
+this signal to insert custom XML tags into the core XML.
+</para>
+
+@gstobject: the object which received the signal.
+@arg1: the xmlNodePtr of the parent node
+
+<!-- ##### SIGNAL GstObject::parent-set ##### -->
+<para>
+Is emitted when the parent of an object is set.
+</para>
+
+@gstobject: the object which received the signal.
+@arg1: the new parent
+
+<!-- ##### SIGNAL GstObject::parent-unset ##### -->
+<para>
+Is emitted when the parent of an object is unset.
+</para>
+
+@gstobject: the object which received the signal.
+@arg1: the old parent
+
+<!-- ##### ARG GstObject:name ##### -->
+<para>
+The name of the object
+</para>
+
 <!-- ##### MACRO GST_FLAGS ##### -->
 <para>
 This macro returns the entire set of flags for the object.
@@ -290,44 +331,3 @@ Check if the object has been destroyed.
 @Returns: 
 
 
-<!-- ##### SIGNAL GstObject::deep-notify ##### -->
-<para>
-The deep notify signal is used to be notified of property changes.
-it is typically attached to the toplevel bin to receive notifications
-from all the elements contained in that bin.
-</para>
-
-@gstobject: the object which received the signal.
-@arg1: the object that originated the signal
-@arg2: the property that changed
-
-<!-- ##### SIGNAL GstObject::object-saved ##### -->
-<para>
-Is trigered whenever a new object is saved to XML. You can connect to
-this signal to insert custom XML tags into the core XML.
-</para>
-
-@gstobject: the object which received the signal.
-@arg1: the xmlNodePtr of the parent node
-
-<!-- ##### SIGNAL GstObject::parent-set ##### -->
-<para>
-Is emitted when the parent of an object is set.
-</para>
-
-@gstobject: the object which received the signal.
-@arg1: the new parent
-
-<!-- ##### SIGNAL GstObject::parent-unset ##### -->
-<para>
-Is emitted when the parent of an object is unset.
-</para>
-
-@gstobject: the object which received the signal.
-@arg1: the old parent
-
-<!-- ##### ARG GstObject:name ##### -->
-<para>
-The name of the object
-</para>
-
index f26e9c5..0396557 100644 (file)
@@ -87,6 +87,15 @@ The padtemplate object.
 </para>
 
 
+<!-- ##### SIGNAL GstPadTemplate::pad-created ##### -->
+<para>
+This signal is fired when an element creates a pad from this 
+template.
+</para>
+
+@gstpadtemplate: the object which received the signal.
+@arg1: The pad that was created.
+
 <!-- ##### ENUM GstPadTemplateFlags ##### -->
 <para>
 Flags for the padtemplate
@@ -199,12 +208,3 @@ Check if the properties of the padtemplate are fixed
 @Returns: 
 
 
-<!-- ##### SIGNAL GstPadTemplate::pad-created ##### -->
-<para>
-This signal is fired when an element creates a pad from this 
-template.
-</para>
-
-@gstpadtemplate: the object which received the signal.
-@arg1: The pad that was created.
-
index bd8e9ba..30dc50f 100644 (file)
@@ -56,20 +56,19 @@ The plugin loading errors
 
 @GST_PLUGIN_ERROR_MODULE: The plugin could not be loaded
 @GST_PLUGIN_ERROR_DEPENDENCIES: The plugin has unresolved dependencies
+@GST_PLUGIN_ERROR_NAME_MISMATCH: 
 
 <!-- ##### STRUCT GstPlugin ##### -->
 <para>
 The plugin object
 </para>
 
-@name: 
-@longname: 
+@desc: 
 @filename: 
 @features: 
 @numfeatures: 
 @manager: 
 @module: 
-@init_called: 
 
 <!-- ##### USER_FUNCTION GstPluginInitFunc ##### -->
 <para>
@@ -77,9 +76,10 @@ A plugin should provide a pointer to a function of this type in the plugin_desc
 It will be called by the loader at statup.
 </para>
 
-@module: The <classname>GModule</classname> it was loaded from
 @plugin: The plugin object that can be used to register stuff for this plugin.
 @Returns: A boolean indicating success or failure.
+<!-- # Unused Parameters # -->
+@module: The <classname>GModule</classname> it was loaded from
 
 
 <!-- ##### STRUCT GstPluginDesc ##### -->
@@ -91,50 +91,14 @@ loaded will use this variable to initialize the plugin.
 @major_version: The minor version of the gstreamer library this plugin was created with
 @minor_version: The minor version of the gstreamer library this plugin was created with
 @name: The name of the plugin
+@description: 
 @plugin_init: The init function of this plugin.
-
-<!-- ##### MACRO GST_PLUGIN_DESC ##### -->
-<para>
-A handy macro to define a plugin description. This macro handles with all the issues
-involved with the different linking methods for this plugin.
-</para>
-
-@major: The major version of GStreamer this plugin was compiled against.
-@minor: The minor version of GStreamer this plugin was compiled against.
-@name: The name of the plugin.
-@init: The init function of this plugin.
-
-
-<!-- ##### MACRO GST_PLUGIN_DESC_DYNAMIC ##### -->
-<para>
-The macro used to define dynamically loaded plugins.
-</para>
-
-@major: The major version of GStreamer this plugin was compiled against.
-@minor: The minor version of GStreamer this plugin was compiled against.
-@name: The name of the plugin.
-@init: The init function of this plugin.
-
-
-<!-- ##### MACRO GST_PLUGIN_DESC_STATIC ##### -->
-<para>
-A macro used to define a statically linked plugin.
-</para>
-
-@major: The major version of GStreamer this plugin was compiled against.
-@minor: The minor version of GStreamer this plugin was compiled against.
-@name: The name of the plugin.
-@init: The init function of this plugin.
-
-
-<!-- ##### FUNCTION gst_plugin_new ##### -->
-<para>
-
-</para>
-
-@filename: 
-@Returns: 
-
+@plugin_exit: 
+@version: 
+@license: 
+@copyright: 
+@package: 
+@origin: 
 
 <!-- ##### FUNCTION gst_plugin_set_name ##### -->
 <para>
@@ -163,15 +127,6 @@ A macro used to define a statically linked plugin.
 @Returns: 
 
 
-<!-- ##### FUNCTION gst_plugin_set_longname ##### -->
-<para>
-
-</para>
-
-@plugin: 
-@longname: 
-
-
 <!-- ##### FUNCTION gst_plugin_get_filename ##### -->
 <para>
 
@@ -210,16 +165,6 @@ A macro used to define a statically linked plugin.
 @Returns: 
 
 
-<!-- ##### FUNCTION gst_plugin_load_plugin ##### -->
-<para>
-
-</para>
-
-@plugin: 
-@error: 
-@Returns: 
-
-
 <!-- ##### FUNCTION gst_plugin_unload_plugin ##### -->
 <para>
 
index 2b29364..7110bc5 100644 (file)
@@ -1056,27 +1056,6 @@ Cache time and byteoffsets.
 GstTimeCache
 
 
-<!-- ##### SECTION ./tmpl/gsttypefind.sgml:Long_Description ##### -->
-<para>
-This element can be added to the pipeline and will notify the listener of
-the detected mime type of the stream. It is used in autoplugging.
-</para>
-
-
-<!-- ##### SECTION ./tmpl/gsttypefind.sgml:See_Also ##### -->
-<para>
-
-</para>
-
-
-<!-- ##### SECTION ./tmpl/gsttypefind.sgml:Short_Description ##### -->
-Detect the mime type of a media stream
-
-
-<!-- ##### SECTION ./tmpl/gsttypefind.sgml:Title ##### -->
-GstTypeFind
-
-
 <!-- ##### SECTION ./tmpl/gsttypes.sgml:Long_Description ##### -->
 <para>
 
@@ -1835,6 +1814,12 @@ to the current function, i.e. "('element')"
 @format: printf-style format string
 @args...: printf arguments
 
+<!-- ##### MACRO GST_DISABLE_TYPEFIND ##### -->
+<para>
+
+</para>
+
+
 <!-- ##### MACRO GST_DISKSRC ##### -->
 <para>
 
@@ -1898,6 +1883,13 @@ Queries whether the cothread holding this element needs to be stopped.
 
 @obj: The element to query
 
+<!-- ##### MACRO GST_ELEMENT_IS_EOS ##### -->
+<para>
+Query wether this element is in the End Of Stream state.
+</para>
+
+@obj: a #GstElement to query
+
 <!-- ##### MACRO GST_ELEMENT_IS_MULTI_IN ##### -->
 <para>
 Query whether this object has multiple input pads.
@@ -1906,6 +1898,31 @@ Query whether this object has multiple input pads.
 
 @obj: Element to query for multiple input pads.
 
+<!-- ##### MACRO GST_ELEMENT_RANK_MARGINAL ##### -->
+<para>
+The element is only marginally usefull for autoplugging
+</para>
+
+
+<!-- ##### MACRO GST_ELEMENT_RANK_NONE ##### -->
+<para>
+The plugin may not be used in autoplugging
+</para>
+
+
+<!-- ##### MACRO GST_ELEMENT_RANK_PRIMARY ##### -->
+<para>
+The plugin is well suited for autoplugging
+</para>
+
+
+<!-- ##### MACRO GST_ELEMENT_RANK_SECONDARY ##### -->
+<para>
+The plugin is suited for autoplugging but only as a second
+candidate.
+</para>
+
+
 <!-- ##### MACRO GST_ESDSINK ##### -->
 <para>
 
@@ -2769,6 +2786,37 @@ Get the flag indicating the properties are fixed from the template.
 
 @plugin: 
 
+<!-- ##### MACRO GST_PLUGIN_DESC ##### -->
+<para>
+A handy macro to define a plugin description. This macro handles with all the issues
+involved with the different linking methods for this plugin.
+</para>
+
+@major: The major version of GStreamer this plugin was compiled against.
+@minor: The minor version of GStreamer this plugin was compiled against.
+@name: The name of the plugin.
+@init: The init function of this plugin.
+
+<!-- ##### MACRO GST_PLUGIN_DESC_DYNAMIC ##### -->
+<para>
+The macro used to define dynamically loaded plugins.
+</para>
+
+@major: The major version of GStreamer this plugin was compiled against.
+@minor: The minor version of GStreamer this plugin was compiled against.
+@name: The name of the plugin.
+@init: The init function of this plugin.
+
+<!-- ##### MACRO GST_PLUGIN_DESC_STATIC ##### -->
+<para>
+A macro used to define a statically linked plugin.
+</para>
+
+@major: The major version of GStreamer this plugin was compiled against.
+@minor: The minor version of GStreamer this plugin was compiled against.
+@name: The name of the plugin.
+@init: The init function of this plugin.
+
 <!-- ##### MACRO GST_PROPS_BOOL_ID ##### -->
 <para>
 
@@ -5717,6 +5765,15 @@ Query the element for the current mime type
 </para>
 
 
+<!-- ##### SIGNAL GstXML::object-loaded ##### -->
+<para>
+
+</para>
+
+@gstxml: the object which received the signal.
+@arg1: 
+@arg2: 
+
 <!-- ##### USER_FUNCTION GstXMLRegistryAddPathList ##### -->
 <para>
 
@@ -7704,6 +7761,14 @@ safely be modified.
 
 @caps: 
 
+<!-- ##### FUNCTION gst_caps_get_type_id ##### -->
+<para>
+
+</para>
+
+@caps: 
+@Returns: 
+
 <!-- ##### FUNCTION gst_caps_list_check_compatibility ##### -->
 <para>
 
@@ -7741,6 +7806,14 @@ safely be modified.
 @Returns: 
 @count: 
 
+<!-- ##### FUNCTION gst_caps_set_type_id ##### -->
+<para>
+
+</para>
+
+@caps: 
+@type_id: 
+
 <!-- ##### FUNCTION gst_clock_activate ##### -->
 <para>
 
@@ -7915,6 +7988,20 @@ safely be modified.
 @data: 
 @Returns: 
 
+<!-- ##### FUNCTION gst_debug_logv ##### -->
+<para>
+
+</para>
+
+@category: 
+@level: 
+@file: 
+@function: 
+@line: 
+@object: 
+@format: 
+@args: 
+
 <!-- ##### FUNCTION gst_debug_print_stack_trace ##### -->
 <para>
 
@@ -7998,6 +8085,14 @@ of an element he doesn't need anymore.
 @a: 
 @b: 
 
+<!-- ##### FUNCTION gst_element_factory_add_pad_template ##### -->
+<para>
+
+</para>
+
+@elementfactory: 
+@templ: 
+
 <!-- ##### FUNCTION gst_element_factory_add_sink ##### -->
 <para>
 
@@ -8054,6 +8149,25 @@ of an element he doesn't need anymore.
 @parent: 
 @Returns: 
 
+<!-- ##### FUNCTION gst_element_factory_make_or_warn ##### -->
+<para>
+
+</para>
+
+@factoryname: 
+@name: 
+@Returns: 
+
+<!-- ##### FUNCTION gst_element_factory_new ##### -->
+<para>
+
+</para>
+
+@name: 
+@type: 
+@details: 
+@Returns: 
+
 <!-- ##### FUNCTION gst_element_factory_register ##### -->
 <para>
 
@@ -8070,6 +8184,14 @@ of an element he doesn't need anymore.
 @parent: 
 @Returns: 
 
+<!-- ##### MACRO gst_element_factory_set_rank ##### -->
+<para>
+
+</para>
+
+@factory: 
+@rank: 
+
 <!-- ##### FUNCTION gst_element_flags_get_type ##### -->
 <para>
 
@@ -9294,6 +9416,15 @@ Destroys the pipeline.
 @name: 
 @Returns: 
 
+<!-- ##### FUNCTION gst_plugin_load_plugin ##### -->
+<para>
+
+</para>
+
+@plugin: 
+@error: 
+@Returns: 
+
 <!-- ##### FUNCTION gst_plugin_load_thyself ##### -->
 <para>
 
@@ -9308,6 +9439,14 @@ Destroys the pipeline.
 
 @mime: 
 
+<!-- ##### FUNCTION gst_plugin_new ##### -->
+<para>
+
+</para>
+
+@filename: 
+@Returns: 
+
 <!-- ##### FUNCTION gst_plugin_save_thyself ##### -->
 <para>
 
@@ -9316,6 +9455,14 @@ Destroys the pipeline.
 @parent: 
 @Returns: 
 
+<!-- ##### FUNCTION gst_plugin_set_longname ##### -->
+<para>
+
+</para>
+
+@plugin: 
+@longname: 
+
 <!-- ##### FUNCTION gst_plugin_unload_all ##### -->
 <para>
 
@@ -10040,6 +10187,19 @@ Destroy the scheduler
 </para>
 
 
+<!-- ##### FUNCTION gst_type_find_factory_register ##### -->
+<para>
+
+</para>
+
+@plugin: 
+@name: 
+@rank: 
+@func: 
+@extensions: 
+@possible_caps: 
+@data: 
+
 <!-- ##### FUNCTION gst_type_get_sink_to_src ##### -->
 <para>
 
index 6d70219..38a7691 100644 (file)
@@ -34,15 +34,6 @@ The GstThread object
 </para>
 
 
-<!-- ##### FUNCTION gst_thread_new ##### -->
-<para>
-
-</para>
-
-@name: 
-@Returns: 
-
-
 <!-- ##### SIGNAL GstThread::shutdown ##### -->
 <para>
 
@@ -55,3 +46,12 @@ The GstThread object
 The thread priority
 </para>
 
+<!-- ##### FUNCTION gst_thread_new ##### -->
+<para>
+
+</para>
+
+@name: 
+@Returns: 
+
+
index 10ae8f6..6cd2b8b 100644 (file)
@@ -98,17 +98,3 @@ gst_type_find_factory_register()
 @Returns: 
 
 
-<!-- ##### FUNCTION gst_type_find_factory_register ##### -->
-<para>
-
-</para>
-
-@plugin: 
-@name: 
-@rank: 
-@func: 
-@extensions: 
-@possible_caps: 
-@data: 
-
-
index 08450b2..ea4e4bd 100644 (file)
@@ -105,25 +105,3 @@ All GstElements can be serialized to an XML presentation and subsequently loaded
 @Returns: 
 
 
-<!-- ##### SIGNAL GstXML::object-loaded ##### -->
-<para>
-
-</para>
-
-@: 
-@: 
-@:
-
-@gstxml: the object which received the signal.
-@arg1: 
-@arg2: 
-
-<!-- ##### SIGNAL GstXML::object-loaded ##### -->
-<para>
-
-</para>
-
-@gstxml: the object which received the signal.
-@arg1: 
-@arg2: 
-
index 30446aa..c20d3bb 100644 (file)
@@ -1,12 +1,15 @@
-Makefile
-Makefile.in
+*.stamp
 html
 xml
-gstreamer-libs-unused.txt
-gstreamer-libs-undocumented.txt
+Makefile
+Makefile.in
 gstreamer-libs-decl.txt
 gstreamer-libs-decl-list.txt
-*.stamp
+gstreamer-libs-presed-scan.c
+gstreamer-libs-undocumented.txt
+gstreamer-libs-unused.txt
 gstreamer-libs.args
 gstreamer-libs.hierarchy
+gstreamer-libs.interfaces
+gstreamer-libs.prerequisites
 gstreamer-libs.signals
index 9751930..69a0458 100644 (file)
@@ -10,13 +10,39 @@ else
 GST_AUTOPLUG_DIRS = autoplug helloworld2
 endif
 
-SUBDIRS = $(GST_AUTOPLUG_DIRS) $(GST_LOADSAVE_DIRS) \
-          helloworld \
-          queue queue2 queue3 queue4 \
-          launch thread plugins mixer cutter pingpong manual
+SUBDIRS =                              \
+       helloworld                      \
+       queue                           \
+       queue2                          \
+       queue3                          \
+       queue4                          \
+       launch                          \
+       thread                          \
+       plugins                         \
+       mixer                           \
+       cutter                          \
+       pingpong                        \
+       manual                          \
+       retag                           \
+       $(GST_LOADSAVE_DIRS)            \
+       $(GST_AUTOPLUG_DIRS) 
 
-DIST_SUBDIRS = autoplug \
-          helloworld helloworld2 \
-          queue queue2 queue3 queue4 \
-          launch thread xml plugins typefind mixer cutter pingpong manual
+
+DIST_SUBDIRS = autoplug                        \
+       helloworld                      \
+       helloworld2                     \
+       queue                           \
+       queue2                          \
+       queue3                          \
+       queue4                          \
+       launch                          \
+       thread                          \
+       plugins                         \
+       mixer                           \
+       cutter                          \
+       pingpong                        \
+       manual                          \
+       xml                             \
+       typefind                        \
+       retag
 
diff --git a/examples/retag/.gitignore b/examples/retag/.gitignore
new file mode 100644 (file)
index 0000000..a54ae58
--- /dev/null
@@ -0,0 +1,2 @@
+retag
+transcode
diff --git a/examples/retag/Makefile.am b/examples/retag/Makefile.am
new file mode 100644 (file)
index 0000000..6a686a5
--- /dev/null
@@ -0,0 +1,7 @@
+noinst_PROGRAMS = retag transcode
+
+retag_LDADD = $(GST_LIBS) 
+retag_CFLAGS = $(GST_CFLAGS)
+
+transcode_LDADD = $(GST_LIBS)
+transcode_CFLAGS = $(GST_CFLAGS)
diff --git a/examples/retag/retag.c b/examples/retag/retag.c
new file mode 100644 (file)
index 0000000..2824ba7
--- /dev/null
@@ -0,0 +1,103 @@
+/* 
+ * This example shows how to use interfaces and the tag subsystem.
+ * It takes an mp3 file as input, and makes an ogg file out of it. While doing
+ * this, it parses the filename and sets artist and title in the ogg file.
+ * It assumes the filename to be "<artist> - <title>.mp3"
+ * 
+ * Run the program as "retag <mp3 file>"
+ *
+ * To run this program, you need to have the gst-plugins package (specifically
+ * the vorbis and mad plugins) installed.
+ */
+
+/* main header */
+#include <gst/gst.h>
+/* and a header we need for the string manipulation */
+#include <string.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  GstElement *bin, *filesrc, *tag_changer, *filesink;
+  gchar *artist, *title, *ext, *filename;
+  
+  /* check that the argument is there */
+  if (argc != 2) {
+    g_print ("usage: %s <mp3 file>\n", argv[0]);
+    return 1;
+  }
+
+  /* initialize GStreamer */
+  gst_init (&argc, &argv);
+
+  /* parse the mp3 name */
+  artist = strrchr (argv[1], '/');
+  if (artist == NULL)
+    artist = argv[1];
+  artist = g_strdup (artist);
+  ext = strrchr (artist, '.');
+  if (ext) *ext = '\0';
+  title = strstr (artist, " - ");
+  if (title == NULL) {
+    g_print ("The format of the mp3 file is invalid.\n");
+    return 1;
+  }
+  *title = '\0';
+  title += 3;
+  
+  
+  /* create a new bin to hold the elements */
+  bin = gst_pipeline_new ("pipeline");
+  g_assert (bin);
+
+  /* create a file reader */
+  filesrc = gst_element_factory_make ("filesrc", "disk_source");
+  g_assert (filesrc);
+
+  /* now it's time to get the tag_changer */
+  tag_changer = gst_element_factory_make ("id3tag", "tag_changer");
+  if (!tag_changer) {
+    g_print ("could not find plugin \"mad\"");
+    return 1;
+  }
+
+  /* and a file writer */
+  filesink = gst_element_factory_make ("filesink", "filesink");
+  g_assert (filesink);
+
+  /* set the filenames */
+  filename = g_strdup_printf ("%s.temp", argv[1]); /* easy solution */
+  g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
+  g_object_set (G_OBJECT (filesink), "location", filename, NULL);
+
+  /* make sure the tag setter uses our stuff 
+     (though that should already be default) */
+  gst_tag_setter_set_merge_mode (GST_TAG_SETTER (tag_changer), GST_TAG_MERGE_KEEP);
+  /* set the tagging information */
+  gst_tag_setter_add (GST_TAG_SETTER (tag_changer), GST_TAG_MERGE_REPLACE,
+                     GST_TAG_ARTIST, artist, 
+                     GST_TAG_TITLE, title, 
+                     NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add_many (GST_BIN (bin), filesrc, tag_changer, filesink, NULL);
+
+  /* link the elements */
+  gst_element_link_many (filesrc, tag_changer, filesink, NULL);
+  
+  /* start playing */
+  gst_element_set_state (bin, GST_STATE_PLAYING);
+
+  while (gst_bin_iterate (GST_BIN (bin)));
+
+  /* stop the bin */
+  gst_element_set_state (bin, GST_STATE_NULL);
+
+  /* rename the file to the correct name and remove the old one */
+  remove (argv[1]);
+  rename (filename, argv[1]);
+  g_free (filename);
+
+  return 0;
+}
+
diff --git a/examples/retag/transcode.c b/examples/retag/transcode.c
new file mode 100644 (file)
index 0000000..c41f96e
--- /dev/null
@@ -0,0 +1,106 @@
+/* 
+ * This example shows how to use interfaces and the tag subsystem.
+ * It takes an mp3 file as input, and makes an ogg file out of it. While doing
+ * this, it parses the filename and sets artist and title in the ogg file.
+ * It assumes the filename to be "<artist> - <title>.mp3"
+ * 
+ * Run the program as "retag <mp3 file>"
+ *
+ * To run this program, you need to have the gst-plugins package (specifically
+ * the vorbis and mad plugins) installed.
+ */
+
+/* main header */
+#include <gst/gst.h>
+/* and a header we need for the string manipulation */
+#include <string.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  GstElement *bin, *filesrc, *decoder, *encoder, *filesink;
+  gchar *artist, *title, *ext, *filename;
+  
+  /* initialize GStreamer */
+  gst_init (&argc, &argv);
+
+  /* check that the argument is there */
+  if (argc != 2) {
+    g_print ("usage: %s <mp3 file>\n", argv[0]);
+    return 1;
+  }
+
+  /* parse the mp3 name */
+  artist = strrchr (argv[1], '/');
+  if (artist == NULL)
+    artist = argv[1];
+  artist = g_strdup (artist);
+  ext = strrchr (artist, '.');
+  if (ext) *ext = '\0';
+  title = strstr (artist, " - ");
+  if (title == NULL) {
+    g_print ("The format of the mp3 file is invalid.\n");
+    return 1;
+  }
+  *title = '\0';
+  title += 3;
+  
+  
+  /* create a new bin to hold the elements */
+  bin = gst_pipeline_new ("pipeline");
+  g_assert (bin);
+
+  /* create a file reader */
+  filesrc = gst_element_factory_make ("filesrc", "disk_source");
+  g_assert (filesrc);
+
+  /* now it's time to get the decoder */
+  decoder = gst_element_factory_make ("mad", "decode");
+  if (!decoder) {
+    g_print ("could not find plugin \"mad\"");
+    return 1;
+  }
+
+  /* create the encoder */
+  encoder = gst_element_factory_make ("vorbisenc", "encoder");
+  if (!encoder) {
+    g_print ("cound not find plugin \"vorbisenc\"");
+    return 1;
+  }
+  
+  /* and a file writer */
+  filesink = gst_element_factory_make ("filesink", "filesink");
+  g_assert (filesink);
+
+  /* set the filenames */
+  filename = g_strdup_printf ("%s.ogg", argv[1]); /* easy solution */
+  g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
+  g_object_set (G_OBJECT (filesink), "location", filename, NULL);
+  g_free (filename);
+
+  /* make sure the tag setter uses our stuff 
+     (though that should already be default) */
+  gst_tag_setter_set_merge_mode (GST_TAG_SETTER (encoder), GST_TAG_MERGE_KEEP);
+  /* set the tagging information */
+  gst_tag_setter_add (GST_TAG_SETTER (encoder), GST_TAG_MERGE_REPLACE,
+                     GST_TAG_ARTIST, artist, 
+                     GST_TAG_TITLE, title, 
+                     NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add_many (GST_BIN (bin), filesrc, decoder, encoder, filesink, NULL);
+
+  /* link the elements */
+  gst_element_link_many (filesrc, decoder, encoder, filesink, NULL);
+  
+  /* start playing */
+  gst_element_set_state (bin, GST_STATE_PLAYING);
+
+  while (gst_bin_iterate (GST_BIN (bin)));
+
+  /* stop the bin */
+  gst_element_set_state (bin, GST_STATE_NULL);
+
+  return 0;
+}
+
index 64e0e5a..a9fe911 100644 (file)
@@ -114,6 +114,8 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES =          \
        gstscheduler.c          \
        gststructure.c          \
        gstsystemclock.c        \
+       gsttag.c                \
+       gsttaginterface.c       \
        gstthread.c             \
        gstthreaddummy.c        \
        $(GST_TRACE_SRC)        \
@@ -131,6 +133,8 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES =          \
 
 
 BUILT_SOURCES = gstversion.h gstconfig.h gstmarshal.h gstmarshal.c gstenumtypes.h $(GST_ENUMTYPES_SRC) 
+CLEANFILES = gstmarshal.h gstmarshal.c gstenumtypes.h $(GST_ENUMTYPES_SRC)
+DISTCLEANFILES = gstversion.h gstconfig.h
 
 libgstreamer_@GST_MAJORMINOR@_la_CFLAGS = -D_GNU_SOURCE \
        $(GST_CFLAGS) \
@@ -176,6 +180,8 @@ gst_headers =                       \
        gstscheduler.h          \
        gststructure.h          \
        gstsystemclock.h        \
+       gsttag.h                \
+       gsttaginterface.h       \
        gstthread.h             \
        gsttrace.h              \
        gsttrashstack.h         \
@@ -238,12 +244,4 @@ gstenumtypes.c: $(gst_headers)
        --vtail "      { 0, NULL, NULL }\n    };\n    etype = g_@type@_register_static (\"@EnumName@\", values);\n  }\n  return etype;\n}\n" \
        $^ > gstenumtypes.c
 
-# Don't want the generated marshal files in the dist
-dist-hook:
-       rm -f $(distdir)/gstmarshal.c $(distdir)/gstmarshal.h
-
-# Clean generated files
-distclean-local:
-       rm -f $(top_builddir)/gst/gstmarshal.c $(top_builddir)/gst/gstmarshal.h
-
 EXTRA_DIST = ROADMAP
index 8b1ef95..f81e567 100644 (file)
--- a/gst/gst.c
+++ b/gst/gst.c
@@ -537,6 +537,7 @@ init_post (void)
   _gst_plugin_initialize ();
   _gst_event_initialize ();
   _gst_buffer_initialize ();
+  _gst_tag_initialize ();
 
 #ifndef GST_DISABLE_REGISTRY
   if (!_gst_registry_fixed) {
index 8f48c40..45662fe 100644 (file)
--- a/gst/gst.h
+++ b/gst/gst.h
 #include <glib.h>
 #include <popt.h>
 
-#include <gst/gstversion.h>
+#include <gst/gstenumtypes.h>
 #include <gst/gsttypes.h>
+#include <gst/gstversion.h>
 
-#include <gst/gstinfo.h>
-#include <gst/gstobject.h>
-#include <gst/gstpad.h>
+#include <gst/gstautoplug.h>
+#include <gst/gstbin.h>
 #include <gst/gstbuffer.h>
 #include <gst/gstbufferpool-default.h>
+#include <gst/gstcaps.h>
+#include <gst/gstclock.h>
 #include <gst/gstcpu.h>
 #include <gst/gstelement.h>
-#include <gst/gstbin.h>
+#include <gst/gstevent.h>
 #include <gst/gstindex.h>
+#include <gst/gstinfo.h>
+#include <gst/gstinterface.h>
+#include <gst/gstobject.h>
+#include <gst/gstpad.h>
 #include <gst/gstpipeline.h>
+#include <gst/gstplugin.h>
+#include <gst/gstprops.h>
+#include <gst/gstscheduler.h>
+#include <gst/gststructure.h>
+#include <gst/gstsystemclock.h>
+#include <gst/gsttag.h>
+#include <gst/gsttaginterface.h>
 #include <gst/gstthread.h>
+#include <gst/gsttrace.h>
 #include <gst/gsttypefind.h>
-#include <gst/gstautoplug.h>
-#include <gst/gstcaps.h>
-#include <gst/gststructure.h>
-#include <gst/gstprops.h>
-#include <gst/gstplugin.h>
 #include <gst/gsturi.h>
 #include <gst/gsturitype.h>
 #include <gst/gstutils.h>
-#include <gst/gsttrace.h>
-#include <gst/gstxml.h>
-#include <gst/gstscheduler.h>
-#include <gst/gstevent.h>
-#include <gst/gstclock.h>
-#include <gst/gstsystemclock.h>
-#include <gst/gstinterface.h>
 #include <gst/gstvalue.h>
+#include <gst/gstxml.h>
 
 #include <gst/gstparse.h>
 #include <gst/gstregistry.h>
 #include <gst/gstregistrypool.h>
-#include <gst/gstenumtypes.h>
 
 /* API compatibility stuff */
 #include <gst/gstcompat.h>
index 6df3cf8..3a01d34 100644 (file)
@@ -167,6 +167,7 @@ gst_buffer_default_copy (GstBuffer *buffer)
   GST_BUFFER_TIMESTAMP (copy)   = GST_BUFFER_TIMESTAMP (buffer);
   GST_BUFFER_DURATION (copy)    = GST_BUFFER_DURATION (buffer);
   GST_BUFFER_OFFSET (copy)      = GST_BUFFER_OFFSET (buffer);
+  GST_BUFFER_OFFSET_END (copy)          = GST_BUFFER_OFFSET_END (buffer);
   GST_BUFFER_BUFFERPOOL (copy)   = NULL;
   GST_BUFFER_POOL_PRIVATE (copy) = NULL;
 
@@ -204,6 +205,7 @@ gst_buffer_new (void)
   GST_BUFFER_TIMESTAMP (newbuf)    = GST_CLOCK_TIME_NONE;
   GST_BUFFER_DURATION (newbuf)     = GST_CLOCK_TIME_NONE;
   GST_BUFFER_OFFSET (newbuf)       = GST_BUFFER_OFFSET_NONE;
+  GST_BUFFER_OFFSET_END (newbuf)   = GST_BUFFER_OFFSET_NONE;
   GST_BUFFER_BUFFERPOOL (newbuf)   = NULL;
   GST_BUFFER_POOL_PRIVATE (newbuf) = NULL;
 
@@ -335,8 +337,9 @@ gst_buffer_create_sub (GstBuffer *parent, guint offset, guint size)
     GST_BUFFER_TIMESTAMP (buffer)    = GST_CLOCK_TIME_NONE;
     GST_BUFFER_OFFSET (buffer)       = GST_BUFFER_OFFSET_NONE;
   }
-  GST_BUFFER_DURATION (buffer)     = GST_CLOCK_TIME_NONE;
 
+  GST_BUFFER_DURATION (buffer)     = GST_CLOCK_TIME_NONE;
+  GST_BUFFER_OFFSET_END (buffer)   = GST_BUFFER_OFFSET_NONE;
   /* make sure nobody overwrites data as it would overwrite in the parent.
    * data in parent cannot be overwritten because we hold a ref */
   GST_DATA_FLAG_SET (parent, GST_DATA_READONLY);
@@ -453,13 +456,17 @@ gst_buffer_span (GstBuffer *buf1, guint32 offset, GstBuffer *buf2, guint32 len)
   /* if we completely merged the two buffers (appended), we can
    * calculate the duration too. Also make sure we's not messing with
    * invalid DURATIONS */
-  if (offset == 0 && buf1->size + buf2->size == len &&
-      GST_BUFFER_DURATION_IS_VALID (buf1) &&
-      GST_BUFFER_DURATION_IS_VALID (buf2)) 
-  {
-    /* add duration */
-    GST_BUFFER_DURATION (newbuf) = GST_BUFFER_DURATION (buf1) + 
-                                   GST_BUFFER_DURATION (buf2);
+  if (offset == 0 && buf1->size + buf2->size == len) {
+    if (GST_BUFFER_DURATION_IS_VALID (buf1) &&
+       GST_BUFFER_DURATION_IS_VALID (buf2)) {
+      /* add duration */
+      GST_BUFFER_DURATION (newbuf) = GST_BUFFER_DURATION (buf1) + 
+                                   GST_BUFFER_DURATION (buf2);
+    }
+    if (GST_BUFFER_OFFSET_END_IS_VALID (buf2)) {
+      /* add offset_end */
+      GST_BUFFER_OFFSET_END (newbuf) = GST_BUFFER_OFFSET_END (buf2);
+    }
   }
 
   return newbuf;
index 4191f7b..ab19247 100644 (file)
@@ -63,6 +63,7 @@ extern GType _gst_buffer_pool_type;
 #define GST_BUFFER_DURATION(buf)               (GST_BUFFER(buf)->duration)
 #define GST_BUFFER_FORMAT(buf)                 (GST_BUFFER(buf)->format)
 #define GST_BUFFER_OFFSET(buf)                 (GST_BUFFER(buf)->offset)
+#define GST_BUFFER_OFFSET_END(buf)             (GST_BUFFER(buf)->offset_end)
 #define GST_BUFFER_BUFFERPOOL(buf)             (GST_BUFFER(buf)->pool)
 #define GST_BUFFER_POOL_PRIVATE(buf)           (GST_BUFFER(buf)->pool_private)
 
@@ -72,6 +73,7 @@ extern GType _gst_buffer_pool_type;
 #define GST_BUFFER_DURATION_IS_VALID(buffer)   (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buffer)))
 #define GST_BUFFER_TIMESTAMP_IS_VALID(buffer)  (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buffer)))
 #define GST_BUFFER_OFFSET_IS_VALID(buffer)     (GST_BUFFER_OFFSET (buffer) != GST_BUFFER_OFFSET_NONE)
+#define GST_BUFFER_OFFSET_END_IS_VALID(buffer) (GST_BUFFER_OFFSET_END (buffer) != GST_BUFFER_OFFSET_NONE)
 #define GST_BUFFER_MAXSIZE_IS_VALID(buffer)    (GST_BUFFER_MAXSIZE (buffer) != GST_BUFFER_MAXSIZE_NONE)
 
 typedef enum {
@@ -101,8 +103,11 @@ struct _GstBuffer {
    * for video frames, this could be the number of frames,
    * for audio data, this could be the number of audio samples,
    * for file data or compressed data, this could be the number of bytes
+   * offset_end is the last offset contained in the buffer. The format specifies
+   * the meaning of both of them exactly.
    */
   guint64               offset;
+  guint64               offset_end;
 
   /* this is a pointer to the buffer pool (if any) */
   GstBufferPool                *pool;
index d0758bb..5af2331 100644 (file)
@@ -39,6 +39,8 @@ enum {
   PAD_REMOVED,
   ERROR,
   EOS,
+  FOUND_TAG,
+  /* add more above */
   LAST_SIGNAL
 };
 
@@ -65,6 +67,7 @@ static void                   gst_element_dispose             (GObject *object);
 
 static GstElementStateReturn   gst_element_change_state        (GstElement *element);
 static void                    gst_element_error_func          (GstElement* element, GstElement *source, gchar *errormsg);
+static void                    gst_element_found_tag_func      (GstElement* element, GstElement *source, GstTagList *tag_list);
 
 #ifndef GST_DISABLE_LOADSAVE
 static xmlNodePtr              gst_element_save_thyself        (GstObject *object, xmlNodePtr parent);
@@ -127,11 +130,16 @@ gst_element_class_init (GstElementClass *klass)
     g_signal_new ("error", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (GstElementClass, error), NULL, NULL,
                   gst_marshal_VOID__OBJECT_STRING, G_TYPE_NONE, 2,
-                  G_TYPE_OBJECT, G_TYPE_STRING);
+                  GST_TYPE_ELEMENT, G_TYPE_STRING);
   gst_element_signals[EOS] =
     g_signal_new ("eos", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
-                  G_STRUCT_OFFSET (GstElementClass,eos), NULL, NULL,
+                  G_STRUCT_OFFSET (GstElementClass, eos), NULL, NULL,
                   gst_marshal_VOID__VOID, G_TYPE_NONE, 0);
+  gst_element_signals[FOUND_TAG] =
+    g_signal_new ("found-tag", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GstElementClass, found_tag), NULL, NULL,
+                  gst_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2,
+                 GST_TYPE_ELEMENT, G_TYPE_POINTER);
 
   gobject_class->set_property          = GST_DEBUG_FUNCPTR (gst_element_real_set_property);
   gobject_class->get_property          = GST_DEBUG_FUNCPTR (gst_element_real_get_property);
@@ -145,6 +153,7 @@ gst_element_class_init (GstElementClass *klass)
 
   klass->change_state                  = GST_DEBUG_FUNCPTR (gst_element_change_state);
   klass->error                         = GST_DEBUG_FUNCPTR (gst_element_error_func);
+  klass->found_tag                     = GST_DEBUG_FUNCPTR (gst_element_found_tag_func);
   klass->padtemplates                  = NULL;
   klass->numpadtemplates               = 0;
 
@@ -931,7 +940,9 @@ gst_element_remove_pad (GstElement *element, GstPad *pad)
   /* FIXME: what if someone calls _remove_pad instead of 
     _remove_ghost_pad? */
   if (GST_IS_REAL_PAD (pad)) {
-    g_return_if_fail (GST_RPAD_PEER (pad) == NULL);
+    if (GST_RPAD_PEER (pad) != NULL) {
+      gst_pad_unlink (pad, GST_PAD (GST_RPAD_PEER (pad)));
+    }
   }
   
   /* remove it from the list */
@@ -2924,6 +2935,71 @@ gst_element_set_loop_function (GstElement *element,
     }
   }
 }
+static inline void
+gst_element_emit_found_tag (GstElement* element, GstElement *source, GstTagList *tag_list)
+{
+  gst_object_ref (GST_OBJECT (element));
+  g_signal_emit (element, gst_element_signals[FOUND_TAG], 0, source, tag_list);
+  gst_object_unref (GST_OBJECT (element));
+}
+static void
+gst_element_found_tag_func (GstElement* element, GstElement *source, GstTagList *tag_list)
+{
+  /* tell the parent */
+  if (GST_OBJECT_PARENT (element)) {
+    GST_CAT_LOG_OBJECT (GST_CAT_EVENT, element, "forwarding tag event to %s", 
+              GST_OBJECT_NAME (GST_OBJECT_PARENT (element)));
+    gst_element_emit_found_tag (GST_ELEMENT (GST_OBJECT_PARENT (element)), source, tag_list);
+  }  
+}
+/**
+ * gst_element_found_tags:
+ * @element: the element that found the tags
+ * @tag_list: the found tags
+ *
+ * This function emits the found_tags signal. This is a recursive signal, so
+ * every parent will emit that signal, too, before this function returns.
+ * Only emit this signal, when you extracted these tags out of the data stream,
+ * not when you handle an event.
+ */
+void
+gst_element_found_tags (GstElement *element, GstTagList *tag_list)
+{
+  gst_element_emit_found_tag (element, element, tag_list);
+}
+/**
+ * gst_element_found_tags_for_pad:
+ * @element: element that found the tag
+ * @pad: src pad the tags correspond to
+ * @timestamp: time the tags were found
+ * @list: the taglist
+ *
+ * This is a convenience routine for tag finding. Most of the time you only
+ * want to push the found tags down one pad, in that case this function is for
+ * you. It takes ownership of the taglist, emits the found-tag signal and pushes 
+ * a tag event down the pad.
+ */
+void
+gst_element_found_tags_for_pad (GstElement *element, GstPad *pad, GstClockTime timestamp,
+                                GstTagList *list)
+{
+  GstEvent *tag_event;
+
+  g_return_if_fail (GST_IS_ELEMENT (element));
+  g_return_if_fail (GST_IS_REAL_PAD (pad));
+  g_return_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SRC);
+  g_return_if_fail (element == GST_PAD_PARENT (pad));
+  g_return_if_fail (list != NULL);
+
+  tag_event = gst_event_new_tag (list);
+  GST_EVENT_TIMESTAMP (tag_event) = timestamp;
+  gst_element_found_tags (element, gst_event_tag_get_list (tag_event));
+  if (GST_PAD_IS_USABLE (pad)) {
+    gst_pad_push (pad, GST_DATA (tag_event));
+  } else {
+    gst_data_unref (GST_DATA (tag_event));
+  }
+}
 
 static inline void
 gst_element_set_eos_recursive (GstElement *element)
index 2a98341..f40547f 100644 (file)
@@ -32,6 +32,7 @@
 #include <gst/gstplugin.h>
 #include <gst/gstpluginfeature.h>
 #include <gst/gstindex.h>
+#include <gst/gsttag.h>
 
 G_BEGIN_DECLS
 
@@ -137,7 +138,6 @@ typedef enum {
 } GstElementFlags;
 
 #define GST_ELEMENT_IS_THREAD_SUGGESTED(obj)   (GST_FLAG_IS_SET(obj,GST_ELEMENT_THREAD_SUGGESTED))
-#define GST_ELEMENT_IS_EOS(obj)                        (GST_FLAG_IS_SET(obj,GST_ELEMENT_EOS))
 #define GST_ELEMENT_IS_EVENT_AWARE(obj)                (GST_FLAG_IS_SET(obj,GST_ELEMENT_EVENT_AWARE))
 #define GST_ELEMENT_IS_DECOUPLED(obj)          (GST_FLAG_IS_SET(obj,GST_ELEMENT_DECOUPLED))
 
@@ -206,6 +206,7 @@ struct _GstElementClass {
   void (*pad_removed)  (GstElement *element, GstPad *pad);
   void (*error)                (GstElement *element, GstElement *source, gchar *error);
   void (*eos)          (GstElement *element);
+  void (*found_tag)    (GstElement *element, GstElement *source, GstTagList *tag_list);
 
   /* local pointers for get/set */
   void (*set_property)         (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
@@ -254,7 +255,6 @@ void                        gst_element_class_set_details           (GstElementClass *klass,
 void                   gst_element_default_error               (GObject *object, GstObject *orig, gchar *error);
 
 GType                  gst_element_get_type            (void);
-
 void                   gst_element_set_loop_function   (GstElement *element,
                                                         GstElementLoopFunction loop);
 
@@ -350,6 +350,10 @@ gboolean           gst_element_convert             (GstElement *element,
                                                         GstFormat  src_format,  gint64  src_value,
                                                         GstFormat *dest_format, gint64 *dest_value);
 
+void                   gst_element_found_tags          (GstElement *element, GstTagList *tag_list);
+void                   gst_element_found_tags_for_pad  (GstElement *element, GstPad *pad, GstClockTime timestamp, 
+                                                        GstTagList *list);
+
 void                   gst_element_set_eos             (GstElement *element);
 
 void                   gst_element_error               (GstElement *element, const gchar *error, ...);
@@ -394,6 +398,8 @@ struct _GstElementFactory {
   GList *              padtemplates;
   guint                        numpadtemplates;
 
+  GList *interfaces;           /* interfaces this element implements */
+
   GST_OBJECT_PADDING
 };
 
@@ -431,6 +437,8 @@ gboolean            gst_element_factory_can_sink_caps       (GstElementFactory *factory,
 
 void                   __gst_element_factory_add_pad_template  (GstElementFactory *elementfactory,
                                                                 GstPadTemplate *templ);
+void                   __gst_element_factory_add_interface     (GstElementFactory *elementfactory,
+                                                                const gchar *interfacename);
 
 
 G_END_DECLS
index 595f0ca..8e1e7ab 100644 (file)
@@ -84,6 +84,8 @@ gst_element_factory_init (GstElementFactory *factory)
 {
   factory->padtemplates = NULL;
   factory->numpadtemplates = 0;
+
+  factory->interfaces = NULL;
 }
 /**
  * gst_element_factory_find:
@@ -148,6 +150,10 @@ gst_element_factory_cleanup (GstElementFactory *factory)
   g_list_free (factory->padtemplates);
   factory->padtemplates = NULL;
   factory->numpadtemplates = 0;
+  
+  g_list_foreach (factory->interfaces, (GFunc) g_free, NULL);
+  g_list_free (factory->interfaces);
+  factory->interfaces = NULL;
 }
 /**
  * gst_element_register:
@@ -165,6 +171,8 @@ gboolean
 gst_element_register (GstPlugin *plugin, const gchar *name, guint rank, GType type)
 {
   GstElementFactory *factory;
+  GType *interfaces;
+  guint n_interfaces, i;
   GstElementClass *klass;
 
   g_return_val_if_fail (name != NULL, FALSE);
@@ -190,6 +198,12 @@ gst_element_register (GstPlugin *plugin, const gchar *name, guint rank, GType ty
   g_list_foreach (factory->padtemplates, (GFunc) g_object_ref, NULL);
   factory->numpadtemplates = klass->numpadtemplates;
 
+  interfaces = g_type_interfaces (type, &n_interfaces);
+  for (i = 0; i < n_interfaces; i++) {
+    __gst_element_factory_add_interface (factory, g_type_name (interfaces[i]));
+  }
+  g_free (interfaces);
+  
   gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank);
 
   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
@@ -388,6 +402,23 @@ gst_element_factory_get_num_pad_templates (GstElementFactory *factory)
   return factory->numpadtemplates;
 }
 /**
+ * __gst_element_factory_add_interface:
+ * @elementfactory: The elementfactory to add the interface to
+ * @interfacename: Name of the interface
+ *
+ * Adds the given interfacename to the list of implemented interfaces of the
+ * element.
+ */
+void
+__gst_element_factory_add_interface (GstElementFactory *elementfactory, const gchar *interfacename)
+{
+  g_return_if_fail (GST_IS_ELEMENT_FACTORY (elementfactory));
+  g_return_if_fail (interfacename != NULL);
+  g_return_if_fail (interfacename[0] != '\0'); /* no empty string */
+  
+  elementfactory->interfaces = g_list_prepend (elementfactory->interfaces, g_strdup (interfacename));
+}
+/**
  * gst_element_factory_get_pad_templates:
  * @factory: a #GstElementFactory
  * 
index a213e8a..ad63b78 100644 (file)
@@ -29,6 +29,8 @@
 #include "gstmemchunk.h"
 #include "gstevent.h"
 #include "gstlog.h"
+#include "gsttag.h"
+
 #ifndef GST_DISABLE_TRACE
 /* #define GST_WITH_ALLOC_TRACE */
 #include "gsttrace.h"
@@ -70,6 +72,12 @@ _gst_event_copy (GstEvent *event)
   memcpy (copy, event, sizeof (GstEvent));
   
   /* FIXME copy/ref additional fields */
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_TAG:
+      copy->event_data.structure.structure = gst_structure_copy (event->event_data.structure.structure);
+    default:
+      break;
+  }
 
   return copy;
 }
@@ -83,6 +91,8 @@ _gst_event_free (GstEvent* event)
     gst_object_unref (GST_EVENT_SRC (event));
   }
   switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_TAG:
+      gst_tag_list_free (event->event_data.structure.structure);
     default:
       break;
   }
index ab8ace7..a060dc9 100644 (file)
@@ -48,7 +48,8 @@ typedef enum {
   GST_EVENT_FILLER             = 12,
   GST_EVENT_TS_OFFSET          = 13,
   GST_EVENT_INTERRUPT          = 14,
-  GST_EVENT_NAVIGATION         = 15
+  GST_EVENT_NAVIGATION         = 15,
+  GST_EVENT_TAG                        = 16
 } GstEventType;
 
 extern GType _gst_event_type;
index 843a31f..2fefe85 100644 (file)
@@ -258,11 +258,11 @@ void gst_debug_log (GstDebugCategory *category, GstDebugLevel level,
   va_list var_args;
   
   va_start (var_args, format);
-  gst_debug_logv (category, level, file, function, line, object, format, var_args);
+  gst_debug_log_valist (category, level, file, function, line, object, format, var_args);
   va_end (var_args);
 }
 /**
- * gst_debug_logv:
+ * gst_debug_log_valist:
  * @category: category to log
  * @level: level of the message is in
  * @file: the file that emitted the message, usually the __FILE__ identifier
@@ -274,9 +274,9 @@ void gst_debug_log (GstDebugCategory *category, GstDebugLevel level,
  * 
  * Logs the given message using the currently registered debugging handlers.
  */
-void gst_debug_logv (GstDebugCategory *category, GstDebugLevel level,
-                    const gchar *file, const gchar *function, gint line,
-                    GObject *object, gchar *format, va_list args)
+void gst_debug_log_valist (GstDebugCategory *category, GstDebugLevel level,
+                          const gchar *file, const gchar *function, gint line,
+                          GObject *object, gchar *format, va_list args)
 {
   gchar *message;
   LogFuncEntry *entry;
index 17ab305..a1a91e8 100644 (file)
@@ -162,7 +162,7 @@ void                gst_debug_log                   (GstDebugCategory *     category,
                                                 GObject *              object,
                                                 gchar *                format,
                                                 ...)  G_GNUC_PRINTF (7, 8) G_GNUC_NO_INSTRUMENT;
-void           gst_debug_logv                  (GstDebugCategory *     category,
+void           gst_debug_log_valist            (GstDebugCategory *     category,
                                                 GstDebugLevel          level,
                                                 const gchar *          file,
                                                 const gchar *          function,
@@ -388,7 +388,7 @@ const gchar*        _gst_debug_nameof_funcptr       (void *                 ptr);
 
 #ifdef __GNUC__
 #  pragma GCC poison gst_debug_log
-#  pragma GCC poison gst_debug_logv
+#  pragma GCC poison gst_debug_log_valist
 #  pragma GCC poison gst_debug_log_default
 #  pragma GCC poison _gst_debug_category_new
 #endif
index e01f5be..f92dd32 100644 (file)
@@ -48,7 +48,7 @@ GType gst_structure_get_type(void)
 
 void _gst_structure_initialize(void)
 {
-  static GTypeValueTable type_value_table = {
+  static const GTypeValueTable type_value_table = {
     _gst_structure_value_init,
     _gst_structure_value_free,
     _gst_structure_value_copy,
@@ -58,7 +58,7 @@ void _gst_structure_initialize(void)
     NULL,
     NULL,
   };
-  static GTypeInfo structure_info = {
+  static const GTypeInfo structure_info = {
     0,
     NULL,
     NULL,
@@ -84,6 +84,27 @@ void _gst_structure_initialize(void)
 }
 
 /**
+ * gst_structure_id_empty_new:
+ * @name: name of new structure
+ *
+ * Creates a new, empty #GstStructure with the given name.
+ *
+ * Returns: a new, empty #GstStructure
+ */
+GstStructure *gst_structure_id_empty_new(GQuark quark)
+{
+  GstStructure *structure;
+
+  g_return_val_if_fail(quark != 0, NULL);
+
+  structure = g_new0(GstStructure, 1);
+  structure->name = quark;
+  structure->fields = g_array_new(FALSE,TRUE,sizeof(GstStructureField));
+
+  return structure;
+}
+
+/**
  * gst_structure_empty_new:
  * @name: name of new structure
  *
@@ -262,15 +283,15 @@ void gst_structure_set_name(GstStructure *structure, const gchar *name)
  * value is freed.
  */
 void gst_structure_id_set_value(GstStructure *structure, GQuark fieldname,
-    GValue *value)
+    const GValue *value)
 {
-  GstStructureField field = { 0 };
+  GstStructureField field = { 0, { 0, } };
 
   g_return_if_fail(structure != NULL);
   g_return_if_fail(G_IS_VALUE(value));
 
   field.name = fieldname;
-  g_value_init(&field.value, G_TYPE_INT);
+  g_value_init(&field.value, G_VALUE_TYPE (value));
   g_value_copy(value, &field.value);
 
   gst_structure_set_field(structure, &field);
@@ -287,7 +308,7 @@ void gst_structure_id_set_value(GstStructure *structure, GQuark fieldname,
  * value is freed.
  */
 void gst_structure_set_value(GstStructure *structure, const gchar *field,
-    GValue *value)
+    const GValue *value)
 {
   g_return_if_fail(structure != NULL);
   g_return_if_fail(field != NULL);
@@ -355,13 +376,6 @@ void gst_structure_set_valist(GstStructure *structure, const gchar *fieldname,
         g_value_init(&field.value, G_TYPE_DOUBLE);
         g_value_set_double(&field.value, d);
        break;
-#if 0
-      case GST_TYPE_FOURCC:
-       i = va_arg(varargs, int);
-        g_value_init(&field.value, G_TYPE_FOURCC);
-        gst_value_set_fourcc(&field.value, i);
-       break;
-#endif
       case G_TYPE_BOOLEAN:
        i = va_arg(varargs, int);
         g_value_init(&field.value, G_TYPE_BOOLEAN);
@@ -373,7 +387,14 @@ void gst_structure_set_valist(GstStructure *structure, const gchar *fieldname,
         g_value_set_string(&field.value, s);
        break;
       default:
-       g_assert_not_reached();
+       if(type == GST_TYPE_FOURCC){
+         i = va_arg(varargs, int);
+         g_value_init(&field.value, GST_TYPE_FOURCC);
+         gst_value_set_fourcc(&field.value, i);
+         break;
+       }else{
+         g_critical("unimplemented vararg field type %d\n", (int)type);
+       }
        break;
     }
 
@@ -384,6 +405,32 @@ void gst_structure_set_valist(GstStructure *structure, const gchar *fieldname,
 }
 
 /**
+ * gst_structure_set_field_copy:
+ * @structure: a #GstStructure
+ * @field: the #GstStructureField to set
+ *
+ * Sets a field in the structure.  If the structure currently contains
+ * a field with the same name, it is replaced with the provided field.
+ * Otherwise, the field is added to the structure.  The field's value
+ * is deeply copied.
+ *
+ * This function is intended mainly for internal use.  The function
+ * #gst_structure_set() is recommended instead of this one.
+ */
+void gst_structure_set_field_copy (GstStructure *structure,
+    const GstStructureField *field)
+{
+  GstStructureField f = { 0 };
+  GType type = G_VALUE_TYPE (&field->value);
+
+  f.name = field->name;
+  g_value_init (&f.value, type);
+  g_value_copy (&field->value, &f.value);
+
+  gst_structure_set_field (structure, &f);
+}
+
+/**
  * gst_structure_set_field:
  * @structure: a #GstStructure
  * @field: the #GstStructureField to set
@@ -424,7 +471,7 @@ void gst_structure_set_field(GstStructure *structure, GstStructureField *field)
  *
  * Returns: the #GstStructureField with the given ID
  */
-GstStructureField *gst_structure_id_get_field(GstStructure *structure,
+GstStructureField *gst_structure_id_get_field(const GstStructure *structure,
     GQuark field_id)
 {
   GstStructureField *field;
@@ -452,7 +499,7 @@ GstStructureField *gst_structure_id_get_field(GstStructure *structure,
  * Returns: the #GstStructureField with the given name
  */
 GstStructureField *
-gst_structure_get_field(GstStructure *structure, const gchar *fieldname)
+gst_structure_get_field(const GstStructure *structure, const gchar *fieldname)
 {
   g_return_val_if_fail(structure != NULL, NULL);
   g_return_val_if_fail(fieldname != NULL, NULL);
@@ -471,7 +518,7 @@ gst_structure_get_field(GstStructure *structure, const gchar *fieldname)
  * Returns: the #GValue corresponding to the field with the given name.
  */
 const GValue *
-gst_structure_get_value(GstStructure *structure, const gchar *fieldname)
+gst_structure_get_value(const GstStructure *structure, const gchar *fieldname)
 {
   GstStructureField *field;
 
@@ -525,6 +572,30 @@ gst_structure_remove_field(GstStructure *structure, const gchar *fieldname)
 }
 
 /**
+ * gst_structure_remove_all_fields:
+ * @structure: a #GstStructure
+ *
+ * Removes all fields in a GstStructure. 
+ */
+void
+gst_structure_remove_all_fields(GstStructure *structure)
+{
+  GstStructureField *field;
+  int i;
+
+  g_return_if_fail(structure != NULL);
+
+  for (i = structure->fields->len - 1; i >= 0; i-- ) {
+    field = GST_STRUCTURE_FIELD(structure, i);
+
+    if (G_IS_VALUE (&field->value)) {
+      g_value_unset(&field->value);
+    }
+    structure->fields = g_array_remove_index (structure->fields, i);
+  }
+}
+
+/**
  * gst_structure_get_field_type:
  * @structure: a #GstStructure
  * @fieldname: the name of the field
@@ -536,7 +607,7 @@ gst_structure_remove_field(GstStructure *structure, const gchar *fieldname)
  * Returns: the #GValue of the field
  */
 GType
-gst_structure_get_field_type(GstStructure *structure, const gchar *fieldname)
+gst_structure_get_field_type(const GstStructure *structure, const gchar *fieldname)
 {
   GstStructureField *field;
 
@@ -558,7 +629,7 @@ gst_structure_get_field_type(GstStructure *structure, const gchar *fieldname)
  * Returns: the number of fields in the structure
  */
 gint
-gst_structure_n_fields(GstStructure *structure)
+gst_structure_n_fields(const GstStructure *structure)
 {
   g_return_val_if_fail(structure != NULL, 0);
 
@@ -597,7 +668,7 @@ gst_structure_field_foreach (GstStructure *structure,
  * Returns: TRUE if the structure contains a field with the given name
  */
 gboolean
-gst_structure_has_field(GstStructure *structure, const gchar *fieldname)
+gst_structure_has_field(const GstStructure *structure, const gchar *fieldname)
 {
   GstStructureField *field;
 
@@ -620,7 +691,7 @@ gst_structure_has_field(GstStructure *structure, const gchar *fieldname)
  * Returns: TRUE if the structure contains a field with the given name and type
  */
 gboolean
-gst_structure_has_field_typed(GstStructure *structure, const gchar *fieldname,
+gst_structure_has_field_typed(const GstStructure *structure, const gchar *fieldname,
     GType type)
 {
   GstStructureField *field;
@@ -650,7 +721,7 @@ gst_structure_has_field_typed(GstStructure *structure, const gchar *fieldname,
  * Returns: TRUE if the value could be set correctly
  */
 gboolean
-gst_structure_get_boolean(GstStructure *structure, const gchar *fieldname,
+gst_structure_get_boolean(const GstStructure *structure, const gchar *fieldname,
     gboolean *value)
 {
   GstStructureField *field;
@@ -681,7 +752,7 @@ gst_structure_get_boolean(GstStructure *structure, const gchar *fieldname,
  * Returns: TRUE if the value could be set correctly
  */
 gboolean
-gst_structure_get_int(GstStructure *structure, const gchar *fieldname,
+gst_structure_get_int(const GstStructure *structure, const gchar *fieldname,
     gint *value)
 {
   GstStructureField *field;
@@ -713,7 +784,7 @@ gst_structure_get_int(GstStructure *structure, const gchar *fieldname,
  * Returns: TRUE if the value could be set correctly
  */
 gboolean
-gst_structure_get_fourcc(GstStructure *structure, const gchar *fieldname,
+gst_structure_get_fourcc(const GstStructure *structure, const gchar *fieldname,
     guint32 *value)
 {
   GstStructureField *field;
@@ -744,7 +815,7 @@ gst_structure_get_fourcc(GstStructure *structure, const gchar *fieldname,
  *
  * Returns: TRUE if the value could be set correctly
  */
-gboolean gst_structure_get_double(GstStructure *structure,
+gboolean gst_structure_get_double(const GstStructure *structure,
     const gchar *fieldname, gdouble *value)
 {
   GstStructureField *field;
@@ -779,7 +850,7 @@ gboolean gst_structure_get_double(GstStructure *structure,
  * Returns: a pointer to the string
  */
 const gchar *
-gst_structure_get_string(GstStructure *structure, const gchar *fieldname)
+gst_structure_get_string(const GstStructure *structure, const gchar *fieldname)
 {
   GstStructureField *field;
 
@@ -803,7 +874,7 @@ gst_structure_get_string(GstStructure *structure, const gchar *fieldname)
  * Returns: a pointer to string allocated by g_malloc()
  */
 gchar *
-gst_structure_to_string(GstStructure *structure)
+gst_structure_to_string(const GstStructure *structure)
 {
   GstStructureField *field;
   GString *s;
index 2717349..4ea6b10 100644 (file)
@@ -51,6 +51,7 @@ GType gst_structure_get_type(void);
 void _gst_structure_initialize(void);
 
 GstStructure *gst_structure_empty_new(const gchar *name);
+GstStructure *gst_structure_id_empty_new(GQuark quark);
 GstStructure *gst_structure_new(const gchar *name,
     const gchar *firstfield, ...);
 GstStructure *gst_structure_new_valist(const gchar *name,
@@ -60,46 +61,49 @@ void gst_structure_free(GstStructure *structure);
 
 const gchar *gst_structure_get_name(GstStructure *structure);
 void gst_structure_set_name(GstStructure *structure, const gchar *name);
-void gst_structure_set_field(GstStructure *structure,
+void gst_structure_set_field_copy (GstStructure *structure,
+    const GstStructureField *field);
+void gst_structure_set_field (GstStructure *structure,
     GstStructureField *field);
 
 void gst_structure_id_set_value(GstStructure *structure, GQuark field,
-    GValue *value);
+    const GValue *value);
 void gst_structure_set_value(GstStructure *structure, const gchar *field,
-    GValue *value);
+    const GValue *value);
 void gst_structure_set(GstStructure *structure, const gchar *field, ...);
 void gst_structure_set_valist(GstStructure *structure, const gchar *field,
     va_list varargs);
-const GValue *gst_structure_get_value(GstStructure *structure, const gchar *field);
-GstStructureField *gst_structure_get_field(GstStructure *structure,
+const GValue *gst_structure_get_value(const GstStructure *structure, const gchar *field);
+GstStructureField *gst_structure_get_field(const GstStructure *structure,
         const gchar *fieldname);
-GstStructureField *gst_structure_id_get_field(GstStructure *structure,
+GstStructureField *gst_structure_id_get_field(const GstStructure *structure,
         GQuark fieldname);
 void gst_structure_remove_field(GstStructure *structure, const gchar *field);
+void gst_structure_remove_all_fields(GstStructure *structure);
 
-GType gst_structure_get_field_type(GstStructure *structure,
+GType gst_structure_get_field_type(const GstStructure *structure,
     const gchar *field);
 void gst_structure_field_foreach (GstStructure *structure,
     GstStructureForeachFunc func, gpointer user_data);
-gint gst_structure_n_fields(GstStructure *structure);
-gboolean gst_structure_has_field(GstStructure *structure, const gchar *field);
-gboolean gst_structure_has_field_typed(GstStructure *structure,
+gint gst_structure_n_fields(const GstStructure *structure);
+gboolean gst_structure_has_field(const GstStructure *structure, const gchar *field);
+gboolean gst_structure_has_field_typed(const GstStructure *structure,
     const gchar *field, GType type);
 
 /* utility functions */
 
-gboolean gst_structure_get_boolean(GstStructure *structure, const gchar *field,
+gboolean gst_structure_get_boolean(const GstStructure *structure, const gchar *field,
     gboolean *value);
-gboolean gst_structure_get_int(GstStructure *structure, const gchar *field,
+gboolean gst_structure_get_int(const GstStructure *structure, const gchar *field,
     gint *value);
-gboolean gst_structure_get_fourcc(GstStructure *structure, const gchar *field,
+gboolean gst_structure_get_fourcc(const GstStructure *structure, const gchar *field,
     guint32 *value);
-gboolean gst_structure_get_double(GstStructure *structure, const gchar *field,
+gboolean gst_structure_get_double(const GstStructure *structure, const gchar *field,
     gdouble *value);
-const gchar *gst_structure_get_string(GstStructure *structure,
+const gchar *gst_structure_get_string(const GstStructure *structure,
     const gchar *field);
 
-gchar * gst_structure_to_string(GstStructure *structure);
+gchar * gst_structure_to_string(const GstStructure *structure);
 GstStructure * gst_structure_from_string (const gchar *string);
 
 
diff --git a/gst/gsttag.c b/gst/gsttag.c
new file mode 100644 (file)
index 0000000..c6c4f3f
--- /dev/null
@@ -0,0 +1,831 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttag.c: tag support (aka metadata)
+ *
+ * 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_private.h"
+#include "gsttag.h"
+#include "gstinfo.h"
+#include "gstvalue.h"
+
+#include <gobject/gvaluecollector.h>
+#include <string.h>
+
+#define GST_TAG_IS_VALID(tag)          (gst_tag_get_info (tag) != NULL)
+
+typedef struct {
+  GType                        type;           /* type the data is in */
+
+  gchar *              nick;           /* translated name */
+  gchar *              blurb;          /* translated description of type */
+
+  GstTagMergeFunc      merge_func;     /* functions to merge the values */
+} GstTagInfo;
+
+#define TAGLIST "taglist"
+static GQuark gst_tag_list_quark;
+static GMutex *__tag_mutex;
+static GHashTable *__tags;
+#define TAG_LOCK g_mutex_lock (__tag_mutex)
+#define TAG_UNLOCK g_mutex_unlock (__tag_mutex)
+
+void
+_gst_tag_initialize (void)
+{
+  gst_tag_list_quark = g_quark_from_static_string (TAGLIST);
+  __tag_mutex = g_mutex_new ();
+  __tags = g_hash_table_new (g_direct_hash, g_direct_equal);
+  gst_tag_register (GST_TAG_TITLE,           
+                   G_TYPE_STRING,
+                   _("title"),
+                   _("commonly used title"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_ARTIST,
+                   G_TYPE_STRING,
+                   _("artist"),
+                   _("person(s) resposible for the recording"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_ALBUM,
+                   G_TYPE_STRING,
+                   _("album"),
+                   _("album containing this data"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_DATE,
+                   G_TYPE_UINT, /* FIXME: own data type for dates? */
+                   _("date"),
+                   _("date the data was created in julien days"),
+                   NULL);
+  gst_tag_register (GST_TAG_GENRE,
+                   G_TYPE_STRING,
+                   _("genre"), 
+                   _("genre this data belongs to"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_COMMENT,
+                   G_TYPE_STRING,
+                   _("comment"),
+                   _("free text commenting the data"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_TRACK_NUMBER,
+                   G_TYPE_UINT,
+                   _("track number"),
+                   _("track number inside a collection"),
+                   gst_tag_merge_use_first);
+  gst_tag_register (GST_TAG_TRACK_COUNT,
+                   G_TYPE_STRING,
+                   _("track count"),
+                   _("count of tracks inside collection this track belongs to"), 
+                   gst_tag_merge_use_first);
+  gst_tag_register (GST_TAG_LOCATION,
+                   G_TYPE_STRING,
+                   _("loccation"),
+                   _("original location of file as a URI"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_DESCRIPTION,
+                   G_TYPE_STRING,
+                   _("description"),
+                   _("short text describing the content of the data"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_VERSION,
+                   G_TYPE_STRING,
+                   _("version"),
+                   _("version of this data"),
+                   NULL);
+  gst_tag_register (GST_TAG_ISRC,
+                   G_TYPE_STRING,
+                   _("ISRC"),
+                   _("International Standard Recording Code - see http://www.ifpi.org/isrc/"),
+                   NULL);
+  gst_tag_register (GST_TAG_ORGANIZATION,
+                   G_TYPE_STRING,
+                   _("organization"),
+                   _("organization"), /* FIXME */
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_COPYRIGHT,
+                   G_TYPE_STRING,
+                   _("copyright"),
+                   _("copyright notice of the data"),
+                   NULL);
+  gst_tag_register (GST_TAG_CONTACT,
+                   G_TYPE_STRING,
+                   _("contact"),
+                   _("contact information"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_LICENSE,   
+                   G_TYPE_STRING,
+                   _("license"),
+                   _("license of data"),
+                   NULL);
+  gst_tag_register (GST_TAG_PERFORMER,
+                   G_TYPE_STRING,
+                   _("performer"),
+                   _("person(s) performing"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_DURATION,
+                   G_TYPE_UINT64,
+                   _("duration"),
+                   _("length in GStreamer time units (nanoseconds)"),
+                   NULL);
+  gst_tag_register (GST_TAG_CODEC,
+                   G_TYPE_STRING,
+                   _("codec"),
+                   _("codec the data is stored in"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_MINIMUM_BITRATE,
+                   G_TYPE_UINT,
+                   _("minimum bitrate"),
+                   _("minimum bitrate in bits/s"),
+                   NULL);
+  gst_tag_register (GST_TAG_BITRATE,
+                   G_TYPE_UINT,
+                   _("bitrate"),
+                   _("exact or average bitrate in bits/s"),
+                   NULL);
+  gst_tag_register (GST_TAG_MAXIMUM_BITRATE,
+                   G_TYPE_UINT,
+                   _("maximum bitrate"),
+                   _("maximum bitrate in bits/s"),
+                   NULL);
+}
+/**
+ * gst_tag_merge_use_first:
+ * @dest: uninitialized GValue to store result in
+ * @src: GValue to copy from
+ *
+ * This is a convenience function for the func argument of gst_tag_register(). 
+ * It creates a copy of the first value from the list.
+ */
+void
+gst_tag_merge_use_first (GValue *dest, const GValue *src)
+{
+  const GValue *ret = gst_value_list_get_value (src, 0);
+
+  g_value_init (dest, G_VALUE_TYPE (ret));
+  g_value_copy (ret, dest);
+}
+/**
+ * gst_tag_merge_strings_with_comma:
+ * @dest: uninitialized GValue to store result in
+ * @src: GValue to copy from
+ * 
+ * This is a convenience function for the func argument of gst_tag_register().
+ * It concatenates all given strings using a comma. The tag must be registered
+ * as a G_TYPE_STRING or this function will fail.
+ */
+void
+gst_tag_merge_strings_with_comma (GValue *dest, const GValue *src)
+{
+  GString *str;
+  gint i, count;
+
+  count = gst_value_list_get_size (src);
+  str = g_string_new (g_value_get_string (gst_value_list_get_value (src, 0)));
+  for (i = 1; i < count; i++) {
+    /* seperator between two string */
+    str = g_string_append (str, _(", "));
+    str = g_string_append (str, g_value_get_string (gst_value_list_get_value (src, 1)));
+  }
+
+  g_value_init (dest, G_TYPE_STRING);
+  g_value_take_string (dest, str->str);
+  g_string_free (str, FALSE);
+}
+static GstTagInfo *
+gst_tag_lookup (GQuark entry)
+{
+  GstTagInfo *ret;
+  
+  TAG_LOCK;
+  ret = g_hash_table_lookup (__tags, GUINT_TO_POINTER (entry));
+  TAG_UNLOCK;
+
+  return ret;
+}
+/**
+ * gst_tag_register:
+ * @name: the name or identifier string
+ * @type: the type this data is in
+ * @nick: human-readable name
+ * @blurb: a human-readable description about this tag
+ * @func: function for merging multiple values of this tag
+ *
+ * Registers a new tag type for the use with GStreamer's type system. If a type
+ * with that name is already registered, that one is used.
+ * The old registration may have used a different type however. So don't rely
+ * on youre supplied values.
+ * If you know the type is already registered, use gst_tag_lookup instead.
+ * This function takes ownership of all supplied variables.
+ */
+void
+gst_tag_register (gchar *name, GType type, gchar *nick, gchar *blurb,
+                 GstTagMergeFunc func)
+{
+  GQuark key;
+  GstTagInfo *info;
+
+  g_return_if_fail (name != NULL);
+  g_return_if_fail (nick != NULL);
+  g_return_if_fail (blurb != NULL);
+  g_return_if_fail (type != 0 && type != GST_VALUE_TYPE_LIST);
+  
+  key = g_quark_from_string (name);
+  info = gst_tag_lookup (key);
+  g_return_if_fail (info == NULL);
+  
+  info = g_new (GstTagInfo, 1);
+  info->type = type;
+  info->nick = nick;
+  info->blurb = blurb;
+  info->merge_func = func;
+    
+  TAG_LOCK;
+  g_hash_table_insert (__tags, GUINT_TO_POINTER (key), info);
+  TAG_UNLOCK;
+}
+/**
+ * gst_tag_exists:
+ * @tag: name of the tag
+ *
+ * Checks if the given type is already registered.
+ *
+ * Returns: TRUE if the type is already registered
+ */
+gboolean
+gst_tag_exists (const gchar *tag)
+{
+  g_return_val_if_fail (tag != NULL, FALSE);
+  
+  return gst_tag_lookup (g_quark_from_string (tag)) != NULL;
+}
+/**
+ * gst_tag_get_type:
+ * @tag: the tag
+ *
+ * Gets the #GType used for this tag.
+ *
+ * Returns: the #GType of this tag
+ */
+GType
+gst_tag_get_type (const gchar *tag)
+{
+  GstTagInfo *info;
+  
+  g_return_val_if_fail (tag != NULL, 0);
+  info = gst_tag_lookup (g_quark_from_string (tag));
+  g_return_val_if_fail (info != NULL, 0);
+  
+  return info->type;
+}
+/**
+ * gst_tag_get_nick
+ * @tag: the tag
+ *
+ * Returns the human-readable name of this tag, You must not change or free 
+ * this string.
+ *
+ * Returns: the human-readable name of this tag
+ */
+const gchar *
+gst_tag_get_nick (const gchar *tag)
+{
+  GstTagInfo *info;
+  
+  g_return_val_if_fail (tag != NULL, NULL);
+  info = gst_tag_lookup (g_quark_from_string (tag));
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  return info->nick;
+}
+/**
+ * gst_tag_get_description:
+ * @tag: the tag
+ *
+ * Returns the human-readable description of this tag, You must not change or 
+ * free this string.
+ *
+ * Return the human-readable description of this tag
+ */
+const gchar *
+gst_tag_get_description (const gchar *tag)
+{
+  GstTagInfo *info;
+  
+  g_return_val_if_fail (tag != NULL, NULL);
+  info = gst_tag_lookup (g_quark_from_string (tag));
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  return info->blurb;
+}
+/**
+ * gst_tag_list_is_fixed:
+ * @tag: tag to check
+ *
+ * Checks if the given tag is fixed. A fixed tag can only contain one value.
+ * Unfixed tags can contain lists of values.
+ *
+ * Returns: TRUE, if the given tag is fixed.
+ */
+gboolean
+gst_tag_is_fixed (const gchar *tag)
+{
+  GstTagInfo *info;
+  
+  g_return_val_if_fail (tag != NULL, FALSE);
+  info = gst_tag_lookup (g_quark_from_string (tag));
+  g_return_val_if_fail (info != NULL, FALSE);
+  
+  return info->merge_func == NULL;
+}
+/**
+ * gst_tag_list_new:
+ *
+ * Creates a new empty GstTagList.
+ *
+ * Returns: An empty tag list
+ */
+GstTagList *
+gst_tag_list_new (void)
+{
+  return GST_TAG_LIST (gst_structure_new (TAGLIST, NULL));
+}
+/**
+ * gst_is_tag_list:
+ * @p: Object that might be a taglist
+ *
+ * Checks if the given pointer is a taglist.
+ *
+ * Returns: TRUE, if the given pointer is a taglist
+ */
+gboolean
+gst_is_tag_list (gconstpointer p)
+{
+  g_return_val_if_fail (p != NULL, FALSE); 
+
+  return ((GstStructure *) p)->name == gst_tag_list_quark;
+}
+typedef struct {
+  GstStructure *       list;
+  GstTagMergeMode      mode;
+} GstTagCopyData;
+static void
+gst_tag_list_add_value_internal (GstStructure *list, GstTagMergeMode mode, GQuark tag, GValue *value)
+{
+  GstTagInfo *info = gst_tag_lookup (tag);
+  GstStructureField *field;
+  
+  g_assert (info != NULL);
+
+  if (info->merge_func && (field = gst_structure_id_get_field (list, tag)) != NULL) {
+    GValue value2 = { 0, };
+    switch (mode) {
+      case GST_TAG_MERGE_REPLACE_ALL:
+      case GST_TAG_MERGE_REPLACE:
+       gst_structure_id_set_value (list, tag, value);
+       break;
+      case GST_TAG_MERGE_PREPEND:
+       gst_value_list_concat (&value2, value, &field->value);
+       gst_structure_id_set_value (list, tag, &value2);
+       g_value_unset (&value2);
+       break;
+      case GST_TAG_MERGE_APPEND:
+       gst_value_list_concat (&value2, &field->value, value);
+       gst_structure_id_set_value (list, tag, &value2);
+       g_value_unset (&value2);
+       break;
+      case GST_TAG_MERGE_KEEP:
+      case GST_TAG_MERGE_KEEP_ALL:
+       break;
+      default:
+       g_assert_not_reached ();
+       break;
+    }
+  } else {
+    switch (mode) {
+      case GST_TAG_MERGE_APPEND:
+      case GST_TAG_MERGE_KEEP:
+       if (gst_structure_id_get_field (list, tag) != NULL)
+         break;
+       /* fall through */
+      case GST_TAG_MERGE_REPLACE_ALL:
+      case GST_TAG_MERGE_REPLACE:
+      case GST_TAG_MERGE_PREPEND:
+       gst_structure_id_set_value (list, tag, value);
+       break;
+      case GST_TAG_MERGE_KEEP_ALL:
+       break;
+      default:
+       g_assert_not_reached ();
+       break;
+    }
+  }
+}
+static void
+gst_tag_list_copy_foreach (GstStructure *structure, GQuark tag, GValue *value, gpointer user_data)
+{
+  GstTagCopyData *copy = (GstTagCopyData *) user_data;
+
+  gst_tag_list_add_value_internal (copy->list, copy->mode, tag, value);
+}
+/**
+ * gst_tag_list_insert:
+ * @into: list to merge into
+ * @from: list to merge from
+ * @mode: the mode to use
+ * 
+ * Inserts the tags of the second list into the first list using the given mode.
+ */
+void
+gst_tag_list_insert (GstTagList *into, const GstTagList *from, GstTagMergeMode mode)
+{
+  GstTagCopyData data;
+  
+  g_return_if_fail (GST_IS_TAG_LIST (into));
+  g_return_if_fail (GST_IS_TAG_LIST (from));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+  data.list = (GstStructure *) into;
+  data.mode = mode;
+  if (mode == GST_TAG_MERGE_REPLACE_ALL) {
+    gst_structure_remove_all_fields (data.list);
+  }
+  gst_structure_field_foreach ((GstStructure *) from, gst_tag_list_copy_foreach, &data);
+}
+/**
+ * gst_tag_list_copy:
+ * @list: list to copy
+ *
+ * Copies a given #GstTagList.
+ *
+ * Returns: copy of the given list
+ */
+GstTagList *
+gst_tag_list_copy (const GstTagList *list)
+{
+  g_return_val_if_fail (GST_IS_TAG_LIST (list), NULL);
+  
+  return GST_TAG_LIST (gst_structure_copy ((GstStructure *) list));
+}
+/**
+ * gst_tag_list_merge:
+ * @list1: first list to merge
+ * @list2: second list to merge
+ * @mode: the mode to use
+ * 
+ * Merges the two given lists into a new list. If one of the lists is NULL, a
+ * copy of the other is returned. If both lists are NULL, NULL is returned.
+ *
+ * Returns: the new list
+ */
+GstTagList *
+gst_tag_list_merge (const GstTagList *list1, const GstTagList *list2, GstTagMergeMode mode)
+{
+  g_return_val_if_fail (list1 == NULL || GST_IS_TAG_LIST (list1), NULL);
+  g_return_val_if_fail (list2 == NULL || GST_IS_TAG_LIST (list2), NULL);
+  g_return_val_if_fail (GST_TAG_MODE_IS_VALID (mode), NULL);
+
+  if (!list1 && !list2) {
+    return NULL;
+  } else if (!list1) {
+    return gst_tag_list_copy (list2);
+  } else if (!list2) {
+    return gst_tag_list_copy (list1);
+  } else {
+    GstTagList *ret;
+
+    ret = gst_tag_list_copy (list1);
+    gst_tag_list_insert (ret, list2, mode);
+    return ret;
+  }
+}
+/**
+ * gst_tag_list_free:
+ * @list: the list to free
+ *
+ * Frees the given list and all associated values.
+ */
+void
+gst_tag_list_free (GstTagList *list)
+{
+  g_return_if_fail (GST_IS_TAG_LIST (list));
+  gst_structure_free ((GstStructure *) list);
+}
+/**
+ * gst_tag_list_get_tag_size:
+ * @list: a taglist
+ * @tag: the tag to query
+ *
+ * Checks how many value are stored in this tag list for the given tag.
+ *
+ * Returns: The number of tags stored
+ */
+guint
+gst_tag_list_get_tag_size (const GstTagList *list, const gchar *tag)
+{
+  const GValue *value;
+
+  g_return_val_if_fail (GST_IS_TAG_LIST (list), 0);
+
+  value = gst_structure_get_value ((GstStructure *) list, tag);
+  if (value == NULL)
+    return 0;
+  if (G_VALUE_TYPE (value) != GST_VALUE_TYPE_LIST)
+    return 1;
+
+  return gst_value_list_get_size (value);
+}
+/**
+ * gst_tag_list_add:
+ * @list: list to set tags in
+ * @mode: the mode to use
+ * @tag: tag
+ * @...: values to set
+ *
+ * Sets the values for the given tags using the specified mode.
+ */
+void
+gst_tag_list_add (GstTagList *list, GstTagMergeMode mode, const gchar *tag, ...)
+{
+  va_list args;
+
+  g_return_if_fail (GST_IS_TAG_LIST (list));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+  g_return_if_fail (tag != NULL);
+  
+  va_start (args, tag);
+  gst_tag_list_add_valist (list, mode, tag, args);
+  va_end (args);
+}
+/**
+ * gst_tag_list_add_valist:
+ * @list: list to set tags in
+ * @mode: the mode to use
+ * @tag: tag
+ * @var_args: tag / value pairs to set
+ *
+ * Sets the values for the given tags using the specified mode.
+ */
+void
+gst_tag_list_add_valist (GstTagList *list, GstTagMergeMode mode, const gchar *tag, va_list var_args)
+{
+  GstTagInfo *info;
+  GQuark quark;
+  gchar *error = NULL;
+  
+  g_return_if_fail (GST_IS_TAG_LIST (list));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+  g_return_if_fail (tag != NULL);
+  
+  while (tag != NULL) {
+    GValue value = { 0, };
+    quark = g_quark_from_string (tag);
+    info = gst_tag_lookup (quark);
+    g_return_if_fail (info != NULL);
+    g_value_init (&value, info->type);
+    G_VALUE_COLLECT (&value, var_args, 0, &error);
+    if (error) {
+      g_warning ("%s: %s", G_STRLOC, error);
+      g_free (error);
+      /* we purposely leak the value here, it might not be
+       * in a sane state if an error condition occoured
+       */
+      return;
+    }
+    gst_tag_list_add_value_internal (list, mode, quark, &value);
+    g_value_unset (&value);
+    tag = va_arg (var_args, gchar *);
+  }
+}
+/**
+ * gst_tag_list_remove_tag:
+ * @list: list to remove tag from
+ * @tag: tag to remove
+ *
+ * Removes the goven tag from the taglist.
+ */
+void
+gst_tag_list_remove_tag (GstTagList *list, const gchar *tag)
+{
+  g_return_if_fail (GST_IS_TAG_LIST (list));
+  g_return_if_fail (tag != NULL);
+
+  gst_structure_remove_field ((GstStructure *) list, tag);
+}
+typedef struct {
+  GstTagForeachFunc    func;
+  gpointer             data;
+} TagForeachData;
+static void 
+structure_foreach_wrapper (GstStructure *structure, GQuark field_id, 
+       GValue *value, gpointer user_data)
+{
+  TagForeachData *data = (TagForeachData *) user_data;
+  data->func (GST_TAG_LIST (structure), g_quark_to_string (field_id), data->data);
+}
+/**
+ * gst_tag_list_foreach:
+ * @list: list to iterate over
+ * @func: function to be called for each tag
+ * @user_data: user specified data
+ *
+ * Calls the given function for each tag inside the tag list. Note that if there
+ * is no tag, the function won't be called at all.
+ */
+void
+gst_tag_list_foreach (GstTagList *list, GstTagForeachFunc func, gpointer user_data)
+{
+  TagForeachData data;
+
+  g_return_if_fail (GST_IS_TAG_LIST (list));
+  g_return_if_fail (func != NULL);
+  
+  data.func = func;
+  data.data = user_data;
+  gst_structure_field_foreach ((GstStructure *) list, structure_foreach_wrapper, &data);
+}
+
+/***** tag events *****/
+
+/**
+ * gst_event_new_tag:
+ * @list: the tag list to put into the event or NULL for an empty list
+ *
+ * Creates a new tag event with the given list and takes ownership of it.
+ *
+ * Returns: a new tag event
+ */
+GstEvent *
+gst_event_new_tag (GstTagList *list)
+{
+  GstEvent *ret;
+  
+  g_return_val_if_fail (list == NULL || GST_IS_TAG_LIST (list), NULL);
+
+  ret = gst_event_new (GST_EVENT_TAG);
+  if (!list)
+    list = gst_tag_list_new ();
+  ret->event_data.structure.structure = (GstStructure *) list;
+  
+  return ret;
+}
+/**
+ * get_event_tag_get_list:
+ * @tag_event: a tagging #GstEvent
+ *
+ * Gets the taglist from a given tagging event.
+ * 
+ * Returns: The #GstTagList of the event
+ */
+GstTagList *
+gst_event_tag_get_list (GstEvent *tag_event)
+{
+  g_return_val_if_fail (GST_IS_EVENT (tag_event), NULL);
+  g_return_val_if_fail (GST_EVENT_TYPE (tag_event) == GST_EVENT_TAG, NULL);
+
+  return GST_TAG_LIST (tag_event->event_data.structure.structure);
+}
+
+/**
+ * gst_tag_list_get_value_index:
+ * @list: a #GStTagList
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ *
+ * Gets the value that is at the given index for the given tag in the given 
+ * list.
+ * 
+ * Returns: The GValue for the specified entry or NULL if the tag wasn't available
+ *         or the tag doesn't have as many entries
+ */
+G_CONST_RETURN GValue *
+gst_tag_list_get_value_index (const GstTagList *list, const gchar *tag, guint index)
+{
+  const GValue *value;
+
+  g_return_val_if_fail (GST_IS_TAG_LIST (list), NULL);
+  g_return_val_if_fail (tag != NULL, NULL);
+  
+  value = gst_structure_get_value ((GstStructure *) list, tag);
+  if (value == NULL) return  NULL;
+  
+  if (GST_VALUE_HOLDS_LIST (value)) {
+    if (index >= gst_value_list_get_size (value)) return NULL;
+    return gst_value_list_get_value (value, index);
+  } else {
+    if (index > 0) return NULL;
+    return value;
+  }
+}
+
+/**
+ * gst_tag_list_copy_value:
+ * @dest: uninitialized #GValue to copy into
+ * @list: list to get the tag from
+ * @tag: tag to read out
+ *
+ * Copies the contents for the given tag into the value, merging multiple values 
+ * into one if multiple values are associated with the tag.
+ * You must g_value_unset() the value after use.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the 
+ *         given list.
+ */
+gboolean
+gst_tag_list_copy_value (GValue *dest, const GstTagList *list, const gchar *tag)
+{
+  const GValue *src;
+  
+  g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);
+  g_return_val_if_fail (tag != NULL, FALSE);
+  g_return_val_if_fail (dest != NULL, FALSE);
+  g_return_val_if_fail (G_VALUE_TYPE (dest) == 0, FALSE);
+  
+  src = gst_structure_get_value ((GstStructure *) list, tag);
+  if (!src) return FALSE;
+  
+  if (G_VALUE_TYPE (src) == GST_VALUE_TYPE_LIST) {    
+    GstTagInfo *info = gst_tag_lookup (g_quark_from_string (tag));
+    /* must be there or lists aren't allowed */
+    g_assert (info->merge_func);
+    info->merge_func (dest, src);
+  } else {
+    g_value_init (dest, G_VALUE_TYPE (src));
+    g_value_copy (src, dest);
+  }
+  return TRUE;
+}
+
+/***** evil macros to get all the gst_tag_list_get_*() functions right *****/
+
+#define TAG_MERGE_FUNCS(name,type)                                             \
+gboolean                                                                       \
+gst_tag_list_get_ ## name (const GstTagList *list, const gchar *tag,           \
+                          type *value)                                         \
+{                                                                              \
+  GValue v = { 0, };                                                           \
+                                                                               \
+  g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);                                \
+  g_return_val_if_fail (tag != NULL, FALSE);                                   \
+  g_return_val_if_fail (value != NULL, FALSE);                                 \
+                                                                               \
+  if (!gst_tag_list_copy_value (&v, list, tag))                                        \
+      return FALSE;                                                            \
+  *value = COPY_FUNC (g_value_get_ ## name (&v));                              \
+  g_value_unset (&v);                                                          \
+  return TRUE;                                                                 \
+}                                                                              \
+                                                                               \
+gboolean                                                                       \
+gst_tag_list_get_ ## name ## _index (const GstTagList *list, const gchar *tag,         \
+                          guint index, type *value)                            \
+{                                                                              \
+  const GValue *v;                                                             \
+                                                                               \
+  g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);                                \
+  g_return_val_if_fail (tag != NULL, FALSE);                                   \
+  g_return_val_if_fail (value != NULL, FALSE);                                 \
+                                                                               \
+  if ((v = gst_tag_list_get_value_index (list, tag, index)) == NULL)           \
+      return FALSE;                                                            \
+  *value = COPY_FUNC (g_value_get_ ## name (v));                               \
+  return TRUE;                                                                 \
+}
+
+#define COPY_FUNC /**/
+TAG_MERGE_FUNCS (char, gchar)
+TAG_MERGE_FUNCS (uchar, guchar)
+TAG_MERGE_FUNCS (boolean, gboolean)
+TAG_MERGE_FUNCS (int, gint)
+TAG_MERGE_FUNCS (uint, guint)
+TAG_MERGE_FUNCS (long, glong)
+TAG_MERGE_FUNCS (ulong, gulong)
+TAG_MERGE_FUNCS (int64, gint64)
+TAG_MERGE_FUNCS (uint64, guint64)
+TAG_MERGE_FUNCS (float, gfloat)
+TAG_MERGE_FUNCS (double, gdouble)
+#undef COPY_FUNC
+  
+#define COPY_FUNC g_strdup
+TAG_MERGE_FUNCS (string, gchar *)
+
+
+
+
diff --git a/gst/gsttag.h b/gst/gsttag.h
new file mode 100644 (file)
index 0000000..6d1efbe
--- /dev/null
@@ -0,0 +1,232 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttag.h: Header for tag support
+ *
+ * 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_H__
+#define __GST_TAG_H__
+
+#include <gst/gststructure.h>
+#include <gst/gstevent.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+  GST_TAG_MERGE_UNDEFINED,
+  GST_TAG_MERGE_REPLACE_ALL,
+  GST_TAG_MERGE_REPLACE,
+  GST_TAG_MERGE_APPEND,
+  GST_TAG_MERGE_PREPEND,
+  GST_TAG_MERGE_KEEP,
+  GST_TAG_MERGE_KEEP_ALL,
+  /* add more */
+  GST_TAG_MERGE_COUNT
+} GstTagMergeMode;
+#define GST_TAG_MODE_IS_VALID(mode)     (((mode) > GST_TAG_MERGE_UNDEFINED) && ((mode) < GST_TAG_MERGE_COUNT))
+
+typedef GstStructure GstTagList;
+#define GST_TAG_LIST(x)                ((GstTagList *) (x))
+#define GST_IS_TAG_LIST(x)     (gst_is_tag_list (GST_TAG_LIST (x)))
+
+typedef void           (* GstTagForeachFunc)   (const GstTagList *list, const gchar *tag, gpointer user_data);
+typedef void           (* GstTagMergeFunc)     (GValue *dest, const GValue *src);
+
+/* initialize tagging system */
+void           _gst_tag_initialize             (void);
+
+void           gst_tag_register                (gchar *                name,
+                                                GType                  type,
+                                                gchar *                nick,
+                                                gchar *                blurb,
+                                                GstTagMergeFunc        func);
+/* some default merging functions */
+void           gst_tag_merge_use_first         (GValue *               dest,
+                                                const GValue *         values);
+void           gst_tag_merge_strings_with_comma (GValue *              dest,
+                                                const GValue *         values);
+
+/* basic tag support */
+gboolean       gst_tag_exists                  (const gchar *          tag);
+GType          gst_tag_get_type                (const gchar *          tag);
+const gchar *   gst_tag_get_nick               (const gchar *          tag);
+const gchar *  gst_tag_get_description         (const gchar *          tag);
+gboolean       gst_tag_is_fixed                (const gchar *          tag);
+
+/* tag lists */
+GstTagList *   gst_tag_list_new                (void);
+gboolean       gst_is_tag_list                 (gconstpointer          p);
+GstTagList *   gst_tag_list_copy               (const GstTagList *     list);
+void           gst_tag_list_insert             (GstTagList *           into,
+                                                const GstTagList *     from,
+                                                GstTagMergeMode        mode);
+GstTagList *   gst_tag_list_merge              (const GstTagList *     list1,
+                                                const GstTagList *     list2,
+                                                GstTagMergeMode        mode);
+void           gst_tag_list_free               (GstTagList *           list);
+guint          gst_tag_list_get_tag_size       (const GstTagList *     list,
+                                                const gchar *          tag);
+void           gst_tag_list_add                (GstTagList *           list,
+                                                GstTagMergeMode        mode,
+                                                const gchar *          tag,
+                                                ...);
+void           gst_tag_list_add_valist         (GstTagList *           list,
+                                                GstTagMergeMode        mode,
+                                                const gchar *          tag,
+                                                va_list                var_args);
+void           gst_tag_list_remove_tag         (GstTagList *           list,
+                                                const gchar *          tag);
+void           gst_tag_list_foreach            (GstTagList *           list,
+                                                GstTagForeachFunc      func,
+                                                gpointer               user_data);
+
+G_CONST_RETURN GValue *
+               gst_tag_list_get_value_index    (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index);
+gboolean       gst_tag_list_copy_value         (GValue *               dest,
+                                                const GstTagList *     list,
+                                                const gchar *          tag);
+
+/* simplifications (FIXME: do we want them?) */
+gboolean       gst_tag_list_get_char           (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gchar *                value);
+gboolean       gst_tag_list_get_char_index     (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gchar *                value);
+gboolean       gst_tag_list_get_uchar          (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guchar *               value);
+gboolean       gst_tag_list_get_uchar_index    (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                guchar *               value);
+gboolean       gst_tag_list_get_boolean        (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gboolean *             value);
+gboolean       gst_tag_list_get_boolean_index  (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gboolean *             value);
+gboolean       gst_tag_list_get_int            (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gint *                 value);
+gboolean       gst_tag_list_get_int_index      (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gint *                 value);
+gboolean       gst_tag_list_get_uint           (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint *                value);
+gboolean       gst_tag_list_get_uint_index     (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                guint *                value);
+gboolean       gst_tag_list_get_long           (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                glong *                value);
+gboolean       gst_tag_list_get_long_index     (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                glong *                value);
+gboolean       gst_tag_list_get_ulong          (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gulong *               value);
+gboolean       gst_tag_list_get_ulong_index    (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gulong *               value);
+gboolean       gst_tag_list_get_int64          (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gint64 *               value);
+gboolean       gst_tag_list_get_int64_index    (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gint64 *               value);
+gboolean       gst_tag_list_get_uint64         (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint64 *              value);
+gboolean       gst_tag_list_get_uint64_index   (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                guint64 *              value);
+gboolean       gst_tag_list_get_float          (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gfloat *               value);
+gboolean       gst_tag_list_get_float_index    (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gfloat *               value);
+gboolean       gst_tag_list_get_double         (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gdouble *              value);
+gboolean       gst_tag_list_get_double_index   (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gdouble *              value);
+gboolean       gst_tag_list_get_string         (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gchar **               value);
+gboolean       gst_tag_list_get_string_index   (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gchar **               value);
+gboolean       gst_tag_list_get_pointer        (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gpointer *             value);
+gboolean       gst_tag_list_get_pointer_index  (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gpointer *             value);
+
+/* tag events */
+GstEvent *     gst_event_new_tag               (GstTagList *           list);
+GstTagList *   gst_event_tag_get_list          (GstEvent *             tag_event);
+
+
+/* GStreamer core tags (need to be discussed) */
+#define GST_TAG_TITLE                  "title"
+#define GST_TAG_ARTIST                 "artist"
+#define GST_TAG_ALBUM                  "album"
+#define GST_TAG_DATE                   "date"
+#define GST_TAG_GENRE                  "genre"
+#define GST_TAG_COMMENT                        "comment"
+#define GST_TAG_TRACK_NUMBER           "track-number"
+#define GST_TAG_TRACK_COUNT            "track-count"
+#define GST_TAG_LOCATION               "location"
+#define GST_TAG_DESCRIPTION            "description"
+#define GST_TAG_VERSION                        "version"
+#define GST_TAG_ISRC                   "isrc"
+#define GST_TAG_ORGANIZATION           "organization"
+#define GST_TAG_COPYRIGHT              "copyright"
+#define GST_TAG_CONTACT                        "contact"
+#define GST_TAG_LICENSE                        "license"
+#define GST_TAG_PERFORMER              "performer"
+#define GST_TAG_DURATION               "duration"
+#define GST_TAG_CODEC                  "codec"
+#define GST_TAG_BITRATE                        "bitrate"
+#define GST_TAG_MINIMUM_BITRATE                "minimum-bitrate"
+#define GST_TAG_MAXIMUM_BITRATE                "maximum-bitrate"
+
+
+G_END_DECLS
+
+#endif /* __GST_EVENT_H__ */
diff --git a/gst/gsttaginterface.c b/gst/gsttaginterface.c
new file mode 100644 (file)
index 0000000..a7e6d71
--- /dev/null
@@ -0,0 +1,214 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttaginterface.c: interface for tag setting on elements
+ *
+ * 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 "gsttaginterface.h"
+#include <gobject/gvaluecollector.h>
+#include <string.h>
+
+GST_DEBUG_CATEGORY_STATIC (gst_tag_interface_debug);
+#define GST_CAT_DEFAULT tag_tag_interface_debug
+
+static GQuark gst_tag_key;
+
+typedef struct {
+  GstTagMergeMode      mode;
+  GstTagList *         list;
+} GstTagData;
+
+GType
+gst_tag_setter_get_type (void)
+{
+  static GType tag_setter_type = 0;
+    
+  if (! tag_setter_type) {
+    static const GTypeInfo tag_setter_info = {
+      sizeof (GstTagSetterIFace), /* class_size */
+      NULL,            /* base_init */
+      NULL,             /* base_finalize */
+      NULL,
+      NULL,             /* class_finalize */
+      NULL,             /* class_data */
+      0,
+      0,
+      NULL
+    };
+    
+    GST_DEBUG_CATEGORY_INIT (gst_tag_interface_debug, "GstTagInterface", 0, "interfaces for tagging");
+    
+    tag_setter_type = g_type_register_static (G_TYPE_INTERFACE, "GstTagSetter",
+                                             &tag_setter_info, 0);
+    
+    g_type_interface_add_prerequisite (tag_setter_type, GST_TYPE_ELEMENT);
+
+    gst_tag_key = g_quark_from_static_string ("GST_TAG_SETTER");
+  }
+  
+  return tag_setter_type;      
+}
+static void
+gst_tag_data_free (gpointer p)
+{
+  GstTagData *data = (GstTagData *) p;
+
+  if (data->list)
+    gst_tag_list_free (data->list);
+
+  g_free (data);
+}
+static GstTagData *
+gst_tag_setter_get_data (GstTagSetter *setter)
+{
+  GstTagData *data;
+
+  data = g_object_get_qdata (G_OBJECT (setter), gst_tag_key);
+  if (!data) {
+    data = g_new (GstTagData, 1);
+    data->list = NULL;
+    data->mode = GST_TAG_MERGE_KEEP;
+    g_object_set_qdata_full (G_OBJECT (setter), gst_tag_key, data, gst_tag_data_free);
+  }
+  
+  return data;
+}
+/**
+ * gst_tag_setter_merge:
+ * @setter: a #GstTagSetter
+ * @list: a tag list to merge from
+ * @mode: the mode to merge with
+ *
+ * Merges the given list into the setter's list using the given mode.
+ */
+void
+gst_tag_setter_merge (GstTagSetter *setter, const GstTagList *list, GstTagMergeMode mode)
+{
+  GstTagData *data;
+
+  g_return_if_fail (GST_IS_TAG_SETTER (setter));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+  data = gst_tag_setter_get_data (setter);
+  if (!data->list) {
+    data->list = gst_tag_list_copy (list);
+  } else {
+    gst_tag_list_merge (data->list, list, mode);
+  }
+}
+/**
+ * gst_tag_setter_add:
+ * @setter: a #GstTagSetter
+ * @mode: the mode to use
+ * @tag: tag to set
+ * @...: more tag / value pairs to set
+ *
+ * Adds the given tag / value pairs on the setter using the given merge mode. 
+ * The list must be terminated with GST_TAG_INVALID.
+ */
+void
+gst_tag_setter_add (GstTagSetter *setter, GstTagMergeMode mode, const gchar *tag, ...)
+{
+  va_list args;
+
+  g_return_if_fail (GST_IS_TAG_SETTER (setter));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+  va_start (args, tag);
+  gst_tag_setter_add_valist (setter, mode, tag, args);
+  va_end (args);
+}
+/**
+ * gst_tag_setter_add_valist:
+ * @setter: a #GstTagSetter
+ * @mode: the mode to use
+ * @tag: tag to set
+ * @var_args: tag / value pairs to set
+ *
+ * Adds the given tag / value pairs on the setter using the given merge mode. 
+ * The list must be terminated with GST_TAG_INVALID.
+ */
+void
+gst_tag_setter_add_valist (GstTagSetter *setter, GstTagMergeMode mode, const gchar *tag, va_list var_args)
+{
+  GstTagData *data;
+  
+  g_return_if_fail (GST_IS_TAG_SETTER (setter));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+  data = gst_tag_setter_get_data (setter);
+  if (!data->list)
+    data->list = gst_tag_list_new ();
+  
+  gst_tag_list_add_valist (data->list, mode, tag, var_args);
+}
+/**
+ * gst_tag_setter_get_list:
+ * @setter: a #GstTagSetter
+ *
+ * Retrieves a copy of the current list of tags the setter uses.
+ * You need to gst_tag_list_free() the list after use.
+ *
+ * Returns: a current snapshot of the taglist used in the setter
+ *         or NULL if none is used.
+ */
+const GstTagList *    
+gst_tag_setter_get_list (GstTagSetter *setter)
+{
+  g_return_val_if_fail (GST_IS_TAG_SETTER (setter), NULL);
+
+  return gst_tag_setter_get_data (setter)->list;
+}
+/**
+ * gst_tag_setter_set_merge_mode:
+ * @setter: a #GstTagSetter
+ * @overwrite: The mode with which tags are added
+ *
+ * Sets the given merge mode that is used for adding tags from events to tags
+ * specified by this interface. The default is #GST_TAG_MERGE_KEEP, which keeps
+ * the tags by this interface and discards tags from events.
+ */
+void
+gst_tag_setter_set_merge_mode (GstTagSetter *setter, GstTagMergeMode mode)
+{
+  g_return_if_fail (GST_IS_TAG_SETTER (setter));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+  gst_tag_setter_get_data (setter)->mode = mode;
+}
+/**
+ * gst_tag_setter_get_merge_mode:
+ * @setter: a #GstTagSetter
+ *
+ * Queries the mode by which tags inside the setter are overwritten by tags 
+ * from events
+ *
+ * Returns: the merge mode used inside the element.
+ */
+GstTagMergeMode
+gst_tag_setter_get_merge_mode (GstTagSetter *setter)
+{
+  g_return_val_if_fail (GST_IS_TAG_SETTER (setter), FALSE);
+
+  return gst_tag_setter_get_data (setter)->mode;
+}
diff --git a/gst/gsttaginterface.h b/gst/gsttaginterface.h
new file mode 100644 (file)
index 0000000..cb85137
--- /dev/null
@@ -0,0 +1,70 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttaginterface.h: Interfaces for tagging
+ *
+ * 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_INTERFACE_H__
+#define __GST_TAG_INTERFACE_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_TAG_SETTER            (gst_tag_setter_get_type ())
+#define GST_TAG_SETTER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TAG_SETTER, GstTagSetter))
+#define GST_TAG_SETTER_CLASS(obj)      (G_TYPE_CHECK_CLASS_CAST ((obj), GST_TYPE_TAG_SETTER, GstTagSetter))
+#define GST_IS_TAG_SETTER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TAG_SETTER))
+#define GST_TAG_SETTER_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GST_TYPE_TAG_SETTER, GstTagSetterIFace))
+
+typedef struct _GstTagSetter                   GstTagSetter; /* Dummy typedef */
+typedef struct _GstTagSetterIFace              GstTagSetterIFace;
+
+/* use an empty interface here to allow detection of elements using user-set
+   tags */
+struct _GstTagSetterIFace
+{
+  GTypeInterface g_iface;
+
+  /* signals */
+
+  /* virtual table */
+};
+
+GType          gst_tag_setter_get_type         (void) G_GNUC_CONST;
+
+void           gst_tag_setter_merge            (GstTagSetter *         setter,
+                                                const GstTagList *     list,
+                                                GstTagMergeMode        mode);
+void           gst_tag_setter_add              (GstTagSetter *         setter,
+                                                GstTagMergeMode        mode,
+                                                const gchar *          tag,
+                                                ...);
+void            gst_tag_setter_add_valist      (GstTagSetter *         setter,
+                                                GstTagMergeMode        mode,
+                                                const gchar *          tag,
+                                                va_list                var_args);
+const GstTagList *gst_tag_setter_get_list      (GstTagSetter *         setter);
+
+void           gst_tag_setter_set_merge_mode   (GstTagSetter *         setter,
+                                                GstTagMergeMode        mode);
+GstTagMergeMode        gst_tag_setter_get_merge_mode   (GstTagSetter *         setter);
+
+G_END_DECLS
+
+#endif /* __GST_TAG_INTERFACE_H__ */
diff --git a/gst/gsttaglist.c b/gst/gsttaglist.c
new file mode 100644 (file)
index 0000000..c6c4f3f
--- /dev/null
@@ -0,0 +1,831 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttag.c: tag support (aka metadata)
+ *
+ * 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_private.h"
+#include "gsttag.h"
+#include "gstinfo.h"
+#include "gstvalue.h"
+
+#include <gobject/gvaluecollector.h>
+#include <string.h>
+
+#define GST_TAG_IS_VALID(tag)          (gst_tag_get_info (tag) != NULL)
+
+typedef struct {
+  GType                        type;           /* type the data is in */
+
+  gchar *              nick;           /* translated name */
+  gchar *              blurb;          /* translated description of type */
+
+  GstTagMergeFunc      merge_func;     /* functions to merge the values */
+} GstTagInfo;
+
+#define TAGLIST "taglist"
+static GQuark gst_tag_list_quark;
+static GMutex *__tag_mutex;
+static GHashTable *__tags;
+#define TAG_LOCK g_mutex_lock (__tag_mutex)
+#define TAG_UNLOCK g_mutex_unlock (__tag_mutex)
+
+void
+_gst_tag_initialize (void)
+{
+  gst_tag_list_quark = g_quark_from_static_string (TAGLIST);
+  __tag_mutex = g_mutex_new ();
+  __tags = g_hash_table_new (g_direct_hash, g_direct_equal);
+  gst_tag_register (GST_TAG_TITLE,           
+                   G_TYPE_STRING,
+                   _("title"),
+                   _("commonly used title"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_ARTIST,
+                   G_TYPE_STRING,
+                   _("artist"),
+                   _("person(s) resposible for the recording"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_ALBUM,
+                   G_TYPE_STRING,
+                   _("album"),
+                   _("album containing this data"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_DATE,
+                   G_TYPE_UINT, /* FIXME: own data type for dates? */
+                   _("date"),
+                   _("date the data was created in julien days"),
+                   NULL);
+  gst_tag_register (GST_TAG_GENRE,
+                   G_TYPE_STRING,
+                   _("genre"), 
+                   _("genre this data belongs to"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_COMMENT,
+                   G_TYPE_STRING,
+                   _("comment"),
+                   _("free text commenting the data"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_TRACK_NUMBER,
+                   G_TYPE_UINT,
+                   _("track number"),
+                   _("track number inside a collection"),
+                   gst_tag_merge_use_first);
+  gst_tag_register (GST_TAG_TRACK_COUNT,
+                   G_TYPE_STRING,
+                   _("track count"),
+                   _("count of tracks inside collection this track belongs to"), 
+                   gst_tag_merge_use_first);
+  gst_tag_register (GST_TAG_LOCATION,
+                   G_TYPE_STRING,
+                   _("loccation"),
+                   _("original location of file as a URI"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_DESCRIPTION,
+                   G_TYPE_STRING,
+                   _("description"),
+                   _("short text describing the content of the data"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_VERSION,
+                   G_TYPE_STRING,
+                   _("version"),
+                   _("version of this data"),
+                   NULL);
+  gst_tag_register (GST_TAG_ISRC,
+                   G_TYPE_STRING,
+                   _("ISRC"),
+                   _("International Standard Recording Code - see http://www.ifpi.org/isrc/"),
+                   NULL);
+  gst_tag_register (GST_TAG_ORGANIZATION,
+                   G_TYPE_STRING,
+                   _("organization"),
+                   _("organization"), /* FIXME */
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_COPYRIGHT,
+                   G_TYPE_STRING,
+                   _("copyright"),
+                   _("copyright notice of the data"),
+                   NULL);
+  gst_tag_register (GST_TAG_CONTACT,
+                   G_TYPE_STRING,
+                   _("contact"),
+                   _("contact information"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_LICENSE,   
+                   G_TYPE_STRING,
+                   _("license"),
+                   _("license of data"),
+                   NULL);
+  gst_tag_register (GST_TAG_PERFORMER,
+                   G_TYPE_STRING,
+                   _("performer"),
+                   _("person(s) performing"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_DURATION,
+                   G_TYPE_UINT64,
+                   _("duration"),
+                   _("length in GStreamer time units (nanoseconds)"),
+                   NULL);
+  gst_tag_register (GST_TAG_CODEC,
+                   G_TYPE_STRING,
+                   _("codec"),
+                   _("codec the data is stored in"),
+                   gst_tag_merge_strings_with_comma);
+  gst_tag_register (GST_TAG_MINIMUM_BITRATE,
+                   G_TYPE_UINT,
+                   _("minimum bitrate"),
+                   _("minimum bitrate in bits/s"),
+                   NULL);
+  gst_tag_register (GST_TAG_BITRATE,
+                   G_TYPE_UINT,
+                   _("bitrate"),
+                   _("exact or average bitrate in bits/s"),
+                   NULL);
+  gst_tag_register (GST_TAG_MAXIMUM_BITRATE,
+                   G_TYPE_UINT,
+                   _("maximum bitrate"),
+                   _("maximum bitrate in bits/s"),
+                   NULL);
+}
+/**
+ * gst_tag_merge_use_first:
+ * @dest: uninitialized GValue to store result in
+ * @src: GValue to copy from
+ *
+ * This is a convenience function for the func argument of gst_tag_register(). 
+ * It creates a copy of the first value from the list.
+ */
+void
+gst_tag_merge_use_first (GValue *dest, const GValue *src)
+{
+  const GValue *ret = gst_value_list_get_value (src, 0);
+
+  g_value_init (dest, G_VALUE_TYPE (ret));
+  g_value_copy (ret, dest);
+}
+/**
+ * gst_tag_merge_strings_with_comma:
+ * @dest: uninitialized GValue to store result in
+ * @src: GValue to copy from
+ * 
+ * This is a convenience function for the func argument of gst_tag_register().
+ * It concatenates all given strings using a comma. The tag must be registered
+ * as a G_TYPE_STRING or this function will fail.
+ */
+void
+gst_tag_merge_strings_with_comma (GValue *dest, const GValue *src)
+{
+  GString *str;
+  gint i, count;
+
+  count = gst_value_list_get_size (src);
+  str = g_string_new (g_value_get_string (gst_value_list_get_value (src, 0)));
+  for (i = 1; i < count; i++) {
+    /* seperator between two string */
+    str = g_string_append (str, _(", "));
+    str = g_string_append (str, g_value_get_string (gst_value_list_get_value (src, 1)));
+  }
+
+  g_value_init (dest, G_TYPE_STRING);
+  g_value_take_string (dest, str->str);
+  g_string_free (str, FALSE);
+}
+static GstTagInfo *
+gst_tag_lookup (GQuark entry)
+{
+  GstTagInfo *ret;
+  
+  TAG_LOCK;
+  ret = g_hash_table_lookup (__tags, GUINT_TO_POINTER (entry));
+  TAG_UNLOCK;
+
+  return ret;
+}
+/**
+ * gst_tag_register:
+ * @name: the name or identifier string
+ * @type: the type this data is in
+ * @nick: human-readable name
+ * @blurb: a human-readable description about this tag
+ * @func: function for merging multiple values of this tag
+ *
+ * Registers a new tag type for the use with GStreamer's type system. If a type
+ * with that name is already registered, that one is used.
+ * The old registration may have used a different type however. So don't rely
+ * on youre supplied values.
+ * If you know the type is already registered, use gst_tag_lookup instead.
+ * This function takes ownership of all supplied variables.
+ */
+void
+gst_tag_register (gchar *name, GType type, gchar *nick, gchar *blurb,
+                 GstTagMergeFunc func)
+{
+  GQuark key;
+  GstTagInfo *info;
+
+  g_return_if_fail (name != NULL);
+  g_return_if_fail (nick != NULL);
+  g_return_if_fail (blurb != NULL);
+  g_return_if_fail (type != 0 && type != GST_VALUE_TYPE_LIST);
+  
+  key = g_quark_from_string (name);
+  info = gst_tag_lookup (key);
+  g_return_if_fail (info == NULL);
+  
+  info = g_new (GstTagInfo, 1);
+  info->type = type;
+  info->nick = nick;
+  info->blurb = blurb;
+  info->merge_func = func;
+    
+  TAG_LOCK;
+  g_hash_table_insert (__tags, GUINT_TO_POINTER (key), info);
+  TAG_UNLOCK;
+}
+/**
+ * gst_tag_exists:
+ * @tag: name of the tag
+ *
+ * Checks if the given type is already registered.
+ *
+ * Returns: TRUE if the type is already registered
+ */
+gboolean
+gst_tag_exists (const gchar *tag)
+{
+  g_return_val_if_fail (tag != NULL, FALSE);
+  
+  return gst_tag_lookup (g_quark_from_string (tag)) != NULL;
+}
+/**
+ * gst_tag_get_type:
+ * @tag: the tag
+ *
+ * Gets the #GType used for this tag.
+ *
+ * Returns: the #GType of this tag
+ */
+GType
+gst_tag_get_type (const gchar *tag)
+{
+  GstTagInfo *info;
+  
+  g_return_val_if_fail (tag != NULL, 0);
+  info = gst_tag_lookup (g_quark_from_string (tag));
+  g_return_val_if_fail (info != NULL, 0);
+  
+  return info->type;
+}
+/**
+ * gst_tag_get_nick
+ * @tag: the tag
+ *
+ * Returns the human-readable name of this tag, You must not change or free 
+ * this string.
+ *
+ * Returns: the human-readable name of this tag
+ */
+const gchar *
+gst_tag_get_nick (const gchar *tag)
+{
+  GstTagInfo *info;
+  
+  g_return_val_if_fail (tag != NULL, NULL);
+  info = gst_tag_lookup (g_quark_from_string (tag));
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  return info->nick;
+}
+/**
+ * gst_tag_get_description:
+ * @tag: the tag
+ *
+ * Returns the human-readable description of this tag, You must not change or 
+ * free this string.
+ *
+ * Return the human-readable description of this tag
+ */
+const gchar *
+gst_tag_get_description (const gchar *tag)
+{
+  GstTagInfo *info;
+  
+  g_return_val_if_fail (tag != NULL, NULL);
+  info = gst_tag_lookup (g_quark_from_string (tag));
+  g_return_val_if_fail (info != NULL, NULL);
+  
+  return info->blurb;
+}
+/**
+ * gst_tag_list_is_fixed:
+ * @tag: tag to check
+ *
+ * Checks if the given tag is fixed. A fixed tag can only contain one value.
+ * Unfixed tags can contain lists of values.
+ *
+ * Returns: TRUE, if the given tag is fixed.
+ */
+gboolean
+gst_tag_is_fixed (const gchar *tag)
+{
+  GstTagInfo *info;
+  
+  g_return_val_if_fail (tag != NULL, FALSE);
+  info = gst_tag_lookup (g_quark_from_string (tag));
+  g_return_val_if_fail (info != NULL, FALSE);
+  
+  return info->merge_func == NULL;
+}
+/**
+ * gst_tag_list_new:
+ *
+ * Creates a new empty GstTagList.
+ *
+ * Returns: An empty tag list
+ */
+GstTagList *
+gst_tag_list_new (void)
+{
+  return GST_TAG_LIST (gst_structure_new (TAGLIST, NULL));
+}
+/**
+ * gst_is_tag_list:
+ * @p: Object that might be a taglist
+ *
+ * Checks if the given pointer is a taglist.
+ *
+ * Returns: TRUE, if the given pointer is a taglist
+ */
+gboolean
+gst_is_tag_list (gconstpointer p)
+{
+  g_return_val_if_fail (p != NULL, FALSE); 
+
+  return ((GstStructure *) p)->name == gst_tag_list_quark;
+}
+typedef struct {
+  GstStructure *       list;
+  GstTagMergeMode      mode;
+} GstTagCopyData;
+static void
+gst_tag_list_add_value_internal (GstStructure *list, GstTagMergeMode mode, GQuark tag, GValue *value)
+{
+  GstTagInfo *info = gst_tag_lookup (tag);
+  GstStructureField *field;
+  
+  g_assert (info != NULL);
+
+  if (info->merge_func && (field = gst_structure_id_get_field (list, tag)) != NULL) {
+    GValue value2 = { 0, };
+    switch (mode) {
+      case GST_TAG_MERGE_REPLACE_ALL:
+      case GST_TAG_MERGE_REPLACE:
+       gst_structure_id_set_value (list, tag, value);
+       break;
+      case GST_TAG_MERGE_PREPEND:
+       gst_value_list_concat (&value2, value, &field->value);
+       gst_structure_id_set_value (list, tag, &value2);
+       g_value_unset (&value2);
+       break;
+      case GST_TAG_MERGE_APPEND:
+       gst_value_list_concat (&value2, &field->value, value);
+       gst_structure_id_set_value (list, tag, &value2);
+       g_value_unset (&value2);
+       break;
+      case GST_TAG_MERGE_KEEP:
+      case GST_TAG_MERGE_KEEP_ALL:
+       break;
+      default:
+       g_assert_not_reached ();
+       break;
+    }
+  } else {
+    switch (mode) {
+      case GST_TAG_MERGE_APPEND:
+      case GST_TAG_MERGE_KEEP:
+       if (gst_structure_id_get_field (list, tag) != NULL)
+         break;
+       /* fall through */
+      case GST_TAG_MERGE_REPLACE_ALL:
+      case GST_TAG_MERGE_REPLACE:
+      case GST_TAG_MERGE_PREPEND:
+       gst_structure_id_set_value (list, tag, value);
+       break;
+      case GST_TAG_MERGE_KEEP_ALL:
+       break;
+      default:
+       g_assert_not_reached ();
+       break;
+    }
+  }
+}
+static void
+gst_tag_list_copy_foreach (GstStructure *structure, GQuark tag, GValue *value, gpointer user_data)
+{
+  GstTagCopyData *copy = (GstTagCopyData *) user_data;
+
+  gst_tag_list_add_value_internal (copy->list, copy->mode, tag, value);
+}
+/**
+ * gst_tag_list_insert:
+ * @into: list to merge into
+ * @from: list to merge from
+ * @mode: the mode to use
+ * 
+ * Inserts the tags of the second list into the first list using the given mode.
+ */
+void
+gst_tag_list_insert (GstTagList *into, const GstTagList *from, GstTagMergeMode mode)
+{
+  GstTagCopyData data;
+  
+  g_return_if_fail (GST_IS_TAG_LIST (into));
+  g_return_if_fail (GST_IS_TAG_LIST (from));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+  data.list = (GstStructure *) into;
+  data.mode = mode;
+  if (mode == GST_TAG_MERGE_REPLACE_ALL) {
+    gst_structure_remove_all_fields (data.list);
+  }
+  gst_structure_field_foreach ((GstStructure *) from, gst_tag_list_copy_foreach, &data);
+}
+/**
+ * gst_tag_list_copy:
+ * @list: list to copy
+ *
+ * Copies a given #GstTagList.
+ *
+ * Returns: copy of the given list
+ */
+GstTagList *
+gst_tag_list_copy (const GstTagList *list)
+{
+  g_return_val_if_fail (GST_IS_TAG_LIST (list), NULL);
+  
+  return GST_TAG_LIST (gst_structure_copy ((GstStructure *) list));
+}
+/**
+ * gst_tag_list_merge:
+ * @list1: first list to merge
+ * @list2: second list to merge
+ * @mode: the mode to use
+ * 
+ * Merges the two given lists into a new list. If one of the lists is NULL, a
+ * copy of the other is returned. If both lists are NULL, NULL is returned.
+ *
+ * Returns: the new list
+ */
+GstTagList *
+gst_tag_list_merge (const GstTagList *list1, const GstTagList *list2, GstTagMergeMode mode)
+{
+  g_return_val_if_fail (list1 == NULL || GST_IS_TAG_LIST (list1), NULL);
+  g_return_val_if_fail (list2 == NULL || GST_IS_TAG_LIST (list2), NULL);
+  g_return_val_if_fail (GST_TAG_MODE_IS_VALID (mode), NULL);
+
+  if (!list1 && !list2) {
+    return NULL;
+  } else if (!list1) {
+    return gst_tag_list_copy (list2);
+  } else if (!list2) {
+    return gst_tag_list_copy (list1);
+  } else {
+    GstTagList *ret;
+
+    ret = gst_tag_list_copy (list1);
+    gst_tag_list_insert (ret, list2, mode);
+    return ret;
+  }
+}
+/**
+ * gst_tag_list_free:
+ * @list: the list to free
+ *
+ * Frees the given list and all associated values.
+ */
+void
+gst_tag_list_free (GstTagList *list)
+{
+  g_return_if_fail (GST_IS_TAG_LIST (list));
+  gst_structure_free ((GstStructure *) list);
+}
+/**
+ * gst_tag_list_get_tag_size:
+ * @list: a taglist
+ * @tag: the tag to query
+ *
+ * Checks how many value are stored in this tag list for the given tag.
+ *
+ * Returns: The number of tags stored
+ */
+guint
+gst_tag_list_get_tag_size (const GstTagList *list, const gchar *tag)
+{
+  const GValue *value;
+
+  g_return_val_if_fail (GST_IS_TAG_LIST (list), 0);
+
+  value = gst_structure_get_value ((GstStructure *) list, tag);
+  if (value == NULL)
+    return 0;
+  if (G_VALUE_TYPE (value) != GST_VALUE_TYPE_LIST)
+    return 1;
+
+  return gst_value_list_get_size (value);
+}
+/**
+ * gst_tag_list_add:
+ * @list: list to set tags in
+ * @mode: the mode to use
+ * @tag: tag
+ * @...: values to set
+ *
+ * Sets the values for the given tags using the specified mode.
+ */
+void
+gst_tag_list_add (GstTagList *list, GstTagMergeMode mode, const gchar *tag, ...)
+{
+  va_list args;
+
+  g_return_if_fail (GST_IS_TAG_LIST (list));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+  g_return_if_fail (tag != NULL);
+  
+  va_start (args, tag);
+  gst_tag_list_add_valist (list, mode, tag, args);
+  va_end (args);
+}
+/**
+ * gst_tag_list_add_valist:
+ * @list: list to set tags in
+ * @mode: the mode to use
+ * @tag: tag
+ * @var_args: tag / value pairs to set
+ *
+ * Sets the values for the given tags using the specified mode.
+ */
+void
+gst_tag_list_add_valist (GstTagList *list, GstTagMergeMode mode, const gchar *tag, va_list var_args)
+{
+  GstTagInfo *info;
+  GQuark quark;
+  gchar *error = NULL;
+  
+  g_return_if_fail (GST_IS_TAG_LIST (list));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+  g_return_if_fail (tag != NULL);
+  
+  while (tag != NULL) {
+    GValue value = { 0, };
+    quark = g_quark_from_string (tag);
+    info = gst_tag_lookup (quark);
+    g_return_if_fail (info != NULL);
+    g_value_init (&value, info->type);
+    G_VALUE_COLLECT (&value, var_args, 0, &error);
+    if (error) {
+      g_warning ("%s: %s", G_STRLOC, error);
+      g_free (error);
+      /* we purposely leak the value here, it might not be
+       * in a sane state if an error condition occoured
+       */
+      return;
+    }
+    gst_tag_list_add_value_internal (list, mode, quark, &value);
+    g_value_unset (&value);
+    tag = va_arg (var_args, gchar *);
+  }
+}
+/**
+ * gst_tag_list_remove_tag:
+ * @list: list to remove tag from
+ * @tag: tag to remove
+ *
+ * Removes the goven tag from the taglist.
+ */
+void
+gst_tag_list_remove_tag (GstTagList *list, const gchar *tag)
+{
+  g_return_if_fail (GST_IS_TAG_LIST (list));
+  g_return_if_fail (tag != NULL);
+
+  gst_structure_remove_field ((GstStructure *) list, tag);
+}
+typedef struct {
+  GstTagForeachFunc    func;
+  gpointer             data;
+} TagForeachData;
+static void 
+structure_foreach_wrapper (GstStructure *structure, GQuark field_id, 
+       GValue *value, gpointer user_data)
+{
+  TagForeachData *data = (TagForeachData *) user_data;
+  data->func (GST_TAG_LIST (structure), g_quark_to_string (field_id), data->data);
+}
+/**
+ * gst_tag_list_foreach:
+ * @list: list to iterate over
+ * @func: function to be called for each tag
+ * @user_data: user specified data
+ *
+ * Calls the given function for each tag inside the tag list. Note that if there
+ * is no tag, the function won't be called at all.
+ */
+void
+gst_tag_list_foreach (GstTagList *list, GstTagForeachFunc func, gpointer user_data)
+{
+  TagForeachData data;
+
+  g_return_if_fail (GST_IS_TAG_LIST (list));
+  g_return_if_fail (func != NULL);
+  
+  data.func = func;
+  data.data = user_data;
+  gst_structure_field_foreach ((GstStructure *) list, structure_foreach_wrapper, &data);
+}
+
+/***** tag events *****/
+
+/**
+ * gst_event_new_tag:
+ * @list: the tag list to put into the event or NULL for an empty list
+ *
+ * Creates a new tag event with the given list and takes ownership of it.
+ *
+ * Returns: a new tag event
+ */
+GstEvent *
+gst_event_new_tag (GstTagList *list)
+{
+  GstEvent *ret;
+  
+  g_return_val_if_fail (list == NULL || GST_IS_TAG_LIST (list), NULL);
+
+  ret = gst_event_new (GST_EVENT_TAG);
+  if (!list)
+    list = gst_tag_list_new ();
+  ret->event_data.structure.structure = (GstStructure *) list;
+  
+  return ret;
+}
+/**
+ * get_event_tag_get_list:
+ * @tag_event: a tagging #GstEvent
+ *
+ * Gets the taglist from a given tagging event.
+ * 
+ * Returns: The #GstTagList of the event
+ */
+GstTagList *
+gst_event_tag_get_list (GstEvent *tag_event)
+{
+  g_return_val_if_fail (GST_IS_EVENT (tag_event), NULL);
+  g_return_val_if_fail (GST_EVENT_TYPE (tag_event) == GST_EVENT_TAG, NULL);
+
+  return GST_TAG_LIST (tag_event->event_data.structure.structure);
+}
+
+/**
+ * gst_tag_list_get_value_index:
+ * @list: a #GStTagList
+ * @tag: tag to read out
+ * @index: number of entry to read out
+ *
+ * Gets the value that is at the given index for the given tag in the given 
+ * list.
+ * 
+ * Returns: The GValue for the specified entry or NULL if the tag wasn't available
+ *         or the tag doesn't have as many entries
+ */
+G_CONST_RETURN GValue *
+gst_tag_list_get_value_index (const GstTagList *list, const gchar *tag, guint index)
+{
+  const GValue *value;
+
+  g_return_val_if_fail (GST_IS_TAG_LIST (list), NULL);
+  g_return_val_if_fail (tag != NULL, NULL);
+  
+  value = gst_structure_get_value ((GstStructure *) list, tag);
+  if (value == NULL) return  NULL;
+  
+  if (GST_VALUE_HOLDS_LIST (value)) {
+    if (index >= gst_value_list_get_size (value)) return NULL;
+    return gst_value_list_get_value (value, index);
+  } else {
+    if (index > 0) return NULL;
+    return value;
+  }
+}
+
+/**
+ * gst_tag_list_copy_value:
+ * @dest: uninitialized #GValue to copy into
+ * @list: list to get the tag from
+ * @tag: tag to read out
+ *
+ * Copies the contents for the given tag into the value, merging multiple values 
+ * into one if multiple values are associated with the tag.
+ * You must g_value_unset() the value after use.
+ *
+ * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the 
+ *         given list.
+ */
+gboolean
+gst_tag_list_copy_value (GValue *dest, const GstTagList *list, const gchar *tag)
+{
+  const GValue *src;
+  
+  g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);
+  g_return_val_if_fail (tag != NULL, FALSE);
+  g_return_val_if_fail (dest != NULL, FALSE);
+  g_return_val_if_fail (G_VALUE_TYPE (dest) == 0, FALSE);
+  
+  src = gst_structure_get_value ((GstStructure *) list, tag);
+  if (!src) return FALSE;
+  
+  if (G_VALUE_TYPE (src) == GST_VALUE_TYPE_LIST) {    
+    GstTagInfo *info = gst_tag_lookup (g_quark_from_string (tag));
+    /* must be there or lists aren't allowed */
+    g_assert (info->merge_func);
+    info->merge_func (dest, src);
+  } else {
+    g_value_init (dest, G_VALUE_TYPE (src));
+    g_value_copy (src, dest);
+  }
+  return TRUE;
+}
+
+/***** evil macros to get all the gst_tag_list_get_*() functions right *****/
+
+#define TAG_MERGE_FUNCS(name,type)                                             \
+gboolean                                                                       \
+gst_tag_list_get_ ## name (const GstTagList *list, const gchar *tag,           \
+                          type *value)                                         \
+{                                                                              \
+  GValue v = { 0, };                                                           \
+                                                                               \
+  g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);                                \
+  g_return_val_if_fail (tag != NULL, FALSE);                                   \
+  g_return_val_if_fail (value != NULL, FALSE);                                 \
+                                                                               \
+  if (!gst_tag_list_copy_value (&v, list, tag))                                        \
+      return FALSE;                                                            \
+  *value = COPY_FUNC (g_value_get_ ## name (&v));                              \
+  g_value_unset (&v);                                                          \
+  return TRUE;                                                                 \
+}                                                                              \
+                                                                               \
+gboolean                                                                       \
+gst_tag_list_get_ ## name ## _index (const GstTagList *list, const gchar *tag,         \
+                          guint index, type *value)                            \
+{                                                                              \
+  const GValue *v;                                                             \
+                                                                               \
+  g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);                                \
+  g_return_val_if_fail (tag != NULL, FALSE);                                   \
+  g_return_val_if_fail (value != NULL, FALSE);                                 \
+                                                                               \
+  if ((v = gst_tag_list_get_value_index (list, tag, index)) == NULL)           \
+      return FALSE;                                                            \
+  *value = COPY_FUNC (g_value_get_ ## name (v));                               \
+  return TRUE;                                                                 \
+}
+
+#define COPY_FUNC /**/
+TAG_MERGE_FUNCS (char, gchar)
+TAG_MERGE_FUNCS (uchar, guchar)
+TAG_MERGE_FUNCS (boolean, gboolean)
+TAG_MERGE_FUNCS (int, gint)
+TAG_MERGE_FUNCS (uint, guint)
+TAG_MERGE_FUNCS (long, glong)
+TAG_MERGE_FUNCS (ulong, gulong)
+TAG_MERGE_FUNCS (int64, gint64)
+TAG_MERGE_FUNCS (uint64, guint64)
+TAG_MERGE_FUNCS (float, gfloat)
+TAG_MERGE_FUNCS (double, gdouble)
+#undef COPY_FUNC
+  
+#define COPY_FUNC g_strdup
+TAG_MERGE_FUNCS (string, gchar *)
+
+
+
+
diff --git a/gst/gsttaglist.h b/gst/gsttaglist.h
new file mode 100644 (file)
index 0000000..6d1efbe
--- /dev/null
@@ -0,0 +1,232 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttag.h: Header for tag support
+ *
+ * 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_H__
+#define __GST_TAG_H__
+
+#include <gst/gststructure.h>
+#include <gst/gstevent.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+  GST_TAG_MERGE_UNDEFINED,
+  GST_TAG_MERGE_REPLACE_ALL,
+  GST_TAG_MERGE_REPLACE,
+  GST_TAG_MERGE_APPEND,
+  GST_TAG_MERGE_PREPEND,
+  GST_TAG_MERGE_KEEP,
+  GST_TAG_MERGE_KEEP_ALL,
+  /* add more */
+  GST_TAG_MERGE_COUNT
+} GstTagMergeMode;
+#define GST_TAG_MODE_IS_VALID(mode)     (((mode) > GST_TAG_MERGE_UNDEFINED) && ((mode) < GST_TAG_MERGE_COUNT))
+
+typedef GstStructure GstTagList;
+#define GST_TAG_LIST(x)                ((GstTagList *) (x))
+#define GST_IS_TAG_LIST(x)     (gst_is_tag_list (GST_TAG_LIST (x)))
+
+typedef void           (* GstTagForeachFunc)   (const GstTagList *list, const gchar *tag, gpointer user_data);
+typedef void           (* GstTagMergeFunc)     (GValue *dest, const GValue *src);
+
+/* initialize tagging system */
+void           _gst_tag_initialize             (void);
+
+void           gst_tag_register                (gchar *                name,
+                                                GType                  type,
+                                                gchar *                nick,
+                                                gchar *                blurb,
+                                                GstTagMergeFunc        func);
+/* some default merging functions */
+void           gst_tag_merge_use_first         (GValue *               dest,
+                                                const GValue *         values);
+void           gst_tag_merge_strings_with_comma (GValue *              dest,
+                                                const GValue *         values);
+
+/* basic tag support */
+gboolean       gst_tag_exists                  (const gchar *          tag);
+GType          gst_tag_get_type                (const gchar *          tag);
+const gchar *   gst_tag_get_nick               (const gchar *          tag);
+const gchar *  gst_tag_get_description         (const gchar *          tag);
+gboolean       gst_tag_is_fixed                (const gchar *          tag);
+
+/* tag lists */
+GstTagList *   gst_tag_list_new                (void);
+gboolean       gst_is_tag_list                 (gconstpointer          p);
+GstTagList *   gst_tag_list_copy               (const GstTagList *     list);
+void           gst_tag_list_insert             (GstTagList *           into,
+                                                const GstTagList *     from,
+                                                GstTagMergeMode        mode);
+GstTagList *   gst_tag_list_merge              (const GstTagList *     list1,
+                                                const GstTagList *     list2,
+                                                GstTagMergeMode        mode);
+void           gst_tag_list_free               (GstTagList *           list);
+guint          gst_tag_list_get_tag_size       (const GstTagList *     list,
+                                                const gchar *          tag);
+void           gst_tag_list_add                (GstTagList *           list,
+                                                GstTagMergeMode        mode,
+                                                const gchar *          tag,
+                                                ...);
+void           gst_tag_list_add_valist         (GstTagList *           list,
+                                                GstTagMergeMode        mode,
+                                                const gchar *          tag,
+                                                va_list                var_args);
+void           gst_tag_list_remove_tag         (GstTagList *           list,
+                                                const gchar *          tag);
+void           gst_tag_list_foreach            (GstTagList *           list,
+                                                GstTagForeachFunc      func,
+                                                gpointer               user_data);
+
+G_CONST_RETURN GValue *
+               gst_tag_list_get_value_index    (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index);
+gboolean       gst_tag_list_copy_value         (GValue *               dest,
+                                                const GstTagList *     list,
+                                                const gchar *          tag);
+
+/* simplifications (FIXME: do we want them?) */
+gboolean       gst_tag_list_get_char           (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gchar *                value);
+gboolean       gst_tag_list_get_char_index     (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gchar *                value);
+gboolean       gst_tag_list_get_uchar          (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guchar *               value);
+gboolean       gst_tag_list_get_uchar_index    (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                guchar *               value);
+gboolean       gst_tag_list_get_boolean        (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gboolean *             value);
+gboolean       gst_tag_list_get_boolean_index  (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gboolean *             value);
+gboolean       gst_tag_list_get_int            (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gint *                 value);
+gboolean       gst_tag_list_get_int_index      (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gint *                 value);
+gboolean       gst_tag_list_get_uint           (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint *                value);
+gboolean       gst_tag_list_get_uint_index     (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                guint *                value);
+gboolean       gst_tag_list_get_long           (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                glong *                value);
+gboolean       gst_tag_list_get_long_index     (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                glong *                value);
+gboolean       gst_tag_list_get_ulong          (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gulong *               value);
+gboolean       gst_tag_list_get_ulong_index    (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gulong *               value);
+gboolean       gst_tag_list_get_int64          (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gint64 *               value);
+gboolean       gst_tag_list_get_int64_index    (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gint64 *               value);
+gboolean       gst_tag_list_get_uint64         (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint64 *              value);
+gboolean       gst_tag_list_get_uint64_index   (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                guint64 *              value);
+gboolean       gst_tag_list_get_float          (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gfloat *               value);
+gboolean       gst_tag_list_get_float_index    (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gfloat *               value);
+gboolean       gst_tag_list_get_double         (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gdouble *              value);
+gboolean       gst_tag_list_get_double_index   (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gdouble *              value);
+gboolean       gst_tag_list_get_string         (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gchar **               value);
+gboolean       gst_tag_list_get_string_index   (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gchar **               value);
+gboolean       gst_tag_list_get_pointer        (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                gpointer *             value);
+gboolean       gst_tag_list_get_pointer_index  (const GstTagList *     list,
+                                                const gchar *          tag,
+                                                guint                  index,
+                                                gpointer *             value);
+
+/* tag events */
+GstEvent *     gst_event_new_tag               (GstTagList *           list);
+GstTagList *   gst_event_tag_get_list          (GstEvent *             tag_event);
+
+
+/* GStreamer core tags (need to be discussed) */
+#define GST_TAG_TITLE                  "title"
+#define GST_TAG_ARTIST                 "artist"
+#define GST_TAG_ALBUM                  "album"
+#define GST_TAG_DATE                   "date"
+#define GST_TAG_GENRE                  "genre"
+#define GST_TAG_COMMENT                        "comment"
+#define GST_TAG_TRACK_NUMBER           "track-number"
+#define GST_TAG_TRACK_COUNT            "track-count"
+#define GST_TAG_LOCATION               "location"
+#define GST_TAG_DESCRIPTION            "description"
+#define GST_TAG_VERSION                        "version"
+#define GST_TAG_ISRC                   "isrc"
+#define GST_TAG_ORGANIZATION           "organization"
+#define GST_TAG_COPYRIGHT              "copyright"
+#define GST_TAG_CONTACT                        "contact"
+#define GST_TAG_LICENSE                        "license"
+#define GST_TAG_PERFORMER              "performer"
+#define GST_TAG_DURATION               "duration"
+#define GST_TAG_CODEC                  "codec"
+#define GST_TAG_BITRATE                        "bitrate"
+#define GST_TAG_MINIMUM_BITRATE                "minimum-bitrate"
+#define GST_TAG_MAXIMUM_BITRATE                "maximum-bitrate"
+
+
+G_END_DECLS
+
+#endif /* __GST_EVENT_H__ */
diff --git a/gst/gsttagsetter.c b/gst/gsttagsetter.c
new file mode 100644 (file)
index 0000000..a7e6d71
--- /dev/null
@@ -0,0 +1,214 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttaginterface.c: interface for tag setting on elements
+ *
+ * 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 "gsttaginterface.h"
+#include <gobject/gvaluecollector.h>
+#include <string.h>
+
+GST_DEBUG_CATEGORY_STATIC (gst_tag_interface_debug);
+#define GST_CAT_DEFAULT tag_tag_interface_debug
+
+static GQuark gst_tag_key;
+
+typedef struct {
+  GstTagMergeMode      mode;
+  GstTagList *         list;
+} GstTagData;
+
+GType
+gst_tag_setter_get_type (void)
+{
+  static GType tag_setter_type = 0;
+    
+  if (! tag_setter_type) {
+    static const GTypeInfo tag_setter_info = {
+      sizeof (GstTagSetterIFace), /* class_size */
+      NULL,            /* base_init */
+      NULL,             /* base_finalize */
+      NULL,
+      NULL,             /* class_finalize */
+      NULL,             /* class_data */
+      0,
+      0,
+      NULL
+    };
+    
+    GST_DEBUG_CATEGORY_INIT (gst_tag_interface_debug, "GstTagInterface", 0, "interfaces for tagging");
+    
+    tag_setter_type = g_type_register_static (G_TYPE_INTERFACE, "GstTagSetter",
+                                             &tag_setter_info, 0);
+    
+    g_type_interface_add_prerequisite (tag_setter_type, GST_TYPE_ELEMENT);
+
+    gst_tag_key = g_quark_from_static_string ("GST_TAG_SETTER");
+  }
+  
+  return tag_setter_type;      
+}
+static void
+gst_tag_data_free (gpointer p)
+{
+  GstTagData *data = (GstTagData *) p;
+
+  if (data->list)
+    gst_tag_list_free (data->list);
+
+  g_free (data);
+}
+static GstTagData *
+gst_tag_setter_get_data (GstTagSetter *setter)
+{
+  GstTagData *data;
+
+  data = g_object_get_qdata (G_OBJECT (setter), gst_tag_key);
+  if (!data) {
+    data = g_new (GstTagData, 1);
+    data->list = NULL;
+    data->mode = GST_TAG_MERGE_KEEP;
+    g_object_set_qdata_full (G_OBJECT (setter), gst_tag_key, data, gst_tag_data_free);
+  }
+  
+  return data;
+}
+/**
+ * gst_tag_setter_merge:
+ * @setter: a #GstTagSetter
+ * @list: a tag list to merge from
+ * @mode: the mode to merge with
+ *
+ * Merges the given list into the setter's list using the given mode.
+ */
+void
+gst_tag_setter_merge (GstTagSetter *setter, const GstTagList *list, GstTagMergeMode mode)
+{
+  GstTagData *data;
+
+  g_return_if_fail (GST_IS_TAG_SETTER (setter));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+  data = gst_tag_setter_get_data (setter);
+  if (!data->list) {
+    data->list = gst_tag_list_copy (list);
+  } else {
+    gst_tag_list_merge (data->list, list, mode);
+  }
+}
+/**
+ * gst_tag_setter_add:
+ * @setter: a #GstTagSetter
+ * @mode: the mode to use
+ * @tag: tag to set
+ * @...: more tag / value pairs to set
+ *
+ * Adds the given tag / value pairs on the setter using the given merge mode. 
+ * The list must be terminated with GST_TAG_INVALID.
+ */
+void
+gst_tag_setter_add (GstTagSetter *setter, GstTagMergeMode mode, const gchar *tag, ...)
+{
+  va_list args;
+
+  g_return_if_fail (GST_IS_TAG_SETTER (setter));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+  va_start (args, tag);
+  gst_tag_setter_add_valist (setter, mode, tag, args);
+  va_end (args);
+}
+/**
+ * gst_tag_setter_add_valist:
+ * @setter: a #GstTagSetter
+ * @mode: the mode to use
+ * @tag: tag to set
+ * @var_args: tag / value pairs to set
+ *
+ * Adds the given tag / value pairs on the setter using the given merge mode. 
+ * The list must be terminated with GST_TAG_INVALID.
+ */
+void
+gst_tag_setter_add_valist (GstTagSetter *setter, GstTagMergeMode mode, const gchar *tag, va_list var_args)
+{
+  GstTagData *data;
+  
+  g_return_if_fail (GST_IS_TAG_SETTER (setter));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+  data = gst_tag_setter_get_data (setter);
+  if (!data->list)
+    data->list = gst_tag_list_new ();
+  
+  gst_tag_list_add_valist (data->list, mode, tag, var_args);
+}
+/**
+ * gst_tag_setter_get_list:
+ * @setter: a #GstTagSetter
+ *
+ * Retrieves a copy of the current list of tags the setter uses.
+ * You need to gst_tag_list_free() the list after use.
+ *
+ * Returns: a current snapshot of the taglist used in the setter
+ *         or NULL if none is used.
+ */
+const GstTagList *    
+gst_tag_setter_get_list (GstTagSetter *setter)
+{
+  g_return_val_if_fail (GST_IS_TAG_SETTER (setter), NULL);
+
+  return gst_tag_setter_get_data (setter)->list;
+}
+/**
+ * gst_tag_setter_set_merge_mode:
+ * @setter: a #GstTagSetter
+ * @overwrite: The mode with which tags are added
+ *
+ * Sets the given merge mode that is used for adding tags from events to tags
+ * specified by this interface. The default is #GST_TAG_MERGE_KEEP, which keeps
+ * the tags by this interface and discards tags from events.
+ */
+void
+gst_tag_setter_set_merge_mode (GstTagSetter *setter, GstTagMergeMode mode)
+{
+  g_return_if_fail (GST_IS_TAG_SETTER (setter));
+  g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
+
+  gst_tag_setter_get_data (setter)->mode = mode;
+}
+/**
+ * gst_tag_setter_get_merge_mode:
+ * @setter: a #GstTagSetter
+ *
+ * Queries the mode by which tags inside the setter are overwritten by tags 
+ * from events
+ *
+ * Returns: the merge mode used inside the element.
+ */
+GstTagMergeMode
+gst_tag_setter_get_merge_mode (GstTagSetter *setter)
+{
+  g_return_val_if_fail (GST_IS_TAG_SETTER (setter), FALSE);
+
+  return gst_tag_setter_get_data (setter)->mode;
+}
diff --git a/gst/gsttagsetter.h b/gst/gsttagsetter.h
new file mode 100644 (file)
index 0000000..cb85137
--- /dev/null
@@ -0,0 +1,70 @@
+/* GStreamer
+ * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
+ *
+ * gsttaginterface.h: Interfaces for tagging
+ *
+ * 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_INTERFACE_H__
+#define __GST_TAG_INTERFACE_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_TAG_SETTER            (gst_tag_setter_get_type ())
+#define GST_TAG_SETTER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TAG_SETTER, GstTagSetter))
+#define GST_TAG_SETTER_CLASS(obj)      (G_TYPE_CHECK_CLASS_CAST ((obj), GST_TYPE_TAG_SETTER, GstTagSetter))
+#define GST_IS_TAG_SETTER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TAG_SETTER))
+#define GST_TAG_SETTER_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GST_TYPE_TAG_SETTER, GstTagSetterIFace))
+
+typedef struct _GstTagSetter                   GstTagSetter; /* Dummy typedef */
+typedef struct _GstTagSetterIFace              GstTagSetterIFace;
+
+/* use an empty interface here to allow detection of elements using user-set
+   tags */
+struct _GstTagSetterIFace
+{
+  GTypeInterface g_iface;
+
+  /* signals */
+
+  /* virtual table */
+};
+
+GType          gst_tag_setter_get_type         (void) G_GNUC_CONST;
+
+void           gst_tag_setter_merge            (GstTagSetter *         setter,
+                                                const GstTagList *     list,
+                                                GstTagMergeMode        mode);
+void           gst_tag_setter_add              (GstTagSetter *         setter,
+                                                GstTagMergeMode        mode,
+                                                const gchar *          tag,
+                                                ...);
+void            gst_tag_setter_add_valist      (GstTagSetter *         setter,
+                                                GstTagMergeMode        mode,
+                                                const gchar *          tag,
+                                                va_list                var_args);
+const GstTagList *gst_tag_setter_get_list      (GstTagSetter *         setter);
+
+void           gst_tag_setter_set_merge_mode   (GstTagSetter *         setter,
+                                                GstTagMergeMode        mode);
+GstTagMergeMode        gst_tag_setter_get_merge_mode   (GstTagSetter *         setter);
+
+G_END_DECLS
+
+#endif /* __GST_TAG_INTERFACE_H__ */
index 03b4ba9..79586e8 100644 (file)
@@ -48,11 +48,175 @@ struct _GstValueIntersectInfo {
 GType gst_type_fourcc;
 GType gst_type_int_range;
 GType gst_type_double_range;
+GType gst_value_type_list;
 
 GArray *gst_value_compare_funcs;
 GArray *gst_value_union_funcs;
 GArray *gst_value_intersect_funcs;
 
+/* list */
+
+static void
+gst_value_init_list (GValue *value)
+{
+  value->data[0].v_pointer = g_array_new (FALSE, TRUE, sizeof(GValue));
+}
+
+static GArray *
+gst_value_list_array_copy (const GArray *src)
+{
+  GArray *dest;
+  gint i;
+  
+  dest = g_array_sized_new (FALSE, TRUE, sizeof(GValue), src->len);
+  g_array_set_size (dest, src->len);
+  for (i = 0; i < src->len; i++) {
+    g_value_init (&g_array_index(dest, GValue, i), G_VALUE_TYPE (&g_array_index(src, GValue, i)));
+    g_value_copy (&g_array_index(src, GValue, i), &g_array_index(dest, GValue, i));
+  }
+
+  return dest;
+}
+
+static void
+gst_value_copy_list (const GValue *src_value, GValue *dest_value)
+{
+  dest_value->data[0].v_pointer = gst_value_list_array_copy ((GArray *) src_value->data[0].v_pointer);
+}
+
+static void
+gst_value_free_list (GValue *value)
+{
+  gint i;
+  GArray *src = (GArray *) value->data[0].v_pointer;
+  
+  if ((value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS) == 0) {
+    for (i = 0; i < src->len; i++) {
+      g_value_unset (&g_array_index(src, GValue, i));
+    }
+    g_array_free (src, TRUE);
+  }
+}
+
+static gpointer
+gst_value_list_peek_pointer (const GValue *value)
+{
+  return value->data[0].v_pointer;
+}
+
+static gchar *
+gst_value_collect_list (GValue *value, guint n_collect_values,
+    GTypeCValue *collect_values, guint collect_flags)
+{
+  if (collect_flags & G_VALUE_NOCOPY_CONTENTS) {
+    value->data[0].v_pointer = collect_values[0].v_pointer;
+    value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
+  } else {
+    value->data[0].v_pointer = gst_value_list_array_copy ((GArray *) collect_values[0].v_pointer);
+  }
+  return NULL;
+}
+
+static gchar *
+gst_value_lcopy_list (const GValue *value, guint n_collect_values,
+    GTypeCValue *collect_values, guint collect_flags)
+{
+  GArray **dest = collect_values[0].v_pointer;
+  if (!dest)
+    return g_strdup_printf ("value location for `%s' passed as NULL",
+       G_VALUE_TYPE_NAME (value));
+  if (!value->data[0].v_pointer)  
+    return g_strdup_printf ("invalid value given for `%s'",
+       G_VALUE_TYPE_NAME (value));
+  if (collect_flags & G_VALUE_NOCOPY_CONTENTS) {
+    *dest = (GArray *) value->data[0].v_pointer;
+  } else {
+    *dest = gst_value_list_array_copy ((GArray *) value->data[0].v_pointer);
+  }
+  return NULL;
+}
+
+void 
+gst_value_list_prepend_value (GValue *value, const GValue *prepend_value)
+{
+  g_return_if_fail (GST_VALUE_HOLDS_LIST (value));
+
+  g_array_prepend_vals ((GArray *) value->data[0].v_pointer, prepend_value, 1);
+}
+
+void 
+gst_value_list_append_value (GValue *value, const GValue *append_value)
+{
+  g_return_if_fail (GST_VALUE_HOLDS_LIST (value));
+
+  g_array_append_vals ((GArray *) value->data[0].v_pointer, append_value, 1);
+}
+
+guint 
+gst_value_list_get_size (const GValue *value)
+{
+  g_return_val_if_fail (GST_VALUE_HOLDS_LIST (value), 0);
+
+  return ((GArray *) value->data[0].v_pointer)->len;
+}
+
+const GValue *
+gst_value_list_get_value (const GValue *value, guint index)
+{
+  g_return_val_if_fail (GST_VALUE_HOLDS_LIST (value), NULL);
+  g_return_val_if_fail (index < gst_value_list_get_size (value), NULL);
+
+  return (const GValue *) &g_array_index ((GArray *) value->data[0].v_pointer, GValue, index);
+}
+
+/**
+ * gst_value_list_concat:
+ * @dest: an uninitialized #GValue to take the result
+ * @value1: first value to put into the union
+ * @value2: second value to put into the union
+ *
+ * Concatenates copies of value1 and value2 into a list. dest will be 
+ * initialized to the type GST_VALUE_TYPE_LIST.
+ */
+void
+gst_value_list_concat (GValue *dest, const GValue *value1, const GValue *value2)
+{
+  guint i, value1_length, value2_length;
+  GArray *array;
+
+  g_return_if_fail (dest != NULL);
+  g_return_if_fail (G_VALUE_TYPE (dest) == 0);
+  g_return_if_fail (G_IS_VALUE (value1));
+  g_return_if_fail (G_IS_VALUE (value2));
+  
+  value1_length = (GST_VALUE_HOLDS_LIST (value1) ? gst_value_list_get_size (value1) : 1);
+  value2_length = (GST_VALUE_HOLDS_LIST (value2) ? gst_value_list_get_size (value2) : 1);
+  g_value_init (dest, GST_VALUE_TYPE_LIST);
+  array = (GArray *) dest->data[0].v_pointer;
+  g_array_set_size (array, value1_length + value2_length);
+  
+  if (GST_VALUE_HOLDS_LIST (value1)) {
+    for (i = 0; i < value1_length; i++) {
+      g_value_init (&g_array_index(array, GValue, i), G_VALUE_TYPE (gst_value_list_get_value (value1, i)));
+      g_value_copy (gst_value_list_get_value (value1, i), &g_array_index(array, GValue, i));
+    }
+  } else {
+    g_value_init (&g_array_index(array, GValue, 0), G_VALUE_TYPE (value1));
+    g_value_copy (value1, &g_array_index(array, GValue, 0));
+  }
+  
+  if (GST_VALUE_HOLDS_LIST (value2)) {
+    for (i = 0; i < value2_length; i++) {
+      g_value_init (&g_array_index(array, GValue, i + value1_length), G_VALUE_TYPE (gst_value_list_get_value (value2, i)));
+      g_value_copy (gst_value_list_get_value (value2, i), &g_array_index(array, GValue, i + value1_length));
+    }
+  } else {
+    g_value_init (&g_array_index(array, GValue, value1_length), G_VALUE_TYPE (value2));
+    g_value_copy (value2, &g_array_index(array, GValue, value1_length));
+  }
+}
+
+/* fourcc */
 
 static void 
 gst_value_init_fourcc (GValue *value)
@@ -147,7 +311,7 @@ gst_value_lcopy_int_range (const GValue *value, guint n_collect_values,
 void
 gst_value_set_int_range (GValue *value, int start, int end)
 {
-  g_return_if_fail (GST_VALUE_HOLDS_FOURCC (value));
+  g_return_if_fail (GST_VALUE_HOLDS_INT_RANGE (value));
 
   value->data[0].v_long = start;
   value->data[1].v_long = end;
@@ -156,7 +320,7 @@ gst_value_set_int_range (GValue *value, int start, int end)
 int
 gst_value_get_int_range_min (const GValue *value)
 {
-  g_return_val_if_fail (GST_VALUE_HOLDS_FOURCC (value), 0);
+  g_return_val_if_fail (GST_VALUE_HOLDS_INT_RANGE (value), 0);
 
   return value->data[0].v_long;
 }
@@ -164,11 +328,82 @@ gst_value_get_int_range_min (const GValue *value)
 int
 gst_value_get_int_range_max (const GValue *value)
 {
-  g_return_val_if_fail (GST_VALUE_HOLDS_FOURCC (value), 0);
+  g_return_val_if_fail (GST_VALUE_HOLDS_INT_RANGE (value), 0);
 
   return value->data[1].v_long;
 }
 
+/* double range */
+
+static void 
+gst_value_init_double_range (GValue *value)
+{
+  value->data[0].v_double = 0;
+  value->data[1].v_double = 0;
+}
+
+static void
+gst_value_copy_double_range (const GValue *src_value, GValue *dest_value)
+{
+  dest_value->data[0].v_double = src_value->data[0].v_double;
+  dest_value->data[1].v_double = src_value->data[1].v_double;
+}
+
+static gchar *
+gst_value_collect_double_range (GValue *value, guint n_collect_values,
+    GTypeCValue *collect_values, guint collect_flags)
+{
+  /* FIXME */
+  value->data[0].v_double = collect_values[0].v_double;
+  value->data[1].v_double = collect_values[1].v_double;
+
+  return NULL;
+}
+
+static gchar *
+gst_value_lcopy_double_range (const GValue *value, guint n_collect_values,
+    GTypeCValue *collect_values, guint collect_flags)
+{
+  guint32 *double_range_p = collect_values[0].v_pointer;
+
+  /* FIXME */
+
+  if (!double_range_p)
+    return g_strdup_printf ("value location for `%s' passed as NULL",
+       G_VALUE_TYPE_NAME (value));
+
+  *double_range_p = value->data[0].v_double;
+
+  return NULL;
+}
+
+void
+gst_value_set_double_range (GValue *value, double start, double end)
+{
+  g_return_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value));
+
+  value->data[0].v_double = start;
+  value->data[1].v_double = end;
+}
+
+double
+gst_value_get_double_range_min (const GValue *value)
+{
+  g_return_val_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value), 0);
+
+  return value->data[0].v_double;
+}
+
+double
+gst_value_get_double_range_max (const GValue *value)
+{
+  g_return_val_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value), 0);
+
+  return value->data[1].v_double;
+}
+
+/* fourcc */
+
 static void
 gst_value_transform_fourcc_string (const GValue *src_value,
     GValue *dest_value)
@@ -190,26 +425,39 @@ gst_value_transform_int_range_string (const GValue *src_value,
 static int
 gst_value_compare_int (const GValue *value1, const GValue *value2)
 {
-  return value2->data[0].v_int - value1->data[0].v_int;
+  if (value1->data[0].v_int > value2->data[0].v_int)
+    return GST_VALUE_GREATER_THAN;
+  if (value1->data[0].v_int < value2->data[0].v_int)
+    return GST_VALUE_LESS_THAN;
+  return GST_VALUE_EQUAL;
 }
 
 static int
 gst_value_compare_double (const GValue *value1, const GValue *value2)
 {
-  return (value2->data[0].v_double > value1->data[0].v_double) -
-    (value2->data[0].v_double < value1->data[0].v_double);
+  if (value1->data[0].v_double > value2->data[0].v_double)
+    return GST_VALUE_GREATER_THAN;
+  if (value1->data[0].v_double < value2->data[0].v_double)
+    return GST_VALUE_LESS_THAN;
+  if (value1->data[0].v_double == value2->data[0].v_double)
+    return GST_VALUE_EQUAL;
+  return GST_VALUE_UNORDERED;
 }
 
 static int
 gst_value_compare_string (const GValue *value1, const GValue *value2)
 {
-  return strcmp(value1->data[0].v_pointer, value2->data[0].v_pointer);
+  int x = strcmp(value1->data[0].v_pointer, value2->data[0].v_pointer);
+  if(x<0) return GST_VALUE_LESS_THAN;
+  if(x>0) return GST_VALUE_GREATER_THAN;
+  return GST_VALUE_EQUAL;
 }
 
 static int
 gst_value_compare_fourcc (const GValue *value1, const GValue *value2)
 {
-  return value2->data[0].v_int - value1->data[0].v_int;
+  if (value2->data[0].v_int == value1->data[0].v_int) return GST_VALUE_EQUAL;
+  return GST_VALUE_UNORDERED;
 }
 
 gboolean
@@ -243,8 +491,8 @@ gst_value_compare (const GValue *value1, const GValue *value2)
     return compare_info->func(value1, value2);
   }
 
-  g_return_val_if_fail(0 /* type not found */, 0);
-  return 0;
+  g_return_val_if_fail(0 /* type not found */, GST_VALUE_UNORDERED);
+  return GST_VALUE_UNORDERED;
 }
 
 void
@@ -275,7 +523,7 @@ gst_value_can_union (const GValue *value1, const GValue *value2)
   return FALSE;
 }
 
-void
+gboolean
 gst_value_union (GValue *dest, const GValue *value1, const GValue *value2)
 {
   GstValueUnionInfo *union_info;
@@ -285,10 +533,11 @@ gst_value_union (GValue *dest, const GValue *value1, const GValue *value2)
     union_info = &g_array_index(gst_value_union_funcs, GstValueUnionInfo, i);
     if(union_info->type1 == G_VALUE_TYPE(value1) &&
        union_info->type2 == G_VALUE_TYPE(value2)) {
-      union_info->func(dest, value1, value2);
-      return;
+      return union_info->func(dest, value1, value2);
     }
   }
+  gst_value_list_concat (dest, value1, value2);
+  return TRUE;
 }
 
 void
@@ -364,7 +613,7 @@ gst_value_can_intersect (const GValue *value1, const GValue *value2)
   return FALSE;
 }
 
-void
+gboolean
 gst_value_intersect (GValue *dest, const GValue *value1, const GValue *value2)
 {
   GstValueIntersectInfo *intersect_info;
@@ -375,10 +624,17 @@ gst_value_intersect (GValue *dest, const GValue *value1, const GValue *value2)
        GstValueIntersectInfo, i);
     if(intersect_info->type1 == G_VALUE_TYPE(value1) &&
        intersect_info->type2 == G_VALUE_TYPE(value2)) {
-      intersect_info->func(dest, value1, value2);
-      return;
+      return intersect_info->func(dest, value1, value2);
     }
   }
+
+  if(gst_value_compare(value1, value2) == GST_VALUE_EQUAL){
+    g_value_init(dest, G_VALUE_TYPE(value1));
+    g_value_copy(value1, dest);
+    return TRUE;
+  }
+
+  return FALSE;
 }
 
 void
@@ -441,6 +697,36 @@ _gst_value_initialize (void)
     gst_type_int_range = g_type_register_static (G_TYPE_BOXED, "GstIntRange", &info, 0);
   }
 
+  {
+    static const GTypeValueTable value_table = {
+      gst_value_init_double_range,
+      NULL,
+      gst_value_copy_double_range,
+      NULL,
+      "i",
+      gst_value_collect_double_range,
+      "p",
+      gst_value_lcopy_double_range
+    };
+    info.value_table = &value_table;
+    gst_type_double_range = g_type_register_static (G_TYPE_BOXED, "GstDoubleRange", &info, 0);
+  }
+  
+  {
+    static const GTypeValueTable value_table = {
+      gst_value_init_list,
+      gst_value_free_list,
+      gst_value_copy_list,
+      gst_value_list_peek_pointer,
+      "p",
+      gst_value_collect_list,
+      "p",
+      gst_value_lcopy_list
+    };
+    info.value_table = &value_table;
+    gst_value_type_list = g_type_register_static (G_TYPE_BOXED, "GstValueList", &info, 0);
+  }
+
   g_value_register_transform_func (GST_TYPE_FOURCC, G_TYPE_STRING,
       gst_value_transform_fourcc_string);
   g_value_register_transform_func (GST_TYPE_INT_RANGE, G_TYPE_STRING,
index 353e1dd..0fa57ca 100644 (file)
@@ -31,15 +31,36 @@ typedef int (* GstValueUnionFunc) (GValue *dest, const GValue *value1,
 typedef int (* GstValueIntersectFunc) (GValue *dest, const GValue *value1,
     const GValue *value2);
 
-#define GST_VALUE_HOLDS_FOURCC(x) TRUE
+#define GST_MAKE_FOURCC(a,b,c,d)        (guint32)((a)|(b)<<8|(c)<<16|(d)<<24)
+#define GST_STR_FOURCC(f)               (guint32)(((f)[0])|((f)[1]<<8)|((f)[2]<<16)|((f)[3]<<24))
+
+#define GST_FOURCC_FORMAT "%c%c%c%c"
+#define GST_FOURCC_ARGS(fourcc) \
+        ((gchar) ((fourcc)     &0xff)), \
+        ((gchar) (((fourcc)>>8 )&0xff)), \
+        ((gchar) (((fourcc)>>16)&0xff)), \
+        ((gchar) (((fourcc)>>24)&0xff))
 
 #define GST_TYPE_FOURCC gst_type_fourcc
 #define GST_TYPE_INT_RANGE gst_type_int_range
 #define GST_TYPE_DOUBLE_RANGE gst_type_double_range
+#define GST_VALUE_TYPE_LIST gst_value_type_list
+
+#define GST_VALUE_HOLDS_FOURCC(x) (G_VALUE_TYPE(x) == gst_type_fourcc)
+#define GST_VALUE_HOLDS_INT_RANGE(x) (G_VALUE_TYPE(x) == gst_type_int_range)
+#define GST_VALUE_HOLDS_DOUBLE_RANGE(x) (G_VALUE_TYPE(x) == gst_type_double_range)
+#define GST_VALUE_HOLDS_LIST(x) (G_VALUE_TYPE(x) == GST_VALUE_TYPE_LIST)
+#define GST_VALUE_HOLDS_CAPS(x) TRUE /* FIXME */
+
+#define GST_VALUE_LESS_THAN (-1)
+#define GST_VALUE_EQUAL 0
+#define GST_VALUE_GREATER_THAN 1
+#define GST_VALUE_UNORDERED 2
 
 extern GType gst_type_fourcc;
 extern GType gst_type_int_range;
 extern GType gst_type_double_range;
+extern GType gst_value_type_list;
 
 void gst_value_set_fourcc (GValue *value, guint32 fourcc);
 guint32 gst_value_get_fourcc (const GValue *value);
@@ -49,11 +70,21 @@ int gst_value_get_int_range_min (const GValue *value);
 int gst_value_get_int_range_max (const GValue *value);
 
 void gst_value_set_double_range (GValue *value, double start, double end);
-double gst_value_get_double_range_start (const GValue *value);
-double gst_value_get_double_range_end (const GValue *value);
+double gst_value_get_double_range_min (const GValue *value);
+double gst_value_get_double_range_max (const GValue *value);
+
+void gst_value_list_prepend_value (GValue *value, const GValue *prepend_value);
+void gst_value_list_append_value (GValue *value, const GValue *prepend_value);
+guint gst_value_list_get_size (const GValue *value);
+const GValue *gst_value_list_get_value (const GValue *value, guint index);
+void gst_value_list_concat (GValue *dest, const GValue *value1, const GValue *value2);
 
 void _gst_value_initialize (void);
 
+int gst_value_compare (const GValue *src1, const GValue *src2);
+gboolean gst_value_intersect (GValue *dest, const GValue *src1, const GValue *src2);
+gboolean gst_value_union (GValue *dest, const GValue *src1, const GValue *src2);
+
 G_END_DECLS
 
 #endif
index c0555a2..76f2679 100644 (file)
@@ -687,8 +687,9 @@ gst_xml_registry_parse_element_factory (GMarkupParseContext *context, const gcha
   GstElementFactory *factory = GST_ELEMENT_FACTORY (registry->current_feature);
 
   if (!strcmp (tag, "name")) {
-    g_free (registry->current_feature->name);
-    registry->current_feature->name = g_strndup (text, text_len);
+    gchar *name = g_strndup (text, text_len);
+    gst_plugin_feature_set_name (registry->current_feature, name);
+    g_free (name);
   }
   else if (!strcmp (tag, "longname")) {
     g_free (factory->details.longname);
@@ -709,12 +710,18 @@ gst_xml_registry_parse_element_factory (GMarkupParseContext *context, const gcha
   else if (!strcmp(tag, "rank")) {
     gint rank;
     gchar *ret;
-     rank = strtol (text, &ret, 0);
+    
+    rank = strtol (text, &ret, 0);
     if (ret == text + text_len) {
      gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank);
     }
   }
-  
+  else if (!strcmp(tag, "interface")) {
+    gchar *tmp = g_strndup (text, text_len);
+    __gst_element_factory_add_interface (factory, tmp);
+    g_free (tmp);
+  }
+
   return TRUE;
 }
 
@@ -1464,23 +1471,29 @@ gst_xml_registry_save_feature (GstXMLRegistry *xmlregistry, GstPluginFeature *fe
     
   if (GST_IS_ELEMENT_FACTORY (feature)) {
     GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
-    GList *templates;
+    GList *walk;
 
     PUT_ESCAPED ("longname", factory->details.longname);
     PUT_ESCAPED ("class", factory->details.klass);
     PUT_ESCAPED ("description", factory->details.description);
     PUT_ESCAPED ("author", factory->details.author);
     
-    templates = factory->padtemplates;
+    walk = factory->padtemplates;
 
-    while (templates) {
-      GstPadTemplate *template = GST_PAD_TEMPLATE (templates->data);
+    while (walk) {
+      GstPadTemplate *template = GST_PAD_TEMPLATE (walk->data);
 
       CLASS (xmlregistry)->save_func (xmlregistry, "<padtemplate>\n");
       gst_xml_registry_save_pad_template (xmlregistry, template);
       CLASS (xmlregistry)->save_func (xmlregistry, "</padtemplate>\n");
       
-      templates = g_list_next (templates);
+      walk = g_list_next (walk);
+    }
+
+    walk = factory->interfaces;
+    while (walk) {
+      PUT_ESCAPED ("interface", (gchar *) walk->data);
+      walk = g_list_next (walk);
     }
   }
   else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
index 3bd8a76..fa04c8f 100644 (file)
@@ -1,3 +1,3 @@
-SUBDIRS = control getbits bytestream
+SUBDIRS = bytestream control getbits 
 
-DIST_SUBDIRS = control getbits bytestream
+DIST_SUBDIRS = bytestream control getbits 
index a294dc1..3aa752e 100644 (file)
@@ -1,10 +1,10 @@
 ### all of the standard pc files we need to generate
-pcfiles = \
-       gstreamer-@GST_MAJORMINOR@.pc \
+pcfiles =                                                      \
+       gstreamer-@GST_MAJORMINOR@.pc                           \
        gstreamer-control-@GST_MAJORMINOR@.pc
 
-pcfiles_uninstalled = \
-       gstreamer-@GST_MAJORMINOR@-uninstalled.pc \
+pcfiles_uninstalled =                                          \
+       gstreamer-@GST_MAJORMINOR@-uninstalled.pc               \
        gstreamer-control-@GST_MAJORMINOR@-uninstalled.pc
 
 all-local: $(pcfiles) $(pcfiles_uninstalled)
@@ -18,10 +18,10 @@ $(pcfiles_uninstalled): %-@GST_MAJORMINOR@-uninstalled.pc: %-uninstalled.pc
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = $(pcfiles)
 
-EXTRA_DIST = \
-       gstreamer.pc.in \
-       gstreamer-uninstalled.pc.in \
-       gstreamer-control.pc.in \
+EXTRA_DIST =                                                   \
+       gstreamer.pc.in                                         \
+       gstreamer-uninstalled.pc.in                             \
+       gstreamer-control.pc.in                                 \
        gstreamer-control-uninstalled.pc.in
 
 CLEANFILES = $(pcfiles) $(pcfiles_uninstalled)
diff --git a/po/.gitignore b/po/.gitignore
new file mode 100644 (file)
index 0000000..91b6c79
--- /dev/null
@@ -0,0 +1,6 @@
+*.gmo
+remove-potcdate.sed
+stamp-po
+POTFILES
+cat-id-tbl.c
+gstreamer-0.7.pot
index 9751930..69a0458 100644 (file)
@@ -10,13 +10,39 @@ else
 GST_AUTOPLUG_DIRS = autoplug helloworld2
 endif
 
-SUBDIRS = $(GST_AUTOPLUG_DIRS) $(GST_LOADSAVE_DIRS) \
-          helloworld \
-          queue queue2 queue3 queue4 \
-          launch thread plugins mixer cutter pingpong manual
+SUBDIRS =                              \
+       helloworld                      \
+       queue                           \
+       queue2                          \
+       queue3                          \
+       queue4                          \
+       launch                          \
+       thread                          \
+       plugins                         \
+       mixer                           \
+       cutter                          \
+       pingpong                        \
+       manual                          \
+       retag                           \
+       $(GST_LOADSAVE_DIRS)            \
+       $(GST_AUTOPLUG_DIRS) 
 
-DIST_SUBDIRS = autoplug \
-          helloworld helloworld2 \
-          queue queue2 queue3 queue4 \
-          launch thread xml plugins typefind mixer cutter pingpong manual
+
+DIST_SUBDIRS = autoplug                        \
+       helloworld                      \
+       helloworld2                     \
+       queue                           \
+       queue2                          \
+       queue3                          \
+       queue4                          \
+       launch                          \
+       thread                          \
+       plugins                         \
+       mixer                           \
+       cutter                          \
+       pingpong                        \
+       manual                          \
+       xml                             \
+       typefind                        \
+       retag
 
diff --git a/tests/old/examples/retag/.gitignore b/tests/old/examples/retag/.gitignore
new file mode 100644 (file)
index 0000000..a54ae58
--- /dev/null
@@ -0,0 +1,2 @@
+retag
+transcode
diff --git a/tests/old/examples/retag/Makefile.am b/tests/old/examples/retag/Makefile.am
new file mode 100644 (file)
index 0000000..6a686a5
--- /dev/null
@@ -0,0 +1,7 @@
+noinst_PROGRAMS = retag transcode
+
+retag_LDADD = $(GST_LIBS) 
+retag_CFLAGS = $(GST_CFLAGS)
+
+transcode_LDADD = $(GST_LIBS)
+transcode_CFLAGS = $(GST_CFLAGS)
diff --git a/tests/old/examples/retag/retag.c b/tests/old/examples/retag/retag.c
new file mode 100644 (file)
index 0000000..2824ba7
--- /dev/null
@@ -0,0 +1,103 @@
+/* 
+ * This example shows how to use interfaces and the tag subsystem.
+ * It takes an mp3 file as input, and makes an ogg file out of it. While doing
+ * this, it parses the filename and sets artist and title in the ogg file.
+ * It assumes the filename to be "<artist> - <title>.mp3"
+ * 
+ * Run the program as "retag <mp3 file>"
+ *
+ * To run this program, you need to have the gst-plugins package (specifically
+ * the vorbis and mad plugins) installed.
+ */
+
+/* main header */
+#include <gst/gst.h>
+/* and a header we need for the string manipulation */
+#include <string.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  GstElement *bin, *filesrc, *tag_changer, *filesink;
+  gchar *artist, *title, *ext, *filename;
+  
+  /* check that the argument is there */
+  if (argc != 2) {
+    g_print ("usage: %s <mp3 file>\n", argv[0]);
+    return 1;
+  }
+
+  /* initialize GStreamer */
+  gst_init (&argc, &argv);
+
+  /* parse the mp3 name */
+  artist = strrchr (argv[1], '/');
+  if (artist == NULL)
+    artist = argv[1];
+  artist = g_strdup (artist);
+  ext = strrchr (artist, '.');
+  if (ext) *ext = '\0';
+  title = strstr (artist, " - ");
+  if (title == NULL) {
+    g_print ("The format of the mp3 file is invalid.\n");
+    return 1;
+  }
+  *title = '\0';
+  title += 3;
+  
+  
+  /* create a new bin to hold the elements */
+  bin = gst_pipeline_new ("pipeline");
+  g_assert (bin);
+
+  /* create a file reader */
+  filesrc = gst_element_factory_make ("filesrc", "disk_source");
+  g_assert (filesrc);
+
+  /* now it's time to get the tag_changer */
+  tag_changer = gst_element_factory_make ("id3tag", "tag_changer");
+  if (!tag_changer) {
+    g_print ("could not find plugin \"mad\"");
+    return 1;
+  }
+
+  /* and a file writer */
+  filesink = gst_element_factory_make ("filesink", "filesink");
+  g_assert (filesink);
+
+  /* set the filenames */
+  filename = g_strdup_printf ("%s.temp", argv[1]); /* easy solution */
+  g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
+  g_object_set (G_OBJECT (filesink), "location", filename, NULL);
+
+  /* make sure the tag setter uses our stuff 
+     (though that should already be default) */
+  gst_tag_setter_set_merge_mode (GST_TAG_SETTER (tag_changer), GST_TAG_MERGE_KEEP);
+  /* set the tagging information */
+  gst_tag_setter_add (GST_TAG_SETTER (tag_changer), GST_TAG_MERGE_REPLACE,
+                     GST_TAG_ARTIST, artist, 
+                     GST_TAG_TITLE, title, 
+                     NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add_many (GST_BIN (bin), filesrc, tag_changer, filesink, NULL);
+
+  /* link the elements */
+  gst_element_link_many (filesrc, tag_changer, filesink, NULL);
+  
+  /* start playing */
+  gst_element_set_state (bin, GST_STATE_PLAYING);
+
+  while (gst_bin_iterate (GST_BIN (bin)));
+
+  /* stop the bin */
+  gst_element_set_state (bin, GST_STATE_NULL);
+
+  /* rename the file to the correct name and remove the old one */
+  remove (argv[1]);
+  rename (filename, argv[1]);
+  g_free (filename);
+
+  return 0;
+}
+
diff --git a/tests/old/examples/retag/transcode.c b/tests/old/examples/retag/transcode.c
new file mode 100644 (file)
index 0000000..c41f96e
--- /dev/null
@@ -0,0 +1,106 @@
+/* 
+ * This example shows how to use interfaces and the tag subsystem.
+ * It takes an mp3 file as input, and makes an ogg file out of it. While doing
+ * this, it parses the filename and sets artist and title in the ogg file.
+ * It assumes the filename to be "<artist> - <title>.mp3"
+ * 
+ * Run the program as "retag <mp3 file>"
+ *
+ * To run this program, you need to have the gst-plugins package (specifically
+ * the vorbis and mad plugins) installed.
+ */
+
+/* main header */
+#include <gst/gst.h>
+/* and a header we need for the string manipulation */
+#include <string.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  GstElement *bin, *filesrc, *decoder, *encoder, *filesink;
+  gchar *artist, *title, *ext, *filename;
+  
+  /* initialize GStreamer */
+  gst_init (&argc, &argv);
+
+  /* check that the argument is there */
+  if (argc != 2) {
+    g_print ("usage: %s <mp3 file>\n", argv[0]);
+    return 1;
+  }
+
+  /* parse the mp3 name */
+  artist = strrchr (argv[1], '/');
+  if (artist == NULL)
+    artist = argv[1];
+  artist = g_strdup (artist);
+  ext = strrchr (artist, '.');
+  if (ext) *ext = '\0';
+  title = strstr (artist, " - ");
+  if (title == NULL) {
+    g_print ("The format of the mp3 file is invalid.\n");
+    return 1;
+  }
+  *title = '\0';
+  title += 3;
+  
+  
+  /* create a new bin to hold the elements */
+  bin = gst_pipeline_new ("pipeline");
+  g_assert (bin);
+
+  /* create a file reader */
+  filesrc = gst_element_factory_make ("filesrc", "disk_source");
+  g_assert (filesrc);
+
+  /* now it's time to get the decoder */
+  decoder = gst_element_factory_make ("mad", "decode");
+  if (!decoder) {
+    g_print ("could not find plugin \"mad\"");
+    return 1;
+  }
+
+  /* create the encoder */
+  encoder = gst_element_factory_make ("vorbisenc", "encoder");
+  if (!encoder) {
+    g_print ("cound not find plugin \"vorbisenc\"");
+    return 1;
+  }
+  
+  /* and a file writer */
+  filesink = gst_element_factory_make ("filesink", "filesink");
+  g_assert (filesink);
+
+  /* set the filenames */
+  filename = g_strdup_printf ("%s.ogg", argv[1]); /* easy solution */
+  g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
+  g_object_set (G_OBJECT (filesink), "location", filename, NULL);
+  g_free (filename);
+
+  /* make sure the tag setter uses our stuff 
+     (though that should already be default) */
+  gst_tag_setter_set_merge_mode (GST_TAG_SETTER (encoder), GST_TAG_MERGE_KEEP);
+  /* set the tagging information */
+  gst_tag_setter_add (GST_TAG_SETTER (encoder), GST_TAG_MERGE_REPLACE,
+                     GST_TAG_ARTIST, artist, 
+                     GST_TAG_TITLE, title, 
+                     NULL);
+
+  /* add objects to the main pipeline */
+  gst_bin_add_many (GST_BIN (bin), filesrc, decoder, encoder, filesink, NULL);
+
+  /* link the elements */
+  gst_element_link_many (filesrc, decoder, encoder, filesink, NULL);
+  
+  /* start playing */
+  gst_element_set_state (bin, GST_STATE_PLAYING);
+
+  while (gst_bin_iterate (GST_BIN (bin)));
+
+  /* stop the bin */
+  gst_element_set_state (bin, GST_STATE_NULL);
+
+  return 0;
+}
+
index 0e18189..261e4d6 100644 (file)
@@ -14,11 +14,11 @@ GST_DEBUG_DIRS = debug
 endif
 
 SUBDIRS = bytestream cleanup dynparams \
-       caps plugin elements clock refcounting threads \
+       caps plugin elements clock refcounting tags threads \
        indexers debug $(GST_PARSE_DIRS) $(GST_DEBUG_DIRS)
 
 DIST_SUBDIRS = bytestream caps cleanup clock dynparams elements indexers \
-               plugin refcounting threads parse debug
+               plugin refcounting tags threads parse debug
 
 tests_pass = test_gst_init
 tests_fail = 
index 2435e31..191ae43 100644 (file)
@@ -76,7 +76,7 @@ main (gint argc, gchar *argv[])
   gst_element_set_state (thread, GST_STATE_PLAYING);
   g_print ("SLEEPING 1 sec\n");
   sleep (1);
-  gst_element_set_state (pipeline, GST_STATE_PLAYING);
+  gst_bin_sync_children_state (GST_BIN (pipeline));
   g_print ("SLEEPING 2 sec\n");
   sleep (2);
 
index 0e18189..261e4d6 100644 (file)
@@ -14,11 +14,11 @@ GST_DEBUG_DIRS = debug
 endif
 
 SUBDIRS = bytestream cleanup dynparams \
-       caps plugin elements clock refcounting threads \
+       caps plugin elements clock refcounting tags threads \
        indexers debug $(GST_PARSE_DIRS) $(GST_DEBUG_DIRS)
 
 DIST_SUBDIRS = bytestream caps cleanup clock dynparams elements indexers \
-               plugin refcounting threads parse debug
+               plugin refcounting tags threads parse debug
 
 tests_pass = test_gst_init
 tests_fail = 
index 2435e31..191ae43 100644 (file)
@@ -76,7 +76,7 @@ main (gint argc, gchar *argv[])
   gst_element_set_state (thread, GST_STATE_PLAYING);
   g_print ("SLEEPING 1 sec\n");
   sleep (1);
-  gst_element_set_state (pipeline, GST_STATE_PLAYING);
+  gst_bin_sync_children_state (GST_BIN (pipeline));
   g_print ("SLEEPING 2 sec\n");
   sleep (2);
 
index 5eb22d8..0cc2315 100644 (file)
@@ -221,6 +221,34 @@ fault_setup (void)
   sigaction (SIGQUIT, &action, NULL);
 }
 
+static void
+print_tag (const GstTagList *list, const gchar *tag, gpointer unused)
+{
+  gint i, count;
+
+  count = gst_tag_list_get_tag_size (list, tag);
+
+  for (i = 0; i < count; i++) {
+    
+    gchar *str = g_strdup_value_contents (
+                   gst_tag_list_get_value_index (list, tag, i));
+  
+    if (i == 0) {
+      g_print ("%15s: %s\n", gst_tag_get_nick (tag), str);
+    } else {
+      g_print ("               : %s\n", str);
+    }
+
+    g_free (str);
+  }
+}
+static void
+found_tag (GObject *pipeline, GstElement *source, GstTagList *tags)
+{
+  g_print ("FOUND TAG      : element \"%s\"\n", GST_STR_NULL (GST_ELEMENT_NAME (source)));
+  gst_tag_list_foreach (tags, print_tag, NULL);
+}
+
 /* we only use sighandler here because the registers are not important */
 static void 
 sigint_handler_sighandler (int signum)
@@ -286,11 +314,14 @@ main(int argc, char *argv[])
   gint i, j;
   /* options */
   gboolean verbose = FALSE;
+  gboolean tags = FALSE;
   gboolean no_fault = FALSE;
   gboolean trace = FALSE;
   gchar *savefile = NULL;
   gchar *exclude_args = NULL;
   struct poptOption options[] = {
+    {"tags",   't',  POPT_ARG_NONE|POPT_ARGFLAG_STRIP,   &tags,   0,
+     "output tags (also known as metadata)", NULL},
     {"verbose",        'v',  POPT_ARG_NONE|POPT_ARGFLAG_STRIP,   &verbose,   0,
      "output status information and property notifications", NULL},
     {"exclude", 'X',  POPT_ARG_STRING|POPT_ARGFLAG_STRIP, &exclude_args,  0,
@@ -301,7 +332,7 @@ main(int argc, char *argv[])
 #endif
     {"no-fault", 'f', POPT_ARG_NONE|POPT_ARGFLAG_STRIP,   &no_fault,   0,
      "Do not install a fault handler", NULL},
-    {"trace",   't',  POPT_ARG_NONE|POPT_ARGFLAG_STRIP,   &trace,   0,
+    {"trace",   'T',  POPT_ARG_NONE|POPT_ARGFLAG_STRIP,   &trace,   0,
      "print alloc trace if enabled at compile time", NULL},
     {"iterations",'i',POPT_ARG_INT|POPT_ARGFLAG_STRIP,    &max_iterations,   0,
      "number of times to iterate pipeline", NULL},
@@ -382,6 +413,9 @@ main(int argc, char *argv[])
     gchar **exclude_list = exclude_args ? g_strsplit (exclude_args, ",", 0) : NULL;
     g_signal_connect (pipeline, "deep_notify", G_CALLBACK (gst_element_default_deep_notify), exclude_list);
   }
+  if (tags) {
+    g_signal_connect (pipeline, "found-tag", G_CALLBACK (found_tag), NULL);
+  }
   g_signal_connect (pipeline, "error", G_CALLBACK (gst_element_default_error), NULL);
   
 #ifndef GST_DISABLE_LOADSAVE