Merge branch '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   GStaticMutex lock;
87 } GstTagData;
88
89 GType
90 gst_tag_setter_get_type (void)
91 {
92   static volatile gsize tag_setter_type = 0;
93
94   if (g_once_init_enter (&tag_setter_type)) {
95     GType _type;
96     static const GTypeInfo tag_setter_info = {
97       sizeof (GstTagSetterInterface),   /* class_size */
98       NULL,                     /* base_init */
99       NULL,                     /* base_finalize */
100       NULL,
101       NULL,                     /* class_finalize */
102       NULL,                     /* class_data */
103       0,
104       0,
105       NULL
106     };
107
108     _type = g_type_register_static (G_TYPE_INTERFACE, "GstTagSetter",
109         &tag_setter_info, 0);
110
111     g_type_interface_add_prerequisite (_type, GST_TYPE_ELEMENT);
112
113     gst_tag_key = g_quark_from_static_string ("GST_TAG_SETTER");
114     g_once_init_leave (&tag_setter_type, _type);
115   }
116
117   return tag_setter_type;
118 }
119
120 static void
121 gst_tag_data_free (gpointer p)
122 {
123   GstTagData *data = (GstTagData *) p;
124
125   if (data->list)
126     gst_tag_list_free (data->list);
127
128   g_static_mutex_free (&data->lock);
129
130   g_slice_free (GstTagData, data);
131 }
132
133 static GstTagData *
134 gst_tag_setter_get_data (GstTagSetter * setter)
135 {
136   GstTagData *data;
137
138   data = g_object_get_qdata (G_OBJECT (setter), gst_tag_key);
139   if (!data) {
140     static GStaticMutex create_mutex = G_STATIC_MUTEX_INIT;
141
142     /* make sure no other thread is creating a GstTagData at the same time */
143     g_static_mutex_lock (&create_mutex);
144     data = g_object_get_qdata (G_OBJECT (setter), gst_tag_key);
145     if (!data) {
146       data = g_slice_new (GstTagData);
147       g_static_mutex_init (&data->lock);
148       data->list = NULL;
149       data->mode = GST_TAG_MERGE_KEEP;
150       g_object_set_qdata_full (G_OBJECT (setter), gst_tag_key, data,
151           gst_tag_data_free);
152     }
153     g_static_mutex_unlock (&create_mutex);
154   }
155
156   return data;
157 }
158
159 /**
160  * gst_tag_setter_reset_tags:
161  * @setter: a #GstTagSetter
162  *
163  * Reset the internal taglist. Elements should call this from within the
164  * state-change handler.
165  *
166  * Since: 0.10.22
167  */
168 void
169 gst_tag_setter_reset_tags (GstTagSetter * setter)
170 {
171   GstTagData *data;
172
173   g_return_if_fail (GST_IS_TAG_SETTER (setter));
174
175   data = gst_tag_setter_get_data (setter);
176
177   g_static_mutex_lock (&data->lock);
178   if (data->list) {
179     gst_tag_list_free (data->list);
180     data->list = NULL;
181   }
182   g_static_mutex_unlock (&data->lock);
183 }
184
185 /**
186  * gst_tag_setter_merge_tags:
187  * @setter: a #GstTagSetter
188  * @list: a tag list to merge from
189  * @mode: the mode to merge with
190  *
191  * Merges the given list into the setter's list using the given mode.
192  */
193 void
194 gst_tag_setter_merge_tags (GstTagSetter * setter, const GstTagList * list,
195     GstTagMergeMode mode)
196 {
197   GstTagData *data;
198
199   g_return_if_fail (GST_IS_TAG_SETTER (setter));
200   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
201   g_return_if_fail (GST_IS_TAG_LIST (list));
202
203   data = gst_tag_setter_get_data (setter);
204
205   g_static_mutex_lock (&data->lock);
206   if (data->list == NULL) {
207     if (mode != GST_TAG_MERGE_KEEP_ALL)
208       data->list = gst_tag_list_copy (list);
209   } else {
210     gst_tag_list_insert (data->list, list, mode);
211   }
212   g_static_mutex_unlock (&data->lock);
213 }
214
215 /**
216  * gst_tag_setter_add_tags:
217  * @setter: a #GstTagSetter
218  * @mode: the mode to use
219  * @tag: tag to set
220  * @...: more tag / value pairs to set
221  *
222  * Adds the given tag / value pairs on the setter using the given merge mode.
223  * The list must be terminated with NULL.
224  */
225 void
226 gst_tag_setter_add_tags (GstTagSetter * setter, GstTagMergeMode mode,
227     const gchar * tag, ...)
228 {
229   va_list args;
230
231   g_return_if_fail (GST_IS_TAG_SETTER (setter));
232   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
233
234   va_start (args, tag);
235   gst_tag_setter_add_tag_valist (setter, mode, tag, args);
236   va_end (args);
237 }
238
239 /**
240  * gst_tag_setter_add_tag_values:
241  * @setter: a #GstTagSetter
242  * @mode: the mode to use
243  * @tag: tag to set
244  * @...: more tag / GValue pairs to set
245  *
246  * Adds the given tag / GValue pairs on the setter using the given merge mode.
247  * The list must be terminated with NULL.
248  */
249 void
250 gst_tag_setter_add_tag_values (GstTagSetter * setter, GstTagMergeMode mode,
251     const gchar * tag, ...)
252 {
253   va_list args;
254
255   g_return_if_fail (GST_IS_TAG_SETTER (setter));
256   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
257
258   va_start (args, tag);
259   gst_tag_setter_add_tag_valist_values (setter, mode, tag, args);
260   va_end (args);
261 }
262
263 /**
264  * gst_tag_setter_add_tag_valist:
265  * @setter: a #GstTagSetter
266  * @mode: the mode to use
267  * @tag: tag to set
268  * @var_args: tag / value pairs to set
269  *
270  * Adds the given tag / value pairs on the setter using the given merge mode.
271  * The list must be terminated with NULL.
272  */
273 void
274 gst_tag_setter_add_tag_valist (GstTagSetter * setter, GstTagMergeMode mode,
275     const gchar * tag, va_list var_args)
276 {
277   GstTagData *data;
278
279   g_return_if_fail (GST_IS_TAG_SETTER (setter));
280   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
281
282   data = gst_tag_setter_get_data (setter);
283
284   g_static_mutex_lock (&data->lock);
285   if (!data->list)
286     data->list = gst_tag_list_new ();
287
288   gst_tag_list_add_valist (data->list, mode, tag, var_args);
289
290   g_static_mutex_unlock (&data->lock);
291 }
292
293 /**
294  * gst_tag_setter_add_tag_valist_values:
295  * @setter: a #GstTagSetter
296  * @mode: the mode to use
297  * @tag: tag to set
298  * @var_args: tag / GValue pairs to set
299  *
300  * Adds the given tag / GValue pairs on the setter using the given merge mode.
301  * The list must be terminated with NULL.
302  */
303 void
304 gst_tag_setter_add_tag_valist_values (GstTagSetter * setter,
305     GstTagMergeMode mode, const gchar * tag, va_list var_args)
306 {
307   GstTagData *data;
308
309   g_return_if_fail (GST_IS_TAG_SETTER (setter));
310   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
311
312   data = gst_tag_setter_get_data (setter);
313
314   g_static_mutex_lock (&data->lock);
315
316   if (!data->list)
317     data->list = gst_tag_list_new ();
318
319   gst_tag_list_add_valist_values (data->list, mode, tag, var_args);
320
321   g_static_mutex_unlock (&data->lock);
322 }
323
324 /**
325  * gst_tag_setter_add_tag_value:
326  * @setter: a #GstTagSetter
327  * @mode: the mode to use
328  * @tag: tag to set
329  * @value: GValue to set for the tag
330  *
331  * Adds the given tag / GValue pair on the setter using the given merge mode.
332  *
333  * Since: 0.10.24
334  */
335 void
336 gst_tag_setter_add_tag_value (GstTagSetter * setter,
337     GstTagMergeMode mode, const gchar * tag, const GValue * value)
338 {
339   GstTagData *data;
340
341   g_return_if_fail (GST_IS_TAG_SETTER (setter));
342   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
343
344   data = gst_tag_setter_get_data (setter);
345
346   g_static_mutex_lock (&data->lock);
347
348   if (!data->list)
349     data->list = gst_tag_list_new ();
350
351   gst_tag_list_add_value (data->list, mode, tag, value);
352
353   g_static_mutex_unlock (&data->lock);
354 }
355
356 /**
357  * gst_tag_setter_get_tag_list:
358  * @setter: a #GstTagSetter
359  *
360  * Returns the current list of tags the setter uses.  The list should not be
361  * modified or freed.
362  *
363  * This function is not thread-safe.
364  *
365  * Returns: (transfer none): a current snapshot of the taglist used in the
366  *          setter or NULL if none is used.
367  */
368 const GstTagList *
369 gst_tag_setter_get_tag_list (GstTagSetter * setter)
370 {
371   g_return_val_if_fail (GST_IS_TAG_SETTER (setter), NULL);
372
373   return gst_tag_setter_get_data (setter)->list;
374 }
375
376 /**
377  * gst_tag_setter_set_tag_merge_mode:
378  * @setter: a #GstTagSetter
379  * @mode: The mode with which tags are added
380  *
381  * Sets the given merge mode that is used for adding tags from events to tags
382  * specified by this interface. The default is #GST_TAG_MERGE_KEEP, which keeps
383  * the tags set with this interface and discards tags from events.
384  */
385 void
386 gst_tag_setter_set_tag_merge_mode (GstTagSetter * setter, GstTagMergeMode mode)
387 {
388   GstTagData *data;
389
390   g_return_if_fail (GST_IS_TAG_SETTER (setter));
391   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
392
393   data = gst_tag_setter_get_data (setter);
394
395   g_static_mutex_lock (&data->lock);
396   data->mode = mode;
397   g_static_mutex_unlock (&data->lock);
398 }
399
400 /**
401  * gst_tag_setter_get_tag_merge_mode:
402  * @setter: a #GstTagSetter
403  *
404  * Queries the mode by which tags inside the setter are overwritten by tags
405  * from events
406  *
407  * Returns: the merge mode used inside the element.
408  */
409 GstTagMergeMode
410 gst_tag_setter_get_tag_merge_mode (GstTagSetter * setter)
411 {
412   GstTagMergeMode mode;
413   GstTagData *data;
414
415   g_return_val_if_fail (GST_IS_TAG_SETTER (setter), GST_TAG_MERGE_UNDEFINED);
416
417   data = gst_tag_setter_get_data (setter);
418
419   g_static_mutex_lock (&data->lock);
420   mode = data->mode;
421   g_static_mutex_unlock (&data->lock);
422
423   return mode;
424 }