diff --git a/configure.ac b/configure.ac index baa2046e00..d8d65de4de 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/docs/.gitignore b/docs/.gitignore index 08f5ed37d8..2cd23a3717 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -5,3 +5,4 @@ Makefile.in *.la .deps .libs +version.entities diff --git a/docs/Makefile.am b/docs/Makefile.am index 9ff0084371..892624abef 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -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 diff --git a/docs/gst/.gitignore b/docs/gst/.gitignore index f6923174cf..d785f34447 100644 --- a/docs/gst/.gitignore +++ b/docs/gst/.gitignore @@ -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 diff --git a/docs/gst/tmpl/gstautoplug.sgml b/docs/gst/tmpl/gstautoplug.sgml index 6c5bd633a2..3acf7c2215 100644 --- a/docs/gst/tmpl/gstautoplug.sgml +++ b/docs/gst/tmpl/gstautoplug.sgml @@ -99,6 +99,14 @@ The autoplug object + + + + + +@gstautoplug: the object which received the signal. +@arg1: + The type of the autoplugger. @@ -141,11 +149,3 @@ The type of the autoplugger. @Returns: - - - - - -@gstautoplug: the object which received the signal. -@arg1: - diff --git a/docs/gst/tmpl/gstbin.sgml b/docs/gst/tmpl/gstbin.sgml index 68c62da4bc..8a0038e0b9 100644 --- a/docs/gst/tmpl/gstbin.sgml +++ b/docs/gst/tmpl/gstbin.sgml @@ -72,6 +72,34 @@ The GstBin object + + + + + +@gstbin: the object which received the signal. +@arg1: the element that was added to the bin + + + + + + +@gstbin: the object which received the signal. +@arg1: the element that was removed from the bin + + + +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. + + +@gstbin: the object which received the signal. +@Returns: TRUE if the state of the bin was advanced. + 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: - - - - - -@gstbin: the object which received the signal. -@arg1: the element that was added to the bin - - - - - - -@gstbin: the object which received the signal. -@arg1: the element that was removed from the bin - - - -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. - - -@gstbin: the object which received the signal. -@Returns: TRUE if the state of the bin was advanced. - diff --git a/docs/gst/tmpl/gstbuffer.sgml b/docs/gst/tmpl/gstbuffer.sgml index 824c1e5b1e..c9474ec5a5 100644 --- a/docs/gst/tmpl/gstbuffer.sgml +++ b/docs/gst/tmpl/gstbuffer.sgml @@ -262,6 +262,7 @@ The basic structure of a buffer. @timestamp: @duration: @offset: +@offset_end: @pool: @pool_private: diff --git a/docs/gst/tmpl/gstcaps.sgml b/docs/gst/tmpl/gstcaps.sgml index 65f1399c44..8cb1606a5d 100644 --- a/docs/gst/tmpl/gstcaps.sgml +++ b/docs/gst/tmpl/gstcaps.sgml @@ -406,24 +406,6 @@ The name used for tracing @Returns: - - - - - -@caps: -@type_id: - - - - - - - -@caps: -@Returns: - - diff --git a/docs/gst/tmpl/gstclock.sgml b/docs/gst/tmpl/gstclock.sgml index da3b748162..ae2cf0de72 100644 --- a/docs/gst/tmpl/gstclock.sgml +++ b/docs/gst/tmpl/gstclock.sgml @@ -235,6 +235,16 @@ The GstClock object + + +Maximum allowed diff for clock sync requests against the real time. + + + + +Boolean property to activate stat generation on the clock. + + @@ -402,13 +412,3 @@ The GstClock object @id: - - -Maximum allowed diff for clock sync requests against the real time. - - - - -Boolean property to activate stat generation on the clock. - - diff --git a/docs/gst/tmpl/gstconfig.sgml b/docs/gst/tmpl/gstconfig.sgml index 26c2e33861..99b80335de 100644 --- a/docs/gst/tmpl/gstconfig.sgml +++ b/docs/gst/tmpl/gstconfig.sgml @@ -108,13 +108,6 @@ If this is defined, the debugging subsystem - - - - - - - diff --git a/docs/gst/tmpl/gstelement.sgml b/docs/gst/tmpl/gstelement.sgml index c06cdbd907..4ce8a7bea1 100644 --- a/docs/gst/tmpl/gstelement.sgml +++ b/docs/gst/tmpl/gstelement.sgml @@ -71,6 +71,58 @@ The element object + + +Signal emited when the element goes to PAUSED due to an end-of-stream +condition. + + +@gstelement: the object which received the signal. + + + +Is triggered whenever an error occured. + + + +@gstelement: the object which received the signal. +@arg1: the error message +@arg2: + + + + + + +@gstelement: the object which received the signal. +@arg1: +@arg2: + + + +Is triggered whenever a new pad is added to an element. + + +@gstelement: the object which received the signal. +@arg1: the new pad that was added + + + +Is triggered whenever a pad has been removed from the element. + + +@gstelement: the object which received the signal. +@arg1: The pad that was removed. + + + +Is triggered whenever the state of an element changes. + + +@gstelement: the object which received the signal. +@arg1: the new state of the object +@arg2: + Gets the name of the element. @@ -880,14 +932,6 @@ Queries if the Element is decoupled. @obj: a #GstElement to query - - -Query wether this element is in the End Of Stream state. - - -@obj: a #GstElement to query - - Query wether this element can handle events. @@ -971,46 +1015,3 @@ Helper macro to create query type functions @...: list of query types. - - -Signal emited when the element goes to PAUSED due to an end-of-stream -condition. - - -@gstelement: the object which received the signal. - - - -Is triggered whenever an error occured. - - - -@gstelement: the object which received the signal. -@arg1: the error message -@arg2: - - - -Is triggered whenever a new pad is added to an element. - - -@gstelement: the object which received the signal. -@arg1: the new pad that was added - - - -Is triggered whenever a pad has been removed from the element. - - -@gstelement: the object which received the signal. -@arg1: The pad that was removed. - - - -Is triggered whenever the state of an element changes. - - -@gstelement: the object which received the signal. -@arg1: the new state of the object -@arg2: - diff --git a/docs/gst/tmpl/gstelementfactory.sgml b/docs/gst/tmpl/gstelementfactory.sgml index eddebe8f0c..6e6cfe15c2 100644 --- a/docs/gst/tmpl/gstelementfactory.sgml +++ b/docs/gst/tmpl/gstelementfactory.sgml @@ -62,22 +62,8 @@ describes the element, mostly for the benefit of editors. @longname: @klass: -@license: @description: -@version: @author: -@copyright: - - - - - - -@name: -@type: -@details: -@Returns: - @@ -88,15 +74,6 @@ describes the element, mostly for the benefit of editors. @Returns: - - - - - -@elementfactory: -@templ: - - @@ -137,51 +114,3 @@ describes the element, mostly for the benefit of editors. @Returns: - - - - - -@factoryname: -@name: -@Returns: - - - - - - - -@factory: -@rank: - - - - -The element is only marginally usefull for autoplugging - - - - - - -The plugin may not be used in autoplugging - - - - - - -The plugin is well suited for autoplugging - - - - - - -The plugin is suited for autoplugging but only as a second -candidate. - - - - diff --git a/docs/gst/tmpl/gstevent.sgml b/docs/gst/tmpl/gstevent.sgml index a32604f5c2..11467fecd0 100644 --- a/docs/gst/tmpl/gstevent.sgml +++ b/docs/gst/tmpl/gstevent.sgml @@ -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: diff --git a/docs/gst/tmpl/gstindex.sgml b/docs/gst/tmpl/gstindex.sgml index d828fa3114..cdc3cfa10e 100644 --- a/docs/gst/tmpl/gstindex.sgml +++ b/docs/gst/tmpl/gstindex.sgml @@ -209,6 +209,19 @@ The GstIndex object + + +Is emited when a new entry is added to the index. + + +@gstindex: the object which received the signal. +@arg1: The entry added to the index. + + + + + + @@ -401,16 +414,3 @@ The GstIndex object @id: - - -Is emited when a new entry is added to the index. - - -@gstindex: the object which received the signal. -@arg1: The entry added to the index. - - - - - - diff --git a/docs/gst/tmpl/gstinfo.sgml b/docs/gst/tmpl/gstinfo.sgml index e2650f43c2..3ae7489d62 100644 --- a/docs/gst/tmpl/gstinfo.sgml +++ b/docs/gst/tmpl/gstinfo.sgml @@ -233,21 +233,6 @@ default. If you want to define a default category, do it like this: @Varargs: - - - - - -@category: -@level: -@file: -@function: -@line: -@object: -@format: -@args: - - diff --git a/docs/gst/tmpl/gstobject.sgml b/docs/gst/tmpl/gstobject.sgml index 52616a2215..cf69ab93ee 100644 --- a/docs/gst/tmpl/gstobject.sgml +++ b/docs/gst/tmpl/gstobject.sgml @@ -40,6 +40,47 @@ The GstObject + + +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. + + +@gstobject: the object which received the signal. +@arg1: the object that originated the signal +@arg2: the property that changed + + + +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. + + +@gstobject: the object which received the signal. +@arg1: the xmlNodePtr of the parent node + + + +Is emitted when the parent of an object is set. + + +@gstobject: the object which received the signal. +@arg1: the new parent + + + +Is emitted when the parent of an object is unset. + + +@gstobject: the object which received the signal. +@arg1: the old parent + + + +The name of the object + + This macro returns the entire set of flags for the object. @@ -290,44 +331,3 @@ Check if the object has been destroyed. @Returns: - - -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. - - -@gstobject: the object which received the signal. -@arg1: the object that originated the signal -@arg2: the property that changed - - - -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. - - -@gstobject: the object which received the signal. -@arg1: the xmlNodePtr of the parent node - - - -Is emitted when the parent of an object is set. - - -@gstobject: the object which received the signal. -@arg1: the new parent - - - -Is emitted when the parent of an object is unset. - - -@gstobject: the object which received the signal. -@arg1: the old parent - - - -The name of the object - - diff --git a/docs/gst/tmpl/gstpadtemplate.sgml b/docs/gst/tmpl/gstpadtemplate.sgml index f26e9c5f12..0396557ef7 100644 --- a/docs/gst/tmpl/gstpadtemplate.sgml +++ b/docs/gst/tmpl/gstpadtemplate.sgml @@ -87,6 +87,15 @@ The padtemplate object. + + +This signal is fired when an element creates a pad from this +template. + + +@gstpadtemplate: the object which received the signal. +@arg1: The pad that was created. + Flags for the padtemplate @@ -199,12 +208,3 @@ Check if the properties of the padtemplate are fixed @Returns: - - -This signal is fired when an element creates a pad from this -template. - - -@gstpadtemplate: the object which received the signal. -@arg1: The pad that was created. - diff --git a/docs/gst/tmpl/gstplugin.sgml b/docs/gst/tmpl/gstplugin.sgml index bd8e9ba321..30dc50fc7f 100644 --- a/docs/gst/tmpl/gstplugin.sgml +++ b/docs/gst/tmpl/gstplugin.sgml @@ -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: The plugin object -@name: -@longname: +@desc: @filename: @features: @numfeatures: @manager: @module: -@init_called: @@ -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. -@module: The GModule 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. + +@module: The GModule it was loaded from @@ -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. - - - -A handy macro to define a plugin description. This macro handles with all the issues -involved with the different linking methods for this plugin. - - -@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. - - - - -The macro used to define dynamically loaded plugins. - - -@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. - - - - -A macro used to define a statically linked plugin. - - -@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. - - - - - - - -@filename: -@Returns: - +@plugin_exit: +@version: +@license: +@copyright: +@package: +@origin: @@ -163,15 +127,6 @@ A macro used to define a statically linked plugin. @Returns: - - - - - -@plugin: -@longname: - - @@ -210,16 +165,6 @@ A macro used to define a statically linked plugin. @Returns: - - - - - -@plugin: -@error: -@Returns: - - diff --git a/docs/gst/tmpl/gstreamer-unused.sgml b/docs/gst/tmpl/gstreamer-unused.sgml index 2b293643a2..7110bc58b6 100644 --- a/docs/gst/tmpl/gstreamer-unused.sgml +++ b/docs/gst/tmpl/gstreamer-unused.sgml @@ -1056,27 +1056,6 @@ Cache time and byteoffsets. GstTimeCache - - -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. - - - - - - - - - - -Detect the mime type of a media stream - - - -GstTypeFind - - @@ -1835,6 +1814,12 @@ to the current function, i.e. "('element')" @format: printf-style format string @args...: printf arguments + + + + + + @@ -1898,6 +1883,13 @@ Queries whether the cothread holding this element needs to be stopped. @obj: The element to query + + +Query wether this element is in the End Of Stream state. + + +@obj: a #GstElement to query + 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. + + +The element is only marginally usefull for autoplugging + + + + + +The plugin may not be used in autoplugging + + + + + +The plugin is well suited for autoplugging + + + + + +The plugin is suited for autoplugging but only as a second +candidate. + + + @@ -2769,6 +2786,37 @@ Get the flag indicating the properties are fixed from the template. @plugin: + + +A handy macro to define a plugin description. This macro handles with all the issues +involved with the different linking methods for this plugin. + + +@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. + + + +The macro used to define dynamically loaded plugins. + + +@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. + + + +A macro used to define a statically linked plugin. + + +@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. + @@ -5717,6 +5765,15 @@ Query the element for the current mime type + + + + + +@gstxml: the object which received the signal. +@arg1: +@arg2: + @@ -7704,6 +7761,14 @@ safely be modified. @caps: + + + + + +@caps: +@Returns: + @@ -7741,6 +7806,14 @@ safely be modified. @Returns: @count: + + + + + +@caps: +@type_id: + @@ -7915,6 +7988,20 @@ safely be modified. @data: @Returns: + + + + + +@category: +@level: +@file: +@function: +@line: +@object: +@format: +@args: + @@ -7998,6 +8085,14 @@ of an element he doesn't need anymore. @a: @b: + + + + + +@elementfactory: +@templ: + @@ -8054,6 +8149,25 @@ of an element he doesn't need anymore. @parent: @Returns: + + + + + +@factoryname: +@name: +@Returns: + + + + + + +@name: +@type: +@details: +@Returns: + @@ -8070,6 +8184,14 @@ of an element he doesn't need anymore. @parent: @Returns: + + + + + +@factory: +@rank: + @@ -9294,6 +9416,15 @@ Destroys the pipeline. @name: @Returns: + + + + + +@plugin: +@error: +@Returns: + @@ -9308,6 +9439,14 @@ Destroys the pipeline. @mime: + + + + + +@filename: +@Returns: + @@ -9316,6 +9455,14 @@ Destroys the pipeline. @parent: @Returns: + + + + + +@plugin: +@longname: + @@ -10040,6 +10187,19 @@ Destroy the scheduler + + + + + +@plugin: +@name: +@rank: +@func: +@extensions: +@possible_caps: +@data: + diff --git a/docs/gst/tmpl/gstthread.sgml b/docs/gst/tmpl/gstthread.sgml index 6d7021906d..38a7691b0a 100644 --- a/docs/gst/tmpl/gstthread.sgml +++ b/docs/gst/tmpl/gstthread.sgml @@ -34,15 +34,6 @@ The GstThread object - - - - - -@name: -@Returns: - - @@ -55,3 +46,12 @@ The GstThread object The thread priority + + + + + +@name: +@Returns: + + diff --git a/docs/gst/tmpl/gsttypefind.sgml b/docs/gst/tmpl/gsttypefind.sgml index 10ae8f64bd..6cd2b8bb88 100644 --- a/docs/gst/tmpl/gsttypefind.sgml +++ b/docs/gst/tmpl/gsttypefind.sgml @@ -98,17 +98,3 @@ gst_type_find_factory_register() @Returns: - - - - - -@plugin: -@name: -@rank: -@func: -@extensions: -@possible_caps: -@data: - - diff --git a/docs/gst/tmpl/gstxml.sgml b/docs/gst/tmpl/gstxml.sgml index 08450b21b9..ea4e4bd24a 100644 --- a/docs/gst/tmpl/gstxml.sgml +++ b/docs/gst/tmpl/gstxml.sgml @@ -105,25 +105,3 @@ All GstElements can be serialized to an XML presentation and subsequently loaded @Returns: - - - - - -@: -@: -@: - -@gstxml: the object which received the signal. -@arg1: -@arg2: - - - - - - -@gstxml: the object which received the signal. -@arg1: -@arg2: - diff --git a/docs/libs/.gitignore b/docs/libs/.gitignore index 30446aa263..c20d3bb023 100644 --- a/docs/libs/.gitignore +++ b/docs/libs/.gitignore @@ -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 diff --git a/examples/Makefile.am b/examples/Makefile.am index 9751930a00..69a0458e61 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -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 index 0000000000..a54ae58a57 --- /dev/null +++ b/examples/retag/.gitignore @@ -0,0 +1,2 @@ +retag +transcode diff --git a/examples/retag/Makefile.am b/examples/retag/Makefile.am new file mode 100644 index 0000000000..6a686a51e5 --- /dev/null +++ b/examples/retag/Makefile.am @@ -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 index 0000000000..2824ba7241 --- /dev/null +++ b/examples/retag/retag.c @@ -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 " - .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 index 0000000000..c41f96ed8e --- /dev/null +++ b/examples/retag/transcode.c @@ -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; +} + diff --git a/gst/Makefile.am b/gst/Makefile.am index 64e0e5a62a..a9fe911bf9 100644 --- a/gst/Makefile.am +++ b/gst/Makefile.am @@ -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 diff --git a/gst/gst.c b/gst/gst.c index 8b1ef9535d..f81e567eba 100644 --- 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) { diff --git a/gst/gst.h b/gst/gst.h index 8f48c40c68..45662fe285 100644 --- a/gst/gst.h +++ b/gst/gst.h @@ -27,42 +27,44 @@ #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/gstthread.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/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/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> diff --git a/gst/gstbuffer.c b/gst/gstbuffer.c index 6df3cf8cb8..3a01d34dd2 100644 --- a/gst/gstbuffer.c +++ b/gst/gstbuffer.c @@ -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; diff --git a/gst/gstbuffer.h b/gst/gstbuffer.h index 4191f7bdc7..ab19247bf1 100644 --- a/gst/gstbuffer.h +++ b/gst/gstbuffer.h @@ -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; diff --git a/gst/gstelement.c b/gst/gstelement.c index d0758bb3cd..5af2331f09 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -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) diff --git a/gst/gstelement.h b/gst/gstelement.h index 2a983413b6..f40547fe65 100644 --- a/gst/gstelement.h +++ b/gst/gstelement.h @@ -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 diff --git a/gst/gstelementfactory.c b/gst/gstelementfactory.c index 595f0ca473..8e1e7abfb6 100644 --- a/gst/gstelementfactory.c +++ b/gst/gstelementfactory.c @@ -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)); @@ -387,6 +401,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 diff --git a/gst/gstevent.c b/gst/gstevent.c index a213e8a7b6..ad63b78148 100644 --- a/gst/gstevent.c +++ b/gst/gstevent.c @@ -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; } diff --git a/gst/gstevent.h b/gst/gstevent.h index ab8ace7fbd..a060dc9dca 100644 --- a/gst/gstevent.h +++ b/gst/gstevent.h @@ -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; diff --git a/gst/gstinfo.c b/gst/gstinfo.c index 843a31fdb9..2fefe85059 100644 --- a/gst/gstinfo.c +++ b/gst/gstinfo.c @@ -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; diff --git a/gst/gstinfo.h b/gst/gstinfo.h index 17ab30544d..a1a91e8a99 100644 --- a/gst/gstinfo.h +++ b/gst/gstinfo.h @@ -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 diff --git a/gst/gststructure.c b/gst/gststructure.c index e01f5bed44..f92dd32f59 100644 --- a/gst/gststructure.c +++ b/gst/gststructure.c @@ -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, @@ -83,6 +83,27 @@ void _gst_structure_initialize(void) _gst_structure_transform_to_string); } +/** + * 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; } @@ -383,6 +404,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 @@ -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; @@ -524,6 +571,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 @@ -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; diff --git a/gst/gststructure.h b/gst/gststructure.h index 2717349392..4ea6b10c6d 100644 --- a/gst/gststructure.h +++ b/gst/gststructure.h @@ -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 index 0000000000..c6c4f3f110 --- /dev/null +++ b/gst/gsttag.c @@ -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 index 0000000000..6d1efbe5a1 --- /dev/null +++ b/gst/gsttag.h @@ -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 index 0000000000..a7e6d7134b --- /dev/null +++ b/gst/gsttaginterface.c @@ -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 index 0000000000..cb85137910 --- /dev/null +++ b/gst/gsttaginterface.h @@ -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 index 0000000000..c6c4f3f110 --- /dev/null +++ b/gst/gsttaglist.c @@ -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 index 0000000000..6d1efbe5a1 --- /dev/null +++ b/gst/gsttaglist.h @@ -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 index 0000000000..a7e6d7134b --- /dev/null +++ b/gst/gsttagsetter.c @@ -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 index 0000000000..cb85137910 --- /dev/null +++ b/gst/gsttagsetter.h @@ -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/gstvalue.c b/gst/gstvalue.c index 03b4ba9cb9..79586e8452 100644 --- a/gst/gstvalue.c +++ b/gst/gstvalue.c @@ -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, diff --git a/gst/gstvalue.h b/gst/gstvalue.h index 353e1dd279..0fa57cacd1 100644 --- a/gst/gstvalue.h +++ b/gst/gstvalue.h @@ -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 diff --git a/gst/registries/gstxmlregistry.c b/gst/registries/gstxmlregistry.c index c0555a2c3c..76f2679e74 100644 --- a/gst/registries/gstxmlregistry.c +++ b/gst/registries/gstxmlregistry.c @@ -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)) { diff --git a/libs/gst/Makefile.am b/libs/gst/Makefile.am index 3bd8a76a39..fa04c8f2b6 100644 --- a/libs/gst/Makefile.am +++ b/libs/gst/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = control getbits bytestream +SUBDIRS = bytestream control getbits -DIST_SUBDIRS = control getbits bytestream +DIST_SUBDIRS = bytestream control getbits diff --git a/pkgconfig/Makefile.am b/pkgconfig/Makefile.am index a294dc11cf..3aa752edad 100644 --- a/pkgconfig/Makefile.am +++ b/pkgconfig/Makefile.am @@ -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 index 0000000000..91b6c79ae0 --- /dev/null +++ b/po/.gitignore @@ -0,0 +1,6 @@ +*.gmo +remove-potcdate.sed +stamp-po +POTFILES +cat-id-tbl.c +gstreamer-0.7.pot diff --git a/tests/old/examples/Makefile.am b/tests/old/examples/Makefile.am index 9751930a00..69a0458e61 100644 --- a/tests/old/examples/Makefile.am +++ b/tests/old/examples/Makefile.am @@ -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 index 0000000000..a54ae58a57 --- /dev/null +++ b/tests/old/examples/retag/.gitignore @@ -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 index 0000000000..6a686a51e5 --- /dev/null +++ b/tests/old/examples/retag/Makefile.am @@ -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 index 0000000000..2824ba7241 --- /dev/null +++ b/tests/old/examples/retag/retag.c @@ -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 index 0000000000..c41f96ed8e --- /dev/null +++ b/tests/old/examples/retag/transcode.c @@ -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; +} + diff --git a/tests/old/testsuite/Makefile.am b/tests/old/testsuite/Makefile.am index 0e181893d5..261e4d6e68 100644 --- a/tests/old/testsuite/Makefile.am +++ b/tests/old/testsuite/Makefile.am @@ -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 = diff --git a/tests/old/testsuite/threads/queue.c b/tests/old/testsuite/threads/queue.c index 2435e31c2f..191ae43f04 100644 --- a/tests/old/testsuite/threads/queue.c +++ b/tests/old/testsuite/threads/queue.c @@ -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); diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am index 0e181893d5..261e4d6e68 100644 --- a/testsuite/Makefile.am +++ b/testsuite/Makefile.am @@ -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 = diff --git a/testsuite/threads/queue.c b/testsuite/threads/queue.c index 2435e31c2f..191ae43f04 100644 --- a/testsuite/threads/queue.c +++ b/testsuite/threads/queue.c @@ -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); diff --git a/tools/gst-launch.c b/tools/gst-launch.c index 5eb22d8b19..0cc2315812 100644 --- a/tools/gst-launch.c +++ b/tools/gst-launch.c @@ -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