summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/gst/gstreamer-docs.sgml1
-rw-r--r--docs/gst/gstreamer-sections.txt52
-rw-r--r--gst/Makefile.am2
-rw-r--r--gst/gst.c1
-rw-r--r--gst/gst.h1
-rw-r--r--gst/gst_private.h10
-rw-r--r--gst/gstcaps.c554
-rw-r--r--gst/gstcaps.h15
-rw-r--r--gst/gstcapsfeatures.c740
-rw-r--r--gst/gstcapsfeatures.h74
-rw-r--r--gst/gstinfo.c4
-rw-r--r--gst/gststructure.c111
-rw-r--r--gst/gstvalue.c81
-rw-r--r--gst/gstvalue.h15
-rw-r--r--tests/check/Makefile.am1
-rw-r--r--tests/check/gst/gstcaps.c88
-rw-r--r--tests/check/gst/gstcapsfeatures.c97
-rw-r--r--win32/common/libgstreamer.def31
18 files changed, 1766 insertions, 112 deletions
diff --git a/docs/gst/gstreamer-docs.sgml b/docs/gst/gstreamer-docs.sgml
index 1cfbe3471..eac3acb36 100644
--- a/docs/gst/gstreamer-docs.sgml
+++ b/docs/gst/gstreamer-docs.sgml
@@ -65,6 +65,7 @@ Windows. It is released under the GNU Library General Public License
<xi:include href="xml/gstbufferpool.xml" />
<xi:include href="xml/gstbus.xml" />
<xi:include href="xml/gstcaps.xml" />
+ <xi:include href="xml/gstcapsfeatures.xml" />
<xi:include href="xml/gstsample.xml" />
<xi:include href="xml/gstchildproxy.xml" />
<xi:include href="xml/gstclock.xml" />
diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt
index 33a2c61ce..d743b4836 100644
--- a/docs/gst/gstreamer-sections.txt
+++ b/docs/gst/gstreamer-sections.txt
@@ -403,11 +403,15 @@ gst_static_caps_cleanup
gst_caps_append
gst_caps_merge
gst_caps_append_structure
+gst_caps_append_structure_full
gst_caps_remove_structure
gst_caps_steal_structure
gst_caps_merge_structure
+gst_caps_merge_structure_full
gst_caps_get_size
gst_caps_get_structure
+gst_caps_get_features
+gst_caps_set_features
gst_caps_set_value
gst_caps_set_simple
gst_caps_set_simple_valist
@@ -420,6 +424,7 @@ gst_caps_is_strictly_equal
gst_caps_is_always_compatible
gst_caps_is_subset
gst_caps_is_subset_structure
+gst_caps_is_subset_structure_full
gst_caps_can_intersect
gst_caps_intersect
gst_caps_intersect_full
@@ -449,6 +454,48 @@ gst_caps_intersect_mode_get_type
</SECTION>
<SECTION>
+<FILE>gstcapsfeatures</FILE>
+<TITLE>GstCapsFeatures</TITLE>
+GstCapsFeatures
+gst_caps_features_new
+gst_caps_features_new_empty
+gst_caps_features_new_id
+gst_caps_features_new_id_valist
+gst_caps_features_new_valist
+
+gst_caps_features_copy
+gst_caps_features_free
+
+gst_caps_features_from_string
+gst_caps_features_to_string
+
+gst_caps_features_set_parent_refcount
+
+gst_caps_features_is_equal
+
+gst_caps_features_contains
+gst_caps_features_contains_id
+
+gst_caps_features_get_size
+
+gst_caps_features_get_nth
+gst_caps_features_get_nth_id
+
+gst_caps_features_add
+gst_caps_features_add_id
+gst_caps_features_remove
+gst_caps_features_remove_id
+<SUBSECTION Standard>
+GST_CAPS_FEATURES
+GST_CAPS_FEATURES_CAST
+GST_IS_CAPS_FEATURES
+GST_TYPE_CAPS_FEATURES
+gst_is_caps_features
+<SUBSECTION Private>
+gst_caps_features_get_type
+</SECTION>
+
+<SECTION>
<FILE>gstsample</FILE>
<TITLE>GstSample</TITLE>
GstSample
@@ -3137,6 +3184,11 @@ GST_VALUE_HOLDS_CAPS
gst_value_set_caps
gst_value_get_caps
+<SUBSECTION capsfeature>
+GST_VALUE_HOLDS_CAPS_FEATURES
+gst_value_set_caps_features
+gst_value_get_caps_features
+
<SUBSECTION structure>
GST_VALUE_HOLDS_STRUCTURE
gst_value_set_structure
diff --git a/gst/Makefile.am b/gst/Makefile.am
index 87db0556f..eb51dc142 100644
--- a/gst/Makefile.am
+++ b/gst/Makefile.am
@@ -55,6 +55,7 @@ libgstreamer_@GST_API_VERSION@_la_SOURCES = \
gstbufferpool.c \
gstbus.c \
gstcaps.c \
+ gstcapsfeatures.c \
gstchildproxy.c \
gstclock.c \
gstcontext.c \
@@ -151,6 +152,7 @@ gst_headers = \
gstbufferpool.h \
gstbus.h \
gstcaps.h \
+ gstcapsfeatures.h \
gstchildproxy.h \
gstclock.h \
gstcompat.h \
diff --git a/gst/gst.c b/gst/gst.c
index 6a7219e11..a5f54161d 100644
--- a/gst/gst.c
+++ b/gst/gst.c
@@ -562,6 +562,7 @@ init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
_priv_gst_query_initialize ();
_priv_gst_structure_initialize ();
_priv_gst_caps_initialize ();
+ _priv_gst_caps_features_initialize ();
_priv_gst_meta_initialize ();
g_type_class_ref (gst_object_get_type ());
diff --git a/gst/gst.h b/gst/gst.h
index 9ccdefab1..82f6bf961 100644
--- a/gst/gst.h
+++ b/gst/gst.h
@@ -36,6 +36,7 @@
#include <gst/gstbufferlist.h>
#include <gst/gstbufferpool.h>
#include <gst/gstcaps.h>
+#include <gst/gstcapsfeatures.h>
#include <gst/gstchildproxy.h>
#include <gst/gstclock.h>
#include <gst/gstcontrolsource.h>
diff --git a/gst/gst_private.h b/gst/gst_private.h
index d42b48fdb..e1117c5f2 100644
--- a/gst/gst_private.h
+++ b/gst/gst_private.h
@@ -105,6 +105,7 @@ G_GNUC_INTERNAL void _priv_gst_buffer_initialize (void);
G_GNUC_INTERNAL void _priv_gst_buffer_list_initialize (void);
G_GNUC_INTERNAL void _priv_gst_structure_initialize (void);
G_GNUC_INTERNAL void _priv_gst_caps_initialize (void);
+G_GNUC_INTERNAL void _priv_gst_caps_features_initialize (void);
G_GNUC_INTERNAL void _priv_gst_event_initialize (void);
G_GNUC_INTERNAL void _priv_gst_format_initialize (void);
G_GNUC_INTERNAL void _priv_gst_message_initialize (void);
@@ -132,10 +133,19 @@ G_GNUC_INTERNAL void _priv_gst_element_state_changed (GstElement *element,
/* used in both gststructure.c and gstcaps.c; numbers are completely made up */
#define STRUCTURE_ESTIMATED_STRING_LEN(s) (16 + gst_structure_n_fields(s) * 22)
+#define FEATURES_ESTIMATED_STRING_LEN(s) (16 + gst_caps_features_get_size(s) * 14)
G_GNUC_INTERNAL
gboolean priv_gst_structure_append_to_gstring (const GstStructure * structure,
GString * s);
+G_GNUC_INTERNAL
+void priv_gst_caps_features_append_to_gstring (const GstCapsFeatures * features, GString *s);
+
+G_GNUC_INTERNAL
+gboolean priv_gst_structure_parse_name (gchar * str, gchar **start, gchar ** end, gchar ** next);
+G_GNUC_INTERNAL
+gboolean priv_gst_structure_parse_fields (gchar *str, gchar ** end, GstStructure *structure);
+
/* registry cache backends */
G_GNUC_INTERNAL
gboolean priv_gst_registry_binary_read_cache (GstRegistry * registry, const char *location);
diff --git a/gst/gstcaps.c b/gst/gstcaps.c
index 3a64a0d21..4b6350dcf 100644
--- a/gst/gstcaps.c
+++ b/gst/gstcaps.c
@@ -71,11 +71,17 @@
#define DEBUG_REFCOUNT
+typedef struct _GstCapsArrayElement
+{
+ GstStructure *structure;
+ GstCapsFeatures *features;
+} GstCapsArrayElement;
+
typedef struct _GstCapsImpl
{
GstCaps caps;
- GPtrArray *array;
+ GArray *array;
} GstCapsImpl;
#define GST_CAPS_ARRAY(c) (((GstCapsImpl *)(c))->array)
@@ -96,15 +102,24 @@ typedef struct _GstCapsImpl
#define CAPS_IS_EMPTY_SIMPLE(caps) \
((GST_CAPS_ARRAY (caps) == NULL) || (GST_CAPS_LEN (caps) == 0))
+#define gst_caps_features_copy_conditional(f) ((f && !gst_caps_features_is_equal (f, GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY)) ? gst_caps_features_copy (f) : NULL)
+
/* quick way to get a caps structure at an index without doing a type or array
* length check */
#define gst_caps_get_structure_unchecked(caps, index) \
- ((GstStructure *)g_ptr_array_index (GST_CAPS_ARRAY (caps), (index)))
+ (g_array_index (GST_CAPS_ARRAY (caps), GstCapsArrayElement, (index)).structure)
+#define gst_caps_get_features_unchecked(caps, index) \
+ (g_array_index (GST_CAPS_ARRAY (caps), GstCapsArrayElement, (index)).features)
/* quick way to append a structure without checking the args */
-#define gst_caps_append_structure_unchecked(caps, structure) G_STMT_START{\
- GstStructure *__s=structure; \
- if (gst_structure_set_parent_refcount (__s, &GST_MINI_OBJECT_REFCOUNT(caps))) \
- g_ptr_array_add (GST_CAPS_ARRAY (caps), __s); \
+#define gst_caps_append_structure_unchecked(caps, s, f) G_STMT_START{\
+ GstCapsArrayElement __e={s, f}; \
+ if (__e.features && gst_caps_features_is_equal (GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY, __e.features)) { \
+ gst_caps_features_free (__e.features); \
+ __e.features = NULL; \
+ } \
+ if (gst_structure_set_parent_refcount (__e.structure, &GST_MINI_OBJECT_REFCOUNT(caps)) && \
+ (!__e.features || gst_caps_features_set_parent_refcount (__e.features, &GST_MINI_OBJECT_REFCOUNT(caps)))) \
+ g_array_append_val (GST_CAPS_ARRAY (caps), __e); \
}G_STMT_END
/* lock to protect multiple invocations of static caps to caps conversion */
@@ -138,6 +153,7 @@ _gst_caps_copy (const GstCaps * caps)
{
GstCaps *newcaps;
GstStructure *structure;
+ GstCapsFeatures *features;
guint i, n;
g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
@@ -151,7 +167,9 @@ _gst_caps_copy (const GstCaps * caps)
for (i = 0; i < n; i++) {
structure = gst_caps_get_structure_unchecked (caps, i);
- gst_caps_append_structure (newcaps, gst_structure_copy (structure));
+ features = gst_caps_get_features_unchecked (caps, i);
+ gst_caps_append_structure_full (newcaps, gst_structure_copy (structure),
+ gst_caps_features_copy_conditional (features));
}
return newcaps;
@@ -162,6 +180,7 @@ static void
_gst_caps_free (GstCaps * caps)
{
GstStructure *structure;
+ GstCapsFeatures *features;
guint i, len;
/* The refcount must be 0, but since we're only called by gst_caps_unref,
@@ -170,11 +189,16 @@ _gst_caps_free (GstCaps * caps)
/* This can be used to get statistics about caps sizes */
/*GST_CAT_INFO (GST_CAT_CAPS, "caps size: %d", len); */
for (i = 0; i < len; i++) {
- structure = (GstStructure *) gst_caps_get_structure_unchecked (caps, i);
+ structure = gst_caps_get_structure_unchecked (caps, i);
gst_structure_set_parent_refcount (structure, NULL);
gst_structure_free (structure);
+ features = gst_caps_get_features_unchecked (caps, i);
+ if (features) {
+ gst_caps_features_set_parent_refcount (features, NULL);
+ gst_caps_features_free (features);
+ }
}
- g_ptr_array_free (GST_CAPS_ARRAY (caps), TRUE);
+ g_array_free (GST_CAPS_ARRAY (caps), TRUE);
#ifdef DEBUG_REFCOUNT
GST_CAT_TRACE (GST_CAT_CAPS, "freeing caps %p", caps);
@@ -194,7 +218,8 @@ gst_caps_init (GstCaps * caps)
* in practice
* GST_CAPS_ARRAY (caps) = g_ptr_array_sized_new (32);
*/
- GST_CAPS_ARRAY (caps) = g_ptr_array_new ();
+ GST_CAPS_ARRAY (caps) =
+ g_array_new (FALSE, TRUE, sizeof (GstCapsArrayElement));
}
/**
@@ -260,7 +285,7 @@ gst_caps_new_empty_simple (const char *media_type)
caps = gst_caps_new_empty ();
structure = gst_structure_new_empty (media_type);
if (structure)
- gst_caps_append_structure_unchecked (caps, structure);
+ gst_caps_append_structure_unchecked (caps, structure, NULL);
return caps;
}
@@ -292,7 +317,7 @@ gst_caps_new_simple (const char *media_type, const char *fieldname, ...)
va_end (var_args);
if (structure)
- gst_caps_append_structure_unchecked (caps, structure);
+ gst_caps_append_structure_unchecked (caps, structure, NULL);
else
gst_caps_replace (&caps, NULL);
@@ -342,7 +367,7 @@ gst_caps_new_full_valist (GstStructure * structure, va_list var_args)
caps = gst_caps_new_empty ();
while (structure) {
- gst_caps_append_structure_unchecked (caps, structure);
+ gst_caps_append_structure_unchecked (caps, structure, NULL);
structure = va_arg (var_args, GstStructure *);
}
@@ -426,16 +451,44 @@ gst_static_caps_cleanup (GstStaticCaps * static_caps)
/* manipulation */
+static void
+gst_caps_remove_and_get_structure_and_features (GstCaps * caps, guint idx,
+ GstStructure ** s, GstCapsFeatures ** f)
+{
+ GstStructure *s_;
+ GstCapsFeatures *f_;
+
+ s_ = gst_caps_get_structure_unchecked (caps, idx);
+ f_ = gst_caps_get_features_unchecked (caps, idx);
+
+ /* don't use index_fast, gst_caps_simplify relies on the order */
+ g_array_remove_index (GST_CAPS_ARRAY (caps), idx);
+
+ gst_structure_set_parent_refcount (s_, NULL);
+ if (f_) {
+ gst_caps_features_set_parent_refcount (f_, NULL);
+ }
+
+ *s = s_;
+ *f = f_;
+}
+
static GstStructure *
gst_caps_remove_and_get_structure (GstCaps * caps, guint idx)
{
- /* don't use index_fast, gst_caps_simplify relies on the order */
- GstStructure *s = g_ptr_array_remove_index (GST_CAPS_ARRAY (caps), idx);
+ GstStructure *s;
+ GstCapsFeatures *f;
+
+ gst_caps_remove_and_get_structure_and_features (caps, idx, &s, &f);
+
+ if (f)
+ gst_caps_features_free (f);
- gst_structure_set_parent_refcount (s, NULL);
return s;
}
+
+
/**
* gst_caps_steal_structure:
* @caps: the #GstCaps to retrieve from
@@ -472,6 +525,7 @@ void
gst_caps_append (GstCaps * caps1, GstCaps * caps2)
{
GstStructure *structure;
+ GstCapsFeatures *features;
int i;
g_return_if_fail (GST_IS_CAPS (caps1));
@@ -485,8 +539,9 @@ gst_caps_append (GstCaps * caps1, GstCaps * caps2)
caps2 = gst_caps_make_writable (caps2);
for (i = GST_CAPS_LEN (caps2); i; i--) {
- structure = gst_caps_remove_and_get_structure (caps2, 0);
- gst_caps_append_structure_unchecked (caps1, structure);
+ gst_caps_remove_and_get_structure_and_features (caps2, 0, &structure,
+ &features);
+ gst_caps_append_structure_unchecked (caps1, structure, features);
}
gst_caps_unref (caps2); /* guaranteed to free it */
}
@@ -508,6 +563,7 @@ GstCaps *
gst_caps_merge (GstCaps * caps1, GstCaps * caps2)
{
GstStructure *structure;
+ GstCapsFeatures *features;
int i;
GstCaps *result;
@@ -524,8 +580,9 @@ gst_caps_merge (GstCaps * caps1, GstCaps * caps2)
caps2 = gst_caps_make_writable (caps2);
for (i = GST_CAPS_LEN (caps2); i; i--) {
- structure = gst_caps_remove_and_get_structure (caps2, 0);
- caps1 = gst_caps_merge_structure (caps1, structure);
+ gst_caps_remove_and_get_structure_and_features (caps2, 0, &structure,
+ &features);
+ caps1 = gst_caps_merge_structure_full (caps1, structure, features);
}
gst_caps_unref (caps2);
result = caps1;
@@ -559,7 +616,28 @@ gst_caps_append_structure (GstCaps * caps, GstStructure * structure)
g_return_if_fail (IS_WRITABLE (caps));
if (G_LIKELY (structure)) {
- gst_caps_append_structure_unchecked (caps, structure);
+ gst_caps_append_structure_unchecked (caps, structure, NULL);
+ }
+}
+
+/**
+ * gst_caps_append_structure_full:
+ * @caps: the #GstCaps that will be appended to
+ * @structure: (transfer full): the #GstStructure to append
+ * @features: (transfer full) (allow-none): the #GstCapsFeatures to append
+ *
+ * Appends @structure with @features to @caps. The structure is not copied; @caps
+ * becomes the owner of @structure.
+ */
+void
+gst_caps_append_structure_full (GstCaps * caps, GstStructure * structure,
+ GstCapsFeatures * features)
+{
+ g_return_if_fail (GST_IS_CAPS (caps));
+ g_return_if_fail (IS_WRITABLE (caps));
+
+ if (G_LIKELY (structure)) {
+ gst_caps_append_structure_unchecked (caps, structure, features);
}
}
@@ -597,6 +675,56 @@ GstCaps *
gst_caps_merge_structure (GstCaps * caps, GstStructure * structure)
{
GstStructure *structure1;
+ GstCapsFeatures *features1;
+ int i;
+ gboolean unique = TRUE;
+
+ g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
+
+ if (G_UNLIKELY (structure == NULL))
+ return caps;
+
+ /* check each structure */
+ for (i = GST_CAPS_LEN (caps) - 1; i >= 0; i--) {
+ structure1 = gst_caps_get_structure_unchecked (caps, i);
+ features1 = gst_caps_get_features_unchecked (caps, i);
+ if (!features1)
+ features1 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
+
+ /* if structure is a subset of structure1 and the
+ * there are no existing features, then skip it */
+ if (gst_structure_is_subset (structure, structure1) &&
+ gst_caps_features_is_equal (features1,
+ GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY)) {
+ unique = FALSE;
+ break;
+ }
+ }
+ if (unique) {
+ caps = gst_caps_make_writable (caps);
+ gst_caps_append_structure_unchecked (caps, structure, NULL);
+ } else {
+ gst_structure_free (structure);
+ }
+ return caps;
+}
+
+/**
+ * gst_caps_merge_structure_full:
+ * @caps: (transfer full): the #GstCaps to merge into
+ * @structure: (transfer full): the #GstStructure to merge
+ * @features: (transfer full) (allow-none): the #GstCapsFeatures to merge
+ *
+ * Appends @structure with @features to @caps if its not already expressed by @caps.
+ *
+ * Returns: (transfer full): the merged caps.
+ */
+GstCaps *
+gst_caps_merge_structure_full (GstCaps * caps, GstStructure * structure,
+ GstCapsFeatures * features)
+{
+ GstStructure *structure1;
+ GstCapsFeatures *features1, *features_tmp;
int i;
gboolean unique = TRUE;
@@ -605,20 +733,30 @@ gst_caps_merge_structure (GstCaps * caps, GstStructure * structure)
if (G_UNLIKELY (structure == NULL))
return caps;
+ /* To make comparisons easier below */
+ features_tmp = features ? features : GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
+
/* check each structure */
for (i = GST_CAPS_LEN (caps) - 1; i >= 0; i--) {
structure1 = gst_caps_get_structure_unchecked (caps, i);
- /* if structure is a subset of structure1, then skip it */
- if (gst_structure_is_subset (structure, structure1)) {
+ features1 = gst_caps_get_features_unchecked (caps, i);
+ if (!features1)
+ features1 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
+ /* if structure is a subset of structure1 and the
+ * the features are a subset, then skip it */
+ if (gst_structure_is_subset (structure, structure1) &&
+ gst_caps_features_is_equal (features_tmp, features1)) {
unique = FALSE;
break;
}
}
if (unique) {
caps = gst_caps_make_writable (caps);
- gst_caps_append_structure_unchecked (caps, structure);
+ gst_caps_append_structure_unchecked (caps, structure, features);
} else {
gst_structure_free (structure);
+ if (features)
+ gst_caps_features_free (features);
}
return caps;
}
@@ -672,6 +810,68 @@ gst_caps_get_structure (const GstCaps * caps, guint index)
}
/**
+ * gst_caps_get_features:
+ * @caps: a #GstCaps
+ * @index: the index of the structure
+ *
+ * Finds the features in @caps that has the index @index, and
+ * returns it.
+ *
+ * WARNING: This function takes a const GstCaps *, but returns a
+ * non-const GstCapsFeatures *. This is for programming convenience --
+ * the caller should be aware that structures inside a constant
+ * #GstCaps should not be modified. However, if you know the caps
+ * are writable, either because you have just copied them or made
+ * them writable with gst_caps_make_writable(), you may modify the
+ * features returned in the usual way, e.g. with functions like
+ * gst_caps_features_add().
+ *
+ * You do not need to free or unref the structure returned, it
+ * belongs to the #GstCaps.
+ *
+ * Returns: (transfer none): a pointer to the #GstCapsFeatures corresponding
+ * to @index
+ */
+GstCapsFeatures *
+gst_caps_get_features (const GstCaps * caps, guint index)
+{
+ GstCapsFeatures *features;
+
+ g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
+ g_return_val_if_fail (index < GST_CAPS_LEN (caps), NULL);
+
+ features = gst_caps_get_features_unchecked (caps, index);
+ if (!features)
+ features = gst_caps_features_copy (GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY);
+
+ return features;
+}
+
+/**
+ * gst_caps_set_features:
+ * @caps: a #GstCaps
+ * @index: the index of the structure
+ * @features: (allow-none) (transfer full): the #GstFeatures to set
+ *
+ * Sets the #GstCapsFeatures @features for the structure at @index.
+ */
+void
+gst_caps_set_features (GstCaps * caps, guint index, GstCapsFeatures * features)
+{
+ GstCapsFeatures **storage, *old;
+
+ g_return_if_fail (caps != NULL);
+ g_return_if_fail (index <= gst_caps_get_size (caps));
+ g_return_if_fail (IS_WRITABLE (caps));
+
+ storage = &gst_caps_get_features_unchecked (caps, index);
+ old = *storage;
+ *storage = features;
+ if (old)
+ gst_caps_features_free (old);
+}
+
+/**
* gst_caps_copy_nth:
* @caps: the #GstCaps to copy
* @nth: the nth structure to copy
@@ -686,6 +886,7 @@ gst_caps_copy_nth (const GstCaps * caps, guint nth)
{
GstCaps *newcaps;
GstStructure *structure;
+ GstCapsFeatures *features;
g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
@@ -694,8 +895,10 @@ gst_caps_copy_nth (const GstCaps * caps, guint nth)
if (G_LIKELY (GST_CAPS_LEN (caps) > nth)) {
structure = gst_caps_get_structure_unchecked (caps, nth);
+ features = gst_caps_get_features_unchecked (caps, nth);
gst_caps_append_structure_unchecked (newcaps,
- gst_structure_copy (structure));
+ gst_structure_copy (structure),
+ gst_caps_features_copy_conditional (features));
}
return newcaps;
@@ -897,14 +1100,22 @@ gboolean
gst_caps_is_equal_fixed (const GstCaps * caps1, const GstCaps * caps2)
{
GstStructure *struct1, *struct2;
+ GstCapsFeatures *features1, *features2;
g_return_val_if_fail (gst_caps_is_fixed (caps1), FALSE);
g_return_val_if_fail (gst_caps_is_fixed (caps2), FALSE);
struct1 = gst_caps_get_structure_unchecked (caps1, 0);
+ features1 = gst_caps_get_features_unchecked (caps1, 0);
+ if (!features1)
+ features1 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
struct2 = gst_caps_get_structure_unchecked (caps2, 0);
+ features2 = gst_caps_get_features_unchecked (caps2, 0);
+ if (!features2)
+ features2 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
- return gst_structure_is_equal (struct1, struct2);
+ return gst_structure_is_equal (struct1, struct2) &&
+ gst_caps_features_is_equal (features1, features2);
}
/**
@@ -942,6 +1153,7 @@ gboolean
gst_caps_is_subset (const GstCaps * subset, const GstCaps * superset)
{
GstStructure *s1, *s2;
+ GstCapsFeatures *f1, *f2;
gboolean ret = TRUE;
gint i, j;
@@ -956,8 +1168,15 @@ gst_caps_is_subset (const GstCaps * subset, const GstCaps * superset)
for (i = GST_CAPS_LEN (subset) - 1; i >= 0; i--) {
for (j = GST_CAPS_LEN (superset) - 1; j >= 0; j--) {
s1 = gst_caps_get_structure_unchecked (subset, i);
+ f1 = gst_caps_get_features_unchecked (subset, i);
+ if (!f1)
+ f1 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
s2 = gst_caps_get_structure_unchecked (superset, j);
- if (gst_structure_is_subset (s1, s2)) {
+ f2 = gst_caps_get_features_unchecked (superset, j);
+ if (!f2)
+ f2 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
+ if (gst_structure_is_subset (s1, s2) &&
+ gst_caps_features_is_equal (f1, f2)) {
/* If we found a superset, continue with the next
* subset structure */
break;
@@ -1011,6 +1230,51 @@ gst_caps_is_subset_structure (const GstCaps * caps,
}
/**
+ * gst_caps_is_subset_structure_full:
+ * @caps: a #GstCaps
+ * @structure: a potential #GstStructure subset of @caps
+ * @features: (allow-none): a #GstCapsFeatures for @structure
+ *
+ * Checks if @structure is a subset of @caps. See gst_caps_is_subset()
+ * for more information.
+ *
+ * Returns: %TRUE if @structure is a subset of @caps
+ */
+gboolean
+gst_caps_is_subset_structure_full (const GstCaps * caps,
+ const GstStructure * structure, const GstCapsFeatures * features)
+{
+ GstStructure *s;
+ GstCapsFeatures *f;
+ gint i;
+
+ g_return_val_if_fail (caps != NULL, FALSE);
+ g_return_val_if_fail (structure != NULL, FALSE);
+
+ if (CAPS_IS_ANY (caps))
+ return TRUE;
+ if (CAPS_IS_EMPTY (caps))
+ return FALSE;
+
+ if (!features)
+ features = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
+
+ for (i = GST_CAPS_LEN (caps) - 1; i >= 0; i--) {
+ s = gst_caps_get_structure_unchecked (caps, i);
+ f = gst_caps_get_features_unchecked (caps, i);
+ if (!f)
+ f = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
+ if (gst_structure_is_subset (structure, s) &&
+ gst_caps_features_is_equal (features, f)) {
+ /* If we found a superset return TRUE */
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
* gst_caps_is_equal:
* @caps1: a #GstCaps
* @caps2: another #GstCaps
@@ -1049,6 +1313,8 @@ gboolean
gst_caps_is_strictly_equal (const GstCaps * caps1, const GstCaps * caps2)
{
int i;
+ GstStructure *s1, *s2;
+ GstCapsFeatures *f1, *f2;
g_return_val_if_fail (GST_IS_CAPS (caps1), FALSE);
g_return_val_if_fail (GST_IS_CAPS (caps2), FALSE);
@@ -1060,8 +1326,17 @@ gst_caps_is_strictly_equal (const GstCaps * caps1, const GstCaps * caps2)
return FALSE;
for (i = 0; i < GST_CAPS_LEN (caps1); i++) {
- if (!gst_structure_is_equal (gst_caps_get_structure_unchecked (caps1, i),
- gst_caps_get_structure_unchecked (caps2, i)))
+ s1 = gst_caps_get_structure_unchecked (caps1, i);
+ f1 = gst_caps_get_features_unchecked (caps1, i);
+ if (!f1)
+ f1 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
+ s2 = gst_caps_get_structure_unchecked (caps2, i);
+ f2 = gst_caps_get_features_unchecked (caps2, i);
+ if (!f2)
+ f2 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
+
+ if (!gst_structure_is_equal (s1, s2)
+ || !gst_caps_features_is_equal (f1, f2))
return FALSE;
}
@@ -1087,6 +1362,8 @@ gst_caps_can_intersect (const GstCaps * caps1, const GstCaps * caps2)
guint j, k, len1, len2;
GstStructure *struct1;
GstStructure *struct2;
+ GstCapsFeatures *features1;
+ GstCapsFeatures *features2;
g_return_val_if_fail (GST_IS_CAPS (caps1), FALSE);
g_return_val_if_fail (GST_IS_CAPS (caps2), FALSE);
@@ -1128,14 +1405,19 @@ gst_caps_can_intersect (const GstCaps * caps1, const GstCaps * caps2)
/* subset index stays 0 until i reaches superset->structs->len, then it
* counts up from 1 to subset->structs->len - 1 */
k = (i > j) ? (i - j) : 0; /* MAX (0, i - j) */
-
/* now run the diagonal line, end condition is the left or bottom
* border */
while (k < len2) {
struct1 = gst_caps_get_structure_unchecked (caps1, j);
+ features1 = gst_caps_get_features_unchecked (caps1, j);
+ if (!features1)
+ features1 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
struct2 = gst_caps_get_structure_unchecked (caps2, k);
-
- if (gst_structure_can_intersect (struct1, struct2)) {
+ features2 = gst_caps_get_features_unchecked (caps2, k);
+ if (!features2)
+ features2 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
+ if (gst_structure_can_intersect (struct1, struct2) &&
+ gst_caps_features_is_equal (features1, features2)) {
return TRUE;
}
/* move down left */
@@ -1145,6 +1427,7 @@ gst_caps_can_intersect (const GstCaps * caps1, const GstCaps * caps2)
j--;
}
}
+
return FALSE;
}
@@ -1153,9 +1436,10 @@ gst_caps_intersect_zig_zag (GstCaps * caps1, GstCaps * caps2)
{
guint64 i; /* index can be up to 2 * G_MAX_UINT */
guint j, k, len1, len2;
-
GstStructure *struct1;
GstStructure *struct2;
+ GstCapsFeatures *features1;
+ GstCapsFeatures *features2;
GstCaps *dest;
GstStructure *istruct;
@@ -1170,11 +1454,11 @@ gst_caps_intersect_zig_zag (GstCaps * caps1, GstCaps * caps2)
/* one of the caps is any, just copy the other caps */
if (G_UNLIKELY (CAPS_IS_ANY (caps1)))
return gst_caps_ref (caps2);
+
if (G_UNLIKELY (CAPS_IS_ANY (caps2)))
return gst_caps_ref (caps1);
dest = gst_caps_new_empty ();
-
/* run zigzag on top line then right line, this preserves the caps order
* much better than a simple loop.
*
@@ -1199,16 +1483,22 @@ gst_caps_intersect_zig_zag (GstCaps * caps1, GstCaps * caps2)
/* caps2 index stays 0 until i reaches GST_CAPS_LEN (caps1), then it counts
* up from 1 to GST_CAPS_LEN (caps2) - 1 */
k = (i > j) ? (i - j) : 0; /* MAX (0, i - j) */
-
/* now run the diagonal line, end condition is the left or bottom
* border */
while (k < len2) {
struct1 = gst_caps_get_structure_unchecked (caps1, j);
+ features1 = gst_caps_get_features_unchecked (caps1, j);
+ if (!features1)
+ features1 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
struct2 = gst_caps_get_structure_unchecked (caps2, k);
-
+ features2 = gst_caps_get_features_unchecked (caps2, k);
+ if (!features2)
+ features2 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
istruct = gst_structure_intersect (struct1, struct2);
-
- dest = gst_caps_merge_structure (dest, istruct);
+ if (istruct && gst_caps_features_is_equal (features1, features2))
+ dest =
+ gst_caps_merge_structure_full (dest, istruct,
+ gst_caps_features_copy_conditional (features1));
/* move down left */
k++;
if (G_UNLIKELY (j == 0))
@@ -1237,9 +1527,10 @@ gst_caps_intersect_first (GstCaps * caps1, GstCaps * caps2)
{
guint i;
guint j, len1, len2;
-
GstStructure *struct1;
GstStructure *struct2;
+ GstCapsFeatures *features1;
+ GstCapsFeatures *features2;
GstCaps *dest;
GstStructure *istruct;
@@ -1254,19 +1545,25 @@ gst_caps_intersect_first (GstCaps * caps1, GstCaps * caps2)
/* one of the caps is any, just copy the other caps */
if (G_UNLIKELY (CAPS_IS_ANY (caps1)))
return gst_caps_ref (caps2);
+
if (G_UNLIKELY (CAPS_IS_ANY (caps2)))
return gst_caps_ref (caps1);
dest = gst_caps_new_empty ();
-
len1 = GST_CAPS_LEN (caps1);
len2 = GST_CAPS_LEN (caps2);
for (i = 0; i < len1; i++) {
struct1 = gst_caps_get_structure_unchecked (caps1, i);
+ features1 = gst_caps_get_features_unchecked (caps1, i);
+ if (!features1)
+ features1 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
for (j = 0; j < len2; j++) {
struct2 = gst_caps_get_structure_unchecked (caps2, j);
+ features2 = gst_caps_get_features_unchecked (caps2, j);
+ if (!features2)
+ features2 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
istruct = gst_structure_intersect (struct1, struct2);
- if (istruct)
+ if (istruct && gst_caps_features_is_equal (features1, features2))
dest = gst_caps_merge_structure (dest, istruct);
}
}
@@ -1320,15 +1617,13 @@ gst_caps_intersect (GstCaps * caps1, GstCaps * caps2)
return gst_caps_intersect_full (caps1, caps2, GST_CAPS_INTERSECT_ZIG_ZAG);
}
-
/* subtract operation */
typedef struct
{
const GstStructure *subtract_from;
GSList *put_into;
-}
-SubtractionEntry;
+} SubtractionEntry;
static gboolean
gst_caps_structure_subtract_field (GQuark field_id, const GValue * value,
@@ -1340,11 +1635,14 @@ gst_caps_structure_subtract_field (GQuark field_id, const GValue * value,
GstStructure *structure;
other = gst_structure_id_get_value (e->subtract_from, field_id);
+
if (!other) {
return FALSE;
}
+
if (!gst_value_subtract (&subtraction, other, value))
return TRUE;
+
if (gst_value_compare (&subtraction, other) == GST_VALUE_EQUAL) {
g_value_unset (&subtraction);
return FALSE;
@@ -1365,9 +1663,9 @@ gst_caps_structure_subtract (GSList ** into, const GstStructure * minuend,
e.subtract_from = minuend;
e.put_into = NULL;
-
ret = gst_structure_foreach ((GstStructure *) subtrahend,
gst_caps_structure_subtract_field, &e);
+
if (ret) {
*into = e.put_into;
} else {
@@ -1378,6 +1676,7 @@ gst_caps_structure_subtract (GSList ** into, const GstStructure * minuend,
}
g_slist_free (e.put_into);
}
+
return ret;
}
@@ -1398,6 +1697,7 @@ gst_caps_subtract (GstCaps * minuend, GstCaps * subtrahend)
guint i, j, sublen;
GstStructure *min;
GstStructure *sub;
+ GstCapsFeatures *min_f, *sub_f;
GstCaps *dest = NULL, *src;
g_return_val_if_fail (minuend != NULL, NULL);
@@ -1406,6 +1706,7 @@ gst_caps_subtract (GstCaps * minuend, GstCaps * subtrahend)
if (CAPS_IS_EMPTY (minuend) || CAPS_IS_ANY (subtrahend)) {
return gst_caps_new_empty ();
}
+
if (CAPS_IS_EMPTY_SIMPLE (subtrahend))
return gst_caps_ref (minuend);
@@ -1414,6 +1715,7 @@ gst_caps_subtract (GstCaps * minuend, GstCaps * subtrahend)
ANY means for specific types, so it's not possible to reduce ANY partially
You can only remove everything or nothing and that is done above.
Note: there's a test that checks this behaviour. */
+
g_return_val_if_fail (!CAPS_IS_ANY (minuend), NULL);
sublen = GST_CAPS_LEN (subtrahend);
g_assert (sublen > 0);
@@ -1423,6 +1725,9 @@ gst_caps_subtract (GstCaps * minuend, GstCaps * subtrahend)
guint srclen;
sub = gst_caps_get_structure_unchecked (subtrahend, i);
+ sub_f = gst_caps_get_features_unchecked (subtrahend, i);
+ if (!sub_f)
+ sub_f = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
if (dest) {
gst_caps_unref (src);
src = dest;
@@ -1431,7 +1736,11 @@ gst_caps_subtract (GstCaps * minuend, GstCaps * subtrahend)
srclen = GST_CAPS_LEN (src);
for (j = 0; j < srclen; j++) {
min = gst_caps_get_structure_unchecked (src, j);
- if (gst_structure_get_name_id (min) == gst_structure_get_name_id (sub)) {
+ min_f = gst_caps_get_features_unchecked (src, j);
+ if (!min_f)
+ min_f = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
+ if (gst_structure_get_name_id (min) == gst_structure_get_name_id (sub) &&
+ gst_caps_features_is_equal (min_f, sub_f)) {
GSList *list;
if (gst_caps_structure_subtract (&list, min, sub)) {
@@ -1439,16 +1748,20 @@ gst_caps_subtract (GstCaps * minuend, GstCaps * subtrahend)
for (walk = list; walk; walk = g_slist_next (walk)) {
gst_caps_append_structure_unchecked (dest,
- (GstStructure *) walk->data);
+ (GstStructure *) walk->data,
+ gst_caps_features_copy_conditional (min_f));
}
g_slist_free (list);
} else {
- gst_caps_append_structure_unchecked (dest, gst_structure_copy (min));
+ gst_caps_append_structure_unchecked (dest, gst_structure_copy (min),
+ gst_caps_features_copy_conditional (min_f));
}
} else {
- gst_caps_append_structure_unchecked (dest, gst_structure_copy (min));
+ gst_caps_append_structure_unchecked (dest, gst_structure_copy (min),
+ gst_caps_features_copy_conditional (min_f));
}
}
+
if (CAPS_IS_EMPTY_SIMPLE (dest)) {
gst_caps_unref (src);
return dest;
@@ -1457,6 +1770,7 @@ gst_caps_subtract (GstCaps * minuend, GstCaps * subtrahend)
gst_caps_unref (src);
dest = gst_caps_simplify (dest);
+
return dest;
}
@@ -1466,8 +1780,8 @@ typedef struct _NormalizeForeach
{
GstCaps *caps;
GstStructure *structure;
-}
-NormalizeForeach;
+ GstCapsFeatures *features;
+} NormalizeForeach;
static gboolean
gst_caps_normalize_foreach (GQuark field_id, const GValue * value, gpointer ptr)
@@ -1478,18 +1792,21 @@ gst_caps_normalize_foreach (GQuark field_id, const GValue * value, gpointer ptr)
if (G_VALUE_TYPE (value) == GST_TYPE_LIST) {
guint len = gst_value_list_get_size (value);
+
for (i = 1; i < len; i++) {
const GValue *v = gst_value_list_get_value (value, i);
GstStructure *structure = gst_structure_copy (nf->structure);
gst_structure_id_set_value (structure, field_id, v);
- gst_caps_append_structure_unchecked (nf->caps, structure);
+ gst_caps_append_structure_unchecked (nf->caps, structure,
+ gst_caps_features_copy_conditional (nf->features));
}
gst_value_init_and_copy (&val, gst_value_list_get_value (value, 0));
gst_structure_id_take_value (nf->structure, field_id, &val);
return FALSE;
}
+
return TRUE;
}
@@ -1514,12 +1831,11 @@ gst_caps_normalize (GstCaps * caps)
g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
caps = gst_caps_make_writable (caps);
-
nf.caps = caps;
for (i = 0; i < gst_caps_get_size (nf.caps); i++) {
nf.structure = gst_caps_get_structure_unchecked (nf.caps, i);
-
+ nf.features = gst_caps_get_features_unchecked (nf.caps, i);
while (!gst_structure_foreach (nf.structure,
gst_caps_normalize_foreach, &nf));
}
@@ -1531,13 +1847,14 @@ static gint
gst_caps_compare_structures (gconstpointer one, gconstpointer two)
{
gint ret;
- const GstStructure *struct1 = *((const GstStructure **) one);
- const GstStructure *struct2 = *((const GstStructure **) two);
+ const GstStructure *struct1 = ((const GstCapsArrayElement *) one)->structure;
+ const GstStructure *struct2 = ((const GstCapsArrayElement *) two)->structure;
/* FIXME: this orders alphabetically, but ordering the quarks might be faster
So what's the best way? */
ret = strcmp (gst_structure_get_name (struct1),
gst_structure_get_name (struct2));
+
if (ret)
return ret;
@@ -1549,8 +1866,7 @@ typedef struct
GQuark name;
GValue value;
GstStructure *compare;
-}
-UnionField;
+} UnionField;
static gboolean
gst_caps_structure_figure_out_union (GQuark field_id, const GValue * value,
@@ -1564,14 +1880,18 @@ gst_caps_structure_figure_out_union (GQuark field_id, const GValue * value,
g_value_unset (&u->value);
return FALSE;
}
+
if (gst_value_compare (val, value) == GST_VALUE_EQUAL)
return TRUE;
+
if (u->name) {
g_value_unset (&u->value);
return FALSE;
}
+
u->name = field_id;
gst_value_union (&u->value, val, value);
+
return TRUE;
}
@@ -1614,7 +1934,8 @@ gst_caps_structure_simplify (GstStructure ** result,
} else {
g_value_unset (&field.value);
}
- } else if (gst_structure_n_fields (simplify) <=
+ } else
+ if (gst_structure_n_fields (simplify) <=
gst_structure_n_fields (compare)) {
/* compare is just more specific, will be optimized away later */
/* FIXME: do this here? */
@@ -1642,7 +1963,7 @@ gst_caps_switch_structures (GstCaps * caps, GstStructure * old,
gst_structure_set_parent_refcount (old, NULL);
gst_structure_free (old);
gst_structure_set_parent_refcount (new, &GST_CAPS_REFCOUNT (caps));
- g_ptr_array_index (GST_CAPS_ARRAY (caps), i) = new;
+ g_array_index (GST_CAPS_ARRAY (caps), GstCapsArrayElement, i).structure = new;
}
/**
@@ -1662,6 +1983,7 @@ GstCaps *
gst_caps_simplify (GstCaps * caps)
{
GstStructure *simplify, *compare, *result = NULL;
+ GstCapsFeatures *simplify_f, *compare_f;
gint i, j, start;
g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
@@ -1673,20 +1995,31 @@ gst_caps_simplify (GstCaps * caps)
caps = gst_caps_make_writable (caps);
- g_ptr_array_sort (GST_CAPS_ARRAY (caps), gst_caps_compare_structures);
+ g_array_sort (GST_CAPS_ARRAY (caps), gst_caps_compare_structures);
for (i = start; i >= 0; i--) {
simplify = gst_caps_get_structure_unchecked (caps, i);
+ simplify_f = gst_caps_get_features_unchecked (caps, i);
+ if (!simplify_f)
+ simplify_f = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
compare = gst_caps_get_structure_unchecked (caps, start);
+ compare_f = gst_caps_get_features_unchecked (caps, start);
+ if (!compare_f)
+ compare_f = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
if (gst_structure_get_name_id (simplify) !=
- gst_structure_get_name_id (compare))
+ gst_structure_get_name_id (compare) ||
+ !gst_caps_features_is_equal (simplify_f, compare_f))
start = i;
for (j = start; j >= 0; j--) {
if (j == i)
continue;
compare = gst_caps_get_structure_unchecked (caps, j);
+ compare_f = gst_caps_get_features_unchecked (caps, j);
+ if (!compare_f)
+ compare_f = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
if (gst_structure_get_name_id (simplify) !=
- gst_structure_get_name_id (compare)) {
+ gst_structure_get_name_id (compare) ||
+ !gst_caps_features_is_equal (simplify_f, compare_f)) {
break;
}
if (gst_caps_structure_simplify (&result, simplify, compare)) {
@@ -1773,14 +2106,20 @@ gst_caps_to_string (const GstCaps * caps)
slen = 0;
clen = GST_CAPS_LEN (caps);
for (i = 0; i < clen; i++) {
+ GstCapsFeatures *f;
+
slen +=
- STRUCTURE_ESTIMATED_STRING_LEN (gst_caps_get_structure_unchecked (caps,
- i));
+ STRUCTURE_ESTIMATED_STRING_LEN (gst_caps_get_structure_unchecked
+ (caps, i));
+ f = gst_caps_get_features_unchecked (caps, i);
+ if (f)
+ slen += FEATURES_ESTIMATED_STRING_LEN (f);
}
s = g_string_sized_new (slen);
for (i = 0; i < clen; i++) {
GstStructure *structure;
+ GstCapsFeatures *features;
if (i > 0) {
/* ';' is now added by gst_structure_to_string */
@@ -1788,6 +2127,16 @@ gst_caps_to_string (const GstCaps * caps)
}
structure = gst_caps_get_structure_unchecked (caps, i);
+ features = gst_caps_get_features_unchecked (caps, i);
+
+ g_string_append (s, gst_structure_get_name (structure));
+ if (features
+ && !gst_caps_features_is_equal (features,
+ GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY)) {
+ g_string_append_c (s, '(');
+ priv_gst_caps_features_append_to_gstring (features, s);
+ g_string_append_c (s, ')');
+ }
priv_gst_structure_append_to_gstring (structure, s);
}
if (s->len && s->str[s->len - 1] == ';') {
@@ -1801,37 +2150,94 @@ static gboolean
gst_caps_from_string_inplace (GstCaps * caps, const gchar * string)
{
GstStructure *structure;
- gchar *s;
+ gchar *s, *copy, *end, *next, save;
if (strcmp ("ANY", string) == 0) {
GST_CAPS_FLAGS (caps) = GST_CAPS_FLAG_ANY;
return TRUE;
}
+
if (strcmp ("EMPTY", string) == 0 || strcmp ("NONE", string) == 0) {
return TRUE;
}
- structure = gst_structure_from_string (string, &s);
- if (structure == NULL) {
- return FALSE;
- }
- gst_caps_append_structure_unchecked (caps, structure);
-
+ copy = s = g_strdup (string);
do {
+ GstCapsFeatures *features = NULL;
while (g_ascii_isspace (*s))
s++;
if (*s == '\0') {
break;
}
- structure = gst_structure_from_string (s, &s);
+
+ if (!priv_gst_structure_parse_name (s, &s, &end, &next)) {
+ g_free (copy);
+ return FALSE;
+ }
+
+ save = *end;
+ *end = '\0';
+ structure = gst_structure_new_empty (s);
+ *end = save;
+
if (structure == NULL) {
+ g_free (copy);
+ return FALSE;
+ }
+
+ s = next;
+
+ if (*s == '\0') {
+ goto append;
+ }
+
+ if (*s == '(') {
+ s++;
+ end = s;
+
+ while (TRUE) {
+ if (*end == '\0') {
+ break;
+ } else if (*end == ')') {
+ break;
+ } else {
+ end++;
+ }
+ }
+
+ save = *end;
+ *end = '\0';
+ features = gst_caps_features_from_string (s);
+ if (!features) {
+ gst_structure_free (structure);
+ g_free (copy);
+ return FALSE;
+ }
+ *end = save;
+ s = end;
+ if (save == ')')
+ s++;
+ }
+
+ if (*s == '\0') {
+ goto append;
+ }
+
+ if (!priv_gst_structure_parse_fields (s, &s, structure)) {
+ gst_structure_free (structure);
+ g_free (copy);
return FALSE;
}
- gst_caps_append_structure_unchecked (caps, structure);
+ append:
+ gst_caps_append_structure_unchecked (caps, structure, features);
+ if (*s == '\0')
+ break;
} while (TRUE);
+ g_free (copy);
+
return TRUE;
}
diff --git a/gst/gstcaps.h b/gst/gstcaps.h
index df740f24b..8df249984 100644
--- a/gst/gstcaps.h
+++ b/gst/gstcaps.h
@@ -23,6 +23,7 @@
#include <gst/gstconfig.h>
#include <gst/gstminiobject.h>
#include <gst/gststructure.h>
+#include <gst/gstcapsfeatures.h>
#include <gst/glib-compat.h>
G_BEGIN_DECLS
@@ -384,16 +385,27 @@ void gst_caps_append (GstCaps *caps1,
GstCaps *caps2);
void gst_caps_append_structure (GstCaps *caps,
GstStructure *structure);
+void gst_caps_append_structure_full (GstCaps *caps,
+ GstStructure *structure,
+ GstCapsFeatures *features);
void gst_caps_remove_structure (GstCaps *caps, guint idx);
GstCaps * gst_caps_merge (GstCaps *caps1,
GstCaps *caps2) G_GNUC_WARN_UNUSED_RESULT;
GstCaps * gst_caps_merge_structure (GstCaps *caps,
GstStructure *structure) G_GNUC_WARN_UNUSED_RESULT;
+GstCaps * gst_caps_merge_structure_full (GstCaps *caps,
+ GstStructure *structure,
+ GstCapsFeatures *features) G_GNUC_WARN_UNUSED_RESULT;
guint gst_caps_get_size (const GstCaps *caps);
GstStructure * gst_caps_get_structure (const GstCaps *caps,
guint index);
GstStructure * gst_caps_steal_structure (GstCaps *caps,
guint index) G_GNUC_WARN_UNUSED_RESULT;
+void gst_caps_set_features (GstCaps *caps,
+ guint index,
+ GstCapsFeatures * features);
+GstCapsFeatures * gst_caps_get_features (const GstCaps *caps,
+ guint index);
GstCaps * gst_caps_copy_nth (const GstCaps *caps, guint nth) G_GNUC_WARN_UNUSED_RESULT;
GstCaps * gst_caps_truncate (GstCaps *caps) G_GNUC_WARN_UNUSED_RESULT;
void gst_caps_set_value (GstCaps *caps,
@@ -415,6 +427,9 @@ gboolean gst_caps_is_subset (const GstCaps *subset,
const GstCaps *superset);
gboolean gst_caps_is_subset_structure (const GstCaps *caps,
const GstStructure *structure);
+gboolean gst_caps_is_subset_structure_full (const GstCaps *caps,
+ const GstStructure *structure,
+ const GstCapsFeatures *features);
gboolean gst_caps_is_equal (const GstCaps *caps1,
const GstCaps *caps2);
gboolean gst_caps_is_equal_fixed (const GstCaps *caps1,
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);
+}
diff --git a/gst/gstcapsfeatures.h b/gst/gstcapsfeatures.h
new file mode 100644
index 000000000..cb0359cb2
--- /dev/null
+++ b/gst/gstcapsfeatures.h
@@ -0,0 +1,74 @@
+/* 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.
+ */
+
+#ifndef __GST_CAPS_FEATURES_H__
+#define __GST_CAPS_FEATURES_H__
+
+#include <gst/gstconfig.h>
+#include <gst/glib-compat.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstCapsFeatures GstCapsFeatures;
+
+#define GST_TYPE_CAPS_FEATURES (gst_caps_features_get_type ())
+#define GST_IS_CAPS_FEATURES(object) (gst_is_caps_features(object))
+#define GST_CAPS_FEATURES_CAST(object) ((GstCapsFeatures *)(object))
+#define GST_CAPS_FEATURES(object) (GST_CAPS_FEATURES_CAST(object))
+
+#define GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY "memory:SystemMemory"
+
+GST_EXPORT GstCapsFeatures *_gst_caps_features_memory_system_memory;
+#define GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY (_gst_caps_features_memory_system_memory)
+
+GType gst_caps_features_get_type (void);
+gboolean gst_is_caps_features (gconstpointer obj);
+
+GstCapsFeatures * gst_caps_features_new_empty (void);
+GstCapsFeatures * gst_caps_features_new (const gchar *feature1, ...);
+GstCapsFeatures * gst_caps_features_new_valist (const gchar *feature1, va_list varargs);
+GstCapsFeatures * gst_caps_features_new_id (GQuark feature1, ...);
+GstCapsFeatures * gst_caps_features_new_id_valist (GQuark feature1, va_list varargs);
+
+gboolean gst_caps_features_set_parent_refcount (GstCapsFeatures *features, gint * refcount);
+
+GstCapsFeatures * gst_caps_features_copy (const GstCapsFeatures * features);
+void gst_caps_features_free (GstCapsFeatures * features);
+
+gchar * gst_caps_features_to_string (const GstCapsFeatures * features);
+GstCapsFeatures * gst_caps_features_from_string (const gchar * features);
+
+guint gst_caps_features_get_size (const GstCapsFeatures * features);
+const gchar * gst_caps_features_get_nth (const GstCapsFeatures * features, guint i);
+GQuark gst_caps_features_get_nth_id (const GstCapsFeatures * features, guint i);
+
+gboolean gst_caps_features_contains (const GstCapsFeatures * features, const gchar * feature);
+gboolean gst_caps_features_contains_id (const GstCapsFeatures * features, GQuark feature);
+gboolean gst_caps_features_is_equal (const GstCapsFeatures * features1, const GstCapsFeatures * features2);
+
+void gst_caps_features_add (GstCapsFeatures * features, const gchar * feature);
+void gst_caps_features_add_id ( GstCapsFeatures * features, GQuark feature);
+
+void gst_caps_features_remove (GstCapsFeatures * features, const gchar * feature);
+void gst_caps_features_remove_id (GstCapsFeatures * features, GQuark feature);
+
+G_END_DECLS
+
+#endif /* __GST_CAPS_FEATURES_H__ */
diff --git a/gst/gstinfo.c b/gst/gstinfo.c
index 21d53d864..9d353fbc6 100644
--- a/gst/gstinfo.c
+++ b/gst/gstinfo.c
@@ -121,6 +121,7 @@
#include "gstquark.h"
#include "gstsegment.h"
#include "gstvalue.h"
+#include "gstcapsfeatures.h"
#ifdef HAVE_VALGRIND_VALGRIND_H
# include <valgrind/valgrind.h>
@@ -628,6 +629,9 @@ gst_debug_print_object (gpointer ptr)
if (*(GType *) ptr == GST_TYPE_STRUCTURE) {
return gst_info_structure_to_string ((const GstStructure *) ptr);
}
+ if (*(GType *) ptr == GST_TYPE_STRUCTURE) {
+ return gst_caps_features_to_string ((const GstCapsFeatures *) ptr);
+ }
if (*(GType *) ptr == GST_TYPE_TAG_LIST) {
/* FIXME: want pretty tag list with long byte dumps removed.. */
return gst_tag_list_to_string ((GstTagList *) ptr);
diff --git a/gst/gststructure.c b/gst/gststructure.c
index f0640295e..d39e03aee 100644
--- a/gst/gststructure.c
+++ b/gst/gststructure.c
@@ -326,7 +326,7 @@ gst_structure_set_parent_refcount (GstStructure * structure, gint * refcount)
*
* Free-function: gst_structure_free
*
- * Returns: (transfer none): a new #GstStructure.
+ * Returns: (transfer full): a new #GstStructure.
*/
GstStructure *
gst_structure_copy (const GstStructure * structure)
@@ -1781,7 +1781,6 @@ priv_gst_structure_append_to_gstring (const GstStructure * structure,
g_return_val_if_fail (s != NULL, FALSE);
- g_string_append (s, g_quark_to_string (structure->name));
len = GST_STRUCTURE_FIELDS (structure)->len;
for (i = 0; i < len; i++) {
char *t;
@@ -1839,6 +1838,7 @@ gst_structure_to_string (const GstStructure * structure)
/* we estimate a minimum size based on the number of fields in order to
* avoid unnecessary reallocs within GString */
s = g_string_sized_new (STRUCTURE_ESTIMATED_STRING_LEN (structure));
+ g_string_append (s, g_quark_to_string (structure->name));
priv_gst_structure_append_to_gstring (structure, s);
return g_string_free (s, FALSE);
}
@@ -2236,54 +2236,41 @@ gst_structure_parse_value (gchar * str,
return ret;
}
-/**
- * gst_structure_from_string:
- * @string: a string representation of a #GstStructure.
- * @end: (out) (allow-none) (transfer none): pointer to store the end of the string in.
- *
- * Creates a #GstStructure from a string representation.
- * If end is not NULL, a pointer to the place inside the given string
- * where parsing ended will be returned.
- *
- * Free-function: gst_structure_free
- *
- * Returns: (transfer full): a new #GstStructure or NULL when the string could
- * not be parsed. Free with gst_structure_free() after use.
- */
-GstStructure *
-gst_structure_from_string (const gchar * string, gchar ** end)
+gboolean
+priv_gst_structure_parse_name (gchar * str, gchar ** start, gchar ** end,
+ gchar ** next)
{
- char *name;
- char *copy;
char *w;
char *r;
- char save;
- GstStructure *structure = NULL;
- GstStructureField field;
-
- g_return_val_if_fail (string != NULL, NULL);
- copy = g_strdup (string);
- r = copy;
+ r = str;
/* skip spaces (FIXME: _isspace treats tabs and newlines as space!) */
while (*r && (g_ascii_isspace (*r) || (r[0] == '\\'
&& g_ascii_isspace (r[1]))))
r++;
- name = r;
+ *start = r;
+
if (G_UNLIKELY (!gst_structure_parse_string (r, &w, &r, TRUE))) {
- GST_WARNING ("Failed to parse structure string '%s'", string);
- goto error;
+ GST_WARNING ("Failed to parse structure string '%s'", str);
+ return FALSE;
}
- save = *w;
- *w = '\0';
- structure = gst_structure_new_empty (name);
- *w = save;
+ *end = w;
+ *next = r;
- if (G_UNLIKELY (structure == NULL))
- goto error;
+ return TRUE;
+}
+
+gboolean
+priv_gst_structure_parse_fields (gchar * str, gchar ** end,
+ GstStructure * structure)
+{
+ gchar *r;
+ GstStructureField field;
+
+ r = str;
do {
while (*r && (g_ascii_isspace (*r) || (r[0] == '\\'
@@ -2300,7 +2287,7 @@ gst_structure_from_string (const gchar * string, gchar ** end)
}
if (G_UNLIKELY (*r != ',')) {
GST_WARNING ("Failed to find delimiter, r=%s", r);
- goto error;
+ return FALSE;
}
r++;
while (*r && (g_ascii_isspace (*r) || (r[0] == '\\'
@@ -2310,11 +2297,59 @@ gst_structure_from_string (const gchar * string, gchar ** end)
memset (&field, 0, sizeof (field));
if (G_UNLIKELY (!gst_structure_parse_field (r, &r, &field))) {
GST_WARNING ("Failed to parse field, r=%s", r);
- goto error;
+ return FALSE;
}
gst_structure_set_field (structure, &field);
} while (TRUE);
+ *end = r;
+
+ return TRUE;
+}
+
+/**
+ * gst_structure_from_string:
+ * @string: a string representation of a #GstStructure.
+ * @end: (out) (allow-none) (transfer none): pointer to store the end of the string in.
+ *
+ * Creates a #GstStructure from a string representation.
+ * If end is not NULL, a pointer to the place inside the given string
+ * where parsing ended will be returned.
+ *
+ * Free-function: gst_structure_free
+ *
+ * Returns: (transfer full): a new #GstStructure or NULL when the string could
+ * not be parsed. Free with gst_structure_free() after use.
+ */
+GstStructure *
+gst_structure_from_string (const gchar * string, gchar ** end)
+{
+ char *name;
+ char *copy;
+ char *w;
+ char *r;
+ char save;
+ GstStructure *structure = NULL;
+
+ g_return_val_if_fail (string != NULL, NULL);
+
+ copy = g_strdup (string);
+ r = copy;
+
+ if (!priv_gst_structure_parse_name (r, &name, &w, &r))
+ goto error;
+
+ save = *w;
+ *w = '\0';
+ structure = gst_structure_new_empty (name);
+ *w = save;
+
+ if (G_UNLIKELY (structure == NULL))
+ goto error;
+
+ if (!priv_gst_structure_parse_fields (r, &r, structure))
+ goto error;
+
if (end)
*end = (char *) string + (r - copy);
else if (*r)
diff --git a/gst/gstvalue.c b/gst/gstvalue.c
index f5fe2f664..bd3369bb3 100644
--- a/gst/gstvalue.c
+++ b/gst/gstvalue.c
@@ -1996,6 +1996,76 @@ gst_value_deserialize_structure (GValue * dest, const gchar * s)
return FALSE;
}
+/*******************
+ * GstCapsFeatures *
+ *******************/
+
+/**
+ * gst_value_set_caps_features:
+ * @value: a GValue initialized to GST_TYPE_CAPS_FEATURES
+ * @features: the features to set the value to
+ *
+ * Sets the contents of @value to @features.
+ */
+void
+gst_value_set_caps_features (GValue * value, const GstCapsFeatures * features)
+{
+ g_return_if_fail (G_IS_VALUE (value));
+ g_return_if_fail (G_VALUE_TYPE (value) == GST_TYPE_CAPS_FEATURES);
+ g_return_if_fail (features == NULL || GST_IS_CAPS_FEATURES (features));
+
+ g_value_set_boxed (value, features);
+}
+
+/**
+ * gst_value_get_caps_features:
+ * @value: a GValue initialized to GST_TYPE_CAPS_FEATURES
+ *
+ * Gets the contents of @value.
+ *
+ * Returns: (transfer none): the contents of @value
+ */
+const GstCapsFeatures *
+gst_value_get_caps_features (const GValue * value)
+{
+ g_return_val_if_fail (G_IS_VALUE (value), NULL);
+ g_return_val_if_fail (G_VALUE_TYPE (value) == GST_TYPE_CAPS_FEATURES, NULL);
+
+ return (GstCapsFeatures *) g_value_get_boxed (value);
+}
+
+static gchar *
+gst_value_serialize_caps_features (const GValue * value)
+{
+ GstCapsFeatures *features = g_value_get_boxed (value);
+
+ return gst_string_take_and_wrap (gst_caps_features_to_string (features));
+}
+
+static gboolean
+gst_value_deserialize_caps_features (GValue * dest, const gchar * s)
+{
+ GstCapsFeatures *features;
+
+ if (*s != '"') {
+ features = gst_caps_features_from_string (s);
+ } else {
+ gchar *str = gst_string_unwrap (s);
+
+ if (G_UNLIKELY (!str))
+ return FALSE;
+
+ features = gst_caps_features_from_string (str);
+ g_free (str);
+ }
+
+ if (G_LIKELY (features)) {
+ g_value_take_boxed (dest, features);
+ return TRUE;
+ }
+ return FALSE;
+}
+
/**************
* GstTagList *
**************/
@@ -6006,6 +6076,17 @@ _priv_gst_value_initialize (void)
static GstValueTable gst_value = {
0,
NULL,
+ gst_value_serialize_caps_features,
+ gst_value_deserialize_caps_features,
+ };
+
+ gst_value.type = GST_TYPE_CAPS_FEATURES;
+ gst_value_register (&gst_value);
+ }
+ {
+ static GstValueTable gst_value = {
+ 0,
+ NULL,
gst_value_serialize_tag_list,
gst_value_deserialize_tag_list,
};
diff --git a/gst/gstvalue.h b/gst/gstvalue.h
index 7e2e88d37..ed1dcdc30 100644
--- a/gst/gstvalue.h
+++ b/gst/gstvalue.h
@@ -23,6 +23,7 @@
#include <gst/gstconfig.h>
#include <gst/gstcaps.h>
#include <gst/gststructure.h>
+#include <gst/gstcapsfeatures.h>
G_BEGIN_DECLS
@@ -150,6 +151,14 @@ G_BEGIN_DECLS
#define GST_VALUE_HOLDS_STRUCTURE(x) (G_VALUE_HOLDS((x), GST_TYPE_STRUCTURE))
/**
+ * GST_VALUE_HOLDS_CAPS_FEATURES:
+ * @x: the #GValue to check
+ *
+ * Checks if the given #GValue contains a #GST_TYPE_CAPS_FEATURES value.
+ */
+#define GST_VALUE_HOLDS_CAPS_FEATURES(x) (G_VALUE_HOLDS((x), GST_TYPE_CAPS_FEATURES))
+
+/**
* GST_VALUE_HOLDS_BUFFER:
* @x: the #GValue to check
*
@@ -472,6 +481,12 @@ const GstStructure *
void gst_value_set_structure (GValue *value,
const GstStructure *structure);
+/* caps features */
+const GstCapsFeatures *
+ gst_value_get_caps_features (const GValue *value);
+void gst_value_set_caps_features (GValue *value,
+ const GstCapsFeatures *features);
+
/* fraction */
void gst_value_set_fraction (GValue *value,
gint numerator,
diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am
index 632180289..c95de81b2 100644
--- a/tests/check/Makefile.am
+++ b/tests/check/Makefile.am
@@ -106,6 +106,7 @@ check_PROGRAMS = \
gst/gstmemory \
gst/gstbus \
gst/gstcaps \
+ gst/gstcapsfeatures \
$(CXX_CHECKS) \
gst/gstdatetime \
gst/gstinfo \
diff --git a/tests/check/gst/gstcaps.c b/tests/check/gst/gstcaps.c
index 7f6014113..41d266d04 100644
--- a/tests/check/gst/gstcaps.c
+++ b/tests/check/gst/gstcaps.c
@@ -982,6 +982,93 @@ GST_START_TEST (test_broken)
GST_END_TEST;
+GST_START_TEST (test_features)
+{
+ GstCaps *c1, *c2, *c3;
+ GstStructure *s1, *s2;
+ GstCapsFeatures *f1, *f2;
+ gchar *str1;
+ static GstStaticCaps scaps =
+ GST_STATIC_CAPS
+ ("video/x-raw(memory:EGLImage), width=320, height=[ 240, 260 ]");
+
+ c1 = gst_caps_new_empty ();
+ fail_unless (c1 != NULL);
+ s1 = gst_structure_new ("video/x-raw", "width", G_TYPE_INT, 320, "height",
+ GST_TYPE_INT_RANGE, 240, 260, NULL);
+ fail_unless (s1 != NULL);
+ f1 = gst_caps_features_new ("memory:EGLImage", NULL);
+ fail_unless (f1 != NULL);
+
+ gst_caps_append_structure_full (c1, s1, f1);
+ s2 = gst_caps_get_structure (c1, 0);
+ fail_unless (s1 == s2);
+ f2 = gst_caps_get_features (c1, 0);
+ fail_unless (f1 == f2);
+
+ str1 = gst_caps_to_string (c1);
+ fail_unless (str1 != NULL);
+ c2 = gst_caps_from_string (str1);
+ fail_unless (c2 != NULL);
+ g_free (str1);
+
+ fail_unless (gst_caps_is_equal (c1, c2));
+ fail_unless (gst_caps_is_subset (c1, c2));
+ fail_unless (gst_caps_is_subset (c2, c1));
+ fail_unless (gst_caps_can_intersect (c1, c2));
+
+ gst_caps_unref (c2);
+
+ c2 = gst_caps_new_empty ();
+ fail_unless (c2 != NULL);
+ s2 = gst_structure_new ("video/x-raw", "width", G_TYPE_INT, 320, "height",
+ GST_TYPE_INT_RANGE, 240, 260, NULL);
+ fail_unless (s2 != NULL);
+ f2 = gst_caps_features_new ("memory:VASurface", "meta:VAMeta", NULL);
+ fail_unless (f2 != NULL);
+ gst_caps_append_structure_full (c2, s2, f2);
+
+ fail_if (gst_caps_is_equal (c1, c2));
+ fail_if (gst_caps_is_subset (c1, c2));
+ fail_if (gst_caps_is_subset (c2, c1));
+ fail_if (gst_caps_can_intersect (c1, c2));
+
+ str1 = gst_caps_to_string (c2);
+ fail_unless (str1 != NULL);
+ c3 = gst_caps_from_string (str1);
+ fail_unless (c3 != NULL);
+ g_free (str1);
+
+ fail_unless (gst_caps_is_equal (c2, c3));
+ fail_unless (gst_caps_is_subset (c2, c3));
+ fail_unless (gst_caps_is_subset (c3, c2));
+ fail_unless (gst_caps_can_intersect (c2, c3));
+
+ f1 = gst_caps_get_features (c3, 0);
+ fail_unless (f1 != NULL);
+ fail_if (f1 == f2);
+ gst_caps_features_contains (f1, "memory:VASurface");
+ gst_caps_features_remove (f1, "memory:VASurface");
+ fail_if (gst_caps_is_equal (c2, c3));
+ fail_if (gst_caps_is_subset (c2, c3));
+ fail_if (gst_caps_is_subset (c3, c2));
+ fail_if (gst_caps_can_intersect (c2, c3));
+
+ gst_caps_unref (c3);
+ gst_caps_unref (c2);
+
+ c2 = gst_static_caps_get (&scaps);
+ fail_unless (c2 != NULL);
+ fail_unless (gst_caps_is_equal (c1, c2));
+ fail_unless (gst_caps_is_subset (c1, c2));
+ fail_unless (gst_caps_is_subset (c2, c1));
+ fail_unless (gst_caps_can_intersect (c1, c2));
+
+ gst_caps_unref (c1);
+ gst_caps_unref (c2);
+}
+
+GST_END_TEST;
static Suite *
gst_caps_suite (void)
@@ -1009,6 +1096,7 @@ gst_caps_suite (void)
tcase_add_test (tc_chain, test_intersect_duplication);
tcase_add_test (tc_chain, test_normalize);
tcase_add_test (tc_chain, test_broken);
+ tcase_add_test (tc_chain, test_features);
return s;
}
diff --git a/tests/check/gst/gstcapsfeatures.c b/tests/check/gst/gstcapsfeatures.c
new file mode 100644
index 000000000..0c4b04ebf
--- /dev/null
+++ b/tests/check/gst/gstcapsfeatures.c
@@ -0,0 +1,97 @@
+/* GStreamer
+ * Copyright (C) 2013 Collabora Ltd.
+ * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * gstcapsfeatures.c: Unit test for GstCapsFeatures
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/gstcapsfeatures.h>
+
+GST_START_TEST (test_basic_operations)
+{
+ GstCapsFeatures *a, *b;
+
+ a = gst_caps_features_new ("m:abc", "m:def", "m:ghi", NULL);
+ fail_unless (a != NULL);
+ b = gst_caps_features_copy (a);
+ fail_unless (b != NULL);
+ fail_unless (gst_caps_features_is_equal (a, b));
+ fail_if (gst_caps_features_is_equal (a,
+ GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY));
+ fail_unless_equals_int (gst_caps_features_get_size (a), 3);
+ fail_unless_equals_string (gst_caps_features_get_nth (a, 1), "m:def");
+ gst_caps_features_add (b, "m:jkl");
+ fail_if (gst_caps_features_is_equal (a, b));
+ fail_unless_equals_int (gst_caps_features_get_size (b), 4);
+ fail_unless_equals_string (gst_caps_features_get_nth (b, 3), "m:jkl");
+ gst_caps_features_add (b, "m:jkl");
+ fail_unless_equals_int (gst_caps_features_get_size (b), 4);
+
+ gst_caps_features_remove (b, "m:jkl");
+ fail_unless (gst_caps_features_is_equal (a, b));
+ gst_caps_features_remove (b, "m:abc");
+ gst_caps_features_add (b, "m:abc");
+ fail_unless (gst_caps_features_is_equal (a, b));
+ gst_caps_features_remove (b, "m:abc");
+ gst_caps_features_remove (b, "m:def");
+ gst_caps_features_remove (b, "m:ghi");
+ fail_unless (gst_caps_features_is_equal (b,
+ GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY));
+ gst_caps_features_add (b, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY);
+
+ gst_caps_features_free (a);
+ gst_caps_features_free (b);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_from_to_string)
+{
+ GstCapsFeatures *a, *b;
+ gchar *str;
+
+ a = gst_caps_features_new ("m:abc", "m:def", "m:ghi", NULL);
+ fail_unless (a != NULL);
+ str = gst_caps_features_to_string (a);
+ fail_unless (str != NULL);
+ fail_unless_equals_string (str, "m:abc, m:def, m:ghi");
+ b = gst_caps_features_from_string (str);
+ fail_unless (b != NULL);
+ fail_unless (gst_caps_features_is_equal (a, b));
+ gst_caps_features_free (a);
+ gst_caps_features_free (b);
+ g_free (str);
+}
+
+GST_END_TEST;
+
+static Suite *
+gst_capsfeatures_suite (void)
+{
+ Suite *s = suite_create ("GstCapsFeatures");
+ TCase *tc_chain = tcase_create ("operations");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_basic_operations);
+ tcase_add_test (tc_chain, test_from_to_string);
+
+ return s;
+}
+
+GST_CHECK_MAIN (gst_capsfeatures);
diff --git a/win32/common/libgstreamer.def b/win32/common/libgstreamer.def
index 3712007d3..f9b0dc55c 100644
--- a/win32/common/libgstreamer.def
+++ b/win32/common/libgstreamer.def
@@ -34,6 +34,8 @@ EXPORTS
_gst_buffer_list_type DATA
_gst_buffer_type DATA
_gst_caps_any DATA
+ _gst_caps_features_memory_system_memory DATA
+ _gst_caps_features_type DATA
_gst_caps_none DATA
_gst_caps_type DATA
_gst_debug_category_new
@@ -190,11 +192,34 @@ EXPORTS
gst_bus_timed_pop_filtered
gst_caps_append
gst_caps_append_structure
+ gst_caps_append_structure_full
gst_caps_can_intersect
gst_caps_copy_nth
+ gst_caps_features_add
+ gst_caps_features_add_id
+ gst_caps_features_contains
+ gst_caps_features_contains_id
+ gst_caps_features_copy
+ gst_caps_features_free
+ gst_caps_features_from_string
+ gst_caps_features_get_nth
+ gst_caps_features_get_nth_id
+ gst_caps_features_get_size
+ gst_caps_features_get_type
+ gst_caps_features_is_equal
+ gst_caps_features_new
+ gst_caps_features_new_empty
+ gst_caps_features_new_id
+ gst_caps_features_new_id_valist
+ gst_caps_features_new_valist
+ gst_caps_features_remove
+ gst_caps_features_remove_id
+ gst_caps_features_set_parent_refcount
+ gst_caps_features_to_string
gst_caps_fixate
gst_caps_flags_get_type
gst_caps_from_string
+ gst_caps_get_features
gst_caps_get_size
gst_caps_get_structure
gst_caps_get_type
@@ -210,8 +235,10 @@ EXPORTS
gst_caps_is_strictly_equal
gst_caps_is_subset
gst_caps_is_subset_structure
+ gst_caps_is_subset_structure_full
gst_caps_merge
gst_caps_merge_structure
+ gst_caps_merge_structure_full
gst_caps_new_any
gst_caps_new_empty
gst_caps_new_empty_simple
@@ -220,6 +247,7 @@ EXPORTS
gst_caps_new_simple
gst_caps_normalize
gst_caps_remove_structure
+ gst_caps_set_features
gst_caps_set_simple
gst_caps_set_simple_valist
gst_caps_set_value
@@ -516,6 +544,7 @@ EXPORTS
gst_init_get_option_group
gst_int64_range_get_type
gst_int_range_get_type
+ gst_is_caps_features
gst_is_initialized
gst_iterator_copy
gst_iterator_filter
@@ -1277,6 +1306,7 @@ EXPORTS
gst_value_fraction_subtract
gst_value_get_bitmask
gst_value_get_caps
+ gst_value_get_caps_features
gst_value_get_double_range_max
gst_value_get_double_range_min
gst_value_get_fraction_denominator
@@ -1305,6 +1335,7 @@ EXPORTS
gst_value_serialize
gst_value_set_bitmask
gst_value_set_caps
+ gst_value_set_caps_features
gst_value_set_double_range
gst_value_set_fraction
gst_value_set_fraction_range