x264enc: Implement getcaps that restricts the input format depending on the output...
authorOlivier Crête <olivier.crete@collabora.com>
Wed, 18 Sep 2013 23:13:45 +0000 (19:13 -0400)
committerOlivier Crête <olivier.crete@collabora.com>
Wed, 18 Sep 2013 23:33:01 +0000 (19:33 -0400)
Also add some x264enc profile tests

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

ext/x264/gstx264enc.c
tests/check/elements/x264enc.c

index 6d527a1..dcaf5d0 100644 (file)
@@ -412,8 +412,9 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ], "
         "stream-format = (string) { avc, byte-stream }, "
         "alignment = (string) au, "
-        "profile = (string) { high-10, high, main, baseline, "
-        "constrained-baseline, high-10-intra }")
+        "profile = (string) { high-4:4:4, high-4:2:2, high-10, high, main,"
+        " baseline, constrained-baseline, high-4:4:4-intra, high-4:2:2-intra,"
+        " high-10-intra }")
     );
 
 static void gst_x264_enc_finalize (GObject * object);
@@ -498,53 +499,51 @@ set_value (GValue * val, gint count, ...)
     g_value_unset (&sval);
 }
 
-static GstCaps *
-gst_x264_enc_get_supported_input_caps (void)
+static void
+gst_x264_enc_add_x264_chroma_format (GstStructure * s,
+    int x264_chroma_format_local)
 {
   GValue fmt = G_VALUE_INIT;
-  GstCaps *caps;
-
-  caps = gst_caps_new_empty_simple ("video/x-raw");
 
   if (x264_bit_depth == 8) {
     GST_INFO ("This x264 build supports 8-bit depth");
-    if (x264_chroma_format == 0) {
+    if (x264_chroma_format_local == 0) {
       set_value (&fmt, 5, "I420", "YV12", "Y42B", "Y444", "NV12");
-    } else if (x264_chroma_format == X264_CSP_I420) {
+    } else if (x264_chroma_format_local == X264_CSP_I420) {
       set_value (&fmt, 3, "I420", "YV12", "NV12");
-    } else if (x264_chroma_format == X264_CSP_I422) {
+    } else if (x264_chroma_format_local == X264_CSP_I422) {
       set_value (&fmt, 1, "Y42B");
-    } else if (x264_chroma_format == X264_CSP_I444) {
+    } else if (x264_chroma_format_local == X264_CSP_I444) {
       set_value (&fmt, 1, "Y444");
     } else {
-      GST_ERROR ("Unsupported chroma format %d", x264_chroma_format);
+      GST_ERROR ("Unsupported chroma format %d", x264_chroma_format_local);
     }
   } else if (x264_bit_depth == 10) {
     GST_INFO ("This x264 build supports 10-bit depth");
 
     if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
-      if (x264_chroma_format == 0) {
+      if (x264_chroma_format_local == 0) {
         set_value (&fmt, 3, "I420_10LE", "I422_10LE", "Y444_10LE");
-      } else if (x264_chroma_format == X264_CSP_I420) {
+      } else if (x264_chroma_format_local == X264_CSP_I420) {
         set_value (&fmt, 1, "I420_10LE");
-      } else if (x264_chroma_format == X264_CSP_I422) {
-        set_value (&fmt, 1, "Y422_10LE");
-      } else if (x264_chroma_format == X264_CSP_I444) {
+      } else if (x264_chroma_format_local == X264_CSP_I422) {
+        set_value (&fmt, 1, "I422_10LE");
+      } else if (x264_chroma_format_local == X264_CSP_I444) {
         set_value (&fmt, 1, "Y444_10LE");
       } else {
-        GST_ERROR ("Unsupported chroma format %d", x264_chroma_format);
+        GST_ERROR ("Unsupported chroma format %d", x264_chroma_format_local);
       }
     } else {
-      if (x264_chroma_format == 0) {
+      if (x264_chroma_format_local == 0) {
         set_value (&fmt, 3, "I420_10BE", "I422_10BE", "Y444_10BE");
-      } else if (x264_chroma_format == X264_CSP_I420) {
+      } else if (x264_chroma_format_local == X264_CSP_I420) {
         set_value (&fmt, 1, "I420_10BE");
-      } else if (x264_chroma_format == X264_CSP_I422) {
-        set_value (&fmt, 1, "Y422_10BE");
-      } else if (x264_chroma_format == X264_CSP_I444) {
+      } else if (x264_chroma_format_local == X264_CSP_I422) {
+        set_value (&fmt, 1, "I422_10BE");
+      } else if (x264_chroma_format_local == X264_CSP_I444) {
         set_value (&fmt, 1, "Y444_10BE");
       } else {
-        GST_ERROR ("Unsupported chroma format %d", x264_chroma_format);
+        GST_ERROR ("Unsupported chroma format %d", x264_chroma_format_local);
       }
     }
   } else {
@@ -553,28 +552,135 @@ gst_x264_enc_get_supported_input_caps (void)
   }
 
   if (G_VALUE_TYPE (&fmt) != G_TYPE_INVALID)
-    gst_structure_take_value (gst_caps_get_structure (caps, 0), "format", &fmt);
+    gst_structure_take_value (s, "format", &fmt);
+}
+
+static GstCaps *
+gst_x264_enc_get_supported_input_caps (void)
+{
+  GstCaps *caps;
 
-  gst_caps_set_simple (caps,
+  caps = gst_caps_new_simple ("video/x-raw",
       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1,
       "width", GST_TYPE_INT_RANGE, 16, G_MAXINT,
       "height", GST_TYPE_INT_RANGE, 16, G_MAXINT, NULL);
 
+  gst_x264_enc_add_x264_chroma_format (gst_caps_get_structure (caps, 0),
+      x264_chroma_format);
+
   GST_DEBUG ("returning %" GST_PTR_FORMAT, caps);
   return caps;
 }
 
+static void
+check_formats (const gchar * str, gboolean * has_420, gboolean * has_422,
+    gboolean * has_444)
+{
+  if (g_str_has_prefix (str, "high-4:4:4"))
+    *has_444 = TRUE;
+  else if (g_str_has_prefix (str, "high-4:2:2"))
+    *has_422 = TRUE;
+  else
+    *has_420 = TRUE;
+}
+
+
 /* allowed input caps depending on whether libx264 was built for 8 or 10 bits */
 static GstCaps *
 gst_x264_enc_sink_getcaps (GstVideoEncoder * enc, GstCaps * filter)
 {
-  GstCaps *supported_incaps, *caps;
+  GstCaps *supported_incaps;
+  GstCaps *allowed;
+  GstCaps *filter_caps, *fcaps;
+  gint i, j, k;
 
   supported_incaps = gst_x264_enc_get_supported_input_caps ();
-  caps = gst_video_encoder_proxy_getcaps (enc, supported_incaps, filter);
+
+  /* Allow downstream to specify width/height/framerate/PAR constraints
+   * and forward them upstream for video converters to handle
+   */
+  if (!supported_incaps)
+    supported_incaps = gst_pad_get_pad_template_caps (enc->sinkpad);
+  allowed = gst_pad_get_allowed_caps (enc->srcpad);
+
+  if (!allowed || gst_caps_is_empty (allowed) || gst_caps_is_any (allowed)) {
+    fcaps = supported_incaps;
+    goto done;
+  }
+
+  GST_LOG_OBJECT (enc, "template caps %" GST_PTR_FORMAT, supported_incaps);
+  GST_LOG_OBJECT (enc, "allowed caps %" GST_PTR_FORMAT, allowed);
+
+  filter_caps = gst_caps_new_empty ();
+
+  for (i = 0; i < gst_caps_get_size (supported_incaps); i++) {
+    GQuark q_name =
+        gst_structure_get_name_id (gst_caps_get_structure (supported_incaps,
+            i));
+
+    for (j = 0; j < gst_caps_get_size (allowed); j++) {
+      const GstStructure *allowed_s = gst_caps_get_structure (allowed, j);
+      const GValue *val;
+      GstStructure *s;
+
+      s = gst_structure_new_id_empty (q_name);
+      if ((val = gst_structure_get_value (allowed_s, "width")))
+        gst_structure_set_value (s, "width", val);
+      if ((val = gst_structure_get_value (allowed_s, "height")))
+        gst_structure_set_value (s, "height", val);
+      if ((val = gst_structure_get_value (allowed_s, "framerate")))
+        gst_structure_set_value (s, "framerate", val);
+      if ((val = gst_structure_get_value (allowed_s, "pixel-aspect-ratio")))
+        gst_structure_set_value (s, "pixel-aspect-ratio", val);
+      if ((val = gst_structure_get_value (allowed_s, "profile"))) {
+        gboolean has_420 = FALSE;
+        gboolean has_422 = FALSE;
+        gboolean has_444 = FALSE;
+
+        if (G_VALUE_HOLDS_STRING (val)) {
+          check_formats (g_value_get_string (val), &has_420, &has_422,
+              &has_444);
+        } else if (GST_VALUE_HOLDS_LIST (val)) {
+          for (k = 0; k < gst_value_list_get_size (val); k++) {
+            const GValue *vlist = gst_value_list_get_value (val, k);
+
+            if (G_VALUE_HOLDS_STRING (vlist))
+              check_formats (g_value_get_string (vlist), &has_420, &has_422,
+                  &has_444);
+          }
+        }
+
+        if (has_444 && has_422 && has_420)
+          gst_x264_enc_add_x264_chroma_format (s, 0);
+        else if (has_444)
+          gst_x264_enc_add_x264_chroma_format (s, X264_CSP_I444);
+        else if (has_422)
+          gst_x264_enc_add_x264_chroma_format (s, X264_CSP_I422);
+        else if (has_420)
+          gst_x264_enc_add_x264_chroma_format (s, X264_CSP_I420);
+      }
+
+      filter_caps = gst_caps_merge_structure (filter_caps, s);
+    }
+  }
+
+  fcaps = gst_caps_intersect (filter_caps, supported_incaps);
+  gst_caps_unref (filter_caps);
   gst_caps_unref (supported_incaps);
 
-  return caps;
+  if (filter) {
+    GST_LOG_OBJECT (enc, "intersecting with %" GST_PTR_FORMAT, filter);
+    filter_caps = gst_caps_intersect (fcaps, filter);
+    gst_caps_unref (fcaps);
+    fcaps = filter_caps;
+  }
+
+done:
+  gst_caps_replace (&allowed, NULL);
+
+  GST_LOG_OBJECT (enc, "proxy caps %" GST_PTR_FORMAT, fcaps);
+
+  return fcaps;
 }
 
 static void
@@ -1712,15 +1818,19 @@ gst_x264_enc_set_format (GstVideoEncoder * video_enc,
       /* FIXME - if libx264 ever adds support for FMO, ASO or redundant slices
        * make sure constrained profile has a separate case which disables
        * those */
+      if (g_str_has_suffix (profile, "-intra")) {
+        encoder->peer_intra_profile = TRUE;
+      }
       if (!strcmp (profile, "constrained-baseline") ||
           !strcmp (profile, "baseline")) {
         encoder->peer_profile = "baseline";
-      } else if (!strcmp (profile, "high-10-intra")) {
-        encoder->peer_intra_profile = TRUE;
-        encoder->peer_profile = "high10";
-      } else if (!strcmp (profile, "high-10")) {
+      } else if (g_str_has_prefix (profile, "high-10")) {
         encoder->peer_profile = "high10";
-      } else if (!strcmp (profile, "high")) {
+      } else if (g_str_has_prefix (profile, "high-4:2:2")) {
+        encoder->peer_profile = "high422";
+      } else if (g_str_has_prefix (profile, "high-4:4:4")) {
+        encoder->peer_profile = "high444";
+      } else if (g_str_has_prefix (profile, "high")) {
         encoder->peer_profile = "high";
       } else if (!strcmp (profile, "main")) {
         encoder->peer_profile = "main";
index f62a80c..e91c7ce 100644 (file)
@@ -30,7 +30,7 @@
 static GstPad *mysrcpad, *mysinkpad;
 
 #define VIDEO_CAPS_STRING "video/x-raw, " \
-                           "format = (string) I420, " \
+                           "format = (string) { I420, Y42B, Y444 }, " \
                            "width = (int) 384, " \
                            "height = (int) 288, " \
                            "framerate = (fraction) 25/1"
@@ -40,7 +40,8 @@ static GstPad *mysrcpad, *mysinkpad;
                            "height = (int) 288, " \
                            "framerate = (fraction) 25/1"
 static GstElement *
-setup_x264enc (const gchar * profile, const gchar * stream_format)
+setup_x264enc (const gchar * profile, const gchar * stream_format,
+    const gchar * input_format)
 {
   GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
       GST_PAD_SINK,
@@ -68,6 +69,7 @@ setup_x264enc (const gchar * profile, const gchar * stream_format)
   gst_pad_set_active (mysinkpad, TRUE);
 
   caps = gst_caps_from_string (VIDEO_CAPS_STRING);
+  gst_caps_set_simple (caps, "format", G_TYPE_STRING, input_format, NULL);
   gst_check_setup_events (mysrcpad, x264enc, caps, GST_FORMAT_TIME);
   gst_caps_unref (caps);
 
@@ -90,11 +92,12 @@ cleanup_x264enc (GstElement * x264enc)
 }
 
 static void
-check_caps (GstCaps * caps, gint profile_id)
+check_caps (GstCaps * caps, const gchar * profile, gint profile_id)
 {
   GstStructure *s;
-  const GValue *sf, *avcc;
+  const GValue *sf, *avcc, *pf;
   const gchar *stream_format;
+  const gchar *caps_profile;
 
   fail_unless (caps != NULL);
 
@@ -125,10 +128,18 @@ check_caps (GstCaps * caps, gint profile_id)
   } else {
     fail_if (TRUE, "unexpected stream-format in caps: %s", stream_format);
   }
+
+  pf = gst_structure_get_value (s, "profile");
+  fail_unless (pf != NULL);
+  fail_unless (G_VALUE_HOLDS_STRING (pf));
+  caps_profile = g_value_get_string (pf);
+  fail_unless (caps_profile != NULL);
+  fail_unless (!strcmp (caps_profile, profile));
 }
 
 static void
-test_video_profile (const gchar * profile, gint profile_id)
+test_video_profile (const gchar * profile, gint profile_id,
+    const gchar * input_format)
 {
   GstElement *x264enc;
   GstBuffer *inbuffer, *outbuffer;
@@ -138,13 +149,19 @@ test_video_profile (const gchar * profile, gint profile_id)
   gsize size;
   int i, num_buffers;
 
-  x264enc = setup_x264enc (profile, "avc");
+  x264enc = setup_x264enc (profile, "avc", input_format);
   fail_unless (gst_element_set_state (x264enc,
           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
       "could not set to playing");
 
   /* corresponds to I420 buffer for the size mentioned in the caps */
-  inbuffer = gst_buffer_new_and_alloc (384 * 288 * 3 / 2);
+  if (!strcmp (input_format, "I420"))
+    inbuffer = gst_buffer_new_and_alloc (384 * 288 * 3 / 2);
+  else if (!strcmp (input_format, "Y42B"))
+    inbuffer = gst_buffer_new_and_alloc (384 * 288 * 2);
+  else if (!strcmp (input_format, "Y444"))
+    inbuffer = gst_buffer_new_and_alloc (384 * 288 * 3);
+
   /* makes valgrind's memcheck happier */
   gst_buffer_memset (inbuffer, 0, 0, -1);
   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
@@ -162,7 +179,7 @@ test_video_profile (const gchar * profile, gint profile_id)
     GstCaps *outcaps;
 
     outcaps = gst_pad_get_current_caps (mysinkpad);
-    check_caps (outcaps, profile_id);
+    check_caps (outcaps, profile, profile_id);
     gst_caps_unref (outcaps);
   }
 
@@ -237,25 +254,41 @@ test_video_profile (const gchar * profile, gint profile_id)
 
 GST_START_TEST (test_video_baseline)
 {
-  test_video_profile ("constrained-baseline", 0x42);
+  test_video_profile ("constrained-baseline", 0x42, "I420");
 }
 
 GST_END_TEST;
 
 GST_START_TEST (test_video_main)
 {
-  test_video_profile ("main", 0x4d);
+  test_video_profile ("main", 0x4d, "I420");
 }
 
 GST_END_TEST;
 
 GST_START_TEST (test_video_high)
 {
-  test_video_profile ("high", 0x64);
+  test_video_profile ("high", 0x64, "I420");
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_video_high422)
+{
+  test_video_profile ("high-4:2:2", 0x7A, "Y42B");
 }
 
 GST_END_TEST;
 
+GST_START_TEST (test_video_high444)
+{
+  test_video_profile ("high-4:4:4", 0xF4, "Y444");
+}
+
+GST_END_TEST;
+
+
+
 Suite *
 x264enc_suite (void)
 {
@@ -266,6 +299,8 @@ x264enc_suite (void)
   tcase_add_test (tc_chain, test_video_baseline);
   tcase_add_test (tc_chain, test_video_main);
   tcase_add_test (tc_chain, test_video_high);
+  tcase_add_test (tc_chain, test_video_high422);
+  tcase_add_test (tc_chain, test_video_high444);
 
   return s;
 }