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