diff options
-rw-r--r-- | docs/gst/gstreamer-docs.sgml | 1 | ||||
-rw-r--r-- | docs/gst/gstreamer-sections.txt | 52 | ||||
-rw-r--r-- | gst/Makefile.am | 2 | ||||
-rw-r--r-- | gst/gst.c | 1 | ||||
-rw-r--r-- | gst/gst.h | 1 | ||||
-rw-r--r-- | gst/gst_private.h | 10 | ||||
-rw-r--r-- | gst/gstcaps.c | 554 | ||||
-rw-r--r-- | gst/gstcaps.h | 15 | ||||
-rw-r--r-- | gst/gstcapsfeatures.c | 740 | ||||
-rw-r--r-- | gst/gstcapsfeatures.h | 74 | ||||
-rw-r--r-- | gst/gstinfo.c | 4 | ||||
-rw-r--r-- | gst/gststructure.c | 111 | ||||
-rw-r--r-- | gst/gstvalue.c | 81 | ||||
-rw-r--r-- | gst/gstvalue.h | 15 | ||||
-rw-r--r-- | tests/check/Makefile.am | 1 | ||||
-rw-r--r-- | tests/check/gst/gstcaps.c | 88 | ||||
-rw-r--r-- | tests/check/gst/gstcapsfeatures.c | 97 | ||||
-rw-r--r-- | win32/common/libgstreamer.def | 31 |
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 \ @@ -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 ()); @@ -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 |