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