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