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