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