From b82703170abde46dcd62fe12e8aed626c2a012e7 Mon Sep 17 00:00:00 2001 From: Olivier CrĂȘte Date: Wed, 18 Sep 2013 19:13:45 -0400 Subject: x264enc: Implement getcaps that restricts the input format depending on the output profile Also add some x264enc profile tests https://bugzilla.gnome.org/show_bug.cgi?id=708326 --- ext/x264/gstx264enc.c | 178 +++++++++++++++++++++++++++++++++-------- tests/check/elements/x264enc.c | 57 ++++++++++--- 2 files changed, 190 insertions(+), 45 deletions(-) diff --git a/ext/x264/gstx264enc.c b/ext/x264/gstx264enc.c index 6d527a10..dcaf5d0f 100644 --- a/ext/x264/gstx264enc.c +++ b/ext/x264/gstx264enc.c @@ -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"; diff --git a/tests/check/elements/x264enc.c b/tests/check/elements/x264enc.c index f62a80c7..e91c7ce6 100644 --- a/tests/check/elements/x264enc.c +++ b/tests/check/elements/x264enc.c @@ -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; } -- cgit v1.2.3