gstcaps: remove unneeded use of gint64
[platform/upstream/gstreamer.git] / gst / gstcaps.c
index 116c1d1..92fd995 100644 (file)
  * 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 registry along with
- * a description of the element.
+ * 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 
- * function. This function describes the possible types that the pad can 
+ * Caps are exposed on the element pads using the gst_pad_get_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
  *  GstCaps *caps;
  *  caps = gst_caps_new_simple ("video/x-raw-yuv",
  *       "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
- *       "framerate", G_TYPE_DOUBLE, 25.0,
+ *       "framerate", GST_TYPE_FRACTION, 25, 1,
  *       "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
- *       "width", G_TYPE_INT, 320, 
- *       "height", G_TYPE_INT, 240, 
+ *       "width", G_TYPE_INT, 320,
+ *       "height", G_TYPE_INT, 240,
  *       NULL);
  *  </programlisting>
  * </example>
@@ -60,7 +60,7 @@
  * Various methods exist to work with the media types such as subtracting
  * or intersecting.
  *
- * Last reviewed on 2005-11-23 (0.9.5)
+ * Last reviewed on 2007-02-13 (0.10.10)
  */
 
 #ifdef HAVE_CONFIG_H
 
 #include "gst_private.h"
 #include <gst/gst.h>
+#include <gobject/gvaluecollector.h>
+
+#ifdef GST_DISABLE_DEPRECATED
+#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
+#include <libxml/parser.h>
+xmlNodePtr gst_caps_save_thyself (const GstCaps * caps, xmlNodePtr parent);
+GstCaps *gst_caps_load_thyself (xmlNodePtr parent);
+#endif
+#endif
 
 #define DEBUG_REFCOUNT
 
 #define IS_WRITABLE(caps) \
   (g_atomic_int_get (&(caps)->refcount) == 1)
 
+/* same as gst_caps_is_any () */
+#define CAPS_IS_ANY(caps)                              \
+  ((caps)->flags & GST_CAPS_FLAGS_ANY)
+
+/* same as gst_caps_is_empty () */
+#define CAPS_IS_EMPTY(caps)                            \
+  (!CAPS_IS_ANY(caps) && CAPS_IS_EMPTY_SIMPLE(caps))
+
+#define CAPS_IS_EMPTY_SIMPLE(caps)                                     \
+  (((caps)->structs == NULL) || ((caps)->structs->len == 0))
+
+/* 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 ((caps)->structs, (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;                                      \
+  gst_structure_set_parent_refcount (__s, &caps->refcount);         \
+  g_ptr_array_add (caps->structs, __s);                             \
+}G_STMT_END
+
+/* lock to protect multiple invocations of static caps to caps conversion */
+G_LOCK_DEFINE_STATIC (static_caps_lock);
 
 static void gst_caps_transform_to_string (const GValue * src_value,
     GValue * dest_value);
@@ -124,19 +157,25 @@ gst_caps_get_type (void)
  * #GstCaps contains no media formats.
  * Caller is responsible for unreffing the returned caps.
  *
- * Returns: the new #GstCaps
+ * Returns: (transfer full): the new #GstCaps
  */
 GstCaps *
 gst_caps_new_empty (void)
 {
-  GstCaps *caps = g_new0 (GstCaps, 1);
+  GstCaps *caps = g_slice_new (GstCaps);
 
-  g_atomic_int_inc (&caps->refcount);
   caps->type = GST_TYPE_CAPS;
+  caps->refcount = 1;
+  caps->flags = GST_CAPS_FLAGS_NONE;
   caps->structs = g_ptr_array_new ();
+  /* 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
+   * caps->structs = g_ptr_array_sized_new (32);
+   */
 
 #ifdef DEBUG_REFCOUNT
-  GST_CAT_LOG (GST_CAT_CAPS, "created caps %p", caps);
+  GST_CAT_TRACE (GST_CAT_CAPS, "created caps %p", caps);
 #endif
 
   return caps;
@@ -148,7 +187,7 @@ gst_caps_new_empty (void)
  * Creates a new #GstCaps that indicates that it is compatible with
  * any media format.
  *
- * Returns: the new #GstCaps
+ * Returns: (transfer full): the new #GstCaps
  */
 GstCaps *
 gst_caps_new_any (void)
@@ -171,7 +210,7 @@ gst_caps_new_any (void)
  * as gst_structure_new().
  * Caller is responsible for unreffing the returned caps.
  *
- * Returns: the new #GstCaps
+ * Returns: (transfer full): the new #GstCaps
  */
 GstCaps *
 gst_caps_new_simple (const char *media_type, const char *fieldname, ...)
@@ -186,7 +225,10 @@ gst_caps_new_simple (const char *media_type, const char *fieldname, ...)
   structure = gst_structure_new_valist (media_type, fieldname, var_args);
   va_end (var_args);
 
-  gst_caps_append_structure (caps, structure);
+  if (structure)
+    gst_caps_append_structure_unchecked (caps, structure);
+  else
+    gst_caps_replace (&caps, NULL);
 
   return caps;
 }
@@ -200,7 +242,7 @@ gst_caps_new_simple (const char *media_type, const char *fieldname, ...)
  * arguments.  The list must be NULL-terminated.  The structures
  * are not copied; the returned #GstCaps owns the structures.
  *
- * Returns: the new #GstCaps
+ * Returns: (transfer full): the new #GstCaps
  */
 GstCaps *
 gst_caps_new_full (GstStructure * struct1, ...)
@@ -224,7 +266,7 @@ gst_caps_new_full (GstStructure * struct1, ...)
  * arguments.  The list must be NULL-terminated.  The structures
  * are not copied; the returned #GstCaps owns the structures.
  *
- * Returns: the new #GstCaps
+ * Returns: (transfer full): the new #GstCaps
  */
 GstCaps *
 gst_caps_new_full_valist (GstStructure * structure, va_list var_args)
@@ -234,7 +276,7 @@ gst_caps_new_full_valist (GstStructure * structure, va_list var_args)
   caps = gst_caps_new_empty ();
 
   while (structure) {
-    gst_caps_append_structure (caps, structure);
+    gst_caps_append_structure_unchecked (caps, structure);
     structure = va_arg (var_args, GstStructure *);
   }
 
@@ -254,23 +296,25 @@ gst_caps_new_full_valist (GstStructure * structure, va_list var_args)
  *
  * When you are finished with the caps, call gst_caps_unref() on it.
  *
