From cd9ac137d269b04d6fa17fc3f14160e9c2770940 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Thu, 8 Jun 2023 14:21:07 -0400 Subject: [PATCH] x264enc: Add monochrome 8bit support This is mapped to GStreamer GRAY8 format. This allow producing files with the chroma_format_idc 0 (monochrome). This is only availbable to high profile. Part-of: --- .../docs/gst_plugins_cache.json | 2 +- .../gst-plugins-ugly/ext/x264/gstx264enc.c | 71 ++++++++++++------- .../tests/check/elements/x264enc.c | 9 ++- 3 files changed, 53 insertions(+), 29 deletions(-) diff --git a/subprojects/gst-plugins-ugly/docs/gst_plugins_cache.json b/subprojects/gst-plugins-ugly/docs/gst_plugins_cache.json index 8e8a004a84..8401974919 100644 --- a/subprojects/gst-plugins-ugly/docs/gst_plugins_cache.json +++ b/subprojects/gst-plugins-ugly/docs/gst_plugins_cache.json @@ -995,7 +995,7 @@ "long-name": "x264 H.264 Encoder", "pad-templates": { "sink": { - "caps": "video/x-raw:\n framerate: [ 0/1, 2147483647/1 ]\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n format: { Y444, Y42B, I420, YV12, NV12, Y444_10LE, I422_10LE, I420_10LE }\n", + "caps": "video/x-raw:\n framerate: [ 0/1, 2147483647/1 ]\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n format: { Y444, Y42B, I420, YV12, NV12, GRAY8, Y444_10LE, I422_10LE, I420_10LE }\n", "direction": "sink", "presence": "always" }, diff --git a/subprojects/gst-plugins-ugly/ext/x264/gstx264enc.c b/subprojects/gst-plugins-ugly/ext/x264/gstx264enc.c index 8d2783fc36..9ad170c45d 100644 --- a/subprojects/gst-plugins-ugly/ext/x264/gstx264enc.c +++ b/subprojects/gst-plugins-ugly/ext/x264/gstx264enc.c @@ -113,6 +113,16 @@ GST_DEBUG_CATEGORY_STATIC (x264_enc_debug); #define GST_CAT_DEFAULT x264_enc_debug +enum AllowedSubsamplingFlags +{ + ALLOW_400_8 = 1 << 0, + ALLOW_420_8 = 1 << 1, + ALLOW_420_10 = 1 << 2, + ALLOW_422 = 1 << 4, + ALLOW_444 = 1 << 5, + ALLOW_ANY = 0xffff +}; + struct _GstX264EncVTable { GModule *module; @@ -210,8 +220,7 @@ unload_x264 (GstX264EncVTable * vtable) static gboolean gst_x264_enc_add_x264_chroma_format (GstStructure * s, - gboolean allow_420_8, gboolean allow_420_10, gboolean allow_422, - gboolean allow_444) + enum AllowedSubsamplingFlags flags) { GValue fmts = G_VALUE_INIT; GValue fmt = G_VALUE_INIT; @@ -223,17 +232,20 @@ gst_x264_enc_add_x264_chroma_format (GstStructure * s, if (vtable_8bit) { gint chroma_format = *vtable_8bit->x264_chroma_format; - if ((chroma_format == 0 || chroma_format == X264_CSP_I444) && allow_444) { + if ((chroma_format == 0 || chroma_format == X264_CSP_I444) && + flags & ALLOW_444) { g_value_set_string (&fmt, "Y444"); gst_value_list_append_value (&fmts, &fmt); } - if ((chroma_format == 0 || chroma_format == X264_CSP_I422) && allow_422) { + if ((chroma_format == 0 || chroma_format == X264_CSP_I422) && + flags & ALLOW_422) { g_value_set_string (&fmt, "Y42B"); gst_value_list_append_value (&fmts, &fmt); } - if ((chroma_format == 0 || chroma_format == X264_CSP_I420) && allow_420_8) { + if ((chroma_format == 0 || chroma_format == X264_CSP_I420) && + flags & ALLOW_420_8) { g_value_set_string (&fmt, "I420"); gst_value_list_append_value (&fmts, &fmt); g_value_set_string (&fmt, "YV12"); @@ -241,12 +253,19 @@ gst_x264_enc_add_x264_chroma_format (GstStructure * s, g_value_set_string (&fmt, "NV12"); gst_value_list_append_value (&fmts, &fmt); } + + if ((chroma_format == 0 || chroma_format == X264_CSP_I400) && + flags & ALLOW_400_8) { + g_value_set_string (&fmt, "GRAY8"); + gst_value_list_append_value (&fmts, &fmt); + } } if (vtable_10bit) { gint chroma_format = *vtable_10bit->x264_chroma_format; - if ((chroma_format == 0 || chroma_format == X264_CSP_I444) && allow_444) { + if ((chroma_format == 0 || chroma_format == X264_CSP_I444) && + flags & ALLOW_444) { if (G_BYTE_ORDER == G_LITTLE_ENDIAN) g_value_set_string (&fmt, "Y444_10LE"); else @@ -255,7 +274,8 @@ gst_x264_enc_add_x264_chroma_format (GstStructure * s, gst_value_list_append_value (&fmts, &fmt); } - if ((chroma_format == 0 || chroma_format == X264_CSP_I422) && allow_422) { + if ((chroma_format == 0 || chroma_format == X264_CSP_I422) && + flags & ALLOW_422) { if (G_BYTE_ORDER == G_LITTLE_ENDIAN) g_value_set_string (&fmt, "I422_10LE"); else @@ -264,7 +284,8 @@ gst_x264_enc_add_x264_chroma_format (GstStructure * s, gst_value_list_append_value (&fmts, &fmt); } - if ((chroma_format == 0 || chroma_format == X264_CSP_I420) && allow_420_10) { + if ((chroma_format == 0 || chroma_format == X264_CSP_I420) && + flags & ALLOW_420_10) { if (G_BYTE_ORDER == G_LITTLE_ENDIAN) g_value_set_string (&fmt, "I420_10LE"); else @@ -785,20 +806,20 @@ GST_ELEMENT_REGISTER_DEFINE_CUSTOM (x264enc, x264_element_init) } static void -check_formats (const gchar * str, gboolean * has_420_8, gboolean * has_420_10, - gboolean * has_422, gboolean * has_444) +check_formats (const gchar * str, enum AllowedSubsamplingFlags *flags) { if (g_str_has_prefix (str, "high-4:4:4")) - *has_444 = TRUE; + *flags |= ALLOW_444; else if (g_str_has_prefix (str, "high-4:2:2")) - *has_422 = TRUE; + *flags |= ALLOW_422; else if (g_str_has_prefix (str, "high-10")) - *has_420_10 = TRUE; + *flags |= ALLOW_420_10; + else if (g_str_has_prefix (str, "high")) + *flags |= ALLOW_420_8 | ALLOW_400_8; else - *has_420_8 = TRUE; + *flags |= ALLOW_420_8; } - /* 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) @@ -853,26 +874,20 @@ gst_x264_enc_sink_getcaps (GstVideoEncoder * enc, GstCaps * filter) gst_structure_set_value (s, "chroma-site", val); if ((val = gst_structure_get_value (allowed_s, "profile"))) { - gboolean has_420_8 = FALSE; - gboolean has_420_10 = FALSE; - gboolean has_422 = FALSE; - gboolean has_444 = FALSE; + enum AllowedSubsamplingFlags flags = 0; if (G_VALUE_HOLDS_STRING (val)) { - check_formats (g_value_get_string (val), &has_420_8, &has_420_10, - &has_422, &has_444); + check_formats (g_value_get_string (val), &flags); } 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_8, - &has_420_10, &has_422, &has_444); + check_formats (g_value_get_string (vlist), &flags); } } - gst_x264_enc_add_x264_chroma_format (s, has_420_8, has_420_10, has_422, - has_444); + gst_x264_enc_add_x264_chroma_format (s, flags); } filter_caps = gst_caps_merge_structure (filter_caps, s); @@ -1211,7 +1226,7 @@ gst_x264_enc_class_init (GstX264EncClass * klass) "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL); gst_x264_enc_add_x264_chroma_format (gst_caps_get_structure - (supported_sinkcaps, 0), TRUE, TRUE, TRUE, TRUE); + (supported_sinkcaps, 0), ALLOW_ANY); sink_templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, supported_sinkcaps); @@ -1517,6 +1532,10 @@ static gint gst_x264_enc_gst_to_x264_video_format (GstVideoFormat format, gint * nplanes) { switch (format) { + case GST_VIDEO_FORMAT_GRAY8: + if (nplanes) + *nplanes = 1; + return X264_CSP_I400; case GST_VIDEO_FORMAT_I420: case GST_VIDEO_FORMAT_YV12: if (nplanes) diff --git a/subprojects/gst-plugins-ugly/tests/check/elements/x264enc.c b/subprojects/gst-plugins-ugly/tests/check/elements/x264enc.c index a26c1ff81c..cc6c9a67ba 100644 --- a/subprojects/gst-plugins-ugly/tests/check/elements/x264enc.c +++ b/subprojects/gst-plugins-ugly/tests/check/elements/x264enc.c @@ -148,6 +148,11 @@ check_caps (GstCaps * caps, const gchar * profile, gint profile_id) fail_unless (!strcmp (caps_profile, profile)); } +static const GstVideoFormat formats_420_8_and_400_8[] = + { GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_NV12, + GST_VIDEO_FORMAT_GRAY8, GST_VIDEO_FORMAT_UNKNOWN +}; + static const GstVideoFormat formats_420_8[] = { GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_NV12, GST_VIDEO_FORMAT_UNKNOWN @@ -358,8 +363,8 @@ GST_START_TEST (test_video_high) { gint i; - for (i = 0; formats_420_8[i] != GST_VIDEO_FORMAT_UNKNOWN; i++) - test_video_profile ("high", 0x64, formats_420_8, i); + for (i = 0; formats_420_8_and_400_8[i] != GST_VIDEO_FORMAT_UNKNOWN; i++) + test_video_profile ("high", 0x64, formats_420_8_and_400_8, i); } GST_END_TEST;