tee: Check for the removed pad flag also in the slow pushing path
[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  * @title: GstTagsetter
25  * @short_description: Element interface that allows setting and retrieval
26  *                     of media metadata
27  *
28  * Element interface that allows setting of media metadata.
29  *
30  * Elements that support changing a stream's metadata will implement this
31  * interface. Examples of such elements are 'vorbisenc', 'theoraenc' and
32  * 'id3v2mux'.
33  *
34  * If you just want to retrieve metadata in your application then all you
35  * need to do is watch for tag messages on your pipeline's bus. This
36  * interface is only for setting metadata, not for extracting it. To set tags
37  * from the application, find tagsetter elements and set tags using e.g.
38  * gst_tag_setter_merge_tags() or gst_tag_setter_add_tags(). Also consider
39  * setting the #GstTagMergeMode that is used for tag events that arrive at the
40  * tagsetter element (default mode is to keep existing tags).
41  * The application should do that before the element goes to %GST_STATE_PAUSED.
42  *
43  * Elements implementing the #GstTagSetter interface often have to merge
44  * any tags received from upstream and the tags set by the application via
45  * the interface. This can be done like this:
46  *
47  * |[<!-- language="C" -->
48  * GstTagMergeMode merge_mode;
49  * const GstTagList *application_tags;
50  * const GstTagList *event_tags;
51  * GstTagSetter *tagsetter;
52  * GstTagList *result;
53  *
54  * tagsetter = GST_TAG_SETTER (element);
55  *
56  * merge_mode = gst_tag_setter_get_tag_merge_mode (tagsetter);
57  * application_tags = gst_tag_setter_get_tag_list (tagsetter);
58  * event_tags = (const GstTagList *) element->event_tags;
59  *
60  * GST_LOG_OBJECT (tagsetter, "merging tags, merge mode = %d", merge_mode);
61  * GST_LOG_OBJECT (tagsetter, "event tags: %" GST_PTR_FORMAT, event_tags);
62  * GST_LOG_OBJECT (tagsetter, "set   tags: %" GST_PTR_FORMAT, application_tags);
63  *
64  * result = gst_tag_list_merge (application_tags, event_tags, merge_mode);
65  *
66  * GST_LOG_OBJECT (tagsetter, "final tags: %" GST_PTR_FORMAT, result);
67  * ]|
68  */
69
70 #ifdef HAVE_CONFIG_H
71 #  include "config.h"
72 #endif
73
74 #include "gst_private.h"
75 #include "gsttagsetter.h"
76 #include <gobject/gvaluecollector.h>
77 #include <string.h>
78
79 static GQuark gst_tag_key;
80
81 typedef struct
82 {
83   GstTagMergeMode mode;
84   GstTagList *list;
85   GMutex lock;
86 } GstTagData;
87
88 #define GST_TAG_DATA_LOCK(data) g_mutex_lock(&data->lock)
89 #define GST_TAG_DATA_UNLOCK(data) g_mutex_unlock(&data->lock)
90
91 G_DEFINE_INTERFACE_WITH_CODE (GstTagSetter, gst_tag_setter, GST_TYPE_ELEMENT,
92     gst_tag_key = g_quark_from_static_string ("gst-tag-setter-data");
93     );
94
95 static void
96 gst_tag_setter_default_init (GstTagSetterInterface * klass)
97 {
98   /* nothing to do here, it's a dummy interface */
99 }
100
101 static void
102 gst_tag_data_free (gpointer p)
103 {
104   GstTagData *data = (GstTagData *) p;
105
106   if (data->list)
107     gst_tag_list_unref (data->list);
108
109   g_mutex_clear (&data->lock);
110
111   g_slice_free (GstTagData, data);
112 }
113
114 static GstTagData *
115 gst_tag_setter_get_data (GstTagSetter * setter)
116 {
117   GstTagData *data;
118
119   data = g_object_get_qdata (G_OBJECT (setter), gst_tag_key);
120   if (!data) {
121     /* make sure no other thread is creating a GstTagData at the same time */
122     static GMutex create_mutex; /* no initialisation required */
123
124     g_mutex_lock (&create_mutex);
125
126     data = g_object_get_qdata (G_OBJECT (setter), gst_tag_key);
127     if (!data) {
128       data = g_slice_new (GstTagData);
129       g_mutex_init (&data->lock);
130       data->list = NULL;
131       data->mode = GST_TAG_MERGE_KEEP;
132       g_object_set_qdata_full (G_OBJECT (setter), gst_tag_key, data,
133           gst_tag_data_free);
134     }
135
136     g_mutex_unlock (&create_mutex);
137   }
138
139   return data;
140 }
141
142 /**
143  * gst_tag_setter_reset_tags:
144  * @setter: a #GstTagSetter
145  *
146  * Reset the internal taglist. Elements should call this from within the
147  * state-change handler.
148  */
149 void
150 gst_tag_setter_reset_tags (GstTagSetter * setter)
151 {
152   GstTagData *data;
153
154   g_return_if_fail (GST_IS_TAG_SETTER (setter));
155
156   data = gst_tag_setter_get_data (setter);
157
158   GST_TAG_DATA_LOCK (data);
159   if (data->list) {
160     gst_tag_list_unref (data->list);
161     data->list = NULL;
162   }
163   GST_TAG_DATA_UNLOCK (data);
164 }
165
166 /**
167  * gst_tag_setter_merge_tags:
168  * @setter: a #GstTagSetter
169  * @list: a tag list to merge from
170  * @mode: the mode to merge with
171  *
172  * Merges the given list into the setter's list using the given mode.
173  */
174 void
175 gst_tag_setter_merge_tags (GstTagSetter * setter, const GstTagList * list,
176     GstTagMergeMode mode)
177 {
178   GstTagData *data;
179
180   g_return_if_fail (GST_IS_TAG_SETTER (setter));
181   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
182   g_return_if_fail (GST_IS_TAG_LIST (list));
183
184   data = gst_tag_setter_get_data (setter);
185
186   GST_TAG_DATA_LOCK (data);
187   if (data->list == NULL) {
188     if (mode != GST_TAG_MERGE_KEEP_ALL)
189       data->list = gst_tag_list_copy (list);
190   } else {
191     gst_tag_list_insert (data->list, list, mode);
192   }
193   GST_TAG_DATA_UNLOCK (data);
194 }
195
196 /**
197  * gst_tag_setter_add_tags:
198  * @setter: a #GstTagSetter
199  * @mode: the mode to use
200  * @tag: tag to set
201  * @...: more tag / value pairs to set
202  *
203  * Adds the given tag / value pairs on the setter using the given merge mode.
204  * The list must be terminated with %NULL.
205  */
206 void
207 gst_tag_setter_add_tags (GstTagSetter * setter, GstTagMergeMode mode,
208     const gchar * tag, ...)
209 {
210   va_list args;
211
212   g_return_if_fail (GST_IS_TAG_SETTER (setter));
213   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
214
215   va_start (args, tag);
216   gst_tag_setter_add_tag_valist (setter, mode, tag, args);
217   va_end (args);
218 }
219
220 /**
221  * gst_tag_setter_add_tag_values:
222  * @setter: a #GstTagSetter
223  * @mode: the mode to use
224  * @tag: tag to set
225  * @...: more tag / GValue pairs to set
226  *
227  * Adds the given tag / GValue 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_tag_values (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_values (setter, mode, tag, args);
241   va_end (args);
242 }
243
244 /**
245  * gst_tag_setter_add_tag_valist:
246  * @setter: a #GstTagSetter
247  * @mode: the mode to use
248  * @tag: tag to set
249  * @var_args: tag / value pairs to set
250  *
251  * Adds the given tag / value 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_valist (GstTagSetter * setter, GstTagMergeMode mode,
256     const gchar * tag, va_list var_args)
257 {
258   GstTagData *data;
259
260   g_return_if_fail (GST_IS_TAG_SETTER (setter));
261   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
262
263   data = gst_tag_setter_get_data (setter);
264
265   GST_TAG_DATA_LOCK (data);
266   if (!data->list)
267     data->list = gst_tag_list_new_empty ();
268
269   gst_tag_list_add_valist (data->list, mode, tag, var_args);
270
271   GST_TAG_DATA_UNLOCK (data);
272 }
273
274 /**
275  * gst_tag_setter_add_tag_valist_values:
276  * @setter: a #GstTagSetter
277  * @mode: the mode to use
278  * @tag: tag to set
279  * @var_args: tag / GValue pairs to set
280  *
281  * Adds the given tag / GValue pairs on the setter using the given merge mode.
282  * The list must be terminated with %NULL.
283  */
284 void
285 gst_tag_setter_add_tag_valist_values (GstTagSetter * setter,
286     GstTagMergeMode mode, const gchar * tag, va_list var_args)
287 {
288   GstTagData *data;
289
290   g_return_if_fail (GST_IS_TAG_SETTER (setter));
291   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
292
293   data = gst_tag_setter_get_data (setter);
294
295   GST_TAG_DATA_LOCK (data);
296
297   if (!data->list)
298     data->list = gst_tag_list_new_empty ();
299
300   gst_tag_list_add_valist_values (data->list, mode, tag, var_args);
301
302   GST_TAG_DATA_UNLOCK (data);
303 }
304
305 /**
306  * gst_tag_setter_add_tag_value:
307  * @setter: a #GstTagSetter
308  * @mode: the mode to use
309  * @tag: tag to set
310  * @value: GValue to set for the tag
311  *
312  * Adds the given tag / GValue pair on the setter using the given merge mode.
313  */
314 void
315 gst_tag_setter_add_tag_value (GstTagSetter * setter,
316     GstTagMergeMode mode, const gchar * tag, const GValue * value)
317 {
318   GstTagData *data;
319
320   g_return_if_fail (GST_IS_TAG_SETTER (setter));
321   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
322
323   data = gst_tag_setter_get_data (setter);
324
325   GST_TAG_DATA_LOCK (data);
326
327   if (!data->list)
328     data->list = gst_tag_list_new_empty ();
329
330   gst_tag_list_add_value (data->list, mode, tag, value);
331
332   GST_TAG_DATA_UNLOCK (data);
333 }
334
335 /**
336  * gst_tag_setter_get_tag_list:
337  * @setter: a #GstTagSetter
338  *
339  * Returns the current list of tags the setter uses.  The list should not be
340  * modified or freed.
341  *
342  * This function is not thread-safe.
343  *
344  * Returns: (transfer none) (nullable): a current snapshot of the
345  *          taglist used in the setter or %NULL if none is used.
346  */
347 const GstTagList *
348 gst_tag_setter_get_tag_list (GstTagSetter * setter)
349 {
350   g_return_val_if_fail (GST_IS_TAG_SETTER (setter), NULL);
351
352   return gst_tag_setter_get_data (setter)->list;
353 }
354
355 /**
356  * gst_tag_setter_set_tag_merge_mode:
357  * @setter: a #GstTagSetter
358  * @mode: The mode with which tags are added
359  *
360  * Sets the given merge mode that is used for adding tags from events to tags
361  * specified by this interface. The default is #GST_TAG_MERGE_KEEP, which keeps
362  * the tags set with this interface and discards tags from events.
363  */
364 void
365 gst_tag_setter_set_tag_merge_mode (GstTagSetter * setter, GstTagMergeMode mode)
366 {
367   GstTagData *data;
368
369   g_return_if_fail (GST_IS_TAG_SETTER (setter));
370   g_return_if_fail (GST_TAG_MODE_IS_VALID (mode));
371
372   data = gst_tag_setter_get_data (setter);
373
374   GST_TAG_DATA_LOCK (data);
375   data->mode = mode;
376   GST_TAG_DATA_UNLOCK (data);
377 }
378
379 /**
380  * gst_tag_setter_get_tag_merge_mode:
381  * @setter: a #GstTagSetter
382  *
383  * Queries the mode by which tags inside the setter are overwritten by tags
384  * from events
385  *
386  * Returns: the merge mode used inside the element.
387  */
388 GstTagMergeMode
389 gst_tag_setter_get_tag_merge_mode (GstTagSetter * setter)
390 {
391   GstTagMergeMode mode;
392   GstTagData *data;
393
394   g_return_val_if_fail (GST_IS_TAG_SETTER (setter), GST_TAG_MERGE_UNDEFINED);
395
396   data = gst_tag_setter_get_data (setter);
397
398   GST_TAG_DATA_LOCK (data);
399   mode = data->mode;
400   GST_TAG_DATA_UNLOCK (data);
401
402   return mode;
403 }