- * Returns: the new #GstCaps
+ * Returns: (transfer full): the new #GstCaps
  */
 GstCaps *
 gst_caps_copy (const GstCaps * caps)
 {
   GstCaps *newcaps;
   GstStructure *structure;
-  guint i;
+  guint i, n;
 
   g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
 
   newcaps = gst_caps_new_empty ();
   newcaps->flags = caps->flags;
+  n = caps->structs->len;
 
-  for (i = 0; i < caps->structs->len; i++) {
-    structure = gst_caps_get_structure (caps, i);
-    gst_caps_append_structure (newcaps, gst_structure_copy (structure));
+  for (i = 0; i < n; i++) {
+    structure = gst_caps_get_structure_unchecked (caps, i);
+    gst_caps_append_structure_unchecked (newcaps,
+        gst_structure_copy (structure));
   }
 
   return newcaps;
@@ -280,13 +324,15 @@ static void
 _gst_caps_free (GstCaps * caps)
 {
   GstStructure *structure;
-  guint i;
+  guint i, len;
 
   /* The refcount must be 0, but since we're only called by gst_caps_unref,
    * don't bother testing. */
-
-  for (i = 0; i < caps->structs->len; i++) {
-    structure = (GstStructure *) gst_caps_get_structure (caps, i);
+  len = caps->structs->len;
+  /* 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);
     gst_structure_set_parent_refcount (structure, NULL);
     gst_structure_free (structure);
   }
@@ -294,12 +340,16 @@ _gst_caps_free (GstCaps * caps)
 #ifdef USE_POISONING
   memset (caps, 0xff, sizeof (GstCaps));
 #endif
-  g_free (caps);
+
+#ifdef DEBUG_REFCOUNT
+  GST_CAT_TRACE (GST_CAT_CAPS, "freeing caps %p", caps);
+#endif
+  g_slice_free (GstCaps, caps);
 }
 
 /**
  * gst_caps_make_writable:
- * @caps: the #GstCaps to make writable
+ * @caps: (transfer full): the #GstCaps to make writable
  *
  * Returns a writable copy of @caps.
  *
@@ -313,7 +363,7 @@ _gst_caps_free (GstCaps * caps)
  * that it returns. Don't access the argument after calling this function. See
  * also: gst_caps_ref().
  *
- * Returns: the same #GstCaps object.
+ * Returns: (transfer full): the same #GstCaps object.
  */
 GstCaps *
 gst_caps_make_writable (GstCaps * caps)
@@ -323,10 +373,11 @@ gst_caps_make_writable (GstCaps * caps)
   g_return_val_if_fail (caps != NULL, NULL);
 
   /* we are the only instance reffing this caps */
-  if (g_atomic_int_get (&caps->refcount) == 1)
+  if (IS_WRITABLE (caps))
     return caps;
 
   /* else copy */
+  GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy caps");
   copy = gst_caps_copy (caps);
   gst_caps_unref (caps);
 
@@ -343,10 +394,10 @@ gst_caps_make_writable (GstCaps * caps)
  * gst_caps_make_writable(), it is guaranteed that the caps object will not
  * change. This means its structures won't change, etc. To use a #GstCaps
  * object, you must always have a refcount on it -- either the one made
- * implicitly by gst_caps_new(), or via taking one explicitly with this
- * function.
+ * implicitly by e.g. gst_caps_new_simple(), or via taking one explicitly with
+ * this function.
  *
- * Returns: the same #GstCaps object.
+ * Returns: (transfer full): the same #GstCaps object.
  */
 GstCaps *
 gst_caps_ref (GstCaps * caps)
@@ -354,7 +405,7 @@ gst_caps_ref (GstCaps * caps)
   g_return_val_if_fail (caps != NULL, NULL);
 
 #ifdef DEBUG_REFCOUNT
-  GST_CAT_LOG (GST_CAT_REFCOUNTING, "%p %d->%d", caps,
+  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "%p %d->%d", caps,
       GST_CAPS_REFCOUNT_VALUE (caps), GST_CAPS_REFCOUNT_VALUE (caps) + 1);
 #endif
   g_return_val_if_fail (GST_CAPS_REFCOUNT_VALUE (caps) > 0, NULL);
@@ -366,9 +417,9 @@ gst_caps_ref (GstCaps * caps)
 
 /**
  * gst_caps_unref:
- * @caps: the #GstCaps to unref
+ * @caps: (transfer full): the #GstCaps to unref
  *
- * Unref a #GstCaps and and free all its structures and the
+ * Unref a #GstCaps and free all its structures and the
  * structures' values when the refcount reaches 0.
  */
 void
@@ -377,16 +428,15 @@ gst_caps_unref (GstCaps * caps)
   g_return_if_fail (caps != NULL);
 
 #ifdef DEBUG_REFCOUNT
-  GST_CAT_LOG (GST_CAT_REFCOUNTING, "%p %d->%d", caps,
+  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "%p %d->%d", caps,
       GST_CAPS_REFCOUNT_VALUE (caps), GST_CAPS_REFCOUNT_VALUE (caps) - 1);
 #endif
 
   g_return_if_fail (GST_CAPS_REFCOUNT_VALUE (caps) > 0);
 
   /* if we ended up with the refcount at zero, free the caps */
-  if (g_atomic_int_dec_and_test (&caps->refcount)) {
+  if (G_UNLIKELY (g_atomic_int_dec_and_test (&caps->refcount)))
     _gst_caps_free (caps);
-  }
 }
 
 GType
@@ -407,36 +457,75 @@ gst_static_caps_get_type (void)
  *
  * Converts a #GstStaticCaps to a #GstCaps.
  *
- * Returns: 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): 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)
 {
-  GstCaps *caps = (GstCaps *) static_caps;
-  gboolean ret;
+  GstCaps *caps;
 
-  if (caps->type == 0) {
-    if (static_caps->string == NULL) {
-      g_warning ("static caps is NULL");
-      return NULL;
-    }
+  g_return_val_if_fail (static_caps != NULL, NULL);
 
-    caps->type = GST_TYPE_CAPS;
-    /* initialize the caps to a refcount of 1 so the caps can be writable... */
-    gst_atomic_int_set (&caps->refcount, 1);
-    caps->structs = g_ptr_array_new ();
-    ret = gst_caps_from_string_inplace (caps, static_caps->string);
+  caps = (GstCaps *) static_caps;
 
-    if (!ret) {
-      g_critical ("Could not convert static caps \"%s\"", static_caps->string);
-    }
+  /* refcount is 0 when we need to convert */
+  if (G_UNLIKELY (g_atomic_int_get (&caps->refcount) == 0)) {
+    const char *string;
+    GstCaps temp;
+
+    G_LOCK (static_caps_lock);
+    /* check if other thread already updated */
+    if (G_UNLIKELY (g_atomic_int_get (&caps->refcount) > 0))
+      goto done;
+
+    string = static_caps->string;
+
+    if (G_UNLIKELY (string == NULL))
+      goto no_string;
+
+    GST_CAT_TRACE (GST_CAT_CAPS, "creating %p", static_caps);
+
+    /* we construct the caps on the stack, then copy over the struct into our
+     * real caps, refcount last. We do this because we must leave the refcount
+     * of the result caps to 0 so that other threads don't run away with the
+     * caps while we are constructing it. */
+    temp.type = GST_TYPE_CAPS;
+    temp.flags = GST_CAPS_FLAGS_NONE;
+    temp.structs = g_ptr_array_new ();
+
+    /* initialize the caps to a refcount of 1 so the caps can be writable for
+     * the next statement */
+    temp.refcount = 1;
+
+    /* convert to string */
+    if (G_UNLIKELY (!gst_caps_from_string_inplace (&temp, string)))
+      g_critical ("Could not convert static caps \"%s\"", string);
+
+    /* now copy stuff over to the real caps. */
+    caps->type = temp.type;
+    caps->flags = temp.flags;
+    caps->structs = temp.structs;
+    /* and bump the refcount so other threads can now read */
+    g_atomic_int_set (&caps->refcount, 1);
+
+    GST_CAT_TRACE (GST_CAT_CAPS, "created %p", static_caps);
+  done:
+    G_UNLOCK (static_caps_lock);
   }
   /* ref the caps, makes it not writable */
   gst_caps_ref (caps);
 
   return caps;
+
+  /* ERRORS */
+no_string:
+  {
+    G_UNLOCK (static_caps_lock);
+    g_warning ("static caps %p string is NULL", static_caps);
+    return NULL;
+  }
 }
 
 /* manipulation */
@@ -444,7 +533,7 @@ gst_static_caps_get (GstStaticCaps * static_caps)
 static GstStructure *
 gst_caps_remove_and_get_structure (GstCaps * caps, guint idx)
 {
-  /* don't use index_fast, gst_caps_simplify relies on the order */
+  /* don't use index_fast, gst_caps_do_simplify relies on the order */
   GstStructure *s = g_ptr_array_remove_index (caps->structs, idx);
 
   gst_structure_set_parent_refcount (s, NULL);
@@ -452,9 +541,34 @@ gst_caps_remove_and_get_structure (GstCaps * caps, guint idx)
 }
 
 /**
+ * gst_caps_steal_structure:
+ * @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
+ * 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
+ */
+GstStructure *
+gst_caps_steal_structure (GstCaps * caps, guint index)
+{
+  g_return_val_if_fail (caps != NULL, NULL);
+  g_return_val_if_fail (IS_WRITABLE (caps), NULL);
+
+  if (G_UNLIKELY (index >= caps->structs->len))
+    return NULL;
+
+  return gst_caps_remove_and_get_structure (caps, index);
+}
+
+/**
  * gst_caps_append:
  * @caps1: the #GstCaps that will be appended to
- * @caps2: the #GstCaps to append
+ * @caps2: (transfer full): the #GstCaps to append
  *
  * Appends the structures contained in @caps2 to @caps1. The structures in
  * @caps2 are not copied -- they are transferred to @caps1, and then @caps2 is
@@ -469,12 +583,13 @@ gst_caps_append (GstCaps * caps1, GstCaps * caps2)
   g_return_if_fail (GST_IS_CAPS (caps1));
   g_return_if_fail (GST_IS_CAPS (caps2));
   g_return_if_fail (IS_WRITABLE (caps1));
-  g_return_if_fail (IS_WRITABLE (caps2));
+
+  caps2 = gst_caps_make_writable (caps2);
 
 #ifdef USE_POISONING
   CAPS_POISON (caps2);
 #endif
-  if (gst_caps_is_any (caps1) || gst_caps_is_any (caps2)) {
+  if (G_UNLIKELY (CAPS_IS_ANY (caps1) || CAPS_IS_ANY (caps2))) {
     /* FIXME: this leaks */
     caps1->flags |= GST_CAPS_FLAGS_ANY;
     for (i = caps2->structs->len - 1; i >= 0; i--) {
@@ -482,12 +597,66 @@ gst_caps_append (GstCaps * caps1, GstCaps * caps2)
       gst_structure_free (structure);
     }
   } else {
-    int len = caps2->structs->len;
+    for (i = caps2->structs->len; i; i--) {
+      structure = gst_caps_remove_and_get_structure (caps2, 0);
+      gst_caps_append_structure_unchecked (caps1, structure);
+    }
+  }
+  gst_caps_unref (caps2);       /* guaranteed to free it */
+}
 
