X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstcaps.c;h=9e6408d574ea34d19b1557bd9575d4de197b992e;hb=0c6f5b3e4c3dc55e684bca1e3fc9a2a9b74407b9;hp=93fd3dd241621cf01beb5f847342f5bc1ead2267;hpb=272142089e415474e55ca85b47b7a8f14dbee3d2;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstcaps.c b/gst/gstcaps.c index 93fd3dd..9e6408d 100644 --- a/gst/gstcaps.c +++ b/gst/gstcaps.c @@ -13,54 +13,50 @@ * * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /** * SECTION:gstcaps + * @title: GstCaps * @short_description: Structure describing sets of media formats - * @see_also: #GstStructure + * @see_also: #GstStructure, #GstMiniObject * - * Caps (capabilities) are lighweight refcounted objects describing media types. + * Caps (capabilities) are lightweight refcounted objects describing media types. * They are composed of an array of #GstStructure. * * Caps are exposed on #GstPadTemplate to describe all possible types a * given pad can handle. They are also stored in the #GstRegistry along with * a description of the #GstElement. * - * Caps are exposed on the element pads using the gst_pad_get_caps() pad + * Caps are exposed on the element pads using the gst_pad_query_caps() pad * function. This function describes the possible types that the pad can * handle or produce at runtime. * - * Caps are also attached to buffers to describe to content of the data - * pointed to by the buffer with gst_buffer_set_caps(). Caps attached to - * a #GstBuffer allow for format negotiation upstream and downstream. - * * A #GstCaps can be constructed with the following code fragment: - * - * - * Creating caps - * - * GstCaps *caps; - * caps = gst_caps_new_simple ("video/x-raw", - * "format", G_TYPE_STRING, "I420", - * "framerate", GST_TYPE_FRACTION, 25, 1, - * "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, - * "width", G_TYPE_INT, 320, - * "height", G_TYPE_INT, 240, - * NULL); - * - * + * |[ + * GstCaps *caps = gst_caps_new_simple ("video/x-raw", + * "format", G_TYPE_STRING, "I420", + * "framerate", GST_TYPE_FRACTION, 25, 1, + * "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, + * "width", G_TYPE_INT, 320, + * "height", G_TYPE_INT, 240, + * NULL); + * ]| * * A #GstCaps is fixed when it has no properties with ranges or lists. Use - * gst_caps_is_fixed() to test for fixed caps. Only fixed caps can be - * set on a #GstPad or #GstBuffer. + * gst_caps_is_fixed() to test for fixed caps. Fixed caps can be used in a + * caps event to notify downstream elements of the current media type. * * Various methods exist to work with the media types such as subtracting * or intersecting. * - * Last reviewed on 2007-02-13 (0.10.10) + * Be aware that the current #GstCaps / #GstStructure serialization into string + * has limited support for nested #GstCaps / #GstStructure fields. It can only + * support one level of nesting. Using more levels will lead to unexpected + * behavior when using serialization features, such as gst_caps_to_string() or + * gst_value_serialize() and their counterparts. */ #ifdef HAVE_CONFIG_H @@ -75,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) @@ -91,7 +93,7 @@ typedef struct _GstCapsImpl /* same as gst_caps_is_any () */ #define CAPS_IS_ANY(caps) \ - (GST_CAPS_FLAGS(caps) & GST_CAPS_FLAG_ANY) + (!!(GST_CAPS_FLAGS(caps) & GST_CAPS_FLAG_ANY)) /* same as gst_caps_is_empty () */ #define CAPS_IS_EMPTY(caps) \ @@ -100,15 +102,22 @@ 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_any (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_storage_unchecked(caps, index) \ + (&g_array_index (GST_CAPS_ARRAY (caps), GstCapsArrayElement, (index)).features) +#define gst_caps_get_features_unchecked(caps, index) \ + (g_atomic_pointer_get (gst_caps_get_features_storage_unchecked (caps, index))) /* 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 (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 */ @@ -137,11 +146,27 @@ _priv_gst_caps_initialize (void) G_TYPE_STRING, gst_caps_transform_to_string); } +void +_priv_gst_caps_cleanup (void) +{ + gst_caps_unref (_gst_caps_any); + _gst_caps_any = NULL; + gst_caps_unref (_gst_caps_none); + _gst_caps_none = NULL; +} + +GstCapsFeatures * +__gst_caps_get_features_unchecked (const GstCaps * caps, guint idx) +{ + return gst_caps_get_features_unchecked (caps, idx); +} + static GstCaps * _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); @@ -150,12 +175,13 @@ _gst_caps_copy (const GstCaps * caps) GST_CAPS_FLAGS (newcaps) = GST_CAPS_FLAGS (caps); n = GST_CAPS_LEN (caps); - GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, caps, "doing copy %p -> %p", - caps, newcaps); + GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "doing copy %p -> %p", caps, newcaps); 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; @@ -166,6 +192,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, @@ -174,33 +201,42 @@ _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_LOG (GST_CAT_CAPS, "freeing caps %p", caps); + GST_CAT_TRACE (GST_CAT_CAPS, "freeing caps %p", caps); #endif - g_slice_free1 (GST_MINI_OBJECT_SIZE (caps), caps); + +#ifdef USE_POISONING + memset (caps, 0xff, sizeof (GstCapsImpl)); +#endif + + g_slice_free1 (sizeof (GstCapsImpl), caps); } static void -gst_caps_init (GstCaps * caps, gsize size) +gst_caps_init (GstCaps * caps) { - gst_mini_object_init (GST_MINI_OBJECT_CAST (caps), _gst_caps_type, size); - - caps->mini_object.copy = (GstMiniObjectCopyFunction) _gst_caps_copy; - caps->mini_object.dispose = NULL; - caps->mini_object.free = (GstMiniObjectFreeFunction) _gst_caps_free; + gst_mini_object_init (GST_MINI_OBJECT_CAST (caps), 0, _gst_caps_type, + (GstMiniObjectCopyFunction) _gst_caps_copy, NULL, + (GstMiniObjectFreeFunction) _gst_caps_free); /* the 32 has been determined by logging caps sizes in _gst_caps_free * but g_ptr_array uses 16 anyway if it expands once, so this does not help - * in practise + * 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)); } /** @@ -220,7 +256,7 @@ gst_caps_new_empty (void) caps = (GstCaps *) g_slice_new (GstCapsImpl); - gst_caps_init (caps, sizeof (GstCapsImpl)); + gst_caps_init (caps); #ifdef DEBUG_REFCOUNT GST_CAT_TRACE (GST_CAT_CAPS, "created caps %p", caps); @@ -266,7 +302,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; } @@ -298,7 +334,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); @@ -311,7 +347,7 @@ gst_caps_new_simple (const char *media_type, const char *fieldname, ...) * @...: additional structures to add * * Creates a new #GstCaps and adds all the structures listed as - * arguments. The list must be NULL-terminated. The structures + * arguments. The list must be %NULL-terminated. The structures * are not copied; the returned #GstCaps owns the structures. * * Returns: (transfer full): the new #GstCaps @@ -335,7 +371,7 @@ gst_caps_new_full (GstStructure * struct1, ...) * @var_args: additional structures to add * * Creates a new #GstCaps and adds all the structures listed as - * arguments. The list must be NULL-terminated. The structures + * arguments. The list must be %NULL-terminated. The structures * are not copied; the returned #GstCaps owns the structures. * * Returns: (transfer full): the new #GstCaps @@ -348,7 +384,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 *); } @@ -363,9 +399,10 @@ G_DEFINE_POINTER_TYPE (GstStaticCaps, gst_static_caps); * * Converts a #GstStaticCaps to a #GstCaps. * - * Returns: (transfer full): a pointer to the #GstCaps. Unref after usage. - * Since the core holds an additional ref to the returned caps, - * use gst_caps_make_writable() on the returned caps to modify it. + * Returns: (transfer full) (nullable): a pointer to the #GstCaps. Unref + * after usage. Since the core holds an additional ref to the + * returned caps, use gst_caps_make_writable() on the returned caps + * to modify it. */ GstCaps * gst_static_caps_get (GstStaticCaps * static_caps) @@ -393,8 +430,13 @@ gst_static_caps_get (GstStaticCaps * static_caps) *caps = gst_caps_from_string (string); /* convert to string */ - if (G_UNLIKELY (*caps == NULL)) + if (G_UNLIKELY (*caps == NULL)) { g_critical ("Could not convert static caps \"%s\"", string); + goto done; + } + + /* Caps generated from static caps are usually leaked */ + GST_MINI_OBJECT_FLAG_SET (*caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED); GST_CAT_TRACE (GST_CAT_CAPS, "created %p from string %s", static_caps, string); @@ -432,13 +474,39 @@ 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; } @@ -447,13 +515,11 @@ gst_caps_remove_and_get_structure (GstCaps * caps, guint idx) * @caps: the #GstCaps to retrieve from * @index: Index of the structure to retrieve * - * Retrieves the stucture with the given index from the list of structures + * Retrieves the structure with the given index from the list of structures * contained in @caps. The caller becomes the owner of the returned structure. * - * Returns: (transfer full): a pointer to the #GstStructure corresponding - * to @index. - * - * Since: 0.10.30 + * Returns: (transfer full) (nullable): a pointer to the #GstStructure + * corresponding to @index. */ GstStructure * gst_caps_steal_structure (GstCaps * caps, guint index) @@ -480,6 +546,7 @@ void gst_caps_append (GstCaps * caps1, GstCaps * caps2) { GstStructure *structure; + GstCapsFeatures *features; int i; g_return_if_fail (GST_IS_CAPS (caps1)); @@ -493,8 +560,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 */ } @@ -511,13 +579,12 @@ gst_caps_append (GstCaps * caps1, GstCaps * caps2) * If either caps is ANY, the resulting caps will be ANY. * * Returns: (transfer full): the merged caps. - * - * Since: 0.10.10 */ GstCaps * gst_caps_merge (GstCaps * caps1, GstCaps * caps2) { GstStructure *structure; + GstCapsFeatures *features; int i; GstCaps *result; @@ -534,8 +601,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; @@ -569,7 +637,30 @@ 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. + * + * Since: 1.2 + */ +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); } } @@ -578,7 +669,7 @@ gst_caps_append_structure (GstCaps * caps, GstStructure * structure) * @caps: the #GstCaps to remove from * @idx: Index of the structure to remove * - * removes the stucture with the given index from the list of structures + * removes the structure with the given index from the list of structures * contained in @caps. */ void @@ -607,6 +698,58 @@ 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_caps_features_is_equal (features1, + GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY) + && gst_structure_is_subset (structure, structure1)) { + 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. + * + * Since: 1.2 + */ +GstCaps * +gst_caps_merge_structure_full (GstCaps * caps, GstStructure * structure, + GstCapsFeatures * features) +{ + GstStructure *structure1; + GstCapsFeatures *features1, *features_tmp; int i; gboolean unique = TRUE; @@ -615,20 +758,37 @@ 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 */ + /* FIXME: We only skip if none of the features are + * ANY and are still equal. That way all ANY structures + * show up in the caps and no non-ANY structures are + * swallowed by ANY structures + */ + if (((!gst_caps_features_is_any (features_tmp) + || gst_caps_features_is_any (features1)) + && gst_caps_features_is_equal (features_tmp, features1)) + && gst_structure_is_subset (structure, structure1)) { 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; } @@ -682,6 +842,129 @@ 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) (nullable): a pointer to the #GstCapsFeatures + * corresponding to @index + * + * Since: 1.2 + */ +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) { + GstCapsFeatures **storage; + + /* We have to do some atomic pointer magic here as the caps + * might not be writable and someone else calls this function + * at the very same time */ + features = gst_caps_features_copy (GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY); + gst_caps_features_set_parent_refcount (features, &GST_CAPS_REFCOUNT (caps)); + + storage = gst_caps_get_features_storage_unchecked (caps, index); + if (!g_atomic_pointer_compare_and_exchange (storage, NULL, features)) { + /* Someone did the same we just tried in the meantime */ + gst_caps_features_set_parent_refcount (features, NULL); + gst_caps_features_free (features); + + features = gst_caps_get_features_unchecked (caps, index); + g_assert (features != NULL); + } + } + + return features; +} + +/** + * gst_caps_set_features: + * @caps: a #GstCaps + * @index: the index of the structure + * @features: (allow-none) (transfer full): the #GstCapsFeatures to set + * + * Sets the #GstCapsFeatures @features for the structure at @index. + * + * Since: 1.2 + */ +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_storage_unchecked (caps, index); + /* Not much problem here as caps are writable */ + old = g_atomic_pointer_get (storage); + g_atomic_pointer_set (storage, features); + + if (features) + gst_caps_features_set_parent_refcount (features, &GST_CAPS_REFCOUNT (caps)); + + if (old) { + gst_caps_features_set_parent_refcount (old, NULL); + gst_caps_features_free (old); + } +} + +/** + * gst_caps_set_features_simple: + * @caps: a #GstCaps + * @features: (allow-none) (transfer full): the #GstCapsFeatures to set + * + * Sets the #GstCapsFeatures @features for all the structures of @caps. + * + * Since: 1.16 + */ +void +gst_caps_set_features_simple (GstCaps * caps, GstCapsFeatures * features) +{ + guint i; + guint n; + + g_return_if_fail (caps != NULL); + g_return_if_fail (IS_WRITABLE (caps)); + + n = gst_caps_get_size (caps); + + for (i = 0; i < n; i++) { + GstCapsFeatures *f; + + /* Transfer ownership of @features to the last structure */ + if (features && i < n - 1) + f = gst_caps_features_copy (features); + else + f = features; + + gst_caps_set_features (caps, i, f); + } +} + +/** * gst_caps_copy_nth: * @caps: the #GstCaps to copy * @nth: the nth structure to copy @@ -690,12 +973,15 @@ gst_caps_get_structure (const GstCaps * caps, guint index) * contained in @caps. * * Returns: (transfer full): the new #GstCaps + * + * Since: 1.16 */ GstCaps * 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); @@ -704,8 +990,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; @@ -718,6 +1006,10 @@ gst_caps_copy_nth (const GstCaps * caps, guint nth) * Discard all but the first structure from @caps. Useful when * fixating. * + * This function takes ownership of @caps and will call gst_caps_make_writable() + * on it if necessary, so you must not use @caps afterwards unless you keep an + * additional reference to it with gst_caps_ref(). + * * Returns: (transfer full): truncated caps */ GstCaps * @@ -747,8 +1039,6 @@ gst_caps_truncate (GstCaps * caps) * Sets the given @field on all structures of @caps to the given @value. * This is a convenience function for calling gst_structure_set_value() on * all structures of @caps. - * - * Since: 0.10.26 **/ void gst_caps_set_value (GstCaps * caps, const char *field, const GValue * value) @@ -774,11 +1064,7 @@ gst_caps_set_value (GstCaps * caps, const char *field, const GValue * value) * @varargs: additional parameters * * Sets fields in a #GstCaps. The arguments must be passed in the same - * manner as gst_structure_set(), and be NULL-terminated. - * Prior to GStreamer version 0.10.26, this function failed when - * @caps was not simple. If your code needs to work with those versions - * of GStreamer, you may only call this function when GST_CAPS_IS_SIMPLE() - * is %TRUE for @caps. + * manner as gst_structure_set(), and be %NULL-terminated. */ void gst_caps_set_simple_valist (GstCaps * caps, const char *field, va_list varargs) @@ -797,6 +1083,7 @@ gst_caps_set_simple_valist (GstCaps * caps, const char *field, va_list varargs) G_VALUE_COLLECT_INIT (&value, type, varargs, 0, &err); if (G_UNLIKELY (err)) { g_critical ("%s", err); + g_free (err); return; } @@ -815,11 +1102,7 @@ gst_caps_set_simple_valist (GstCaps * caps, const char *field, va_list varargs) * @...: additional parameters * * Sets fields in a #GstCaps. The arguments must be passed in the same - * manner as gst_structure_set(), and be NULL-terminated. - * Prior to GStreamer version 0.10.26, this function failed when - * @caps was not simple. If your code needs to work with those versions - * of GStreamer, you may only call this function when GST_CAPS_IS_SIMPLE() - * is %TRUE for @caps. + * manner as gst_structure_set(), and be %NULL-terminated. */ void gst_caps_set_simple (GstCaps * caps, const char *field, ...) @@ -842,7 +1125,7 @@ gst_caps_set_simple (GstCaps * caps, const char *field, ...) * * Determines if @caps represents any media format. * - * Returns: TRUE if @caps represents any format. + * Returns: %TRUE if @caps represents any format. */ gboolean gst_caps_is_any (const GstCaps * caps) @@ -858,7 +1141,7 @@ gst_caps_is_any (const GstCaps * caps) * * Determines if @caps represents no media formats. * - * Returns: TRUE if @caps represents no formats. + * Returns: %TRUE if @caps represents no formats. */ gboolean gst_caps_is_empty (const GstCaps * caps) @@ -886,18 +1169,23 @@ gst_caps_is_fixed_foreach (GQuark field_id, const GValue * value, * one structure, and each field in the structure describes a fixed type. * Examples of non-fixed types are GST_TYPE_INT_RANGE and GST_TYPE_LIST. * - * Returns: TRUE if @caps is fixed + * Returns: %TRUE if @caps is fixed */ gboolean gst_caps_is_fixed (const GstCaps * caps) { GstStructure *structure; + GstCapsFeatures *features; g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); if (GST_CAPS_LEN (caps) != 1) return FALSE; + features = gst_caps_get_features_unchecked (caps, 0); + if (features && gst_caps_features_is_any (features)) + return FALSE; + structure = gst_caps_get_structure_unchecked (caps, 0); return gst_structure_foreach (structure, gst_caps_is_fixed_foreach, NULL); @@ -911,20 +1199,28 @@ gst_caps_is_fixed (const GstCaps * caps) * Tests if two #GstCaps are equal. This function only works on fixed * #GstCaps. * - * Returns: TRUE if the arguments represent the same format + * Returns: %TRUE if the arguments represent the same format */ 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); } /** @@ -936,7 +1232,7 @@ gst_caps_is_equal_fixed (const GstCaps * caps1, const GstCaps * caps2) * every media format that is in the first is also contained in the * second. That is, @caps1 is a subset of @caps2. * - * Returns: TRUE if @caps1 is a subset of @caps2. + * Returns: %TRUE if @caps1 is a subset of @caps2. */ gboolean gst_caps_is_always_compatible (const GstCaps * caps1, const GstCaps * caps2) @@ -953,8 +1249,6 @@ gst_caps_is_always_compatible (const GstCaps * caps1, const GstCaps * caps2) * @superset: a potentially greater #GstCaps * * Checks if all caps represented by @subset are also represented by @superset. - * This function does not work reliably if optional properties for caps - * are included on one caps and omitted on the other. * * Returns: %TRUE if @subset is a subset of @superset */ @@ -962,6 +1256,7 @@ gboolean gst_caps_is_subset (const GstCaps * subset, const GstCaps * superset) { GstStructure *s1, *s2; + GstCapsFeatures *f1, *f2; gboolean ret = TRUE; gint i, j; @@ -976,8 +1271,16 @@ 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_caps_features_is_any (f1) || gst_caps_features_is_any (f2)) && + gst_caps_features_is_equal (f1, f2) + && gst_structure_is_subset (s1, s2)) { /* If we found a superset, continue with the next * subset structure */ break; @@ -1003,8 +1306,6 @@ gst_caps_is_subset (const GstCaps * subset, const GstCaps * superset) * for more information. * * Returns: %TRUE if @structure is a subset of @caps - * - * Since: 0.10.36 */ gboolean gst_caps_is_subset_structure (const GstCaps * caps, @@ -1033,17 +1334,61 @@ 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 + * + * Since: 1.2 + */ +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_caps_features_is_any (features) || gst_caps_features_is_any (f)) + && gst_caps_features_is_equal (features, f) + && gst_structure_is_subset (structure, s)) { + /* If we found a superset return TRUE */ + return TRUE; + } + } + + return FALSE; +} + +/** * gst_caps_is_equal: * @caps1: a #GstCaps * @caps2: another #GstCaps * * Checks if the given caps represent the same set of caps. - * This function does not work reliably if optional properties for caps - * are included on one caps and omitted on the other. - * - * This function deals correctly with passing NULL for any of the caps. * - * Returns: TRUE if both caps are equal. + * Returns: %TRUE if both caps are equal. */ gboolean gst_caps_is_equal (const GstCaps * caps1, const GstCaps * caps2) @@ -1067,16 +1412,14 @@ gst_caps_is_equal (const GstCaps * caps1, const GstCaps * caps2) * * Checks if the given caps are exactly the same set of caps. * - * This function deals correctly with passing NULL for any of the caps. - * - * Returns: TRUE if both caps are strictly equal. - * - * Since: 0.10.36 + * Returns: %TRUE if both caps are strictly equal. */ 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); @@ -1088,8 +1431,18 @@ 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_caps_features_is_any (f1) != gst_caps_features_is_any (f2) || + !gst_caps_features_is_equal (f1, f2) || + !gst_structure_is_equal (s1, s2)) return FALSE; } @@ -1107,8 +1460,6 @@ gst_caps_is_strictly_equal (const GstCaps * caps1, const GstCaps * caps2) * be empty * * Returns: %TRUE if intersection would be not empty - * - * Since: 0.10.25 */ gboolean gst_caps_can_intersect (const GstCaps * caps1, const GstCaps * caps2) @@ -1117,6 +1468,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); @@ -1137,7 +1490,7 @@ gst_caps_can_intersect (const GstCaps * caps1, const GstCaps * caps2) * much better than a simple loop. * * This algorithm zigzags over the caps structures as demonstrated in - * the folowing matrix: + * the following matrix: * * caps1 0 1 2 3 * +------------- total distance: +------------- @@ -1153,19 +1506,24 @@ gst_caps_can_intersect (const GstCaps * caps1, const GstCaps * caps2) len1 = GST_CAPS_LEN (caps1); len2 = GST_CAPS_LEN (caps2); for (i = 0; i < len1 + len2 - 1; i++) { - /* superset index goes from 0 to sgst_caps_structure_intersectuperset->structs->len-1 */ + /* superset index goes from 0 to superset->structs->len-1 */ j = MIN (i, len1 - 1); /* 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_caps_features_is_equal (features1, features2) && + gst_structure_can_intersect (struct1, struct2)) { return TRUE; } /* move down left */ @@ -1175,6 +1533,7 @@ gst_caps_can_intersect (const GstCaps * caps1, const GstCaps * caps2) j--; } } + return FALSE; } @@ -1183,9 +1542,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; @@ -1200,16 +1560,16 @@ 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. * * This algorithm zigzags over the caps structures as demonstrated in - * the folowing matrix: + * the following matrix: * * caps1 * +------------- @@ -1229,16 +1589,30 @@ 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); - - istruct = gst_structure_intersect (struct1, struct2); - - dest = gst_caps_merge_structure (dest, istruct); + features2 = gst_caps_get_features_unchecked (caps2, k); + if (!features2) + features2 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY; + if (gst_caps_features_is_equal (features1, features2)) { + istruct = gst_structure_intersect (struct1, struct2); + if (istruct) { + if (gst_caps_features_is_any (features1)) + dest = + gst_caps_merge_structure_full (dest, istruct, + gst_caps_features_copy_conditional (features2)); + else + dest = + gst_caps_merge_structure_full (dest, istruct, + gst_caps_features_copy_conditional (features1)); + } + } /* move down left */ k++; if (G_UNLIKELY (j == 0)) @@ -1260,16 +1634,17 @@ gst_caps_intersect_zig_zag (GstCaps * caps1, GstCaps * caps2) * Unlike @gst_caps_intersect, the returned caps will be ordered in a similar * fashion as @caps1. * - * Returns: the new #GstCaps + * Returns: (transfer full): the new #GstCaps */ static GstCaps * 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; @@ -1284,20 +1659,36 @@ 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); - istruct = gst_structure_intersect (struct1, struct2); - if (istruct) - dest = gst_caps_merge_structure (dest, istruct); + features2 = gst_caps_get_features_unchecked (caps2, j); + if (!features2) + features2 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY; + if (gst_caps_features_is_equal (features1, features2)) { + istruct = gst_structure_intersect (struct1, struct2); + if (istruct) { + if (gst_caps_features_is_any (features1)) + dest = + gst_caps_merge_structure_full (dest, istruct, + gst_caps_features_copy_conditional (features2)); + else + dest = + gst_caps_merge_structure_full (dest, istruct, + gst_caps_features_copy_conditional (features1)); + } + } } } @@ -1314,8 +1705,7 @@ gst_caps_intersect_first (GstCaps * caps1, GstCaps * caps2) * to both @caps1 and @caps2, the order is defined by the #GstCapsIntersectMode * used. * - * Returns: the new #GstCaps - * Since: 0.10.33 + * Returns: (transfer full): the new #GstCaps */ GstCaps * gst_caps_intersect_full (GstCaps * caps1, GstCaps * caps2, @@ -1343,7 +1733,7 @@ gst_caps_intersect_full (GstCaps * caps1, GstCaps * caps2, * Creates a new #GstCaps that contains all the formats that are common * to both @caps1 and @caps2. Defaults to %GST_CAPS_INTERSECT_ZIG_ZAG mode. * - * Returns: the new #GstCaps + * Returns: (transfer full): the new #GstCaps */ GstCaps * gst_caps_intersect (GstCaps * caps1, GstCaps * caps2) @@ -1351,15 +1741,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, @@ -1371,18 +1759,20 @@ 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; } else { structure = gst_structure_copy (e->subtract_from); - gst_structure_id_set_value (structure, field_id, &subtraction); - g_value_unset (&subtraction); + gst_structure_id_take_value (structure, field_id, &subtraction); e->put_into = g_slist_prepend (e->put_into, structure); return TRUE; } @@ -1397,9 +1787,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 { @@ -1410,6 +1800,7 @@ gst_caps_structure_subtract (GSList ** into, const GstStructure * minuend, } g_slist_free (e.put_into); } + return ret; } @@ -1419,10 +1810,10 @@ gst_caps_structure_subtract (GSList ** into, const GstStructure * minuend, * @subtrahend: #GstCaps to subtract * * Subtracts the @subtrahend from the @minuend. - * This function does not work reliably if optional properties for caps - * are included on one caps and omitted on the other. + * > This function does not work reliably if optional properties for caps + * > are included on one caps and omitted on the other. * - * Returns: the resulting caps + * Returns: (transfer full): the resulting caps */ GstCaps * gst_caps_subtract (GstCaps * minuend, GstCaps * subtrahend) @@ -1430,6 +1821,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); @@ -1438,6 +1830,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); @@ -1446,6 +1839,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); @@ -1455,6 +1849,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; @@ -1463,7 +1860,15 @@ 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; + + /* Same reason as above for ANY caps */ + g_return_val_if_fail (!gst_caps_features_is_any (min_f), NULL); + + 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)) { @@ -1471,16 +1876,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; @@ -1489,6 +1898,7 @@ gst_caps_subtract (GstCaps * minuend, GstCaps * subtrahend) gst_caps_unref (src); dest = gst_caps_simplify (dest); + return dest; } @@ -1498,9 +1908,8 @@ typedef struct _NormalizeForeach { GstCaps *caps; GstStructure *structure; - gboolean writable; -} -NormalizeForeach; + GstCapsFeatures *features; +} NormalizeForeach; static gboolean gst_caps_normalize_foreach (GQuark field_id, const GValue * value, gpointer ptr) @@ -1511,24 +1920,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); - if (G_UNLIKELY (!nf->writable)) { - nf->caps = gst_caps_make_writable (nf->caps); - nf->writable = TRUE; - } - 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_set_value (nf->structure, field_id, &val); - g_value_unset (&val); - + gst_structure_id_take_value (nf->structure, field_id, &val); return FALSE; } + return TRUE; } @@ -1540,7 +1946,9 @@ gst_caps_normalize_foreach (GQuark field_id, const GValue * value, gpointer ptr) * @caps, but contains no lists. Each list is expanded into separate * @GstStructures. * - * This function takes ownership of @caps. + * This function takes ownership of @caps and will call gst_caps_make_writable() + * on it so you must not use @caps afterwards unless you keep an additional + * reference to it with gst_caps_ref(). * * Returns: (transfer full): the normalized #GstCaps */ @@ -1552,12 +1960,12 @@ gst_caps_normalize (GstCaps * caps) g_return_val_if_fail (GST_IS_CAPS (caps), NULL); + caps = gst_caps_make_writable (caps); nf.caps = caps; - nf.writable = FALSE; 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)); } @@ -1569,13 +1977,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; @@ -1587,8 +1996,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, @@ -1602,14 +2010,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; } @@ -1646,12 +2058,14 @@ gst_caps_structure_simplify (GstStructure ** result, * but at most one field: field.name */ if (G_IS_VALUE (&field.value)) { if (gst_structure_n_fields (simplify) == gst_structure_n_fields (compare)) { - gst_structure_id_set_value (compare, field.name, &field.value); + gst_structure_id_take_value (compare, field.name, &field.value); *result = NULL; ret = TRUE; + } else { + g_value_unset (&field.value); } - 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? */ @@ -1679,7 +2093,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; } /** @@ -1691,12 +2105,19 @@ gst_caps_switch_structures (GstCaps * caps, GstStructure * old, * identical are merged. Component structures that have values that can be * merged are also merged. * - * Returns: The simplified caps. + * This function takes ownership of @caps and will call gst_caps_make_writable() + * on it if necessary, so you must not use @caps afterwards unless you keep an + * additional reference to it with gst_caps_ref(). + * + * This method does not preserve the original order of @caps. + * + * Returns: (transfer full): The simplified caps. */ 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); @@ -1708,20 +2129,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)) { @@ -1747,12 +2179,17 @@ gst_caps_simplify (GstCaps * caps) * values. First the caps will be truncated and then the first structure will be * fixated with gst_structure_fixate(). * + * This function takes ownership of @caps and will call gst_caps_make_writable() + * on it so you must not use @caps afterwards unless you keep an additional + * reference to it with gst_caps_ref(). + * * Returns: (transfer full): the fixated caps */ GstCaps * gst_caps_fixate (GstCaps * caps) { GstStructure *s; + GstCapsFeatures *f; g_return_val_if_fail (GST_IS_CAPS (caps), NULL); @@ -1762,6 +2199,13 @@ gst_caps_fixate (GstCaps * caps) s = gst_caps_get_structure (caps, 0); gst_structure_fixate (s); + /* Set features to sysmem if they're still ANY */ + f = gst_caps_get_features_unchecked (caps, 0); + if (f && gst_caps_features_is_any (f)) { + f = gst_caps_features_new_empty (); + gst_caps_set_features (caps, 0, f); + } + return caps; } @@ -1775,10 +2219,13 @@ gst_caps_fixate (GstCaps * caps) * can be converted back to a #GstCaps by gst_caps_from_string(). * * For debugging purposes its easier to do something like this: - * |[ + * |[ * GST_LOG ("caps are %" GST_PTR_FORMAT, caps); * ]| - * This prints the caps in human readble form. + * This prints the caps in human readable form. + * + * The current implementation of serialization will lead to unexpected results + * when there are nested #GstCaps / #GstStructure deeper than one level. * * Returns: (transfer full): a newly allocated string representing @caps. */ @@ -1808,14 +2255,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 */ @@ -1823,6 +2276,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_any (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] == ';') { @@ -1836,37 +2299,97 @@ 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) { - return TRUE; - } - structure = gst_structure_from_string (string, &s); - if (structure == NULL) { - return FALSE; + if (strcmp ("EMPTY", string) == 0 || strcmp ("NONE", string) == 0) { + return TRUE; } - 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); + if (features) + gst_caps_features_free (features); + g_free (copy); return FALSE; } - gst_caps_append_structure_unchecked (caps, structure); + append: + gst_caps_append_structure_unchecked (caps, structure, features); + features = NULL; + if (*s == '\0') + break; } while (TRUE); + g_free (copy); + return TRUE; } @@ -1876,7 +2399,10 @@ gst_caps_from_string_inplace (GstCaps * caps, const gchar * string) * * Converts @caps from a string representation. * - * Returns: (transfer full): a newly allocated #GstCaps + * The current implementation of serialization will lead to unexpected results + * when there are nested #GstCaps / #GstStructure deeper than one level. + * + * Returns: (transfer full) (nullable): a newly allocated #GstCaps */ GstCaps * gst_caps_from_string (const gchar * string) @@ -1906,3 +2432,171 @@ gst_caps_transform_to_string (const GValue * src_value, GValue * dest_value) g_value_take_string (dest_value, gst_caps_to_string (gst_value_get_caps (src_value))); } + +/** + * gst_caps_foreach: + * @caps: a #GstCaps + * @func: (scope call): a function to call for each field + * @user_data: (closure): private data + * + * Calls the provided function once for each structure and caps feature in the + * #GstCaps. The function must not modify the fields. + * Also see gst_caps_map_in_place() and gst_caps_filter_and_map_in_place(). + * + * Returns: %TRUE if the supplied function returns %TRUE for each call, + * %FALSE otherwise. + * + * Since: 1.6 + */ +gboolean +gst_caps_foreach (const GstCaps * caps, GstCapsForeachFunc func, + gpointer user_data) +{ + guint i, n; + GstCapsFeatures *features; + GstStructure *structure; + gboolean ret; + + g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); + g_return_val_if_fail (func != NULL, FALSE); + + n = GST_CAPS_LEN (caps); + + for (i = 0; i < n; i++) { + features = gst_caps_get_features_unchecked (caps, i); + structure = gst_caps_get_structure_unchecked (caps, i); + + ret = func (features, structure, user_data); + if (G_UNLIKELY (!ret)) + return FALSE; + } + + return TRUE; +} + +/** + * gst_caps_map_in_place: + * @caps: a #GstCaps + * @func: (scope call): a function to call for each field + * @user_data: (closure): private data + * + * Calls the provided function once for each structure and caps feature in the + * #GstCaps. In contrast to gst_caps_foreach(), the function may modify but not + * delete the structures and features. The caps must be mutable. + * + * Returns: %TRUE if the supplied function returns %TRUE for each call, + * %FALSE otherwise. + * + * Since: 1.6 + */ +gboolean +gst_caps_map_in_place (GstCaps * caps, GstCapsMapFunc func, gpointer user_data) +{ + guint i, n; + GstCapsFeatures *features; + GstStructure *structure; + gboolean ret; + + g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); + g_return_val_if_fail (gst_caps_is_writable (caps), FALSE); + g_return_val_if_fail (func != NULL, FALSE); + + n = GST_CAPS_LEN (caps); + + for (i = 0; i < n; i++) { + features = gst_caps_get_features_unchecked (caps, i); + structure = gst_caps_get_structure_unchecked (caps, i); + + /* Provide sysmem features if there are none yet */ + if (!features) { + features = + gst_caps_features_copy (GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY); + gst_caps_set_features (caps, i, features); + } + + ret = func (features, structure, user_data); + if (G_UNLIKELY (!ret)) + return FALSE; + } + + return TRUE; +} + +/** + * gst_caps_filter_and_map_in_place: + * @caps: a #GstCaps + * @func: (scope call): a function to call for each field + * @user_data: (closure): private data + * + * Calls the provided function once for each structure and caps feature in the + * #GstCaps. In contrast to gst_caps_foreach(), the function may modify the + * structure and features. In contrast to gst_caps_filter_and_map_in_place(), + * the structure and features are removed from the caps if %FALSE is returned + * from the function. + * The caps must be mutable. + * + * Since: 1.6 + */ +void +gst_caps_filter_and_map_in_place (GstCaps * caps, GstCapsFilterMapFunc func, + gpointer user_data) +{ + guint i, n; + GstCapsFeatures *features; + GstStructure *structure; + gboolean ret; + + g_return_if_fail (GST_IS_CAPS (caps)); + g_return_if_fail (gst_caps_is_writable (caps)); + g_return_if_fail (func != NULL); + + n = GST_CAPS_LEN (caps); + + for (i = 0; i < n;) { + features = gst_caps_get_features_unchecked (caps, i); + structure = gst_caps_get_structure_unchecked (caps, i); + + /* Provide sysmem features if there are none yet */ + if (!features) { + features = + gst_caps_features_copy (GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY); + gst_caps_set_features (caps, i, features); + } + + ret = func (features, structure, user_data); + if (!ret) { + GST_CAPS_ARRAY (caps) = g_array_remove_index (GST_CAPS_ARRAY (caps), i); + + gst_structure_set_parent_refcount (structure, NULL); + gst_structure_free (structure); + if (features) { + gst_caps_features_set_parent_refcount (features, NULL); + gst_caps_features_free (features); + } + + n = GST_CAPS_LEN (caps); + } else { + i++; + } + } +} + +/** + * gst_caps_copy: + * @caps: a #GstCaps. + * + * Creates a new #GstCaps as a copy of the old @caps. The new caps will have a + * refcount of 1, owned by the caller. The structures are copied as well. + * + * Note that this function is the semantic equivalent of a gst_caps_ref() + * followed by a gst_caps_make_writable(). If you only want to hold on to a + * reference to the data, you should use gst_caps_ref(). + * + * When you are finished with the caps, call gst_caps_unref() on it. + * + * Returns: the new #GstCaps + */ +GstCaps *(gst_caps_copy) (const GstCaps * caps) +{ + return GST_CAPS (gst_mini_object_copy (GST_MINI_OBJECT_CAST (caps))); +}