fix some marked strings
[platform/upstream/gstreamer.git] / gst / gsttag.c
1 /* GStreamer
2  * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
3  *
4  * gsttag.c: tag support (aka metadata)
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include "gst_private.h"
27 #include "gst-i18n-lib.h"
28 #include "gsttag.h"
29 #include "gstinfo.h"
30 #include "gstvalue.h"
31
32 #include <gobject/gvaluecollector.h>
33 #include <string.h>
34
35 #define GST_TAG_IS_VALID(tag)           (gst_tag_get_info (tag) != NULL)
36
37 typedef struct {
38   GType                 type;           /* type the data is in */
39
40   gchar *               nick;           /* translated name */
41   gchar *               blurb;          /* translated description of type */
42
43   GstTagMergeFunc       merge_func;     /* functions to merge the values */
44 } GstTagInfo;
45
46 #define TAGLIST "taglist"
47 static GQuark gst_tag_list_quark;
48 static GMutex *__tag_mutex;
49 static GHashTable *__tags;
50 #define TAG_LOCK g_mutex_lock (__tag_mutex)
51 #define TAG_UNLOCK g_mutex_unlock (__tag_mutex)
52
53 void
54 _gst_tag_initialize (void)
55 {
56   gst_tag_list_quark = g_quark_from_static_string (TAGLIST);
57   __tag_mutex = g_mutex_new ();
58   __tags = g_hash_table_new (g_direct_hash, g_direct_equal);
59   gst_tag_register (GST_TAG_TITLE,            
60                     G_TYPE_STRING,
61                     _("title"),
62                     _("commonly used title"),
63                     gst_tag_merge_strings_with_comma);
64   gst_tag_register (GST_TAG_ARTIST,
65                     G_TYPE_STRING,
66                     _("artist"),
67                     _("person(s) responsible for the recording"),
68                     gst_tag_merge_strings_with_comma);
69   gst_tag_register (GST_TAG_ALBUM,
70                     G_TYPE_STRING,
71                     _("album"),
72                     _("album containing this data"),
73                     gst_tag_merge_strings_with_comma);
74   gst_tag_register (GST_TAG_DATE,
75                     G_TYPE_UINT, /* FIXME: own data type for dates? */
76                     _("date"),
77                     _("date the data was created (in Julian calendar days)"),
78                     NULL);
79   gst_tag_register (GST_TAG_GENRE,
80                     G_TYPE_STRING,
81                     _("genre"), 
82                     _("genre this data belongs to"),
83                     gst_tag_merge_strings_with_comma);
84   gst_tag_register (GST_TAG_COMMENT,
85                     G_TYPE_STRING,
86                     _("comment"),
87                     _("free text commenting the data"),
88                     gst_tag_merge_strings_with_comma);
89   gst_tag_register (GST_TAG_TRACK_NUMBER,
90                     G_TYPE_UINT,
91                     _("track number"),
92                     _("track number inside a collection"),
93                     gst_tag_merge_use_first);
94   gst_tag_register (GST_TAG_TRACK_COUNT,
95                     G_TYPE_UINT,
96                     _("track count"),
97                     _("count of tracks inside collection this track belongs to"), 
98                     gst_tag_merge_use_first);
99   gst_tag_register (GST_TAG_LOCATION,
100                     G_TYPE_STRING,
101                     _("location"),
102                     _("original location of file as a URI"),
103                     gst_tag_merge_strings_with_comma);
104   gst_tag_register (GST_TAG_DESCRIPTION,
105                     G_TYPE_STRING,
106                     _("description"),
107                     _("short text describing the content of the data"),
108                     gst_tag_merge_strings_with_comma);
109   gst_tag_register (GST_TAG_VERSION,
110                     G_TYPE_STRING,
111                     _("version"),
112                     _("version of this data"),
113                     NULL);
114   gst_tag_register (GST_TAG_ISRC,
115                     G_TYPE_STRING,
116                     _("ISRC"),
117                     _("International Standard Recording Code - see http://www.ifpi.org/isrc/"),
118                     NULL);
119   gst_tag_register (GST_TAG_ORGANIZATION,
120                     G_TYPE_STRING,
121                     _("organization"),
122                     _("organization"), /* FIXME */
123                     gst_tag_merge_strings_with_comma);
124   gst_tag_register (GST_TAG_COPYRIGHT,
125                     G_TYPE_STRING,
126                     _("copyright"),
127                     _("copyright notice of the data"),
128                     NULL);
129   gst_tag_register (GST_TAG_CONTACT,
130                     G_TYPE_STRING,
131                     _("contact"),
132                     _("contact information"),
133                     gst_tag_merge_strings_with_comma);
134   gst_tag_register (GST_TAG_LICENSE,    
135                     G_TYPE_STRING,
136                     _("license"),
137                     _("license of data"),
138                     NULL);
139   gst_tag_register (GST_TAG_PERFORMER,
140                     G_TYPE_STRING,
141                     _("performer"),
142                     _("person(s) performing"),
143                     gst_tag_merge_strings_with_comma);
144   gst_tag_register (GST_TAG_DURATION,
145                     G_TYPE_UINT64,
146                     _("duration"),
147                     _("length in GStreamer time units (nanoseconds)"),
148                     NULL);
149   gst_tag_register (GST_TAG_CODEC,
150                     G_TYPE_STRING,
151                     _("codec"),
152                     _("codec the data is stored in"),
153                     gst_tag_merge_strings_with_comma);
154   gst_tag_register (GST_TAG_MINIMUM_BITRATE,
155                     G_TYPE_UINT,
156                     _("minimum bitrate"),
157                     _("minimum bitrate in bits/s"),
158                     NULL);
159   gst_tag_register (GST_TAG_BITRATE,
160                     G_TYPE_UINT,
161                     _("bitrate"),
162                     _("exact or average bitrate in bits/s"),
163                     NULL);
164   gst_tag_register (GST_TAG_MAXIMUM_BITRATE,
165                     G_TYPE_UINT,
166                     _("maximum bitrate"),
167                     _("maximum bitrate in bits/s"),
168                     NULL);
169 }
170 /**
171  * gst_tag_merge_use_first:
172  * @dest: uninitialized GValue to store result in
173  * @src: GValue to copy from
174  *
175  * This is a convenience function for the func argument of gst_tag_register(). 
176  * It creates a copy of the first value from the list.
177  */
178 void
179 gst_tag_merge_use_first (GValue *dest, const GValue *src)
180 {
181   const GValue *ret = gst_value_list_get_value (src, 0);
182
183   g_value_init (dest, G_VALUE_TYPE (ret));
184   g_value_copy (ret, dest);
185 }
186 /**
187  * gst_tag_merge_strings_with_comma:
188  * @dest: uninitialized GValue to store result in
189  * @src: GValue to copy from
190  * 
191  * This is a convenience function for the func argument of gst_tag_register().
192  * It concatenates all given strings using a comma. The tag must be registered
193  * as a G_TYPE_STRING or this function will fail.
194  */
195 void
196 gst_tag_merge_strings_with_comma (GValue *dest, const GValue *src)
197 {
198   GString *str;
199   gint i, count;
200
201   count = gst_value_list_get_size (src);
202   str = g_string_new (g_value_get_string (gst_value_list_get_value (src, 0)));
203   for (i = 1; i < count; i++) {
204     /* seperator between two string */
205     str = g_string_append (str, _(", "));
206     str = g_string_append (str, g_value_get_string (gst_value_list_get_value (src, 1)));
207   }
208
209   g_value_init (dest, G_TYPE_STRING);
210   g_value_set_string_take_ownership (dest, str->str);
211   g_string_free (str, FALSE);
212 }
213 static GstTagInfo *
214 gst_tag_lookup (GQuark entry)
215 {
216   GstTagInfo *ret;
217   
218   TAG_LOCK;
219   ret = g_hash_table_lookup (__tags, GUINT_TO_POINTER (entry));
220   TAG_UNLOCK;
221
222   return ret;
223 }
224 /**
225  * gst_tag_register:
226  * @name: the name or identifier string
227  * @type: the type this data is in
228  * @nick: human-readable name
229  * @blurb: a human-readable description about this tag
230  * @func: function for merging multiple values of this tag
231  *
232  * Registers a new tag type for the use with GStreamer's type system. If a type
233  * with that name is already registered, that one is used.
234  * The old registration may have used a different type however. So don't rely
235  * on youre supplied values.
236  * If you know the type is already registered, use gst_tag_lookup instead.
237  * This function takes ownership of all supplied variables.
238  */
239 void
240 gst_tag_register (gchar *name, GType type, gchar *nick, gchar *blurb,
241                   GstTagMergeFunc func)
242 {
243   GQuark key;
244   GstTagInfo *info;
245
246   g_return_if_fail (name != NULL);
247   g_return_if_fail (nick != NULL);
248   g_return_if_fail (blurb != NULL);
249   g_return_if_fail (type != 0 && type != GST_TYPE_LIST);
250   
251   key = g_quark_from_string (name);
252   info = gst_tag_lookup (key);
253   g_return_if_fail (info == NULL);
254   
255   info = g_new (GstTagInfo, 1);
256   info->type = type;
257   info->nick = nick;
258   info->blurb = blurb;
259   info->merge_func = func;
260     
261   TAG_LOCK;
262   g_hash_table_insert (__tags, GUINT_TO_POINTER (key), info);
263   TAG_UNLOCK;
264 }
265 /**
266  * gst_tag_exists:
267  * @tag: name of the tag
268  *
269  * Checks if the given type is already registered.
270  *
271  * Returns: TRUE if the type is already registered
272  */
273 gboolean
274 gst_tag_exists (const gchar *tag)
275 {
276   g_return_val_if_fail (tag != NULL, FALSE);
277   
278   return gst_tag_lookup (g_quark_from_string (tag)) != NULL;
279 }
280 /**
281  * gst_tag_get_type:
282  * @tag: the tag
283  *
284  * Gets the #GType used for this tag.
285  *
286  * Returns: the #GType of this tag
287  */
288 GType
289 gst_tag_get_type (const gchar *tag)
290 {
291   GstTagInfo *info;
292   
293   g_return_val_if_fail (tag != NULL, 0);
294   info = gst_tag_lookup (g_quark_from_string (tag));
295   g_return_val_if_fail (info != NULL, 0);
296   
297   return info->type;
298 }
299 /**
300  * gst_tag_get_nick
301  * @tag: the tag
302  *
303  * Returns the human-readable name of this tag, You must not change or free 
304  * this string.
305  *
306  * Returns: the human-readable name of this tag
307  */
308 const gchar *
309 gst_tag_get_nick (const gchar *tag)
310 {
311   GstTagInfo *info;
312   
313   g_return_val_if_fail (tag != NULL, NULL);
314   info = gst_tag_lookup (g_quark_from_string (tag));
315   g_return_val_if_fail (info != NULL, NULL);
316   
317   return info->nick;
318 }
319 /**
320  * gst_tag_get_description:
321  * @tag: the tag
322  *
323  * Returns the human-readable description of this tag, You must not change or 
324  * free this string.
325  *
326  * Return the human-readable description of this tag
327  */
328 const gchar *
329 gst_tag_get_description (const gchar *tag)
330 {
331   GstTagInfo *info;
332   
333   g_return_val_if_fail (tag != NULL, NULL);
334   info = gst_tag_lookup (g_quark_from_string (tag));
335   g_return_val_if_fail (info != NULL, NULL);
336   
337   return info->blurb;
338 }
339 /**
340  * gst_tag_list_is_fixed:
341  * @tag: tag to check
342  *
343  * Checks if the given tag is fixed. A fixed tag can only contain one value.
344  * Unfixed tags can contain lists of values.
345  *
346  * Returns: TRUE, if the given tag is fixed.
347  */
348 gboolean
349 gst_tag_is_fixed (const gchar *tag)
350 {
351   GstTagInfo *info;
352   
353   g_return_val_if_fail (tag != NULL, FALSE);
354   info = gst_tag_lookup (g_quark_from_string (tag));
355   g_return_val_if_fail (info != NULL, FALSE);
356   
357   return info->merge_func == NULL;
358 }
359 /**
360  * gst_tag_list_new:
361  *
362  * Creates a new empty GstTagList.
363  *
364  * Returns: An empty tag list
365  */
366 GstTagList *
367 gst_tag_list_new (void)
368 {
369   return GST_TAG_LIST (gst_structure_new (TAGLIST, NULL));
370 }
371 /**
372  * gst_is_tag_list:
373  * @p: Object that might be a taglist
374  *
375  * Checks if the given pointer is a taglist.
376  *
377  * Returns: TRUE, if the given pointer is a taglist
378  */
379 gboolean
380 gst_is_tag_list (gconstpointer p)
381 {
382   g_return_val_if_fail (p != NULL, FALSE); 
383
384   return ((GstStructure *) p)->name == gst_tag_list_quark;
385 }
386 typedef struct {
387   GstStructure *        list;
388   GstTagMergeMode       mode;
389 } GstTagCopyData;
390 static void
391 gst_tag_list_add_value_internal (GstStructure *list, GstTagMergeMode mode, GQuark tag, GValue *value)
392 {
393   GstTagInfo *info = gst_tag_lookup (tag);
394   const GValue *value2;
395   
396   g_assert (info != NULL);
397
398   if (info->merge_func && (value2 = gst_structure_id_get_value (list, tag)) != NULL) {
399     GValue dest = { 0, };
400     switch (mode) {
401       case GST_TAG_MERGE_REPLACE_ALL:
402       case GST_TAG_MERGE_REPLACE:
403         gst_structure_id_set_value (list, tag, value);
404         break;
405       case GST_TAG_MERGE_PREPEND:
406         gst_value_list_concat (&dest, value, value2);
407         gst_structure_id_set_value (list, tag, &dest);
408         g_value_unset (&dest);
409         break;
410       case GST_TAG_MERGE_APPEND:
411         gst_value_list_concat (&dest, value2, value);
412         gst_structure_id_set_value (list, tag, &dest);
413         g_value_unset (&dest);
414         break;
415       case GST_TAG_MERGE_KEEP:
416       case GST_TAG_MERGE_KEEP_ALL:
417         break;
418       default:
419         g_assert_not_reached ();
420         break;
421     }
422   } else {
423     switch (mode) {
424       case GST_TAG_MERGE_APPEND:
425       case GST_TAG_MERGE_KEEP:
426         if (gst_structure_id_get_value (list, tag) != NULL)
427           break;
428         /* fall through */
429       case GST_TAG_MERGE_REPLACE_ALL:
430       case GST_TAG_MERGE_REPLACE:
431       case GST_TAG_MERGE_PREPEND:
432         gst_structure_id_set_value (list, tag, value);
433         break;
434       case GST_TAG_MERGE_KEEP_ALL:
435         break;
436       default:
437         g_assert_not_reached ();
438         break;
439     }
440   }
441 }
442 static gboolean
443 gst_tag_list_copy_foreach (GQuark tag, GValue *value, gpointer user_data)
444 {
445   GstTagCopyData *copy = (GstTagCopyData *) user_data;
446
447   gst_tag_list_add_value_internal (copy->list, copy->mode, tag, value);
448
449   return TRUE;
450 }
451 /**
452  * gst_tag_list_insert:
453  * @into: list to merge into
454  * @from: list to merge from
455  * @mode: the mode to use
456  * 
457  * Inserts the tags of the second list into the first list using the given mode.
458  */
459 void
460 gst_tag_list_insert (GstTagList *into, const GstTagList *from, GstTagMergeMode mode)
461 {
462   GstTagCopyData data;
463   
464   g_return_if_fail (GST_IS_TAG_LIST (into));
465   g_return_if_fail (GST_IS_TAG_LIST (from));
466   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
467
468   data.list = (GstStructure *) into;
469   data.mode = mode;
470   if (mode == GST_TAG_MERGE_REPLACE_ALL) {
471     gst_structure_remove_all_fields (data.list);
472   }
473   gst_structure_foreach ((GstStructure *) from, gst_tag_list_copy_foreach, &data);
474 }
475 /**
476  * gst_tag_list_copy:
477  * @list: list to copy
478  *
479  * Copies a given #GstTagList.
480  *
481  * Returns: copy of the given list
482  */
483 GstTagList *
484 gst_tag_list_copy (const GstTagList *list)
485 {
486   g_return_val_if_fail (GST_IS_TAG_LIST (list), NULL);
487   
488   return GST_TAG_LIST (gst_structure_copy ((GstStructure *) list));
489 }
490 /**
491  * gst_tag_list_merge:
492  * @list1: first list to merge
493  * @list2: second list to merge
494  * @mode: the mode to use
495  * 
496  * Merges the two given lists into a new list. If one of the lists is NULL, a
497  * copy of the other is returned. If both lists are NULL, NULL is returned.
498  *
499  * Returns: the new list
500  */
501 GstTagList *
502 gst_tag_list_merge (const GstTagList *list1, const GstTagList *list2, GstTagMergeMode mode)
503 {
504   g_return_val_if_fail (list1 == NULL || GST_IS_TAG_LIST (list1), NULL);
505   g_return_val_if_fail (list2 == NULL || GST_IS_TAG_LIST (list2), NULL);
506   g_return_val_if_fail (GST_TAG_MODE_IS_VALID (mode), NULL);
507
508   if (!list1 && !list2) {
509     return NULL;
510   } else if (!list1) {
511     return gst_tag_list_copy (list2);
512   } else if (!list2) {
513     return gst_tag_list_copy (list1);
514   } else {
515     GstTagList *ret;
516
517     ret = gst_tag_list_copy (list1);
518     gst_tag_list_insert (ret, list2, mode);
519     return ret;
520   }
521 }
522 /**
523  * gst_tag_list_free:
524  * @list: the list to free
525  *
526  * Frees the given list and all associated values.
527  */
528 void
529 gst_tag_list_free (GstTagList *list)
530 {
531   g_return_if_fail (GST_IS_TAG_LIST (list));
532   gst_structure_free ((GstStructure *) list);
533 }
534 /**
535  * gst_tag_list_get_tag_size:
536  * @list: a taglist
537  * @tag: the tag to query
538  *
539  * Checks how many value are stored in this tag list for the given tag.
540  *
541  * Returns: The number of tags stored
542  */
543 guint
544 gst_tag_list_get_tag_size (const GstTagList *list, const gchar *tag)
545 {
546   const GValue *value;
547
548   g_return_val_if_fail (GST_IS_TAG_LIST (list), 0);
549
550   value = gst_structure_get_value ((GstStructure *) list, tag);
551   if (value == NULL)
552     return 0;
553   if (G_VALUE_TYPE (value) != GST_TYPE_LIST)
554     return 1;
555
556   return gst_value_list_get_size (value);
557 }
558 /**
559  * gst_tag_list_add:
560  * @list: list to set tags in
561  * @mode: the mode to use
562  * @tag: tag
563  * @...: values to set
564  *
565  * Sets the values for the given tags using the specified mode.
566  */
567 void
568 gst_tag_list_add (GstTagList *list, GstTagMergeMode mode, const gchar *tag, ...)
569 {
570   va_list args;
571
572   g_return_if_fail (GST_IS_TAG_LIST (list));
573   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
574   g_return_if_fail (tag != NULL);
575   
576   va_start (args, tag);
577   gst_tag_list_add_valist (list, mode, tag, args);
578   va_end (args);
579 }
580 /**
581  * gst_tag_list_add_values:
582  * @list: list to set tags in
583  * @mode: the mode to use
584  * @tag: tag
585  * @...: GValues to set
586  *
587  * Sets the GValues for the given tags using the specified mode.
588  */
589 void
590 gst_tag_list_add_values (GstTagList *list, GstTagMergeMode mode, const gchar *tag, ...)
591 {
592   va_list args;
593
594   g_return_if_fail (GST_IS_TAG_LIST (list));
595   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
596   g_return_if_fail (tag != NULL);
597   
598   va_start (args, tag);
599   gst_tag_list_add_valist_values (list, mode, tag, args);
600   va_end (args);
601 }
602 /**
603  * gst_tag_list_add_valist:
604  * @list: list to set tags in
605  * @mode: the mode to use
606  * @tag: tag
607  * @var_args: tag / value pairs to set
608  *
609  * Sets the values for the given tags using the specified mode.
610  */
611 void
612 gst_tag_list_add_valist (GstTagList *list, GstTagMergeMode mode, const gchar *tag, va_list var_args)
613 {
614   GstTagInfo *info;
615   GQuark quark;
616   gchar *error = NULL;
617   
618   g_return_if_fail (GST_IS_TAG_LIST (list));
619   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
620   g_return_if_fail (tag != NULL);
621   
622   while (tag != NULL) {
623     GValue value = { 0, };
624     quark = g_quark_from_string (tag);
625     info = gst_tag_lookup (quark);
626     g_return_if_fail (info != NULL);
627     g_value_init (&value, info->type);
628     G_VALUE_COLLECT (&value, var_args, 0, &error);
629     if (error) {
630       g_warning ("%s: %s", G_STRLOC, error);
631       g_free (error);
632       /* we purposely leak the value here, it might not be
633        * in a sane state if an error condition occoured
634        */
635       return;
636     }
637     gst_tag_list_add_value_internal (list, mode, quark, &value);
638     g_value_unset (&value);
639     tag = va_arg (var_args, gchar *);
640   }
641 }
642 /**
643  * gst_tag_list_add_valist_values:
644  * @list: list to set tags in
645  * @mode: the mode to use
646  * @tag: tag
647  * @var_args: tag / GValue pairs to set
648  *
649  * Sets the GValues for the given tags using the specified mode.
650  */
651 void
652 gst_tag_list_add_valist_values (GstTagList *list, GstTagMergeMode mode, const gchar *tag, va_list var_args)
653 {
654   GstTagInfo *info;
655   GQuark quark;
656   
657   g_return_if_fail (GST_IS_TAG_LIST (list));
658   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
659   g_return_if_fail (tag != NULL);
660   
661   while (tag != NULL) {
662     quark = g_quark_from_string (tag);
663     info = gst_tag_lookup (quark);
664     g_return_if_fail (info != NULL);
665     gst_tag_list_add_value_internal (list, mode, quark, va_arg (var_args, GValue *));
666     tag = va_arg (var_args, gchar *);
667   }
668 }
669 /**
670  * gst_tag_list_remove_tag:
671  * @list: list to remove tag from
672  * @tag: tag to remove
673  *
674  * Removes the goven tag from the taglist.
675  */
676 void
677 gst_tag_list_remove_tag (GstTagList *list, const gchar *tag)
678 {
679   g_return_if_fail (GST_IS_TAG_LIST (list));
680   g_return_if_fail (tag != NULL);
681
682   gst_structure_remove_field ((GstStructure *) list, tag);
683 }
684 typedef struct {
685   GstTagForeachFunc     func;
686   GstTagList *          tag_list;
687   gpointer              data;
688 } TagForeachData;
689 static int
690 structure_foreach_wrapper (GQuark field_id, 
691         GValue *value, gpointer user_data)
692 {
693   TagForeachData *data = (TagForeachData *) user_data;
694   data->func (data->tag_list, g_quark_to_string (field_id), data->data);
695   return TRUE;
696 }
697 /**
698  * gst_tag_list_foreach:
699  * @list: list to iterate over
700  * @func: function to be called for each tag
701  * @user_data: user specified data
702  *
703  * Calls the given function for each tag inside the tag list. Note that if there
704  * is no tag, the function won't be called at all.
705  */
706 void
707 gst_tag_list_foreach (GstTagList *list, GstTagForeachFunc func, gpointer user_data)
708 {
709   TagForeachData data;
710
711   g_return_if_fail (GST_IS_TAG_LIST (list));
712   g_return_if_fail (func != NULL);
713   
714   data.func = func;
715   data.tag_list = list;
716   data.data = user_data;
717   gst_structure_foreach ((GstStructure *) list, structure_foreach_wrapper, &data);
718 }
719
720 /***** tag events *****/
721
722 /**
723  * gst_event_new_tag:
724  * @list: the tag list to put into the event or NULL for an empty list
725  *
726  * Creates a new tag event with the given list and takes ownership of it.
727  *
728  * Returns: a new tag event
729  */
730 GstEvent *
731 gst_event_new_tag (GstTagList *list)
732 {
733   GstEvent *ret;
734   
735   g_return_val_if_fail (list == NULL || GST_IS_TAG_LIST (list), NULL);
736
737   ret = gst_event_new (GST_EVENT_TAG);
738   if (!list)
739     list = gst_tag_list_new ();
740   ret->event_data.structure.structure = (GstStructure *) list;
741   
742   return ret;
743 }
744 /**
745  * get_event_tag_get_list:
746  * @tag_event: a tagging #GstEvent
747  *
748  * Gets the taglist from a given tagging event.
749  * 
750  * Returns: The #GstTagList of the event
751  */
752 GstTagList *
753 gst_event_tag_get_list (GstEvent *tag_event)
754 {
755   g_return_val_if_fail (GST_IS_EVENT (tag_event), NULL);
756   g_return_val_if_fail (GST_EVENT_TYPE (tag_event) == GST_EVENT_TAG, NULL);
757
758   return GST_TAG_LIST (tag_event->event_data.structure.structure);
759 }
760
761 /**
762  * gst_tag_list_get_value_index:
763  * @list: a #GStTagList
764  * @tag: tag to read out
765  * @index: number of entry to read out
766  *
767  * Gets the value that is at the given index for the given tag in the given 
768  * list.
769  * 
770  * Returns: The GValue for the specified entry or NULL if the tag wasn't available
771  *          or the tag doesn't have as many entries
772  */
773 G_CONST_RETURN GValue *
774 gst_tag_list_get_value_index (const GstTagList *list, const gchar *tag, guint index)
775 {
776   const GValue *value;
777
778   g_return_val_if_fail (GST_IS_TAG_LIST (list), NULL);
779   g_return_val_if_fail (tag != NULL, NULL);
780   
781   value = gst_structure_get_value ((GstStructure *) list, tag);
782   if (value == NULL) return  NULL;
783   
784   if (GST_VALUE_HOLDS_LIST (value)) {
785     if (index >= gst_value_list_get_size (value)) return NULL;
786     return gst_value_list_get_value (value, index);
787   } else {
788     if (index > 0) return NULL;
789     return value;
790   }
791 }
792
793 /**
794  * gst_tag_list_copy_value:
795  * @dest: uninitialized #GValue to copy into
796  * @list: list to get the tag from
797  * @tag: tag to read out
798  *
799  * Copies the contents for the given tag into the value, merging multiple values 
800  * into one if multiple values are associated with the tag.
801  * You must g_value_unset() the value after use.
802  *
803  * Returns: TRUE, if a value was copied, FALSE if the tag didn't exist in the 
804  *          given list.
805  */
806 gboolean
807 gst_tag_list_copy_value (GValue *dest, const GstTagList *list, const gchar *tag)
808 {
809   const GValue *src;
810   
811   g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);
812   g_return_val_if_fail (tag != NULL, FALSE);
813   g_return_val_if_fail (dest != NULL, FALSE);
814   g_return_val_if_fail (G_VALUE_TYPE (dest) == 0, FALSE);
815   
816   src = gst_structure_get_value ((GstStructure *) list, tag);
817   if (!src) return FALSE;
818   
819   if (G_VALUE_TYPE (src) == GST_TYPE_LIST) {    
820     GstTagInfo *info = gst_tag_lookup (g_quark_from_string (tag));
821     /* must be there or lists aren't allowed */
822     g_assert (info->merge_func);
823     info->merge_func (dest, src);
824   } else {
825     g_value_init (dest, G_VALUE_TYPE (src));
826     g_value_copy (src, dest);
827   }
828   return TRUE;
829 }
830
831 /***** evil macros to get all the gst_tag_list_get_*() functions right *****/
832
833 #define TAG_MERGE_FUNCS(name,type)                                              \
834 gboolean                                                                        \
835 gst_tag_list_get_ ## name (const GstTagList *list, const gchar *tag,            \
836                            type *value)                                         \
837 {                                                                               \
838   GValue v = { 0, };                                                            \
839                                                                                 \
840   g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);                         \
841   g_return_val_if_fail (tag != NULL, FALSE);                                    \
842   g_return_val_if_fail (value != NULL, FALSE);                                  \
843                                                                                 \
844   if (!gst_tag_list_copy_value (&v, list, tag))                                 \
845       return FALSE;                                                             \
846   *value = COPY_FUNC (g_value_get_ ## name (&v));                               \
847   g_value_unset (&v);                                                           \
848   return TRUE;                                                                  \
849 }                                                                               \
850                                                                                 \
851 gboolean                                                                        \
852 gst_tag_list_get_ ## name ## _index (const GstTagList *list, const gchar *tag,  \
853                            guint index, type *value)                            \
854 {                                                                               \
855   const GValue *v;                                                              \
856                                                                                 \
857   g_return_val_if_fail (GST_IS_TAG_LIST (list), FALSE);                         \
858   g_return_val_if_fail (tag != NULL, FALSE);                                    \
859   g_return_val_if_fail (value != NULL, FALSE);                                  \
860                                                                                 \
861   if ((v = gst_tag_list_get_value_index (list, tag, index)) == NULL)            \
862       return FALSE;                                                             \
863   *value = COPY_FUNC (g_value_get_ ## name (v));                                \
864   return TRUE;                                                                  \
865 }
866
867 #define COPY_FUNC /**/
868 TAG_MERGE_FUNCS (char, gchar)
869 TAG_MERGE_FUNCS (uchar, guchar)
870 TAG_MERGE_FUNCS (boolean, gboolean)
871 TAG_MERGE_FUNCS (int, gint)
872 TAG_MERGE_FUNCS (uint, guint)
873 TAG_MERGE_FUNCS (long, glong)
874 TAG_MERGE_FUNCS (ulong, gulong)
875 TAG_MERGE_FUNCS (int64, gint64)
876 TAG_MERGE_FUNCS (uint64, guint64)
877 TAG_MERGE_FUNCS (float, gfloat)
878 TAG_MERGE_FUNCS (double, gdouble)
879 #undef COPY_FUNC
880   
881 #define COPY_FUNC g_strdup
882 TAG_MERGE_FUNCS (string, gchar *)
883
884
885
886