-    for (i = 0; i < len; i++) {
+/**
+ * gst_caps_merge:
+ * @caps1: the #GstCaps that will take the new entries
+ * @caps2: (transfer full): the #GstCaps to merge in
+ *
+ * Appends the structures contained in @caps2 to @caps1 if they are not yet
+ * expressed by @caps1. The structures in @caps2 are not copied -- they are
+ * transferred to @caps1, and then @caps2 is freed.
+ * If either caps is ANY, the resulting caps will be ANY.
+ *
+ * Since: 0.10.10
+ */
+void
+gst_caps_merge (GstCaps * caps1, GstCaps * caps2)
+{
+  GstStructure *structure;
+  int i;
+
+  g_return_if_fail (GST_IS_CAPS (caps1));
+  g_return_if_fail (GST_IS_CAPS (caps2));
+  g_return_if_fail (IS_WRITABLE (caps1));
+
+  caps2 = gst_caps_make_writable (caps2);
+
+#ifdef USE_POISONING
+  CAPS_POISON (caps2);
+#endif
+  if (G_UNLIKELY (CAPS_IS_ANY (caps1))) {
+    for (i = caps2->structs->len - 1; i >= 0; i--) {
+      structure = gst_caps_remove_and_get_structure (caps2, i);
+      gst_structure_free (structure);
+    }
+  } else if (G_UNLIKELY (CAPS_IS_ANY (caps2))) {
+    caps1->flags |= GST_CAPS_FLAGS_ANY;
+    for (i = caps1->structs->len - 1; i >= 0; i--) {
+      structure = gst_caps_remove_and_get_structure (caps1, i);
+      gst_structure_free (structure);
+    }
+  } else {
+    for (i = caps2->structs->len; i; i--) {
       structure = gst_caps_remove_and_get_structure (caps2, 0);
-      gst_caps_append_structure (caps1, structure);
+      gst_caps_merge_structure (caps1, structure);
     }
+    /* this is too naive
+       GstCaps *com = gst_caps_intersect (caps1, caps2);
+       GstCaps *add = gst_caps_subtract (caps2, com);
+
+       GST_DEBUG ("common : %d", gst_caps_get_size (com));
+       GST_DEBUG ("adding : %d", gst_caps_get_size (add));
+       gst_caps_append (caps1, add);
+       gst_caps_unref (com);
+     */
   }
   gst_caps_unref (caps2);       /* guaranteed to free it */
 }
@@ -495,7 +664,7 @@ gst_caps_append (GstCaps * caps1, GstCaps * caps2)
 /**
  * gst_caps_append_structure:
  * @caps: the #GstCaps that will be appended to
- * @structure: the #GstStructure to append
+ * @structure: (transfer full): the #GstStructure to append
  *
  * Appends @structure to @caps.  The structure is not copied; @caps
  * becomes the owner of @structure.
@@ -506,19 +675,18 @@ gst_caps_append_structure (GstCaps * caps, GstStructure * structure)
   g_return_if_fail (GST_IS_CAPS (caps));
   g_return_if_fail (IS_WRITABLE (caps));
 
-  if (structure) {
+  if (G_LIKELY (structure)) {
     g_return_if_fail (structure->parent_refcount == NULL);
 #if 0
 #ifdef USE_POISONING
     STRUCTURE_POISON (structure);
 #endif
 #endif
-    gst_structure_set_parent_refcount (structure, &caps->refcount);
-    g_ptr_array_add (caps->structs, structure);
+    gst_caps_append_structure_unchecked (caps, structure);
   }
 }
 
-/*
+/**
  * gst_caps_remove_structure:
  * @caps: the #GstCaps to remove from
  * @idx: Index of the structure to remove
@@ -540,20 +708,45 @@ gst_caps_remove_structure (GstCaps * caps, guint idx)
 }
 
 /**
- * gst_caps_split_one:
- * @caps: a #GstCaps
- *
- * This function is not implemented.
+ * gst_caps_merge_structure:
+ * @caps: the #GstCaps that will the new structure
+ * @structure: (transfer full): the #GstStructure to merge
  *
- * Returns: NULL
+ * Appends @structure to @caps if its not already expressed by @caps.  The
+ * structure is not copied; @caps becomes the owner of @structure.
  */
-GstCaps *
-gst_caps_split_one (GstCaps * caps)
+void
+gst_caps_merge_structure (GstCaps * caps, GstStructure * structure)
 {
-  /* FIXME */
-  g_critical ("unimplemented");
+  g_return_if_fail (GST_IS_CAPS (caps));
+  g_return_if_fail (IS_WRITABLE (caps));
 
-  return NULL;
+  if (G_LIKELY (structure)) {
+    GstStructure *structure1;
+    int i;
+    gboolean unique = TRUE;
+
+    g_return_if_fail (structure->parent_refcount == NULL);
+#if 0
+#ifdef USE_POISONING
+    STRUCTURE_POISON (structure);
+#endif
+#endif
+    /* check each structure */
+    for (i = caps->structs->len - 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)) {
+        unique = FALSE;
+        break;
+      }
+    }
+    if (unique) {
+      gst_caps_append_structure_unchecked (caps, structure);
+    } else {
+      gst_structure_free (structure);
+    }
+  }
 }
 
 /**
@@ -583,18 +776,25 @@ gst_caps_get_size (const GstCaps * caps)
  * WARNING: This function takes a const GstCaps *, but returns a
  * non-const GstStructure *.  This is for programming convenience --
  * the caller should be aware that structures inside a constant
- * #GstCaps should not be modified.
+ * #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
+ * structure returned in the usual way, e.g. with functions like
+ * gst_structure_set().
+ *
+ * You do not need to free or unref the structure returned, it
+ * belongs to the #GstCaps.
  *
- * Returns: a pointer to the #GstStructure corresponding to @index
+ * Returns: (transfer none): a pointer to the #GstStructure corresponding
+ *     to @index
  */
 GstStructure *
 gst_caps_get_structure (const GstCaps * caps, guint index)
 {
   g_return_val_if_fail (GST_IS_CAPS (caps), NULL);
-  g_return_val_if_fail (index >= 0, NULL);
   g_return_val_if_fail (index < caps->structs->len, NULL);
 
-  return g_ptr_array_index (caps->structs, index);
+  return gst_caps_get_structure_unchecked (caps, index);
 }
 
 /**
@@ -605,7 +805,7 @@ gst_caps_get_structure (const GstCaps * caps, guint index)
  * Creates a new #GstCaps and appends a copy of the nth structure
  * contained in @caps.
  *
- * Returns: the new #GstCaps
+ * Returns: (transfer full): the new #GstCaps
  */
 GstCaps *
 gst_caps_copy_nth (const GstCaps * caps, guint nth)
@@ -618,9 +818,10 @@ gst_caps_copy_nth (const GstCaps * caps, guint nth)
   newcaps = gst_caps_new_empty ();
   newcaps->flags = caps->flags;
 
