diff options
Diffstat (limited to 'gst/gstcapsfeatures.c')
-rw-r--r-- | gst/gstcapsfeatures.c | 740 |
1 files changed, 740 insertions, 0 deletions
diff --git a/gst/gstcapsfeatures.c b/gst/gstcapsfeatures.c new file mode 100644 index 000000000..5fa6bbaba --- /dev/null +++ b/gst/gstcapsfeatures.c @@ -0,0 +1,740 @@ +/* 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 + * @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. + * + * 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". + */ + +#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; +}; + +GType _gst_caps_features_type = 0; +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_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_memory_system_memory = + gst_caps_features_new_id (_gst_caps_feature_memory_system_memory, 0); + + GST_DEBUG_CATEGORY_INIT (gst_caps_features_debug, "caps-features", 0, + "GstCapsFeatures debug"); +} + +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 + */ +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)); + + GST_TRACE ("created caps features %p", features); + + 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 + */ +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 + */ +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 + */ +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 + */ +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. + */ +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. + */ +GstCapsFeatures * +gst_caps_features_copy (const GstCapsFeatures * features) +{ + GstCapsFeatures *copy; + guint i, n; + + g_return_val_if_fail (features != NULL, NULL); + g_return_val_if_fail (features->parent_refcount == 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)); + + 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. + */ +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: + * |[ + * GST_LOG ("features is %" GST_PTR_FORMAT, features); + * ]| + * This prints the features in human readble form. + * + * Free-function: g_free + * + * Returns: (transfer full): a pointer to string allocated by g_malloc(). + * g_free() after usage. + */ +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); + + 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): a new #GstCapsFeatures or NULL when the string could + * not be parsed. Free with gst_caps_features_free() after use. + */ +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; + + /* 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. + */ +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: The @i-th feature of @features. + */ +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. + */ +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 + * + * Returns %TRUE if @features contains @feature. + * + * Returns: %TRUE if @features contains @feature. + */ +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 + * + * Returns %TRUE if @features contains @feature. + * + * Returns: %TRUE if @features contains @feature. + */ +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); + + 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. + * + * Returns %TRUE if @features1 and @features2 are equal. + * + * Returns: %TRUE if @features1 and @features2 are equal. + */ +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); + + /* 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_add: + * @features: a #GstCapsFeatures. + * @feature: a feature. + * + * Adds @feature to @features. + */ +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); + + 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. + */ +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); + + 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. + */ +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. + */ +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); +} |