Merge remote-tracking branch 'origin/master' into 0.11
[platform/upstream/gstreamer.git] / gst / gsttagsetter.c
1 /* GStreamer
2  * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
3  *
4  * gsttagsetter.c: interface for tag setting on elements
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 /**
23  * SECTION:gsttagsetter
24  * @short_description: Element interface that allows setting and retrieval
25  *                     of media metadata
26  *
27  * Element interface that allows setting of media metadata.
28  *
29  * Elements that support changing a stream's metadata will implement this
30  * interface. Examples of such elements are 'vorbisenc', 'theoraenc' and
31  * 'id3v2mux'.
32  * 
33  * If you just want to retrieve metadata in your application then all you
34  * need to do is watch for tag messages on your pipeline's bus. This
35  * interface is only for setting metadata, not for extracting it. To set tags
36  * from the application, find tagsetter elements and set tags using e.g.
37  * gst_tag_setter_merge_tags() or gst_tag_setter_add_tags(). Also consider
38  * setting the #GstTagMergeMode that is used for tag events that arrive at the
39  * tagsetter element (default mode is to keep existing tags).
40  * The application should do that before the element goes to %GST_STATE_PAUSED.
41  * 
42  * Elements implementing the #GstTagSetter interface often have to merge
43  * any tags received from upstream and the tags set by the application via
44  * the interface. This can be done like this:
45  *
46  * |[
47  * GstTagMergeMode merge_mode;
48  * const GstTagList *application_tags;
49  * const GstTagList *event_tags;
50  * GstTagSetter *tagsetter;
51  * GstTagList *result;
52  *  
53  * tagsetter = GST_TAG_SETTER (element);
54  *  
55  * merge_mode = gst_tag_setter_get_tag_merge_mode (tagsetter);
56  * application_tags = gst_tag_setter_get_tag_list (tagsetter);
57  * event_tags = (const GstTagList *) element->event_tags;
58  *  
59  * GST_LOG_OBJECT (tagsetter, "merging tags, merge mode = %d", merge_mode);
60  * GST_LOG_OBJECT (tagsetter, "event tags: %" GST_PTR_FORMAT, event_tags);
61  * GST_LOG_OBJECT (tagsetter, "set   tags: %" GST_PTR_FORMAT, application_tags);
62  *  
63  * result = gst_tag_list_merge (application_tags, event_tags, merge_mode);
64  *  
65  * GST_LOG_OBJECT (tagsetter, "final tags: %" GST_PTR_FORMAT, result);
66  * ]|
67  * 
68  * Last reviewed on 2006-05-18 (0.10.6)
69  */
70
71 #ifdef HAVE_CONFIG_H
72 #  include "config.h"
73 #endif
74
75 #include "gst_private.h"
76 #include "gsttagsetter.h"
77 #include <gobject/gvaluecollector.h>
78 #include <string.h>
79
80 static GQuark gst_tag_key;
81
82 typedef struct
83 {
84   GstTagMergeMode mode;
85   GstTagList *list;
86 #if !GLIB_CHECK_VERSION (2, 31, 0)
87   GStaticMutex lock;
88 #else
89   GMutex lock;
90 #endif
91 } GstTagData;
92
93 #if !GLIB_CHECK_VERSION (2, 31, 0)
94 #define GST_TAG_DATA_LOCK(data) g_static_mutex_lock(&data->lock)
95 #define GST_TAG_DATA_UNLOCK(data) g_static_mutex_unlock(&data->lock)
96 #else
97 #define GST_TAG_DATA_LOCK(data) g_mutex_lock(&data->lock)
98 #define GST_TAG_DATA_UNLOCK(data) g_mutex_unlock(&data->lock)
99 #endif
100
101 GType
102 gst_tag_setter_get_type (void)
103 {
104   static volatile gsize tag_setter_type = 0;
105
106   if (g_once_init_enter (&tag_setter_type)) {
107     GType _type;
108     static const GTypeInfo tag_setter_info = {
109       sizeof (GstTagSetterInterface),   /* class_size */
110       NULL,                     /* base_init */
111       NULL,                     /* base_finalize */
112       NULL,
113       NULL,                     /* class_finalize */
114       NULL,                     /* class_data */
115       0,
116       0,
117       NULL
118     };
119
120     _type = g_type_register_static (G_TYPE_INTERFACE, "GstTagSetter",
121         &tag_setter_info, 0);
122
123     g_type_interface_add_prerequisite (_type, GST_TYPE_ELEMENT);
124
125     gst_tag_key = g_quark_from_static_string ("GST_TAG_SETTER");
126     g_once_init_leave (&tag_setter_type, _type);
127   }
128
129   return tag_setter_type;
130 }
131
132 static void
133 gst_tag_data_free (gpointer p)
134 {
135   GstTagData *data = (GstTagData *) p;
136
137   if (data->list)
138     gst_tag_list_free (data->list);
139
140 #if !GLIB_CHECK_VERSION (2, 31, 0)
141   g_static_mutex_free (&data->lock);
142 #else
143   g_mutex_clear (&data->lock);
144 #endif
145
146   g_slice_free (GstTagData, data);
147 }
148
149 static GstTagData *
150 gst_tag_setter_get_data (GstTagSetter * setter)
151 {
152   GstTagData *data;
153
154   data = g_object_get_qdata (G_OBJECT (setter), gst_tag_key);
155   if (!data) {
156     /* make sure no other thread is creating a GstTagData at the same time */
157 #if !GLIB_CHECK_VERSION (2, 31, 0)
158     static GStaticMutex create_mutex = G_STATIC_MUTEX_INIT;
159
160     g_static_mutex_lock (&create_mutex);
161 #else
162     static GMutex create_mutex; /* no initialisation required */
163
164     g_mutex_lock (&create_mutex);
165 #endif
166
167     data = g_object_get_qdata (G_OBJECT (setter), gst_tag_key);
168     if (!data) {
169       data = g_slice_new (GstTagData);
170 #if !GLIB_CHECK_VERSION (2, 31, 0)
171       g_static_mutex_init (&data->lock);
172 #else
173       g_mutex_init (&data->lock);
174 #endif
175       data->list = NULL;
176       data->mode = GST_TAG_MERGE_KEEP;
177       g_object_set_qdata_full (G_OBJECT (setter), gst_tag_key, data,
178           gst_tag_data_free);
179     }
180 #if !GLIB_CHECK_VERSION (2, 31, 0)
181     g_static_mutex_unlock (&create_mutex);
182 #else
183     g_mutex_unlock (&create_mutex);
184 #endif
185   }
186
187   return data;
188 }
189
190 /**
191  * gst_tag_setter_reset_tags:
192  * @setter: a #GstTagSetter
193  *
194  * Reset the internal taglist. Elements should call this from within the
195  * state-change handler.
196  *
197  * Since: 0.10.22
198  */
199 void
200 gst_tag_setter_reset_tags (GstTagSetter * setter)
201 {
202   GstTagData *data;
203
204   g_return_if_fail (GST_IS_TAG_SETTER (setter));
205
206   data = gst_tag_setter_get_data (setter);
207
208   GST_TAG_DATA_LOCK (data);
209   if (data->list) {
210     gst_tag_list_free (data->list);
211     data->list = NULL;
212   }
213   GST_TAG_DATA_UNLOCK (data);
214 }
215
216 /**
217  * gst_tag_setter_merge_tags:
218  * @setter: a #GstTagSetter
219  * @list: a tag list to merge from
220  * @mode: the mode to merge with
221  *
222  * Merges the given list into the setter's list using the given mode.
223  */
224 void
225 gst_tag_setter_merge_tags (GstTagSetter * setter, const GstTagList * list,
226     GstTagMergeMode mode)
227 {
228   GstTagData *data;
229
230   g_return_if_fail (GST_IS_TAG_SETTER (setter));
231   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
232   g_return_if_fail (GST_IS_TAG_LIST (list));
233
234   data = gst_tag_setter_get_data (setter);
235
236   GST_TAG_DATA_LOCK (data);
237   if (data->list == NULL) {
238     if (mode != GST_TAG_MERGE_KEEP_ALL)
239       data->list = gst_tag_list_copy (list);
240   } else {
241     gst_tag_list_insert (data->list, list, mode);
242   }
243   GST_TAG_DATA_UNLOCK (data);
244 }
245
246 /**
247  * gst_tag_setter_add_tags:
248  * @setter: a #GstTagSetter
249  * @mode: the mode to use
250  * @tag: tag to set
251  * @...: more tag / value pairs to set
252  *
253  * Adds the given tag / value pairs on the setter using the given merge mode.
254  * The list must be terminated with NULL.
255  */
256 void
257 gst_tag_setter_add_tags (GstTagSetter * setter, GstTagMergeMode mode,
258     const gchar * tag, ...)
259 {
260   va_list args;
261
262   g_return_if_fail (GST_IS_TAG_SETTER (setter));
263   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
264
265   va_start (args, tag);
266   gst_tag_setter_add_tag_valist (setter, mode, tag, args);
267   va_end (args);
268 }
269
270 /**
271  * gst_tag_setter_add_tag_values:
272  * @setter: a #GstTagSetter
273  * @mode: the mode to use
274  * @tag: tag to set
275  * @...: more tag / GValue pairs to set
276  *
277  * Adds the given tag / GValue pairs on the setter using the given merge mode.
278  * The list must be terminated with NULL.
279  */
280 void
281 gst_tag_setter_add_tag_values (GstTagSetter * setter, GstTagMergeMode mode,
282     const gchar * tag, ...)
283 {
284   va_list args;
285
286   g_return_if_fail (GST_IS_TAG_SETTER (setter));
287   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
288
289   va_start (args, tag);
290   gst_tag_setter_add_tag_valist_values (setter, mode, tag, args);
291   va_end (args);
292 }
293
294 /**
295  * gst_tag_setter_add_tag_valist:
296  * @setter: a #GstTagSetter
297  * @mode: the mode to use
298  * @tag: tag to set
299  * @var_args: tag / value pairs to set
300  *
301  * Adds the given tag / value pairs on the setter using the given merge mode.
302  * The list must be terminated with NULL.
303  */
304 void
305 gst_tag_setter_add_tag_valist (GstTagSetter * setter, GstTagMergeMode mode,
306     const gchar * tag, va_list var_args)
307 {
308   GstTagData *data;
309
310   g_return_if_fail (GST_IS_TAG_SETTER (setter));
311   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
312
313   data = gst_tag_setter_get_data (setter);
314
315   GST_TAG_DATA_LOCK (data);
316   if (!data->list)
317     data->list = gst_tag_list_new_empty ();
318
319   gst_tag_list_add_valist (data->list, mode, tag, var_args);
320
321   GST_TAG_DATA_UNLOCK (data);
322 }
323
324 /**
325  * gst_tag_setter_add_tag_valist_values:
326  * @setter: a #GstTagSetter
327  * @mode: the mode to use
328  * @tag: tag to set
329  * @var_args: tag / GValue pairs to set
330  *
331  * Adds the given tag / GValue pairs on the setter using the given merge mode.
332  * The list must be terminated with NULL.
333  */
334 void
335 gst_tag_setter_add_tag_valist_values (GstTagSetter * setter,
336     GstTagMergeMode mode, const gchar * tag, va_list var_args)
337 {
338   GstTagData *data;
339
340   g_return_if_fail (GST_IS_TAG_SETTER (setter));
341   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
342
343   data = gst_tag_setter_get_data (setter);
344
345   GST_TAG_DATA_LOCK (data);
346
347   if (!data->list)
348     data->list = gst_tag_list_new_empty ();
349
350   gst_tag_list_add_valist_values (data->list, mode, tag, var_args);
351
352   GST_TAG_DATA_UNLOCK (data);
353 }
354
355 /**
356  * gst_tag_setter_add_tag_value:
357  * @setter: a #GstTagSetter
358  * @mode: the mode to use
359  * @tag: tag to set
360  * @value: GValue to set for the tag
361  *
362  * Adds the given tag / GValue pair on the setter using the given merge mode.
363  *
364  * Since: 0.10.24
365  */
366 void
367 gst_tag_setter_add_tag_value (GstTagSetter * setter,
368     GstTagMergeMode mode, const gchar * tag, const GValue * value)
369 {
370   GstTagData *data;
371
372   g_return_if_fail (GST_IS_TAG_SETTER (setter));
373   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
374
375   data = gst_tag_setter_get_data (setter);
376
377   GST_TAG_DATA_LOCK (data);
378
379   if (!data->list)
380     data->list = gst_tag_list_new_empty ();
381
382   gst_tag_list_add_value (data->list, mode, tag, value);
383
384   GST_TAG_DATA_UNLOCK (data);
385 }
386
387 /**
388  * gst_tag_setter_get_tag_list:
389  * @setter: a #GstTagSetter
390  *
391  * Returns the current list of tags the setter uses.  The list should not be
392  * modified or freed.
393  *
394  * This function is not thread-safe.
395  *
396  * Returns: (transfer none): a current snapshot of the taglist used in the
397  *          setter or NULL if none is used.
398  */
399 const GstTagList *
400 gst_tag_setter_get_tag_list (GstTagSetter * setter)
401 {
402   g_return_val_if_fail (GST_IS_TAG_SETTER (setter), NULL);
403
404   return gst_tag_setter_get_data (setter)->list;
405 }
406
407 /**
408  * gst_tag_setter_set_tag_merge_mode:
409  * @setter: a #GstTagSetter
410  * @mode: The mode with which tags are added
411  *
412  * Sets the given merge mode that is used for adding tags from events to tags
413  * specified by this interface. The default is #GST_TAG_MERGE_KEEP, which keeps
414  * the tags set with this interface and discards tags from events.
415  */
416 void
417 gst_tag_setter_set_tag_merge_mode (GstTagSetter * setter, GstTagMergeMode mode)
418 {
419   GstTagData *data;
420
421   g_return_if_fail (GST_IS_TAG_SETTER (setter));
422   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
423
424   data = gst_tag_setter_get_data (setter);
425
426   GST_TAG_DATA_LOCK (data);
427   data->mode = mode;
428   GST_TAG_DATA_UNLOCK (data);
429 }
430
431 /**
432  * gst_tag_setter_get_tag_merge_mode:
433  * @setter: a #GstTagSetter
434  *
435  * Queries the mode by which tags inside the setter are overwritten by tags
436  * from events
437  *
438  * Returns: the merge mode used inside the element.
439  */
440 GstTagMergeMode
441 gst_tag_setter_get_tag_merge_mode (GstTagSetter * setter)
442 {
443   GstTagMergeMode mode;
444   GstTagData *data;
445
446   g_return_val_if_fail (GST_IS_TAG_SETTER (setter), GST_TAG_MERGE_UNDEFINED);
447
448   data = gst_tag_setter_get_data (setter);
449
450   GST_TAG_DATA_LOCK (data);
451   mode = data->mode;
452   GST_TAG_DATA_UNLOCK (data);
453
454   return mode;
455 }