| /* GStreamer |
| * Copyright (C) 2013 Collabora Ltd. |
| * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| /** |
| * SECTION:gstcapsfeatures |
| * @title: GstCapsFeatures |
| * @short_description: A set of features in caps |
| * @see_also: #GstCaps |
| * |
| * #GstCapsFeatures can optionally be set on a #GstCaps to add requirements |
| * for additional features for a specific #GstStructure. Caps structures with |
| * the same name but with a non-equal set of caps features are not compatible. |
| * If a pad supports multiple sets of features it has to add multiple equal |
| * structures with different feature sets to the caps. |
| * |
| * Empty #GstCapsFeatures are equivalent with the #GstCapsFeatures that only |
| * contain #GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY. ANY #GstCapsFeatures as |
| * created by gst_caps_features_new_any() are equal to any other #GstCapsFeatures |
| * and can be used to specify that any #GstCapsFeatures would be supported, e.g. |
| * for elements that don't touch buffer memory. #GstCaps with ANY #GstCapsFeatures |
| * are considered non-fixed and during negotiation some #GstCapsFeatures have |
| * to be selected. |
| * |
| * Examples for caps features would be the requirement of a specific #GstMemory |
| * types or the requirement of having a specific #GstMeta on the buffer. Features |
| * are given as a string of the format "memory:GstMemoryTypeName" or |
| * "meta:GstMetaAPIName". |
| * |
| * Since: 1.2 |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <string.h> |
| #include "gst_private.h" |
| #include "gstcapsfeatures.h" |
| #include <gst/gst.h> |
| |
| GST_DEBUG_CATEGORY_STATIC (gst_caps_features_debug); |
| #define GST_CAT_DEFAULT gst_caps_features_debug |
| |
| struct _GstCapsFeatures |
| { |
| GType type; |
| gint *parent_refcount; |
| GArray *array; |
| gboolean is_any; |
| }; |
| |
| GType _gst_caps_features_type = 0; |
| static gint static_caps_features_parent_refcount = G_MAXINT; |
| GstCapsFeatures *_gst_caps_features_any = NULL; |
| GstCapsFeatures *_gst_caps_features_memory_system_memory = NULL; |
| static GQuark _gst_caps_feature_memory_system_memory = 0; |
| |
| G_DEFINE_BOXED_TYPE (GstCapsFeatures, gst_caps_features, |
| gst_caps_features_copy, gst_caps_features_free); |
| |
| #define IS_MUTABLE(features) \ |
| (!features->parent_refcount || \ |
| g_atomic_int_get (features->parent_refcount) == 1) |
| |
| static void |
| gst_caps_features_transform_to_string (const GValue * src_value, |
| GValue * dest_value); |
| |
| void |
| _priv_gst_caps_features_initialize (void) |
| { |
| GST_DEBUG_CATEGORY_INIT (gst_caps_features_debug, "caps-features", 0, |
| "GstCapsFeatures debug"); |
| |
| _gst_caps_features_type = gst_caps_features_get_type (); |
| _gst_caps_feature_memory_system_memory = |
| g_quark_from_static_string (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY); |
| |
| g_value_register_transform_func (_gst_caps_features_type, G_TYPE_STRING, |
| gst_caps_features_transform_to_string); |
| |
| _gst_caps_features_any = gst_caps_features_new_any (); |
| gst_caps_features_set_parent_refcount (_gst_caps_features_any, |
| &static_caps_features_parent_refcount); |
| _gst_caps_features_memory_system_memory = |
| gst_caps_features_new_id (_gst_caps_feature_memory_system_memory, 0); |
| gst_caps_features_set_parent_refcount |
| (_gst_caps_features_memory_system_memory, |
| &static_caps_features_parent_refcount); |
| } |
| |
| void |
| _priv_gst_caps_features_cleanup (void) |
| { |
| gst_caps_features_set_parent_refcount (_gst_caps_features_any, NULL); |
| gst_caps_features_free (_gst_caps_features_any); |
| _gst_caps_features_any = NULL; |
| gst_caps_features_set_parent_refcount |
| (_gst_caps_features_memory_system_memory, NULL); |
| gst_caps_features_free (_gst_caps_features_memory_system_memory); |
| _gst_caps_features_memory_system_memory = NULL; |
| } |
| |
| gboolean |
| gst_is_caps_features (gconstpointer obj) |
| { |
| const GstCapsFeatures *features = obj; |
| |
| return (obj != NULL && features->type == _gst_caps_features_type); |
| } |
| |
| static gboolean |
| gst_caps_feature_name_is_valid (const gchar * feature) |
| { |
| #ifndef G_DISABLE_CHECKS |
| while (TRUE) { |
| if (g_ascii_isalpha (*feature)) |
| feature++; |
| else if (*feature == ':') |
| break; |
| else |
| return FALSE; |
| } |
| |
| if (*feature != ':') |
| return FALSE; |
| |
| feature++; |
| if (*feature == '\0' || !g_ascii_isalpha (*feature)) |
| return FALSE; |
| |
| while (TRUE) { |
| if (g_ascii_isalnum (*feature)) |
| feature++; |
| else if (*feature == '\0') |
| break; |
| else |
| return FALSE; |
| } |
| #endif |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_caps_features_new_empty: |
| * |
| * Creates a new, empty #GstCapsFeatures. |
| * |
| * Free-function: gst_caps_features_free |
| * |
| * Returns: (transfer full): a new, empty #GstCapsFeatures |
| * |
| * Since: 1.2 |
| */ |
| GstCapsFeatures * |
| gst_caps_features_new_empty (void) |
| { |
| GstCapsFeatures *features; |
| |
| features = g_slice_new (GstCapsFeatures); |
| features->type = _gst_caps_features_type; |
| features->parent_refcount = NULL; |
| features->array = g_array_new (FALSE, FALSE, sizeof (GQuark)); |
| features->is_any = FALSE; |
| |
| GST_TRACE ("created caps features %p", features); |
| |
| return features; |
| } |
| |
| /** |
| * gst_caps_features_new_any: |
| * |
| * Creates a new, ANY #GstCapsFeatures. This will be equal |
| * to any other #GstCapsFeatures but caps with these are |
| * unfixed. |
| * |
| * Free-function: gst_caps_features_free |
| * |
| * Returns: (transfer full): a new, ANY #GstCapsFeatures |
| * |
| * Since: 1.2 |
| */ |
| GstCapsFeatures * |
| gst_caps_features_new_any (void) |
| { |
| GstCapsFeatures *features; |
| |
| features = gst_caps_features_new_empty (); |
| features->is_any = TRUE; |
| |
| return features; |
| } |
| |
| /** |
| * gst_caps_features_new: |
| * @feature1: name of first feature to set |
| * @...: additional features |
| * |
| * Creates a new #GstCapsFeatures with the given features. |
| * The last argument must be %NULL. |
| * |
| * Free-function: gst_caps_features_free |
| * |
| * Returns: (transfer full): a new, empty #GstCapsFeatures |
| * |
| * Since: 1.2 |
| */ |
| GstCapsFeatures * |
| gst_caps_features_new (const gchar * feature1, ...) |
| { |
| GstCapsFeatures *features; |
| va_list varargs; |
| |
| g_return_val_if_fail (feature1 != NULL, NULL); |
| |
| va_start (varargs, feature1); |
| features = gst_caps_features_new_valist (feature1, varargs); |
| va_end (varargs); |
| |
| return features; |
| } |
| |
| /** |
| * gst_caps_features_new_valist: |
| * @feature1: name of first feature to set |
| * @varargs: variable argument list |
| * |
| * Creates a new #GstCapsFeatures with the given features. |
| * |
| * Free-function: gst_caps_features_free |
| * |
| * Returns: (transfer full): a new, empty #GstCapsFeatures |
| * |
| * Since: 1.2 |
| */ |
| GstCapsFeatures * |
| gst_caps_features_new_valist (const gchar * feature1, va_list varargs) |
| { |
| GstCapsFeatures *features; |
| |
| g_return_val_if_fail (feature1 != NULL, NULL); |
| |
| features = gst_caps_features_new_empty (); |
| |
| while (feature1) { |
| gst_caps_features_add (features, feature1); |
| feature1 = va_arg (varargs, const gchar *); |
| } |
| |
| return features; |
| } |
| |
| /** |
| * gst_caps_features_new_id: |
| * @feature1: name of first feature to set |
| * @...: additional features |
| * |
| * Creates a new #GstCapsFeatures with the given features. |
| * The last argument must be 0. |
| * |
| * Free-function: gst_caps_features_free |
| * |
| * Returns: (transfer full): a new, empty #GstCapsFeatures |
| * |
| * Since: 1.2 |
| */ |
| GstCapsFeatures * |
| gst_caps_features_new_id (GQuark feature1, ...) |
| { |
| GstCapsFeatures *features; |
| va_list varargs; |
| |
| g_return_val_if_fail (feature1 != 0, NULL); |
| |
| va_start (varargs, feature1); |
| features = gst_caps_features_new_id_valist (feature1, varargs); |
| va_end (varargs); |
| |
| return features; |
| } |
| |
| /** |
| * gst_caps_features_new_id_valist: |
| * @feature1: name of first feature to set |
| * @varargs: variable argument list |
| * |
| * Creates a new #GstCapsFeatures with the given features. |
| * |
| * Free-function: gst_caps_features_free |
| * |
| * Returns: (transfer full): a new, empty #GstCapsFeatures |
| * |
| * Since: 1.2 |
| */ |
| GstCapsFeatures * |
| gst_caps_features_new_id_valist (GQuark feature1, va_list varargs) |
| { |
| GstCapsFeatures *features; |
| |
| g_return_val_if_fail (feature1 != 0, NULL); |
| |
| features = gst_caps_features_new_empty (); |
| |
| while (feature1) { |
| gst_caps_features_add_id (features, feature1); |
| feature1 = va_arg (varargs, GQuark); |
| } |
| |
| return features; |
| } |
| |
| /** |
| * gst_caps_features_set_parent_refcount: |
| * @features: a #GstCapsFeatures |
| * @refcount: (in): a pointer to the parent's refcount |
| * |
| * Sets the parent_refcount field of #GstCapsFeatures. This field is used to |
| * determine whether a caps features is mutable or not. This function should only be |
| * called by code implementing parent objects of #GstCapsFeatures, as described in |
| * the MT Refcounting section of the design documents. |
| * |
| * Returns: %TRUE if the parent refcount could be set. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_caps_features_set_parent_refcount (GstCapsFeatures * features, |
| gint * refcount) |
| { |
| g_return_val_if_fail (features != NULL, FALSE); |
| |
| /* if we have a parent_refcount already, we can only clear |
| * if with a NULL refcount */ |
| if (features->parent_refcount) { |
| if (refcount != NULL) { |
| g_return_val_if_fail (refcount == NULL, FALSE); |
| return FALSE; |
| } |
| } else { |
| if (refcount == NULL) { |
| g_return_val_if_fail (refcount != NULL, FALSE); |
| return FALSE; |
| } |
| } |
| |
| features->parent_refcount = refcount; |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_caps_features_copy: |
| * @features: a #GstCapsFeatures to duplicate |
| * |
| * Duplicates a #GstCapsFeatures and all its values. |
| * |
| * Free-function: gst_caps_features_free |
| * |
| * Returns: (transfer full): a new #GstCapsFeatures. |
| * |
| * Since: 1.2 |
| */ |
| GstCapsFeatures * |
| gst_caps_features_copy (const GstCapsFeatures * features) |
| { |
| GstCapsFeatures *copy; |
| guint i, n; |
| |
| g_return_val_if_fail (features != NULL, NULL); |
| |
| copy = gst_caps_features_new_empty (); |
| n = gst_caps_features_get_size (features); |
| for (i = 0; i < n; i++) |
| gst_caps_features_add_id (copy, gst_caps_features_get_nth_id (features, i)); |
| copy->is_any = features->is_any; |
| |
| return copy; |
| } |
| |
| /** |
| * gst_caps_features_free: |
| * @features: (in) (transfer full): the #GstCapsFeatures to free |
| * |
| * Frees a #GstCapsFeatures and all its values. The caps features must not |
| * have a parent when this function is called. |
| * |
| * Since: 1.2 |
| */ |
| void |
| gst_caps_features_free (GstCapsFeatures * features) |
| { |
| g_return_if_fail (features != NULL); |
| g_return_if_fail (features->parent_refcount == NULL); |
| |
| g_array_free (features->array, TRUE); |
| #ifdef USE_POISONING |
| memset (features, 0xff, sizeof (GstCapsFeatures)); |
| #endif |
| GST_TRACE ("free caps features %p", features); |
| |
| g_slice_free (GstCapsFeatures, features); |
| } |
| |
| /** |
| * gst_caps_features_to_string: |
| * @features: a #GstCapsFeatures |
| * |
| * Converts @features to a human-readable string representation. |
| * |
| * For debugging purposes its easier to do something like this: |
| * |[<!-- language="C" --> |
| * GST_LOG ("features is %" GST_PTR_FORMAT, features); |
| * ]| |
| * This prints the features in human readable form. |
| * |
| * Free-function: g_free |
| * |
| * Returns: (transfer full): a pointer to string allocated by g_malloc(). |
| * g_free() after usage. |
| * |
| * Since: 1.2 |
| */ |
| gchar * |
| gst_caps_features_to_string (const GstCapsFeatures * features) |
| { |
| GString *s; |
| |
| g_return_val_if_fail (features != NULL, NULL); |
| |
| s = g_string_sized_new (FEATURES_ESTIMATED_STRING_LEN (features)); |
| |
| priv_gst_caps_features_append_to_gstring (features, s); |
| |
| return g_string_free (s, FALSE); |
| } |
| |
| void |
| priv_gst_caps_features_append_to_gstring (const GstCapsFeatures * features, |
| GString * s) |
| { |
| guint i, n; |
| |
| g_return_if_fail (features != NULL); |
| |
| if (features->array->len == 0 && features->is_any) { |
| g_string_append (s, "ANY"); |
| return; |
| } |
| |
| n = features->array->len; |
| for (i = 0; i < n; i++) { |
| GQuark *quark = &g_array_index (features->array, GQuark, i); |
| |
| g_string_append (s, g_quark_to_string (*quark)); |
| if (i + 1 < n) |
| g_string_append (s, ", "); |
| } |
| } |
| |
| /** |
| * gst_caps_features_from_string: |
| * @features: a string representation of a #GstCapsFeatures. |
| * |
| * Creates a #GstCapsFeatures from a string representation. |
| * |
| * Free-function: gst_caps_features_free |
| * |
| * Returns: (transfer full) (nullable): a new #GstCapsFeatures or |
| * %NULL when the string could not be parsed. Free with |
| * gst_caps_features_free() after use. |
| * |
| * Since: 1.2 |
| */ |
| GstCapsFeatures * |
| gst_caps_features_from_string (const gchar * features) |
| { |
| GstCapsFeatures *ret; |
| gboolean escape = FALSE; |
| const gchar *features_orig = features; |
| const gchar *feature; |
| |
| ret = gst_caps_features_new_empty (); |
| |
| if (!features || *features == '\0') |
| return ret; |
| |
| if (strcmp (features, "ANY") == 0) { |
| ret->is_any = TRUE; |
| return ret; |
| } |
| |
| /* Skip trailing spaces */ |
| while (*features == ' ') |
| features++; |
| |
| feature = features; |
| while (TRUE) { |
| gchar c = *features; |
| |
| if (c == '\\') { |
| escape = TRUE; |
| features++; |
| continue; |
| } else if ((!escape && c == ',') || c == '\0') { |
| guint len = features - feature + 1; |
| gchar *tmp; |
| gchar *p; |
| |
| if (len == 1) { |
| g_warning ("Failed deserialize caps features '%s'", features_orig); |
| gst_caps_features_free (ret); |
| return NULL; |
| } |
| |
| tmp = g_malloc (len); |
| memcpy (tmp, feature, len - 1); |
| tmp[len - 1] = '\0'; |
| |
| p = tmp + len - 1; |
| while (*p == ' ') { |
| *p = '\0'; |
| p--; |
| } |
| |
| if (strstr (tmp, " ") != NULL || *tmp == '\0') { |
| g_free (tmp); |
| g_warning ("Failed deserialize caps features '%s'", features_orig); |
| gst_caps_features_free (ret); |
| return NULL; |
| } |
| |
| gst_caps_features_add (ret, tmp); |
| g_free (tmp); |
| |
| if (c == '\0') |
| break; |
| |
| /* Skip to the next value */ |
| features++; |
| while (*features == ' ') |
| features++; |
| feature = features; |
| } else { |
| escape = FALSE; |
| features++; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * gst_caps_features_get_size: |
| * @features: a #GstCapsFeatures. |
| * |
| * Returns the number of features in @features. |
| * |
| * Returns: The number of features in @features. |
| * |
| * Since: 1.2 |
| */ |
| guint |
| gst_caps_features_get_size (const GstCapsFeatures * features) |
| { |
| g_return_val_if_fail (features != NULL, 0); |
| |
| return features->array->len; |
| } |
| |
| /** |
| * gst_caps_features_get_nth: |
| * @features: a #GstCapsFeatures. |
| * @i: index of the feature |
| * |
| * Returns the @i-th feature of @features. |
| * |
| * Returns: (nullable): The @i-th feature of @features. |
| * |
| * Since: 1.2 |
| */ |
| const gchar * |
| gst_caps_features_get_nth (const GstCapsFeatures * features, guint i) |
| { |
| const gchar *feature; |
| GQuark quark; |
| |
| g_return_val_if_fail (features != NULL, NULL); |
| |
| quark = gst_caps_features_get_nth_id (features, i); |
| if (!quark) |
| return NULL; |
| |
| feature = g_quark_to_string (quark); |
| return feature; |
| } |
| |
| /** |
| * gst_caps_features_get_nth_id: |
| * @features: a #GstCapsFeatures. |
| * @i: index of the feature |
| * |
| * Returns the @i-th feature of @features. |
| * |
| * Returns: The @i-th feature of @features. |
| * |
| * Since: 1.2 |
| */ |
| GQuark |
| gst_caps_features_get_nth_id (const GstCapsFeatures * features, guint i) |
| { |
| GQuark *quark; |
| |
| g_return_val_if_fail (features != NULL, 0); |
| g_return_val_if_fail (i < features->array->len, 0); |
| |
| quark = &g_array_index (features->array, GQuark, i); |
| |
| return *quark; |
| } |
| |
| /** |
| * gst_caps_features_contains: |
| * @features: a #GstCapsFeatures. |
| * @feature: a feature |
| * |
| * Check if @features contains @feature. |
| * |
| * Returns: %TRUE if @features contains @feature. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_caps_features_contains (const GstCapsFeatures * features, |
| const gchar * feature) |
| { |
| g_return_val_if_fail (features != NULL, FALSE); |
| g_return_val_if_fail (feature != NULL, FALSE); |
| |
| return gst_caps_features_contains_id (features, |
| g_quark_from_string (feature)); |
| } |
| |
| /** |
| * gst_caps_features_contains_id: |
| * @features: a #GstCapsFeatures. |
| * @feature: a feature |
| * |
| * Check if @features contains @feature. |
| * |
| * Returns: %TRUE if @features contains @feature. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_caps_features_contains_id (const GstCapsFeatures * features, GQuark feature) |
| { |
| guint i, n; |
| |
| g_return_val_if_fail (features != NULL, FALSE); |
| g_return_val_if_fail (feature != 0, FALSE); |
| |
| if (features->is_any) |
| return TRUE; |
| |
| n = features->array->len; |
| if (n == 0) |
| return feature == _gst_caps_feature_memory_system_memory; |
| |
| for (i = 0; i < n; i++) { |
| if (gst_caps_features_get_nth_id (features, i) == feature) |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| /** |
| * gst_caps_features_is_equal: |
| * @features1: a #GstCapsFeatures. |
| * @features2: a #GstCapsFeatures. |
| * |
| * Check if @features1 and @features2 are equal. |
| * |
| * Returns: %TRUE if @features1 and @features2 are equal. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_caps_features_is_equal (const GstCapsFeatures * features1, |
| const GstCapsFeatures * features2) |
| { |
| guint i, n; |
| |
| g_return_val_if_fail (features1 != NULL, FALSE); |
| g_return_val_if_fail (features2 != NULL, FALSE); |
| |
| if (features1->is_any || features2->is_any) |
| return TRUE; |
| |
| /* Check for the sysmem==empty case */ |
| if (features1->array->len == 0 && features2->array->len == 0) |
| return TRUE; |
| if (features1->array->len == 0 && features2->array->len == 1 |
| && gst_caps_features_contains_id (features2, |
| _gst_caps_feature_memory_system_memory)) |
| return TRUE; |
| if (features2->array->len == 0 && features1->array->len == 1 |
| && gst_caps_features_contains_id (features1, |
| _gst_caps_feature_memory_system_memory)) |
| return TRUE; |
| |
| if (features1->array->len != features2->array->len) |
| return FALSE; |
| |
| n = features1->array->len; |
| for (i = 0; i < n; i++) |
| if (!gst_caps_features_contains_id (features2, |
| gst_caps_features_get_nth_id (features1, i))) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_caps_features_is_any: |
| * @features: a #GstCapsFeatures. |
| * |
| * Check if @features is %GST_CAPS_FEATURES_ANY. |
| * |
| * Returns: %TRUE if @features is %GST_CAPS_FEATURES_ANY. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_caps_features_is_any (const GstCapsFeatures * features) |
| { |
| g_return_val_if_fail (features != NULL, FALSE); |
| |
| return features->is_any; |
| } |
| |
| /** |
| * gst_caps_features_add: |
| * @features: a #GstCapsFeatures. |
| * @feature: a feature. |
| * |
| * Adds @feature to @features. |
| * |
| * Since: 1.2 |
| */ |
| void |
| gst_caps_features_add (GstCapsFeatures * features, const gchar * feature) |
| { |
| g_return_if_fail (features != NULL); |
| g_return_if_fail (IS_MUTABLE (features)); |
| g_return_if_fail (feature != NULL); |
| g_return_if_fail (!features->is_any); |
| |
| gst_caps_features_add_id (features, g_quark_from_string (feature)); |
| } |
| |
| /** |
| * gst_caps_features_add_id: |
| * @features: a #GstCapsFeatures. |
| * @feature: a feature. |
| * |
| * Adds @feature to @features. |
| * |
| * Since: 1.2 |
| */ |
| void |
| gst_caps_features_add_id (GstCapsFeatures * features, GQuark feature) |
| { |
| g_return_if_fail (features != NULL); |
| g_return_if_fail (IS_MUTABLE (features)); |
| g_return_if_fail (feature != 0); |
| g_return_if_fail (!features->is_any); |
| |
| if (!gst_caps_feature_name_is_valid (g_quark_to_string (feature))) { |
| g_warning ("Invalid caps feature name: %s", g_quark_to_string (feature)); |
| return; |
| } |
| |
| /* If features is empty it will contain sysmem, however |
| * we want to add it explicitely if it is tried to be |
| * added as first features |
| */ |
| if (features->array->len > 0 |
| && gst_caps_features_contains_id (features, feature)) |
| return; |
| |
| g_array_append_val (features->array, feature); |
| } |
| |
| /** |
| * gst_caps_features_remove: |
| * @features: a #GstCapsFeatures. |
| * @feature: a feature. |
| * |
| * Removes @feature from @features. |
| * |
| * Since: 1.2 |
| */ |
| void |
| gst_caps_features_remove (GstCapsFeatures * features, const gchar * feature) |
| { |
| g_return_if_fail (features != NULL); |
| g_return_if_fail (IS_MUTABLE (features)); |
| g_return_if_fail (feature != NULL); |
| |
| gst_caps_features_remove_id (features, g_quark_from_string (feature)); |
| } |
| |
| /** |
| * gst_caps_features_remove_id: |
| * @features: a #GstCapsFeatures. |
| * @feature: a feature. |
| * |
| * Removes @feature from @features. |
| * |
| * Since: 1.2 |
| */ |
| void |
| gst_caps_features_remove_id (GstCapsFeatures * features, GQuark feature) |
| { |
| guint i, n; |
| |
| g_return_if_fail (features != NULL); |
| g_return_if_fail (IS_MUTABLE (features)); |
| g_return_if_fail (feature != 0); |
| |
| n = features->array->len; |
| for (i = 0; i < n; i++) { |
| GQuark quark = gst_caps_features_get_nth_id (features, i); |
| |
| if (quark == feature) { |
| g_array_remove_index_fast (features->array, i); |
| return; |
| } |
| } |
| } |
| |
| static void |
| gst_caps_features_transform_to_string (const GValue * src_value, |
| GValue * dest_value) |
| { |
| g_return_if_fail (src_value != NULL); |
| g_return_if_fail (dest_value != NULL); |
| |
| dest_value->data[0].v_pointer = |
| gst_caps_features_to_string (src_value->data[0].v_pointer); |
| } |