-  if (caps->structs->len > nth) {
-    structure = gst_caps_get_structure (caps, nth);
-    gst_caps_append_structure (newcaps, gst_structure_copy (structure));
+  if (G_LIKELY (caps->structs->len > nth)) {
+    structure = gst_caps_get_structure_unchecked (caps, nth);
+    gst_caps_append_structure_unchecked (newcaps,
+        gst_structure_copy (structure));
   }
 
   return newcaps;
@@ -648,54 +849,103 @@ gst_caps_truncate (GstCaps * caps)
 }
 
 /**
- * gst_caps_set_simple:
+ * gst_caps_set_value:
+ * @caps: a writable caps
+ * @field: name of the field to set
+ * @value: value to set the field to
+ *
+ * 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)
+{
+  guint i, len;
+
+  g_return_if_fail (GST_IS_CAPS (caps));
+  g_return_if_fail (IS_WRITABLE (caps));
+  g_return_if_fail (field != NULL);
+  g_return_if_fail (G_IS_VALUE (value));
+
+  len = caps->structs->len;
+  for (i = 0; i < len; i++) {
+    GstStructure *structure = gst_caps_get_structure_unchecked (caps, i);
+    gst_structure_set_value (structure, field, value);
+  }
+}
+
+/**
+ * gst_caps_set_simple_valist:
  * @caps: the #GstCaps to set
  * @field: first field to set
- * @...: additional parameters
+ * @varargs: additional parameters
  *
- * Sets fields in a simple #GstCaps.  A simple #GstCaps is one that
- * only has one structure.  The arguments must be passed in the same
+ * Sets fields in a #GstCaps.  The arguments must be passed in the same
  * manner as gst_structure_set(), and be NULL-terminated.
+ * <note>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.</note>
  */
 void
-gst_caps_set_simple (GstCaps * caps, char *field, ...)
+gst_caps_set_simple_valist (GstCaps * caps, const char *field, va_list varargs)
 {
-  GstStructure *structure;
-  va_list var_args;
+  GValue value = { 0, };
 
   g_return_if_fail (GST_IS_CAPS (caps));
-  g_return_if_fail (caps->structs->len == 1);
   g_return_if_fail (IS_WRITABLE (caps));
 
-  structure = gst_caps_get_structure (caps, 0);
+  while (field) {
+    GType type;
+    char *err;
 
-  va_start (var_args, field);
-  gst_structure_set_valist (structure, field, var_args);
-  va_end (var_args);
+    type = va_arg (varargs, GType);
+
+    if (G_UNLIKELY (type == G_TYPE_DATE)) {
+      g_warning ("Don't use G_TYPE_DATE, use GST_TYPE_DATE instead\n");
+      type = GST_TYPE_DATE;
+    }
+    G_VALUE_COLLECT_INIT (&value, type, varargs, 0, &err);
+    if (G_UNLIKELY (err)) {
+      g_critical ("%s", err);
+      return;
+    }
+
+    gst_caps_set_value (caps, field, &value);
+
+    g_value_unset (&value);
+
+    field = va_arg (varargs, const gchar *);
+  }
 }
 
 /**
- * gst_caps_set_simple_valist:
- * @caps: the #GstCaps to copy
+ * gst_caps_set_simple:
+ * @caps: the #GstCaps to set
  * @field: first field to set
- * @varargs: additional parameters
+ * @...: additional parameters
  *
- * Sets fields in a simple #GstCaps.  A simple #GstCaps is one that
- * only has one structure.  The arguments must be passed in the same
+ * Sets fields in a #GstCaps.  The arguments must be passed in the same
  * manner as gst_structure_set(), and be NULL-terminated.
+ * <note>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.</note>
  */
 void
-gst_caps_set_simple_valist (GstCaps * caps, char *field, va_list varargs)
+gst_caps_set_simple (GstCaps * caps, const char *field, ...)
 {
-  GstStructure *structure;
+  va_list var_args;
 
   g_return_if_fail (GST_IS_CAPS (caps));
-  g_return_if_fail (caps->structs->len != 1);
   g_return_if_fail (IS_WRITABLE (caps));
 
-  structure = gst_caps_get_structure (caps, 0);
-
-  gst_structure_set_valist (structure, field, varargs);
+  va_start (var_args, field);
+  gst_caps_set_simple_valist (caps, field, var_args);
+  va_end (var_args);
 }
 
 /* tests */
