2 * Copyright (C) 2013 Collabora Ltd.
3 * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
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.
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.
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.
22 * SECTION:gstcapsfeatures
23 * @short_description: A set of features in caps
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.
32 * Empty #GstCapsFeatures are equivalent with the #GstCapsFeatures that only
33 * contain #GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY.
35 * Examples for caps features would be the requirement of a specific #GstMemory
36 * types or the requirement of having a specific #GstMeta on the buffer. Features
37 * are given as a string of the format "memory:GstMemoryTypeName" or
38 * "meta:GstMetaAPIName".
46 #include "gst_private.h"
47 #include "gstcapsfeatures.h"
50 GST_DEBUG_CATEGORY_STATIC (gst_caps_features_debug);
51 #define GST_CAT_DEFAULT gst_caps_features_debug
53 struct _GstCapsFeatures
56 gint *parent_refcount;
60 GType _gst_caps_features_type = 0;
61 GstCapsFeatures *_gst_caps_features_memory_system_memory = NULL;
62 static GQuark _gst_caps_feature_memory_system_memory = 0;
64 G_DEFINE_BOXED_TYPE (GstCapsFeatures, gst_caps_features,
65 gst_caps_features_copy, gst_caps_features_free);
67 #define IS_MUTABLE(features) \
68 (!features->parent_refcount || \
69 g_atomic_int_get (features->parent_refcount) == 1)
72 gst_caps_features_transform_to_string (const GValue * src_value,
76 _priv_gst_caps_features_initialize (void)
78 _gst_caps_features_type = gst_caps_features_get_type ();
79 _gst_caps_feature_memory_system_memory =
80 g_quark_from_static_string (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
82 g_value_register_transform_func (_gst_caps_features_type, G_TYPE_STRING,
83 gst_caps_features_transform_to_string);
85 _gst_caps_features_memory_system_memory =
86 gst_caps_features_new_id (_gst_caps_feature_memory_system_memory, 0);
88 GST_DEBUG_CATEGORY_INIT (gst_caps_features_debug, "caps-features", 0,
89 "GstCapsFeatures debug");
93 gst_is_caps_features (gconstpointer obj)
95 const GstCapsFeatures *features = obj;
97 return (obj != NULL && features->type == _gst_caps_features_type);
101 gst_caps_feature_name_is_valid (const gchar * feature)
103 #ifndef G_DISABLE_CHECKS
105 if (g_ascii_isalpha (*feature))
107 else if (*feature == ':')
117 if (*feature == '\0' || !g_ascii_isalpha (*feature))
121 if (g_ascii_isalnum (*feature))
123 else if (*feature == '\0')
134 * gst_caps_features_new_empty:
136 * Creates a new, empty #GstCapsFeatures.
138 * Free-function: gst_caps_features_free
140 * Returns: (transfer full): a new, empty #GstCapsFeatures
143 gst_caps_features_new_empty (void)
145 GstCapsFeatures *features;
147 features = g_slice_new (GstCapsFeatures);
148 features->type = _gst_caps_features_type;
149 features->parent_refcount = NULL;
150 features->array = g_array_new (FALSE, FALSE, sizeof (GQuark));
152 GST_TRACE ("created caps features %p", features);
158 * gst_caps_features_new:
159 * @feature1: name of first feature to set
160 * @...: additional features
162 * Creates a new #GstCapsFeatures with the given features.
163 * The last argument must be NULL.
165 * Free-function: gst_caps_features_free
167 * Returns: (transfer full): a new, empty #GstCapsFeatures
170 gst_caps_features_new (const gchar * feature1, ...)
172 GstCapsFeatures *features;
175 g_return_val_if_fail (feature1 != NULL, NULL);
177 va_start (varargs, feature1);
178 features = gst_caps_features_new_valist (feature1, varargs);
185 * gst_caps_features_new_valist:
186 * @feature1: name of first feature to set
187 * @varargs: variable argument list
189 * Creates a new #GstCapsFeatures with the given features.
191 * Free-function: gst_caps_features_free
193 * Returns: (transfer full): a new, empty #GstCapsFeatures
196 gst_caps_features_new_valist (const gchar * feature1, va_list varargs)
198 GstCapsFeatures *features;
200 g_return_val_if_fail (feature1 != NULL, NULL);
202 features = gst_caps_features_new_empty ();
205 gst_caps_features_add (features, feature1);
206 feature1 = va_arg (varargs, const gchar *);
213 * gst_caps_features_new_id:
214 * @feature1: name of first feature to set
215 * @...: additional features
217 * Creates a new #GstCapsFeatures with the given features.
218 * The last argument must be 0.
220 * Free-function: gst_caps_features_free
222 * Returns: (transfer full): a new, empty #GstCapsFeatures
225 gst_caps_features_new_id (GQuark feature1, ...)
227 GstCapsFeatures *features;
230 g_return_val_if_fail (feature1 != 0, NULL);
232 va_start (varargs, feature1);
233 features = gst_caps_features_new_id_valist (feature1, varargs);
240 * gst_caps_features_new_id_valist:
241 * @feature1: name of first feature to set
242 * @varargs: variable argument list
244 * Creates a new #GstCapsFeatures with the given features.
246 * Free-function: gst_caps_features_free
248 * Returns: (transfer full): a new, empty #GstCapsFeatures
251 gst_caps_features_new_id_valist (GQuark feature1, va_list varargs)
253 GstCapsFeatures *features;
255 g_return_val_if_fail (feature1 != 0, NULL);
257 features = gst_caps_features_new_empty ();
260 gst_caps_features_add_id (features, feature1);
261 feature1 = va_arg (varargs, GQuark);
268 * gst_caps_features_set_parent_refcount:
269 * @features: a #GstCapsFeatures
270 * @refcount: (in): a pointer to the parent's refcount
272 * Sets the parent_refcount field of #GstCapsFeatures. This field is used to
273 * determine whether a caps features is mutable or not. This function should only be
274 * called by code implementing parent objects of #GstCapsFeatures, as described in
275 * the MT Refcounting section of the design documents.
277 * Returns: %TRUE if the parent refcount could be set.
280 gst_caps_features_set_parent_refcount (GstCapsFeatures * features,
283 g_return_val_if_fail (features != NULL, FALSE);
285 /* if we have a parent_refcount already, we can only clear
286 * if with a NULL refcount */
287 if (features->parent_refcount) {
288 if (refcount != NULL) {
289 g_return_val_if_fail (refcount == NULL, FALSE);
293 if (refcount == NULL) {
294 g_return_val_if_fail (refcount != NULL, FALSE);
299 features->parent_refcount = refcount;
305 * gst_caps_features_copy:
306 * @features: a #GstCapsFeatures to duplicate
308 * Duplicates a #GstCapsFeatures and all its values.
310 * Free-function: gst_caps_features_free
312 * Returns: (transfer full): a new #GstCapsFeatures.
315 gst_caps_features_copy (const GstCapsFeatures * features)
317 GstCapsFeatures *copy;
320 g_return_val_if_fail (features != NULL, NULL);
322 copy = gst_caps_features_new_empty ();
323 n = gst_caps_features_get_size (features);
324 for (i = 0; i < n; i++)
325 gst_caps_features_add_id (copy, gst_caps_features_get_nth_id (features, i));
331 * gst_caps_features_free:
332 * @features: (in) (transfer full): the #GstCapsFeatures to free
334 * Frees a #GstCapsFeatures and all its values. The caps features must not
335 * have a parent when this function is called.
338 gst_caps_features_free (GstCapsFeatures * features)
340 g_return_if_fail (features != NULL);
341 g_return_if_fail (features->parent_refcount == NULL);
343 g_array_free (features->array, TRUE);
345 memset (features, 0xff, sizeof (GstCapsFeatures));
347 GST_TRACE ("free caps features %p", features);
349 g_slice_free (GstCapsFeatures, features);
353 * gst_caps_features_to_string:
354 * @features: a #GstCapsFeatures
356 * Converts @features to a human-readable string representation.
358 * For debugging purposes its easier to do something like this:
360 * GST_LOG ("features is %" GST_PTR_FORMAT, features);
362 * This prints the features in human readble form.
364 * Free-function: g_free
366 * Returns: (transfer full): a pointer to string allocated by g_malloc().
367 * g_free() after usage.
370 gst_caps_features_to_string (const GstCapsFeatures * features)
374 g_return_val_if_fail (features != NULL, NULL);
376 s = g_string_sized_new (FEATURES_ESTIMATED_STRING_LEN (features));
378 priv_gst_caps_features_append_to_gstring (features, s);
380 return g_string_free (s, FALSE);
384 priv_gst_caps_features_append_to_gstring (const GstCapsFeatures * features,
389 g_return_if_fail (features != NULL);
391 n = features->array->len;
392 for (i = 0; i < n; i++) {
393 GQuark *quark = &g_array_index (features->array, GQuark, i);
395 g_string_append (s, g_quark_to_string (*quark));
397 g_string_append (s, ", ");
402 * gst_caps_features_from_string:
403 * @features: a string representation of a #GstCapsFeatures.
405 * Creates a #GstCapsFeatures from a string representation.
407 * Free-function: gst_caps_features_free
409 * Returns: (transfer full): a new #GstCapsFeatures or NULL when the string could
410 * not be parsed. Free with gst_caps_features_free() after use.
413 gst_caps_features_from_string (const gchar * features)
415 GstCapsFeatures *ret;
416 gboolean escape = FALSE;
417 const gchar *features_orig = features;
418 const gchar *feature;
420 ret = gst_caps_features_new_empty ();
422 if (!features || *features == '\0')
425 /* Skip trailing spaces */
426 while (*features == ' ')
437 } else if ((!escape && c == ',') || c == '\0') {
438 guint len = features - feature + 1;
443 g_warning ("Failed deserialize caps features '%s'", features_orig);
444 gst_caps_features_free (ret);
448 tmp = g_malloc (len);
449 memcpy (tmp, feature, len - 1);
458 if (strstr (tmp, " ") != NULL || *tmp == '\0') {
460 g_warning ("Failed deserialize caps features '%s'", features_orig);
461 gst_caps_features_free (ret);
465 gst_caps_features_add (ret, tmp);
471 /* Skip to the next value */
473 while (*features == ' ')
486 * gst_caps_features_get_size:
487 * @features: a #GstCapsFeatures.
489 * Returns the number of features in @features.
491 * Returns: The number of features in @features.
494 gst_caps_features_get_size (const GstCapsFeatures * features)
496 g_return_val_if_fail (features != NULL, 0);
498 return features->array->len;
502 * gst_caps_features_get_nth:
503 * @features: a #GstCapsFeatures.
504 * @i: index of the feature
506 * Returns the @i-th feature of @features.
508 * Returns: The @i-th feature of @features.
511 gst_caps_features_get_nth (const GstCapsFeatures * features, guint i)
513 const gchar *feature;
516 g_return_val_if_fail (features != NULL, NULL);
518 quark = gst_caps_features_get_nth_id (features, i);
522 feature = g_quark_to_string (quark);
527 * gst_caps_features_get_nth_id:
528 * @features: a #GstCapsFeatures.
529 * @i: index of the feature
531 * Returns the @i-th feature of @features.
533 * Returns: The @i-th feature of @features.
536 gst_caps_features_get_nth_id (const GstCapsFeatures * features, guint i)
540 g_return_val_if_fail (features != NULL, 0);
541 g_return_val_if_fail (i < features->array->len, 0);
543 quark = &g_array_index (features->array, GQuark, i);
549 * gst_caps_features_contains:
550 * @features: a #GstCapsFeatures.
551 * @feature: a feature
553 * Returns %TRUE if @features contains @feature.
555 * Returns: %TRUE if @features contains @feature.
558 gst_caps_features_contains (const GstCapsFeatures * features,
559 const gchar * feature)
561 g_return_val_if_fail (features != NULL, FALSE);
562 g_return_val_if_fail (feature != NULL, FALSE);
564 return gst_caps_features_contains_id (features,
565 g_quark_from_string (feature));
569 * gst_caps_features_contains_id:
570 * @features: a #GstCapsFeatures.
571 * @feature: a feature
573 * Returns %TRUE if @features contains @feature.
575 * Returns: %TRUE if @features contains @feature.
578 gst_caps_features_contains_id (const GstCapsFeatures * features, GQuark feature)
582 g_return_val_if_fail (features != NULL, FALSE);
583 g_return_val_if_fail (feature != 0, FALSE);
585 n = features->array->len;
587 return feature == _gst_caps_feature_memory_system_memory;
589 for (i = 0; i < n; i++) {
590 if (gst_caps_features_get_nth_id (features, i) == feature)
598 * gst_caps_features_is_equal:
599 * @features1: a #GstCapsFeatures.
600 * @features2: a #GstCapsFeatures.
602 * Returns %TRUE if @features1 and @features2 are equal.
604 * Returns: %TRUE if @features1 and @features2 are equal.
607 gst_caps_features_is_equal (const GstCapsFeatures * features1,
608 const GstCapsFeatures * features2)
612 g_return_val_if_fail (features1 != NULL, FALSE);
613 g_return_val_if_fail (features2 != NULL, FALSE);
615 /* Check for the sysmem==empty case */
616 if (features1->array->len == 0 && features2->array->len == 0)
618 if (features1->array->len == 0 && features2->array->len == 1
619 && gst_caps_features_contains_id (features2,
620 _gst_caps_feature_memory_system_memory))
622 if (features2->array->len == 0 && features1->array->len == 1
623 && gst_caps_features_contains_id (features1,
624 _gst_caps_feature_memory_system_memory))
627 if (features1->array->len != features2->array->len)
630 n = features1->array->len;
631 for (i = 0; i < n; i++)
632 if (!gst_caps_features_contains_id (features2,
633 gst_caps_features_get_nth_id (features1, i)))
640 * gst_caps_features_add:
641 * @features: a #GstCapsFeatures.
642 * @feature: a feature.
644 * Adds @feature to @features.
647 gst_caps_features_add (GstCapsFeatures * features, const gchar * feature)
649 g_return_if_fail (features != NULL);
650 g_return_if_fail (IS_MUTABLE (features));
651 g_return_if_fail (feature != NULL);
653 gst_caps_features_add_id (features, g_quark_from_string (feature));
657 * gst_caps_features_add_id:
658 * @features: a #GstCapsFeatures.
659 * @feature: a feature.
661 * Adds @feature to @features.
664 gst_caps_features_add_id (GstCapsFeatures * features, GQuark feature)
666 g_return_if_fail (features != NULL);
667 g_return_if_fail (IS_MUTABLE (features));
668 g_return_if_fail (feature != 0);
670 if (!gst_caps_feature_name_is_valid (g_quark_to_string (feature))) {
671 g_warning ("Invalid caps feature name: %s", g_quark_to_string (feature));
675 /* If features is empty it will contain sysmem, however
676 * we want to add it explicitely if it is tried to be
677 * added as first features
679 if (features->array->len > 0
680 && gst_caps_features_contains_id (features, feature))
683 g_array_append_val (features->array, feature);
687 * gst_caps_features_remove:
688 * @features: a #GstCapsFeatures.
689 * @feature: a feature.
691 * Removes @feature from @features.
694 gst_caps_features_remove (GstCapsFeatures * features, const gchar * feature)
696 g_return_if_fail (features != NULL);
697 g_return_if_fail (IS_MUTABLE (features));
698 g_return_if_fail (feature != NULL);
700 gst_caps_features_remove_id (features, g_quark_from_string (feature));
704 * gst_caps_features_remove_id:
705 * @features: a #GstCapsFeatures.
706 * @feature: a feature.
708 * Removes @feature from @features.
711 gst_caps_features_remove_id (GstCapsFeatures * features, GQuark feature)
715 g_return_if_fail (features != NULL);
716 g_return_if_fail (IS_MUTABLE (features));
717 g_return_if_fail (feature != 0);
719 n = features->array->len;
720 for (i = 0; i < n; i++) {
721 GQuark quark = gst_caps_features_get_nth_id (features, i);
723 if (quark == feature) {
724 g_array_remove_index_fast (features->array, i);
731 gst_caps_features_transform_to_string (const GValue * src_value,
734 g_return_if_fail (src_value != NULL);
735 g_return_if_fail (dest_value != NULL);
737 dest_value->data[0].v_pointer =
738 gst_caps_features_to_string (src_value->data[0].v_pointer);