encodebin: Select muxer further
authorThiago Santos <thiago.sousa.santos@collabora.co.uk>
Wed, 3 Aug 2011 16:31:59 +0000 (13:31 -0300)
committerThiago Santos <thiago.sousa.santos@collabora.co.uk>
Mon, 5 Sep 2011 20:48:36 +0000 (17:48 -0300)
Sort muxers based on their caps and ranking before iterating to
find one that fits the profile.

Sorting is done by putting the elements that have a pad template
that can produce the exact caps that is on the profile. For example:
when asking for "video/quicktime, variant=iso", muxers that
have this exact caps on their pad templates will be put first on
the list than ones that have only "video/quicktime".

https://bugzilla.gnome.org/show_bug.cgi?id=651496

gst/encoding/gstencodebin.c

index e4a5833..062bae7 100644 (file)
@@ -1479,17 +1479,59 @@ cleanup:
 }
 
 static gboolean
-_factory_can_sink_caps (GstElementFactory * factory, const GstCaps * caps)
+_gst_caps_match_foreach (GQuark field_id, const GValue * value, gpointer data)
+{
+  GstStructure *structure = data;
+  const GValue *other_value = gst_structure_id_get_value (structure, field_id);
+
+  if (G_UNLIKELY (other_value == NULL))
+    return FALSE;
+  if (gst_value_compare (value, other_value) == GST_VALUE_EQUAL) {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/*
+ * checks that there is at least one structure on caps_a that has
+ * all its fields exactly the same as one structure on caps_b
+ */
+static gboolean
+_gst_caps_match (const GstCaps * caps_a, const GstCaps * caps_b)
+{
+  gint i, j;
+  gboolean res = FALSE;
+
+  for (i = 0; i < gst_caps_get_size (caps_a); i++) {
+    GstStructure *structure_a = gst_caps_get_structure (caps_a, i);
+    for (j = 0; j < gst_caps_get_size (caps_b); j++) {
+      GstStructure *structure_b = gst_caps_get_structure (caps_b, j);
+
+      res = gst_structure_foreach (structure_a, _gst_caps_match_foreach,
+          structure_b);
+      if (res)
+        goto end;
+    }
+  }
+end:
+  return res;
+}
+
+static gboolean
+_factory_can_handle_caps (GstElementFactory * factory, const GstCaps * caps,
+    GstPadDirection dir, gboolean exact)
 {
   GList *templates = factory->staticpadtemplates;
 
   while (templates) {
     GstStaticPadTemplate *template = (GstStaticPadTemplate *) templates->data;
 
-    if (template->direction == GST_PAD_SINK) {
+    if (template->direction == dir) {
       GstCaps *tmp = gst_static_caps_get (&template->static_caps);
 
-      if (gst_caps_can_intersect (tmp, caps)) {
+      if ((exact && _gst_caps_match (caps, tmp)) ||
+          (!exact && gst_caps_can_intersect (tmp, caps))) {
         gst_caps_unref (tmp);
         return TRUE;
       }
@@ -1540,6 +1582,31 @@ beach:
   return formatter;
 }
 
+static gint
+compare_elements (gconstpointer a, gconstpointer b, gpointer udata)
+{
+  GstCaps *caps = udata;
+  GstElementFactory *fac_a = (GstElementFactory *) a;
+  GstElementFactory *fac_b = (GstElementFactory *) b;
+
+  /* FIXME not quite sure this is the best algorithm to order the elements
+   * Some caps similarity comparison algorithm would fit better than going
+   * boolean (equals/not equals).
+   */
+  gboolean equals_a = _factory_can_handle_caps (fac_a, caps, GST_PAD_SRC, TRUE);
+  gboolean equals_b = _factory_can_handle_caps (fac_b, caps, GST_PAD_SRC, TRUE);
+
+  if (equals_a == equals_b) {
+    return gst_plugin_feature_get_rank ((GstPluginFeature *) fac_b) -
+        gst_plugin_feature_get_rank ((GstPluginFeature *) fac_a);
+  } else if (equals_a) {
+    return -1;
+  } else if (equals_b) {
+    return 1;
+  }
+  return 0;
+}
+
 static inline GstElement *
 _get_muxer (GstEncodeBin * ebin)
 {
@@ -1562,6 +1629,10 @@ _get_muxer (GstEncodeBin * ebin)
       gst_element_factory_list_filter (ebin->formatters, format, GST_PAD_SRC,
       TRUE);
 
+  muxers = g_list_sort_with_data (muxers, compare_elements, (gpointer) format);
+  formatters =
+      g_list_sort_with_data (formatters, compare_elements, (gpointer) format);
+
   muxers = g_list_concat (muxers, formatters);
 
   if (muxers == NULL)
@@ -1582,10 +1653,10 @@ _get_muxer (GstEncodeBin * ebin)
     for (tmp = profiles; tmp; tmp = tmp->next) {
       GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data;
 
-      if (!_factory_can_sink_caps (muxerfact,
-              gst_encoding_profile_get_format (sprof))) {
-        GST_DEBUG ("Skipping muxer because it can't sink caps %" GST_PTR_FORMAT,
-            gst_encoding_profile_get_format (sprof));
+      if (!_factory_can_handle_caps (muxerfact,
+              gst_encoding_profile_get_format (sprof), GST_PAD_SINK, FALSE)) {
+        GST_DEBUG ("Skipping muxer because it can't sink caps %"
+            GST_PTR_FORMAT, gst_encoding_profile_get_format (sprof));
         cansinkstreams = FALSE;
         break;
       }