@@ -713,7 +963,7 @@ gst_caps_is_any (const GstCaps * caps)
 {
   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
 
-  return (caps->flags & GST_CAPS_FLAGS_ANY);
+  return (CAPS_IS_ANY (caps));
 }
 
 /**
@@ -729,10 +979,10 @@ gst_caps_is_empty (const GstCaps * caps)
 {
   g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
 
-  if (caps->flags & GST_CAPS_FLAGS_ANY)
+  if (CAPS_IS_ANY (caps))
     return FALSE;
 
-  return (caps->structs == NULL) || (caps->structs->len == 0);
+  return CAPS_IS_EMPTY_SIMPLE (caps);
 }
 
 static gboolean
@@ -762,27 +1012,11 @@ gst_caps_is_fixed (const GstCaps * caps)
   if (caps->structs->len != 1)
     return FALSE;
 
-  structure = gst_caps_get_structure (caps, 0);
+  structure = gst_caps_get_structure_unchecked (caps, 0);
 
   return gst_structure_foreach (structure, gst_caps_is_fixed_foreach, NULL);
 }
 
-static gboolean
-gst_structure_is_equal_foreach (GQuark field_id, const GValue * val2,
-    gpointer data)
-{
-  GstStructure *struct1 = (GstStructure *) data;
-  const GValue *val1 = gst_structure_id_get_value (struct1, field_id);
-
-  if (val1 == NULL)
-    return FALSE;
-  if (gst_value_compare (val1, val2) == GST_VALUE_EQUAL) {
-    return TRUE;
-  }
-
-  return FALSE;
-}
-
 /**
  * gst_caps_is_equal_fixed:
  * @caps1: the #GstCaps to test
@@ -801,18 +1035,10 @@ gst_caps_is_equal_fixed (const GstCaps * caps1, const GstCaps * caps2)
   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 (caps1, 0);
-  struct2 = gst_caps_get_structure (caps2, 0);
+  struct1 = gst_caps_get_structure_unchecked (caps1, 0);
+  struct2 = gst_caps_get_structure_unchecked (caps2, 0);
 
-  if (struct1->name != struct2->name) {
-    return FALSE;
-  }
-  if (struct1->fields->len != struct2->fields->len) {
-    return FALSE;
-  }
-
-  return gst_structure_foreach (struct1, gst_structure_is_equal_foreach,
-      struct2);
+  return gst_structure_is_equal (struct1, struct2);
 }
 
 /**
@@ -840,33 +1066,87 @@ gst_caps_is_always_compatible (const GstCaps * caps1, const GstCaps * caps2)
  * @subset: a #GstCaps
  * @superset: a potentially greater #GstCaps
  *
- * Checks if all caps represented by @subset are also represented by @superset
+ * Checks if all caps represented by @subset are also represented by @superset.
  * <note>This function does not work reliably if optional properties for caps
  * are included on one caps and omitted on the other.</note>
  *
- * Returns: TRUE if @subset is a subset of @superset
+ * Returns: %TRUE if @subset is a subset of @superset
  */
 gboolean
 gst_caps_is_subset (const GstCaps * subset, const GstCaps * superset)
 {
-  GstCaps *caps;
-  gboolean ret;
+  GstStructure *s1, *s2;
+  gboolean ret = TRUE;
+  gint i, j;
 
   g_return_val_if_fail (subset != NULL, FALSE);
   g_return_val_if_fail (superset != NULL, FALSE);
 
-  if (gst_caps_is_empty (subset) || gst_caps_is_any (superset))
+  if (CAPS_IS_EMPTY (subset) || CAPS_IS_ANY (superset))
     return TRUE;
-  if (gst_caps_is_any (subset) || gst_caps_is_empty (superset))
+  if (CAPS_IS_ANY (subset) || CAPS_IS_EMPTY (superset))
     return FALSE;
 
-  caps = gst_caps_subtract (subset, superset);
-  ret = gst_caps_is_empty (caps);
-  gst_caps_unref (caps);
+  for (i = subset->structs->len - 1; i >= 0; i--) {
+    for (j = superset->structs->len - 1; j >= 0; j--) {
+      s1 = gst_caps_get_structure_unchecked (subset, i);
+      s2 = gst_caps_get_structure_unchecked (superset, j);
+      if (gst_structure_is_subset (s1, s2)) {
+        /* If we found a superset, continue with the next
+         * subset structure */
+        break;
+      }
+    }
+    /* If we found no superset for this subset structure
+     * we return FALSE immediately */
+    if (j == -1) {
+      ret = FALSE;
+      break;
+    }
+  }
+
   return ret;
 }
 
 /**
+ * gst_caps_is_subset_structure:
+ * @caps: a #GstCaps
+ * @structure: a potential #GstStructure subset of @caps
+ *
+ * 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: 0.10.35
+ */
+gboolean
+gst_caps_is_subset_structure (const GstCaps * caps,
+    const GstStructure * structure)
+{
+  GstStructure *s;
+  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;
+
+  for (i = caps->structs->len - 1; i >= 0; i--) {
+    s = gst_caps_get_structure_unchecked (caps, i);
+    if (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
@@ -875,162 +1155,177 @@ gst_caps_is_subset (const GstCaps * subset, const GstCaps * superset)
  * <note>This function does not work reliably if optional properties for caps
  * are included on one caps and omitted on the other.</note>
  *
- * This function deals correctly with passing NULL for any of the caps. 
+ * 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)
 {
+  /* FIXME 0.11: NULL pointers are no valid Caps but indicate an error
+   * So there should be an assertion that caps1 and caps2 != NULL */
+
   /* NULL <-> NULL is allowed here */
-  if (caps1 == caps2)
+  if (G_UNLIKELY (caps1 == caps2))
     return TRUE;
 
   /* one of them NULL => they are different (can't be both NULL because
    * we checked that above) */
-  if (caps1 == NULL || caps2 == NULL)
+  if (G_UNLIKELY (caps1 == NULL || caps2 == NULL))
     return FALSE;
 
-  if (gst_caps_is_fixed (caps1) && gst_caps_is_fixed (caps2))
+  if (G_UNLIKELY (gst_caps_is_fixed (caps1) && gst_caps_is_fixed (caps2)))
     return gst_caps_is_equal_fixed (caps1, caps2);
 
   return gst_caps_is_subset (caps1, caps2) && gst_caps_is_subset (caps2, caps1);
 }
 
-typedef struct
+/**
+ * gst_caps_is_strictly_equal:
+ * @caps1: a #GstCaps
+ * @caps2: another #GstCaps
+ *
+ * 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
+ */
+gboolean
+gst_caps_is_strictly_equal (const GstCaps * caps1, const GstCaps * caps2)
 {
-  GstStructure *dest;
-  const GstStructure *intersect;
-  gboolean first_run;
-}
-IntersectData;
+  int i;
+  /* FIXME 0.11: NULL pointers are no valid Caps but indicate an error
+   * So there should be an assertion that caps1 and caps2 != NULL */
 
-static gboolean
-gst_caps_structure_intersect_field (GQuark id, const GValue * val1,
-    gpointer data)
-{
-  IntersectData *idata = (IntersectData *) data;
-  GValue dest_value = { 0 };
-  const GValue *val2 = gst_structure_id_get_value (idata->intersect, id);
-
-  if (val2 == NULL) {
-    gst_structure_id_set_value (idata->dest, id, val1);
-  } else if (idata->first_run) {
-    if (gst_value_intersect (&dest_value, val1, val2)) {
-      gst_structure_id_set_value (idata->dest, id, &dest_value);
-      g_value_unset (&dest_value);
-    } else {
+  /* NULL <-> NULL is allowed here */
+  if (G_UNLIKELY (caps1 == caps2))
+    return TRUE;
+
+  /* one of them NULL => they are different (can't be both NULL because
+   * we checked that above) */
+  if (G_UNLIKELY (caps1 == NULL || caps2 == NULL))
+    return FALSE;
+
+  if (caps1->structs->len != caps2->structs->len)
+    return FALSE;
+
+  for (i = 0; i < caps1->structs->len; i++) {
+    if (!gst_structure_is_equal (gst_caps_get_structure_unchecked (caps1, i),
+            gst_caps_get_structure_unchecked (caps2, i)))
       return FALSE;
-    }
   }
 
   return TRUE;
 }
 
-static GstStructure *
-gst_caps_structure_intersect (const GstStructure * struct1,
-    const GstStructure * struct2)
-{
-  IntersectData data;
-
-  g_return_val_if_fail (struct1 != NULL, NULL);
-  g_return_val_if_fail (struct2 != NULL, NULL);
-
-  if (struct1->name != struct2->name)
-    return NULL;
-
-  data.dest = gst_structure_id_empty_new (struct1->name);
-  data.intersect = struct2;
-  data.first_run = TRUE;
-  if (!gst_structure_foreach ((GstStructure *) struct1,
-          gst_caps_structure_intersect_field, &data))
-    goto error;
+/* intersect operation */
 
-  data.intersect = struct1;
-  data.first_run = FALSE;
-  if (!gst_structure_foreach ((GstStructure *) struct2,
-          gst_caps_structure_intersect_field, &data))
-    goto error;
-
-  return data.dest;
-
-error:
-  gst_structure_free (data.dest);
-  return NULL;
-}
-
-#if 0
-static GstStructure *
-gst_caps_structure_union (const GstStructure * struct1,
-    const GstStructure * struct2)
+/**
+ * gst_caps_can_intersect:
+ * @caps1: a #GstCaps to intersect
+ * @caps2: a #GstCaps to intersect
+ *
+ * Tries intersecting @caps1 and @caps2 and reports whether the result would not
+ * 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)
 {
-  int i;
-  GstStructure *dest;
-  const GstStructureField *field1;
-  const GstStructureField *field2;
-  int ret;
+  guint64 i;                    /* index can be up to 2 * G_MAX_UINT */
+  guint j, k, len1, len2;
+  GstStructure *struct1;
+  GstStructure *struct2;
 
-  /* FIXME this doesn't actually work */
+  g_return_val_if_fail (GST_IS_CAPS (caps1), FALSE);
+  g_return_val_if_fail (GST_IS_CAPS (caps2), FALSE);
 
-  if (struct1->name != struct2->name)
-    return NULL;
+  /* caps are exactly the same pointers */
+  if (G_UNLIKELY (caps1 == caps2))
+    return TRUE;
 
-  dest = gst_structure_id_empty_new (struct1->name);
+  /* empty caps on either side, return empty */
+  if (G_UNLIKELY (CAPS_IS_EMPTY (caps1) || CAPS_IS_EMPTY (caps2)))
+    return FALSE;
 
-  for (i = 0; i < struct1->fields->len; i++) {
-    GValue dest_value = { 0 };
+  /* one of the caps is any */
+  if (G_UNLIKELY (CAPS_IS_ANY (caps1) || CAPS_IS_ANY (caps2)))
+    return TRUE;
 
-    field1 = GST_STRUCTURE_FIELD (struct1, i);
-    field2 = gst_structure_id_get_field (struct2, field1->name);
+  /* 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:
+   *
+   *          caps1                              0  1  2  3
+   *       +-------------     total distance:  +-------------
+   *       | 1  2  4  7                      0 | 0  1  2  3
+   * caps2 | 3  5  8 10                      1 | 1  2  3  4
+   *       | 6  9 11 12                      2 | 2  3  4  5
+   *
+   * First we iterate over the caps1 structures (top line) intersecting
+   * the structures diagonally down, then we iterate over the caps2
+   * structures. The result is that the intersections are ordered based on the
+   * sum of the indexes in the list.
+   */
+  len1 = caps1->structs->len;
+  len2 = caps2->structs->len;
+  for (i = 0; i < len1 + len2 - 1; i++) {
+    /* superset index goes from 0 to sgst_caps_structure_intersectuperset->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) */
 
-    if (field2 == NULL) {
-      continue;
-    } else {
-      if (gst_value_union (&dest_value, &field1->value, &field2->value)) {
-        gst_structure_set_value (dest, g_quark_to_string (field1->name),
-            &dest_value);
-      } else {
-        ret = gst_value_compare (&field1->value, &field2->value);
+    /* now run the diagonal line, end condition is the left or bottom
+     * border */
+    while (k < len2) {
+      struct1 = gst_caps_get_structure_unchecked (caps1, j);
+      struct2 = gst_caps_get_structure_unchecked (caps2, k);
+
+      if (gst_structure_can_intersect (struct1, struct2)) {
+        return TRUE;
       }
+      /* move down left */
+      k++;
+      if (G_UNLIKELY (j == 0))
+        break;                  /* so we don't roll back to G_MAXUINT */
+      j--;
     }
   }
-
-  return dest;
+  return FALSE;
 }
-#endif
-
-/* operations */
 
-/**
- * gst_caps_intersect:
- * @caps1: a #GstCaps to intersect
- * @caps2: a #GstCaps to intersect
- *
- * Creates a new #GstCaps that contains all the formats that are common
- * to both @caps1 and @caps2.
- *
- * Returns: the new #GstCaps
- */
-GstCaps *
-gst_caps_intersect (const GstCaps * caps1, const GstCaps * caps2)
+static GstCaps *
+gst_caps_intersect_zig_zag (const GstCaps * caps1, const GstCaps * caps2)
 {
   guint64 i;                    /* index can be up to 2 * G_MAX_UINT */
-  guint j, k;
+  guint j, k, len1, len2;
 
   GstStructure *struct1;
   GstStructure *struct2;
   GstCaps *dest;
   GstStructure *istruct;
 
-  g_return_val_if_fail (GST_IS_CAPS (caps1), NULL);
-  g_return_val_if_fail (GST_IS_CAPS (caps2), NULL);
+  /* caps are exactly the same pointers, just copy one caps */
+  if (G_UNLIKELY (caps1 == caps2))
+    return gst_caps_copy (caps1);
 
-  if (gst_caps_is_empty (caps1) || gst_caps_is_empty (caps2)) {
+  /* empty caps on either side, return empty */
+  if (G_UNLIKELY (CAPS_IS_EMPTY (caps1) || CAPS_IS_EMPTY (caps2)))
     return gst_caps_new_empty ();
-  }
-  if (gst_caps_is_any (caps1))
+
+  /* one of the caps is any, just copy the other caps */
+  if (G_UNLIKELY (CAPS_IS_ANY (caps1)))
     return gst_caps_copy (caps2);
-  if (gst_caps_is_any (caps2))
+  if (G_UNLIKELY (CAPS_IS_ANY (caps2)))
     return gst_caps_copy (caps1);
 
   dest = gst_caps_new_empty ();
@@ -1051,25 +1346,27 @@ gst_caps_intersect (const GstCaps * caps1, const GstCaps * caps2)
    * the structures diagonally down, then we iterate over the caps2
    * structures.
    */
-  for (i = 0; i < caps1->structs->len + caps2->structs->len - 1; i++) {
+  len1 = caps1->structs->len;
+  len2 = caps2->structs->len;
+  for (i = 0; i < len1 + len2 - 1; i++) {
     /* caps1 index goes from 0 to caps1->structs->len-1 */
-    j = MIN (i, caps1->structs->len - 1);
+    j = MIN (i, len1 - 1);
     /* caps2 index stays 0 until i reaches caps1->structs->len, then it counts
      * up from 1 to caps2->structs->len - 1 */
-    k = MAX (0, i - j);
+    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 < caps2->structs->len) {
-      struct1 = gst_caps_get_structure (caps1, j);
-      struct2 = gst_caps_get_structure (caps2, k);
+    while (k < len2) {
+      struct1 = gst_caps_get_structure_unchecked (caps1, j);
+      struct2 = gst_caps_get_structure_unchecked (caps2, k);
 
-      istruct = gst_caps_structure_intersect (struct1, struct2);
+      istruct = gst_structure_intersect (struct1, struct2);
 
-      gst_caps_append_structure (dest, istruct);
+      gst_caps_merge_structure (dest, istruct);
       /* move down left */
       k++;
-      if (j == 0)
+      if (G_UNLIKELY (j == 0))
         break;                  /* so we don't roll back to G_MAXUINT */
       j--;
     }
@@ -1077,6 +1374,111 @@ gst_caps_intersect (const GstCaps * caps1, const GstCaps * caps2)
   return dest;
 }
 
+/**
+ * gst_caps_intersect_first:
+ * @caps1: a #GstCaps to intersect
+ * @caps2: a #GstCaps to intersect
+ *
+ * Creates a new #GstCaps that contains all the formats that are common
+ * to both @caps1 and @caps2.
+ *
+ * Unlike @gst_caps_intersect, the returned caps will be ordered in a similar
+ * fashion as @caps1.
+ *
+ * Returns: the new #GstCaps
+ */
+static GstCaps *
+gst_caps_intersect_first (const GstCaps * caps1, const GstCaps * caps2)
+{
+  guint i;
+  guint j, len1, len2;
+
+  GstStructure *struct1;
+  GstStructure *struct2;
+  GstCaps *dest;
+  GstStructure *istruct;
+
+  /* caps are exactly the same pointers, just copy one caps */
+  if (G_UNLIKELY (caps1 == caps2))
+    return gst_caps_copy (caps1);
+
+  /* empty caps on either side, return empty */
+  if (G_UNLIKELY (CAPS_IS_EMPTY (caps1) || CAPS_IS_EMPTY (caps2)))
+    return gst_caps_new_empty ();
+
+  /* one of the caps is any, just copy the other caps */
+  if (G_UNLIKELY (CAPS_IS_ANY (caps1)))
+    return gst_caps_copy (caps2);
+  if (G_UNLIKELY (CAPS_IS_ANY (caps2)))
+    return gst_caps_copy (caps1);
+
+  dest = gst_caps_new_empty ();
+
+  len1 = caps1->structs->len;
+  len2 = caps2->structs->len;
+  for (i = 0; i < len1; i++) {
+    struct1 = gst_caps_get_structure_unchecked (caps1, i);
+    for (j = 0; j < len2; j++) {
+      struct2 = gst_caps_get_structure_unchecked (caps2, j);
+      istruct = gst_structure_intersect (struct1, struct2);
+      if (istruct)
+        gst_caps_merge_structure (dest, istruct);
+    }
+  }
+
+  return dest;
+}
+
+/**
+ * gst_caps_intersect_full:
+ * @caps1: a #GstCaps to intersect
+ * @caps2: a #GstCaps to intersect
+ * @mode: The intersection algorithm/mode to use
+ *
+ * Creates a new #GstCaps that contains all the formats that are common
+ * to both @caps1 and @caps2, the order is defined by the #GstCapsIntersectMode
+ * used.
+ *
+ * Returns: the new #GstCaps
+ * Since: 0.10.33
+ */
+GstCaps *
+gst_caps_intersect_full (const GstCaps * caps1, const GstCaps * caps2,
+    GstCapsIntersectMode mode)
+{
+  g_return_val_if_fail (GST_IS_CAPS (caps1), NULL);
+  g_return_val_if_fail (GST_IS_CAPS (caps2), NULL);
+
+  switch (mode) {
+    case GST_CAPS_INTERSECT_FIRST:
+      return gst_caps_intersect_first (caps1, caps2);
+    default:
+      g_warning ("Unknown caps intersect mode: %d", mode);
+      /* fallthrough */
+    case GST_CAPS_INTERSECT_ZIG_ZAG:
+      return gst_caps_intersect_zig_zag (caps1, caps2);
+  }
+}
+
+/**
+ * gst_caps_intersect:
+ * @caps1: a #GstCaps to intersect
+ * @caps2: a #GstCaps to intersect
+ *
+ * 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
+ */
+GstCaps *
+gst_caps_intersect (const GstCaps * caps1, const GstCaps * caps2)
+{
+  return gst_caps_intersect_full (caps1, caps2, GST_CAPS_INTERSECT_ZIG_ZAG);
+}
+
+
+/* subtract operation */
+
 typedef struct
 {
   const GstStructure *subtract_from;
@@ -1084,7 +1486,6 @@ typedef struct
 }
 SubtractionEntry;
 
-
 static gboolean
 gst_caps_structure_subtract_field (GQuark field_id, const GValue * value,
     gpointer user_data)
@@ -1139,8 +1540,8 @@ gst_caps_structure_subtract (GSList ** into, const GstStructure * minuend,
 
 /**
  * gst_caps_subtract:
- * @minuend: #GstCaps to substract from
- * @subtrahend: #GstCaps to substract
+ * @minuend: #GstCaps to subtract from
+ * @subtrahend: #GstCaps to subtract
  *
  * Subtracts the @subtrahend from the @minuend.
  * <note>This function does not work reliably if optional properties for caps
@@ -1151,7 +1552,7 @@ gst_caps_structure_subtract (GSList ** into, const GstStructure * minuend,
 GstCaps *
 gst_caps_subtract (const GstCaps * minuend, const GstCaps * subtrahend)
 {
-  guint i, j;
+  guint i, j, sublen;
   GstStructure *min;
   GstStructure *sub;
   GstCaps *dest = NULL, *src;
@@ -1159,10 +1560,10 @@ gst_caps_subtract (const GstCaps * minuend, const GstCaps * subtrahend)
   g_return_val_if_fail (minuend != NULL, NULL);
   g_return_val_if_fail (subtrahend != NULL, NULL);
 
-  if (gst_caps_is_empty (minuend) || gst_caps_is_any (subtrahend)) {
+  if (CAPS_IS_EMPTY (minuend) || CAPS_IS_ANY (subtrahend)) {
     return gst_caps_new_empty ();
   }
-  if (gst_caps_is_empty (subtrahend))
+  if (CAPS_IS_EMPTY_SIMPLE (subtrahend))
     return gst_caps_copy (minuend);
 
   /* FIXME: Do we want this here or above?
@@ -1170,19 +1571,23 @@ gst_caps_subtract (const GstCaps * minuend, const 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 (!gst_caps_is_any (minuend), NULL);
-  g_assert (subtrahend->structs->len > 0);
+  g_return_val_if_fail (!CAPS_IS_ANY (minuend), NULL);
+  sublen = subtrahend->structs->len;
+  g_assert (sublen > 0);
 
   src = gst_caps_copy (minuend);
-  for (i = 0; i < subtrahend->structs->len; i++) {
-    sub = gst_caps_get_structure (subtrahend, i);
+  for (i = 0; i < sublen; i++) {
+    guint srclen;
+
+    sub = gst_caps_get_structure_unchecked (subtrahend, i);
     if (dest) {
       gst_caps_unref (src);
       src = dest;
     }
     dest = gst_caps_new_empty ();
-    for (j = 0; j < src->structs->len; j++) {
-      min = gst_caps_get_structure (src, j);
+    srclen = src->structs->len;
+    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)) {
         GSList *list;
 
@@ -1190,17 +1595,18 @@ gst_caps_subtract (const GstCaps * minuend, const GstCaps * subtrahend)
           GSList *walk;
 
           for (walk = list; walk; walk = g_slist_next (walk)) {
-            gst_caps_append_structure (dest, (GstStructure *) walk->data);
+            gst_caps_append_structure_unchecked (dest,
+                (GstStructure *) walk->data);
           }
           g_slist_free (list);
         } else {
-          gst_caps_append_structure (dest, gst_structure_copy (min));
+          gst_caps_append_structure_unchecked (dest, gst_structure_copy (min));
         }
       } else {
-        gst_caps_append_structure (dest, gst_structure_copy (min));
+        gst_caps_append_structure_unchecked (dest, gst_structure_copy (min));
       }
     }
-    if (gst_caps_is_empty (dest)) {
+    if (CAPS_IS_EMPTY_SIMPLE (dest)) {
       gst_caps_unref (src);
       return dest;
     }
@@ -1211,6 +1617,48 @@ gst_caps_subtract (const GstCaps * minuend, const GstCaps * subtrahend)
   return dest;
 }
 
+/* union operation */
+
+#if 0
+static GstStructure *
+gst_caps_structure_union (const GstStructure * struct1,
+    const GstStructure * struct2)
+{
+  int i;
+  GstStructure *dest;
+  const GstStructureField *field1;
+  const GstStructureField *field2;
+  int ret;
+
+  /* FIXME this doesn't actually work */
+
+  if (struct1->name != struct2->name)
+    return NULL;
+
+  dest = gst_structure_id_empty_new (struct1->name);
+
+  for (i = 0; i < struct1->fields->len; i++) {
+    GValue dest_value = { 0 };
+
+    field1 = GST_STRUCTURE_FIELD (struct1, i);
+    field2 = gst_structure_id_get_field (struct2, field1->name);
+
+    if (field2 == NULL) {
+      continue;
+    } else {
+      if (gst_value_union (&dest_value, &field1->value, &field2->value)) {
+        gst_structure_set_value (dest, g_quark_to_string (field1->name),
+            &dest_value);
+      } else {
+        ret = gst_value_compare (&field1->value, &field2->value);
+      }
+    }
+  }
+
+  return dest;
+}
+#endif
+
 /**
  * gst_caps_union:
  * @caps1: a #GstCaps to union
@@ -1227,7 +1675,17 @@ gst_caps_union (const GstCaps * caps1, const GstCaps * caps2)
   GstCaps *dest1;
   GstCaps *dest2;
 
-  if (gst_caps_is_any (caps1) || gst_caps_is_any (caps2))
+  /* NULL pointers are no correct GstCaps */
+  g_return_val_if_fail (caps1 != NULL, NULL);
+  g_return_val_if_fail (caps2 != NULL, NULL);
+
+  if (CAPS_IS_EMPTY (caps1))
+    return gst_caps_copy (caps2);
+
+  if (CAPS_IS_EMPTY (caps2))
+    return gst_caps_copy (caps1);
+
+  if (CAPS_IS_ANY (caps1) || CAPS_IS_ANY (caps2))
     return gst_caps_new_any ();
 
   dest1 = gst_caps_copy (caps1);
@@ -1238,6 +1696,8 @@ gst_caps_union (const GstCaps * caps1, const GstCaps * caps2)
   return dest1;
 }
 
+/* normalize/simplify operations */
+
 typedef struct _NormalizeForeach
 {
   GstCaps *caps;
@@ -1253,12 +1713,13 @@ gst_caps_normalize_foreach (GQuark field_id, const GValue * value, gpointer ptr)
   guint i;
 
   if (G_VALUE_TYPE (value) == GST_TYPE_LIST) {
-    for (i = 1; i < gst_value_list_get_size (value); i++) {
+    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 (nf->caps, structure);
+      gst_caps_append_structure_unchecked (nf->caps, structure);
     }
 
     gst_value_init_and_copy (&val, gst_value_list_get_value (value, 0));
@@ -1292,8 +1753,8 @@ gst_caps_normalize (const GstCaps * caps)
   newcaps = gst_caps_copy (caps);
   nf.caps = newcaps;
 
-  for (i = 0; i < newcaps->structs->len; i++) {
-    nf.structure = gst_caps_get_structure (newcaps, i);
+  for (i = 0; i < gst_caps_get_size (newcaps); i++) {
+    nf.structure = gst_caps_get_structure_unchecked (newcaps, i);
 
     while (!gst_structure_foreach (nf.structure,
             gst_caps_normalize_foreach, &nf));
@@ -1309,7 +1770,7 @@ gst_caps_compare_structures (gconstpointer one, gconstpointer two)
   const GstStructure *struct1 = *((const GstStructure **) one);
   const GstStructure *struct2 = *((const GstStructure **) two);
 
-  /* FIXME: this orders aphabetically, but ordering the quarks might be faster
+  /* 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));
@@ -1319,30 +1780,6 @@ gst_caps_compare_structures (gconstpointer one, gconstpointer two)
   return gst_structure_n_fields (struct2) - gst_structure_n_fields (struct1);
 }
 
-/**
- * gst_caps_simplify:
- * @caps: a #GstCaps to simplify
- *
- * Creates a new #GstCaps that represents the same set of formats as
- * @caps, but simpler.  Component structures that are identical are
- * merged.  Component structures that have ranges or lists that can
- * be merged are also merged.
- *
- * Returns: the new #GstCaps
- */
-GstCaps *
-gst_caps_simplify (const GstCaps * caps)
-{
-  GstCaps *ret;
-
-  g_return_val_if_fail (caps != NULL, NULL);
-
-  ret = gst_caps_copy (caps);
-  gst_caps_do_simplify (ret);
-
-  return ret;
-}
-
 typedef struct
 {
   GQuark name;
@@ -1383,24 +1820,17 @@ gst_caps_structure_simplify (GstStructure ** result,
 
   /* try to subtract to get a real subset */
   if (gst_caps_structure_subtract (&list, simplify, compare)) {
-    switch (g_slist_length (list)) {
-      case 0:
-        *result = NULL;
-        return TRUE;
-      case 1:
-        *result = list->data;
-        g_slist_free (list);
-        return TRUE;
-      default:
-      {
-        GSList *walk;
-
-        for (walk = list; walk; walk = g_slist_next (walk)) {
-          gst_structure_free (walk->data);
-        }
-        g_slist_free (list);
-        break;
-      }
+    if (list == NULL) {         /* no result */
+      *result = NULL;
+      return TRUE;
+    } else if (list->next == NULL) {    /* one result */
+      *result = list->data;
+      g_slist_free (list);
+      return TRUE;
+    } else {                    /* multiple results */
+      g_slist_foreach (list, (GFunc) gst_structure_free, NULL);
+      g_slist_free (list);
+      list = NULL;
     }
   }
 
@@ -1478,14 +1908,15 @@ gst_caps_do_simplify (GstCaps * caps)
 
   start = caps->structs->len - 1;
   for (i = caps->structs->len - 1; i >= 0; i--) {
-    simplify = gst_caps_get_structure (caps, i);
+    simplify = gst_caps_get_structure_unchecked (caps, i);
     if (gst_structure_get_name_id (simplify) !=
-        gst_structure_get_name_id (gst_caps_get_structure (caps, start)))
+        gst_structure_get_name_id (gst_caps_get_structure_unchecked (caps,
+                start)))
       start = i;
     for (j = start; j >= 0; j--) {
       if (j == i)
         continue;
-      compare = gst_caps_get_structure (caps, j);
+      compare = gst_caps_get_structure_unchecked (caps, j);
       if (gst_structure_get_name_id (simplify) !=
           gst_structure_get_name_id (compare)) {
         break;
@@ -1511,7 +1942,9 @@ gst_caps_do_simplify (GstCaps * caps)
   return TRUE;
 }
 
-#ifndef GST_DISABLE_LOADSAVE
+/* persistence */
+
+#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
 /**
  * gst_caps_save_thyself:
  * @caps: a #GstCaps structure
@@ -1554,32 +1987,36 @@ gst_caps_load_thyself (xmlNodePtr parent)
 
 /**
  * gst_caps_replace:
- * @caps: a pointer to #GstCaps
+ * @caps: (inout) (transfer full): a pointer to #GstCaps
  * @newcaps: a #GstCaps to replace *caps
  *
  * Replaces *caps with @newcaps.  Unrefs the #GstCaps in the location
  * pointed to by @caps, if applicable, then modifies @caps to point to
  * @newcaps. An additional ref on @newcaps is taken.
+ *
+ * This function does not take any locks so you might want to lock
+ * the object owning @caps pointer.
  */
 void
 gst_caps_replace (GstCaps ** caps, GstCaps * newcaps)
 {
   GstCaps *oldcaps;
 
-#if 0                           /* disable this, since too many plugins rely on undefined behavior */
-#ifdef USE_POISONING
-  //if (newcaps) CAPS_POISON (newcaps);
-#endif
-#endif
+  g_return_if_fail (caps != NULL);
+
   oldcaps = *caps;
 
-  if (newcaps)
-    gst_caps_ref (newcaps);
+  GST_CAT_TRACE (GST_CAT_REFCOUNTING, "%p, %p -> %p", caps, oldcaps, newcaps);
 
-  *caps = newcaps;
+  if (newcaps != oldcaps) {
+    if (newcaps)
+      gst_caps_ref (newcaps);
 
-  if (oldcaps)
-    gst_caps_unref (oldcaps);
+    *caps = newcaps;
+
+    if (oldcaps)
+      gst_caps_unref (oldcaps);
+  }
 }
 
 /**
@@ -1589,12 +2026,18 @@ gst_caps_replace (GstCaps ** caps, GstCaps * newcaps)
  * Converts @caps to a string representation.  This string representation
  * can be converted back to a #GstCaps by gst_caps_from_string().
  *
- * Returns: a newly allocated string representing @caps.
+ * 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.
+ *
+ * Returns: (transfer full): a newly allocated string representing @caps.
  */
 gchar *
 gst_caps_to_string (const GstCaps * caps)
 {
-  guint i;
+  guint i, slen, clen;
   GString *s;
 
   /* NOTE:  This function is potentially called by the debug system,
@@ -1606,27 +2049,38 @@ gst_caps_to_string (const GstCaps * caps)
   if (caps == NULL) {
     return g_strdup ("NULL");
   }
-  if (gst_caps_is_any (caps)) {
+  if (CAPS_IS_ANY (caps)) {
     return g_strdup ("ANY");
   }
-  if (gst_caps_is_empty (caps)) {
+  if (CAPS_IS_EMPTY_SIMPLE (caps)) {
     return g_strdup ("EMPTY");
   }
 
-  s = g_string_new ("");
-  for (i = 0; i < caps->structs->len; i++) {
+  /* estimate a rough string length to avoid unnecessary reallocs in GString */
+  slen = 0;
+  clen = caps->structs->len;
+  for (i = 0; i < clen; i++) {
+    slen +=
+        STRUCTURE_ESTIMATED_STRING_LEN (gst_caps_get_structure_unchecked (caps,
+            i));
+  }
+
+  s = g_string_sized_new (slen);
+  for (i = 0; i < clen; i++) {
     GstStructure *structure;
-    char *sstr;
 
-    if (i > 0)
-      g_string_append (s, "; ");
+    if (i > 0) {
+      /* ';' is now added by gst_structure_to_string */
+      g_string_append_c (s, ' ');
+    }
 
-    structure = gst_caps_get_structure (caps, i);
-    sstr = gst_structure_to_string (structure);
-    g_string_append (s, sstr);
-    g_free (sstr);
+    structure = gst_caps_get_structure_unchecked (caps, i);
+    priv_gst_structure_append_to_gstring (structure, s);
+  }
+  if (s->len && s->str[s->len - 1] == ';') {
+    /* remove latest ';' */
+    s->str[--s->len] = '\0';
   }
-
   return g_string_free (s, FALSE);
 }
 
@@ -1636,7 +2090,6 @@ gst_caps_from_string_inplace (GstCaps * caps, const gchar * string)
   GstStructure *structure;
   gchar *s;
 
-  g_return_val_if_fail (string, FALSE);
   if (strcmp ("ANY", string) == 0) {
     caps->flags = GST_CAPS_FLAGS_ANY;
     return TRUE;
@@ -1649,24 +2102,22 @@ gst_caps_from_string_inplace (GstCaps * caps, const gchar * string)
   if (structure == NULL) {
     return FALSE;
   }
-  gst_caps_append_structure (caps, structure);
+  gst_caps_append_structure_unchecked (caps, structure);
+
+  do {
 
-  while (*s == ';') {
-    s++;
     while (g_ascii_isspace (*s))
       s++;
+    if (*s == '\0') {
+      break;
+    }
     structure = gst_structure_from_string (s, &s);
     if (structure == NULL) {
       return FALSE;
     }
-    gst_caps_append_structure (caps, structure);
-    while (g_ascii_isspace (*s))
-      s++;
-  }
+    gst_caps_append_structure_unchecked (caps, structure);
 
-  if (*s != 0) {
-    return FALSE;
-  }
+  } while (TRUE);
 
   return TRUE;
 }
@@ -1677,13 +2128,15 @@ gst_caps_from_string_inplace (GstCaps * caps, const gchar * string)
  *
  * Converts @caps from a string representation.
  *
- * Returns: a newly allocated #GstCaps
+ * Returns: (transfer full): a newly allocated #GstCaps
  */
 GstCaps *
 gst_caps_from_string (const gchar * string)
 {
   GstCaps *caps;
 
+  g_return_val_if_fail (string, FALSE);
+
   caps = gst_caps_new_empty ();
   if (gst_caps_from_string_inplace (caps, string)) {
     return caps;
@@ -1702,8 +2155,8 @@ gst_caps_transform_to_string (const GValue * src_value, GValue * dest_value)
   g_return_if_fail (G_VALUE_HOLDS (dest_value, G_TYPE_STRING)
       || G_VALUE_HOLDS (dest_value, G_TYPE_POINTER));
 
-  dest_value->data[0].v_pointer =
-      gst_caps_to_string (src_value->data[0].v_pointer);
+  g_value_take_string (dest_value,
+      gst_caps_to_string (gst_value_get_caps (src_value)));
 }
 
 static GstCaps *