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