gstpad: Fix non-serialized sticky event push
[platform/upstream/gstreamer.git] / subprojects / gstreamer / gst / gstcapsfeatures.c
1 /* GStreamer
2  * Copyright (C) 2013 Collabora Ltd.
3  *   Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:gstcapsfeatures
23  * @title: GstCapsFeatures
24  * @short_description: A set of features in caps
25  * @see_also: #GstCaps
26  *
27  * #GstCapsFeatures can optionally be set on a #GstCaps to add requirements
28  * for additional features for a specific #GstStructure. Caps structures with
29  * the same name but with a non-equal set of caps features are not compatible.
30  * If a pad supports multiple sets of features it has to add multiple equal
31  * structures with different feature sets to the caps.
32  *
33  * Empty #GstCapsFeatures are equivalent with the #GstCapsFeatures that only
34  * contain #GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY. ANY #GstCapsFeatures as
35  * created by gst_caps_features_new_any() are equal to any other #GstCapsFeatures
36  * and can be used to specify that any #GstCapsFeatures would be supported, e.g.
37  * for elements that don't touch buffer memory. #GstCaps with ANY #GstCapsFeatures
38  * are considered non-fixed and during negotiation some #GstCapsFeatures have
39  * to be selected.
40  *
41  * Examples for caps features would be the requirement of a specific #GstMemory
42  * types or the requirement of having a specific #GstMeta on the buffer. Features
43  * are given as a string of the format `memory:GstMemoryTypeName` or
44  * `meta:GstMetaAPIName`.
45  *
46  * Since: 1.2
47  */
48
49 #ifdef HAVE_CONFIG_H
50 #include "config.h"
51 #endif
52
53 #include <string.h>
54 #include "gst_private.h"
55 #include "gstcapsfeatures.h"
56 #include <gst/gst.h>
57
58 GST_DEBUG_CATEGORY_STATIC (gst_caps_features_debug);
59 #define GST_CAT_DEFAULT gst_caps_features_debug
60
61 struct _GstCapsFeatures
62 {
63   GType type;
64   gint *parent_refcount;
65   GArray *array;
66   gboolean is_any;
67 };
68
69 GType _gst_caps_features_type = 0;
70 static gint static_caps_features_parent_refcount = G_MAXINT;
71 GstCapsFeatures *_gst_caps_features_any = NULL;
72 GstCapsFeatures *_gst_caps_features_memory_system_memory = NULL;
73 static GQuark _gst_caps_feature_memory_system_memory = 0;
74
75 G_DEFINE_BOXED_TYPE (GstCapsFeatures, gst_caps_features,
76     gst_caps_features_copy, gst_caps_features_free);
77
78 #define IS_MUTABLE(features) \
79     (!features->parent_refcount || \
80      g_atomic_int_get (features->parent_refcount) == 1)
81
82 static void
83 gst_caps_features_transform_to_string (const GValue * src_value,
84     GValue * dest_value);
85
86 void
87 _priv_gst_caps_features_initialize (void)
88 {
89   GST_DEBUG_CATEGORY_INIT (gst_caps_features_debug, "caps-features", 0,
90       "GstCapsFeatures debug");
91
92   _gst_caps_features_type = gst_caps_features_get_type ();
93   _gst_caps_feature_memory_system_memory =
94       g_quark_from_static_string (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
95
96   g_value_register_transform_func (_gst_caps_features_type, G_TYPE_STRING,
97       gst_caps_features_transform_to_string);
98
99   _gst_caps_features_any = gst_caps_features_new_any ();
100   gst_caps_features_set_parent_refcount (_gst_caps_features_any,
101       &static_caps_features_parent_refcount);
102   _gst_caps_features_memory_system_memory =
103       gst_caps_features_new_id (_gst_caps_feature_memory_system_memory, 0);
104   gst_caps_features_set_parent_refcount
105       (_gst_caps_features_memory_system_memory,
106       &static_caps_features_parent_refcount);
107 }
108
109 void
110 _priv_gst_caps_features_cleanup (void)
111 {
112   gst_caps_features_set_parent_refcount (_gst_caps_features_any, NULL);
113   gst_caps_features_free (_gst_caps_features_any);
114   _gst_caps_features_any = NULL;
115   gst_caps_features_set_parent_refcount
116       (_gst_caps_features_memory_system_memory, NULL);
117   gst_caps_features_free (_gst_caps_features_memory_system_memory);
118   _gst_caps_features_memory_system_memory = NULL;
119 }
120
121 /**
122  * gst_is_caps_features:
123  *
124  * Checks if @obj is a #GstCapsFeatures
125  *
126  * Returns: %TRUE if @obj is a #GstCapsFeatures %FALSE otherwise
127  */
128 gboolean
129 gst_is_caps_features (gconstpointer obj)
130 {
131   const GstCapsFeatures *features = obj;
132
133   return (obj != NULL && features->type == _gst_caps_features_type);
134 }
135
136 static gboolean
137 gst_caps_feature_name_is_valid (const gchar * feature)
138 {
139 #ifndef G_DISABLE_CHECKS
140   while (TRUE) {
141     if (g_ascii_isalpha (*feature))
142       feature++;
143     else if (*feature == ':')
144       break;
145     else
146       return FALSE;
147   }
148
149   if (*feature != ':')
150     return FALSE;
151
152   feature++;
153   if (*feature == '\0' || !g_ascii_isalpha (*feature))
154     return FALSE;
155
156   while (TRUE) {
157     if (g_ascii_isalnum (*feature))
158       feature++;
159     else if (*feature == '\0')
160       break;
161     else
162       return FALSE;
163   }
164 #endif
165
166   return TRUE;
167 }
168
169 /**
170  * gst_caps_features_new_empty:
171  *
172  * Creates a new, empty #GstCapsFeatures.
173  *
174  * Returns: (transfer full): a new, empty #GstCapsFeatures
175  *
176  * Since: 1.2
177  */
178 GstCapsFeatures *
179 gst_caps_features_new_empty (void)
180 {
181   GstCapsFeatures *features;
182
183   features = g_slice_new (GstCapsFeatures);
184   features->type = _gst_caps_features_type;
185   features->parent_refcount = NULL;
186   features->array = g_array_new (FALSE, FALSE, sizeof (GQuark));
187   features->is_any = FALSE;
188
189   GST_TRACE ("created caps features %p", features);
190
191   return features;
192 }
193
194 /**
195  * gst_caps_features_new_any:
196  *
197  * Creates a new, ANY #GstCapsFeatures. This will be equal
198  * to any other #GstCapsFeatures but caps with these are
199  * unfixed.
200  *
201  * Returns: (transfer full): a new, ANY #GstCapsFeatures
202  *
203  * Since: 1.2
204  */
205 GstCapsFeatures *
206 gst_caps_features_new_any (void)
207 {
208   GstCapsFeatures *features;
209
210   features = gst_caps_features_new_empty ();
211   features->is_any = TRUE;
212
213   return features;
214 }
215
216 /**
217  * gst_caps_features_new_single:
218  * @feature: The feature
219  *
220  * Creates a new #GstCapsFeatures with a single feature.
221  *
222  * Returns: (transfer full): a new #GstCapsFeatures
223  *
224  * Since: 1.20
225  */
226 GstCapsFeatures *
227 gst_caps_features_new_single (const gchar * feature)
228 {
229   GstCapsFeatures *features;
230   g_return_val_if_fail (feature != NULL, NULL);
231
232   features = gst_caps_features_new_empty ();
233   gst_caps_features_add (features, feature);
234   return features;
235 }
236
237 /**
238  * gst_caps_features_new:
239  * @feature1: name of first feature to set
240  * @...: additional features
241  *
242  * Creates a new #GstCapsFeatures with the given features.
243  * The last argument must be %NULL.
244  *
245  * Returns: (transfer full): a new, empty #GstCapsFeatures
246  *
247  * Since: 1.2
248  */
249 GstCapsFeatures *
250 gst_caps_features_new (const gchar * feature1, ...)
251 {
252   GstCapsFeatures *features;
253   va_list varargs;
254
255   g_return_val_if_fail (feature1 != NULL, NULL);
256
257   va_start (varargs, feature1);
258   features = gst_caps_features_new_valist (feature1, varargs);
259   va_end (varargs);
260
261   return features;
262 }
263
264 /**
265  * gst_caps_features_new_valist:
266  * @feature1: name of first feature to set
267  * @varargs: variable argument list
268  *
269  * Creates a new #GstCapsFeatures with the given features.
270  *
271  * Returns: (transfer full): a new, empty #GstCapsFeatures
272  *
273  * Since: 1.2
274  */
275 GstCapsFeatures *
276 gst_caps_features_new_valist (const gchar * feature1, va_list varargs)
277 {
278   GstCapsFeatures *features;
279
280   g_return_val_if_fail (feature1 != NULL, NULL);
281
282   features = gst_caps_features_new_empty ();
283
284   while (feature1) {
285     gst_caps_features_add (features, feature1);
286     feature1 = va_arg (varargs, const gchar *);
287   }
288
289   return features;
290 }
291
292 /**
293  * gst_caps_features_new_id:
294  * @feature1: name of first feature to set
295  * @...: additional features
296  *
297  * Creates a new #GstCapsFeatures with the given features.
298  * The last argument must be 0.
299  *
300  * Returns: (transfer full): a new, empty #GstCapsFeatures
301  *
302  * Since: 1.2
303  */
304 GstCapsFeatures *
305 gst_caps_features_new_id (GQuark feature1, ...)
306 {
307   GstCapsFeatures *features;
308   va_list varargs;
309
310   g_return_val_if_fail (feature1 != 0, NULL);
311
312   va_start (varargs, feature1);
313   features = gst_caps_features_new_id_valist (feature1, varargs);
314   va_end (varargs);
315
316   return features;
317 }
318
319 /**
320  * gst_caps_features_new_id_valist:
321  * @feature1: name of first feature to set
322  * @varargs: variable argument list
323  *
324  * Creates a new #GstCapsFeatures with the given features.
325  *
326  * Returns: (transfer full): a new, empty #GstCapsFeatures
327  *
328  * Since: 1.2
329  */
330 GstCapsFeatures *
331 gst_caps_features_new_id_valist (GQuark feature1, va_list varargs)
332 {
333   GstCapsFeatures *features;
334
335   g_return_val_if_fail (feature1 != 0, NULL);
336
337   features = gst_caps_features_new_empty ();
338
339   while (feature1) {
340     gst_caps_features_add_id (features, feature1);
341     feature1 = va_arg (varargs, GQuark);
342   }
343
344   return features;
345 }
346
347 /**
348  * gst_caps_features_set_parent_refcount:
349  * @features: a #GstCapsFeatures
350  * @refcount: (in): a pointer to the parent's refcount
351  *
352  * Sets the parent_refcount field of #GstCapsFeatures. This field is used to
353  * determine whether a caps features is mutable or not. This function should only be
354  * called by code implementing parent objects of #GstCapsFeatures, as described in
355  * [the MT refcounting design document](additional/design/MT-refcounting.md).
356  *
357  * Returns: %TRUE if the parent refcount could be set.
358  *
359  * Since: 1.2
360  */
361 gboolean
362 gst_caps_features_set_parent_refcount (GstCapsFeatures * features,
363     gint * refcount)
364 {
365   g_return_val_if_fail (features != NULL, FALSE);
366
367   /* if we have a parent_refcount already, we can only clear
368    * if with a NULL refcount */
369   if (features->parent_refcount) {
370     if (refcount != NULL) {
371       g_return_val_if_fail (refcount == NULL, FALSE);
372       return FALSE;
373     }
374   } else {
375     if (refcount == NULL) {
376       g_return_val_if_fail (refcount != NULL, FALSE);
377       return FALSE;
378     }
379   }
380
381   features->parent_refcount = refcount;
382
383   return TRUE;
384 }
385
386 /**
387  * gst_caps_features_copy:
388  * @features: a #GstCapsFeatures to duplicate
389  *
390  * Duplicates a #GstCapsFeatures and all its values.
391  *
392  * Returns: (transfer full): a new #GstCapsFeatures.
393  *
394  * Since: 1.2
395  */
396 GstCapsFeatures *
397 gst_caps_features_copy (const GstCapsFeatures * features)
398 {
399   GstCapsFeatures *copy;
400   guint i, n;
401
402   g_return_val_if_fail (features != NULL, NULL);
403
404   copy = gst_caps_features_new_empty ();
405   n = gst_caps_features_get_size (features);
406   for (i = 0; i < n; i++)
407     gst_caps_features_add_id (copy, gst_caps_features_get_nth_id (features, i));
408   copy->is_any = features->is_any;
409
410   return copy;
411 }
412
413 /**
414  * gst_caps_features_free:
415  * @features: (in) (transfer full): the #GstCapsFeatures to free
416  *
417  * Frees a #GstCapsFeatures and all its values. The caps features must not
418  * have a parent when this function is called.
419  *
420  * Since: 1.2
421  */
422 void
423 gst_caps_features_free (GstCapsFeatures * features)
424 {
425   g_return_if_fail (features != NULL);
426   g_return_if_fail (features->parent_refcount == NULL);
427
428   g_array_free (features->array, TRUE);
429 #ifdef USE_POISONING
430   memset (features, 0xff, sizeof (GstCapsFeatures));
431 #endif
432   GST_TRACE ("free caps features %p", features);
433
434   g_slice_free (GstCapsFeatures, features);
435 }
436
437 /**
438  * gst_caps_features_to_string:
439  * @features: a #GstCapsFeatures
440  *
441  * Converts @features to a human-readable string representation.
442  *
443  * For debugging purposes its easier to do something like this:
444  *
445  * ``` C
446  * GST_LOG ("features is %" GST_PTR_FORMAT, features);
447  * ```
448  *
449  * This prints the features in human readable form.
450  *
451  * Returns: (transfer full): a pointer to string allocated by g_malloc().
452  *
453  * Since: 1.2
454  */
455 gchar *
456 gst_caps_features_to_string (const GstCapsFeatures * features)
457 {
458   GString *s;
459
460   g_return_val_if_fail (features != NULL, NULL);
461
462   s = g_string_sized_new (FEATURES_ESTIMATED_STRING_LEN (features));
463
464   priv_gst_caps_features_append_to_gstring (features, s);
465
466   return g_string_free (s, FALSE);
467 }
468
469 void
470 priv_gst_caps_features_append_to_gstring (const GstCapsFeatures * features,
471     GString * s)
472 {
473   guint i, n;
474
475   g_return_if_fail (features != NULL);
476
477   if (features->array->len == 0 && features->is_any) {
478     g_string_append (s, "ANY");
479     return;
480   }
481
482   n = features->array->len;
483   for (i = 0; i < n; i++) {
484     GQuark *quark = &g_array_index (features->array, GQuark, i);
485
486     g_string_append (s, g_quark_to_string (*quark));
487     if (i + 1 < n)
488       g_string_append (s, ", ");
489   }
490 }
491
492 /**
493  * gst_caps_features_from_string:
494  * @features: a string representation of a #GstCapsFeatures.
495  *
496  * Creates a #GstCapsFeatures from a string representation.
497  *
498  * Returns: (transfer full) (nullable): a new #GstCapsFeatures or
499  *     %NULL when the string could not be parsed.
500  *
501  * Since: 1.2
502  */
503 GstCapsFeatures *
504 gst_caps_features_from_string (const gchar * features)
505 {
506   GstCapsFeatures *ret;
507   gboolean escape = FALSE;
508   const gchar *features_orig = features;
509   const gchar *feature;
510
511   ret = gst_caps_features_new_empty ();
512
513   if (!features || *features == '\0')
514     return ret;
515
516   if (strcmp (features, "ANY") == 0) {
517     ret->is_any = TRUE;
518     return ret;
519   }
520
521   /* Skip trailing spaces */
522   while (*features == ' ')
523     features++;
524
525   feature = features;
526   while (TRUE) {
527     gchar c = *features;
528
529     if (c == '\\') {
530       escape = TRUE;
531       features++;
532       continue;
533     } else if ((!escape && c == ',') || c == '\0') {
534       guint len = features - feature + 1;
535       gchar *tmp;
536       gchar *p;
537
538       if (len == 1) {
539         g_warning ("Failed deserialize caps features '%s'", features_orig);
540         gst_caps_features_free (ret);
541         return NULL;
542       }
543
544       tmp = g_malloc (len);
545       memcpy (tmp, feature, len - 1);
546       tmp[len - 1] = '\0';
547
548       p = tmp + len - 1;
549       while (*p == ' ') {
550         *p = '\0';
551         p--;
552       }
553
554       if (strstr (tmp, " ") != NULL || *tmp == '\0') {
555         g_free (tmp);
556         g_warning ("Failed deserialize caps features '%s'", features_orig);
557         gst_caps_features_free (ret);
558         return NULL;
559       }
560
561       gst_caps_features_add (ret, tmp);
562       g_free (tmp);
563
564       if (c == '\0')
565         break;
566
567       /* Skip to the next value */
568       features++;
569       while (*features == ' ')
570         features++;
571       feature = features;
572     } else {
573       escape = FALSE;
574       features++;
575     }
576   }
577
578   return ret;
579 }
580
581 /**
582  * gst_caps_features_get_size:
583  * @features: a #GstCapsFeatures.
584  *
585  * Returns the number of features in @features.
586  *
587  * Returns: The number of features in @features.
588  *
589  * Since: 1.2
590  */
591 guint
592 gst_caps_features_get_size (const GstCapsFeatures * features)
593 {
594   g_return_val_if_fail (features != NULL, 0);
595
596   return features->array->len;
597 }
598
599 /**
600  * gst_caps_features_get_nth:
601  * @features: a #GstCapsFeatures.
602  * @i: index of the feature
603  *
604  * Returns the @i-th feature of @features.
605  *
606  * Returns: (nullable): The @i-th feature of @features.
607  *
608  * Since: 1.2
609  */
610 const gchar *
611 gst_caps_features_get_nth (const GstCapsFeatures * features, guint i)
612 {
613   const gchar *feature;
614   GQuark quark;
615
616   g_return_val_if_fail (features != NULL, NULL);
617
618   quark = gst_caps_features_get_nth_id (features, i);
619   if (!quark)
620     return NULL;
621
622   feature = g_quark_to_string (quark);
623   return feature;
624 }
625
626 /**
627  * gst_caps_features_get_nth_id:
628  * @features: a #GstCapsFeatures.
629  * @i: index of the feature
630  *
631  * Returns the @i-th feature of @features.
632  *
633  * Returns: The @i-th feature of @features.
634  *
635  * Since: 1.2
636  */
637 GQuark
638 gst_caps_features_get_nth_id (const GstCapsFeatures * features, guint i)
639 {
640   GQuark *quark;
641
642   g_return_val_if_fail (features != NULL, 0);
643   g_return_val_if_fail (i < features->array->len, 0);
644
645   quark = &g_array_index (features->array, GQuark, i);
646
647   return *quark;
648 }
649
650 /**
651  * gst_caps_features_contains:
652  * @features: a #GstCapsFeatures.
653  * @feature: a feature
654  *
655  * Checks if @features contains @feature.
656  *
657  * Returns: %TRUE if @features contains @feature.
658  *
659  * Since: 1.2
660  */
661 gboolean
662 gst_caps_features_contains (const GstCapsFeatures * features,
663     const gchar * feature)
664 {
665   g_return_val_if_fail (features != NULL, FALSE);
666   g_return_val_if_fail (feature != NULL, FALSE);
667
668   return gst_caps_features_contains_id (features,
669       g_quark_from_string (feature));
670 }
671
672 /**
673  * gst_caps_features_contains_id:
674  * @features: a #GstCapsFeatures.
675  * @feature: a feature
676  *
677  * Checks if @features contains @feature.
678  *
679  * Returns: %TRUE if @features contains @feature.
680  *
681  * Since: 1.2
682  */
683 gboolean
684 gst_caps_features_contains_id (const GstCapsFeatures * features, GQuark feature)
685 {
686   guint i, n;
687
688   g_return_val_if_fail (features != NULL, FALSE);
689   g_return_val_if_fail (feature != 0, FALSE);
690
691   if (features->is_any)
692     return TRUE;
693
694   n = features->array->len;
695   if (n == 0)
696     return feature == _gst_caps_feature_memory_system_memory;
697
698   for (i = 0; i < n; i++) {
699     if (gst_caps_features_get_nth_id (features, i) == feature)
700       return TRUE;
701   }
702
703   return FALSE;
704 }
705
706 /**
707  * gst_caps_features_is_equal:
708  * @features1: a #GstCapsFeatures.
709  * @features2: a #GstCapsFeatures.
710  *
711  * Checks if @features1 and @features2 are equal.
712  *
713  * Returns: %TRUE if @features1 and @features2 are equal.
714  *
715  * Since: 1.2
716  */
717 gboolean
718 gst_caps_features_is_equal (const GstCapsFeatures * features1,
719     const GstCapsFeatures * features2)
720 {
721   guint i, n;
722
723   g_return_val_if_fail (features1 != NULL, FALSE);
724   g_return_val_if_fail (features2 != NULL, FALSE);
725
726   if (features1->is_any || features2->is_any)
727     return TRUE;
728
729   /* Check for the sysmem==empty case */
730   if (features1->array->len == 0 && features2->array->len == 0)
731     return TRUE;
732   if (features1->array->len == 0 && features2->array->len == 1
733       && gst_caps_features_contains_id (features2,
734           _gst_caps_feature_memory_system_memory))
735     return TRUE;
736   if (features2->array->len == 0 && features1->array->len == 1
737       && gst_caps_features_contains_id (features1,
738           _gst_caps_feature_memory_system_memory))
739     return TRUE;
740
741   if (features1->array->len != features2->array->len)
742     return FALSE;
743
744   n = features1->array->len;
745   for (i = 0; i < n; i++)
746     if (!gst_caps_features_contains_id (features2,
747             gst_caps_features_get_nth_id (features1, i)))
748       return FALSE;
749
750   return TRUE;
751 }
752
753 /**
754  * gst_caps_features_is_any:
755  * @features: a #GstCapsFeatures.
756  *
757  * Checks if @features is %GST_CAPS_FEATURES_ANY.
758  *
759  * Returns: %TRUE if @features is %GST_CAPS_FEATURES_ANY.
760  *
761  * Since: 1.2
762  */
763 gboolean
764 gst_caps_features_is_any (const GstCapsFeatures * features)
765 {
766   g_return_val_if_fail (features != NULL, FALSE);
767
768   return features->is_any;
769 }
770
771 /**
772  * gst_caps_features_add:
773  * @features: a #GstCapsFeatures.
774  * @feature: a feature.
775  *
776  * Adds @feature to @features.
777  *
778  * Since: 1.2
779  */
780 void
781 gst_caps_features_add (GstCapsFeatures * features, const gchar * feature)
782 {
783   g_return_if_fail (features != NULL);
784   g_return_if_fail (IS_MUTABLE (features));
785   g_return_if_fail (feature != NULL);
786   g_return_if_fail (!features->is_any);
787
788   gst_caps_features_add_id (features, g_quark_from_string (feature));
789 }
790
791 /**
792  * gst_caps_features_add_id:
793  * @features: a #GstCapsFeatures.
794  * @feature: a feature.
795  *
796  * Adds @feature to @features.
797  *
798  * Since: 1.2
799  */
800 void
801 gst_caps_features_add_id (GstCapsFeatures * features, GQuark feature)
802 {
803   g_return_if_fail (features != NULL);
804   g_return_if_fail (IS_MUTABLE (features));
805   g_return_if_fail (feature != 0);
806   g_return_if_fail (!features->is_any);
807
808   if (!gst_caps_feature_name_is_valid (g_quark_to_string (feature))) {
809     g_warning ("Invalid caps feature name: %s", g_quark_to_string (feature));
810     return;
811   }
812
813   /* If features is empty it will contain sysmem, however
814    * we want to add it explicitly if it is attempted to be
815    * added as first features
816    */
817   if (features->array->len > 0
818       && gst_caps_features_contains_id (features, feature))
819     return;
820
821   g_array_append_val (features->array, feature);
822 }
823
824 /**
825  * gst_caps_features_remove:
826  * @features: a #GstCapsFeatures.
827  * @feature: a feature.
828  *
829  * Removes @feature from @features.
830  *
831  * Since: 1.2
832  */
833 void
834 gst_caps_features_remove (GstCapsFeatures * features, const gchar * feature)
835 {
836   g_return_if_fail (features != NULL);
837   g_return_if_fail (IS_MUTABLE (features));
838   g_return_if_fail (feature != NULL);
839
840   gst_caps_features_remove_id (features, g_quark_from_string (feature));
841 }
842
843 /**
844  * gst_caps_features_remove_id:
845  * @features: a #GstCapsFeatures.
846  * @feature: a feature.
847  *
848  * Removes @feature from @features.
849  *
850  * Since: 1.2
851  */
852 void
853 gst_caps_features_remove_id (GstCapsFeatures * features, GQuark feature)
854 {
855   guint i, n;
856
857   g_return_if_fail (features != NULL);
858   g_return_if_fail (IS_MUTABLE (features));
859   g_return_if_fail (feature != 0);
860
861   n = features->array->len;
862   for (i = 0; i < n; i++) {
863     GQuark quark = gst_caps_features_get_nth_id (features, i);
864
865     if (quark == feature) {
866       g_array_remove_index_fast (features->array, i);
867       return;
868     }
869   }
870 }
871
872 static void
873 gst_caps_features_transform_to_string (const GValue * src_value,
874     GValue * dest_value)
875 {
876   g_return_if_fail (src_value != NULL);
877   g_return_if_fail (dest_value != NULL);
878
879   dest_value->data[0].v_pointer =
880       gst_caps_features_to_string (src_value->data[0].v_pointer);
881 }