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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4812>
This commit is contained in:
Nicolas Dufresne 2023-06-08 14:21:07 -04:00
parent 0b648f9a2d
commit cd9ac137d2
3 changed files with 53 additions and 29 deletions

View file

@ -995,7 +995,7 @@
"long-name": "x264 H.264 Encoder", "long-name": "x264 H.264 Encoder",
"pad-templates": { "pad-templates": {
"sink": { "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", "direction": "sink",
"presence": "always" "presence": "always"
}, },

View file

@ -113,6 +113,16 @@
GST_DEBUG_CATEGORY_STATIC (x264_enc_debug); GST_DEBUG_CATEGORY_STATIC (x264_enc_debug);
#define GST_CAT_DEFAULT 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 struct _GstX264EncVTable
{ {
GModule *module; GModule *module;
@ -210,8 +220,7 @@ unload_x264 (GstX264EncVTable * vtable)
static gboolean static gboolean
gst_x264_enc_add_x264_chroma_format (GstStructure * s, gst_x264_enc_add_x264_chroma_format (GstStructure * s,
gboolean allow_420_8, gboolean allow_420_10, gboolean allow_422, enum AllowedSubsamplingFlags flags)
gboolean allow_444)
{ {
GValue fmts = G_VALUE_INIT; GValue fmts = G_VALUE_INIT;
GValue fmt = G_VALUE_INIT; GValue fmt = G_VALUE_INIT;
@ -223,17 +232,20 @@ gst_x264_enc_add_x264_chroma_format (GstStructure * s,
if (vtable_8bit) { if (vtable_8bit) {
gint chroma_format = *vtable_8bit->x264_chroma_format; 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"); g_value_set_string (&fmt, "Y444");
gst_value_list_append_value (&fmts, &fmt); 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"); g_value_set_string (&fmt, "Y42B");
gst_value_list_append_value (&fmts, &fmt); 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"); g_value_set_string (&fmt, "I420");
gst_value_list_append_value (&fmts, &fmt); gst_value_list_append_value (&fmts, &fmt);
g_value_set_string (&fmt, "YV12"); 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"); g_value_set_string (&fmt, "NV12");
gst_value_list_append_value (&fmts, &fmt); 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) { if (vtable_10bit) {
gint chroma_format = *vtable_10bit->x264_chroma_format; 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) if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
g_value_set_string (&fmt, "Y444_10LE"); g_value_set_string (&fmt, "Y444_10LE");
else else
@ -255,7 +274,8 @@ gst_x264_enc_add_x264_chroma_format (GstStructure * s,
gst_value_list_append_value (&fmts, &fmt); 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) if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
g_value_set_string (&fmt, "I422_10LE"); g_value_set_string (&fmt, "I422_10LE");
else else
@ -264,7 +284,8 @@ gst_x264_enc_add_x264_chroma_format (GstStructure * s,
gst_value_list_append_value (&fmts, &fmt); 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) if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
g_value_set_string (&fmt, "I420_10LE"); g_value_set_string (&fmt, "I420_10LE");
else else
@ -785,20 +806,20 @@ GST_ELEMENT_REGISTER_DEFINE_CUSTOM (x264enc, x264_element_init)
} }
static void static void
check_formats (const gchar * str, gboolean * has_420_8, gboolean * has_420_10, check_formats (const gchar * str, enum AllowedSubsamplingFlags *flags)
gboolean * has_422, gboolean * has_444)
{ {
if (g_str_has_prefix (str, "high-4:4:4")) 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")) 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")) 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 else
*has_420_8 = TRUE; *flags |= ALLOW_420_8;
} }
/* allowed input caps depending on whether libx264 was built for 8 or 10 bits */ /* allowed input caps depending on whether libx264 was built for 8 or 10 bits */
static GstCaps * static GstCaps *
gst_x264_enc_sink_getcaps (GstVideoEncoder * enc, GstCaps * filter) 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); gst_structure_set_value (s, "chroma-site", val);
if ((val = gst_structure_get_value (allowed_s, "profile"))) { if ((val = gst_structure_get_value (allowed_s, "profile"))) {
gboolean has_420_8 = FALSE; enum AllowedSubsamplingFlags flags = 0;
gboolean has_420_10 = FALSE;
gboolean has_422 = FALSE;
gboolean has_444 = FALSE;
if (G_VALUE_HOLDS_STRING (val)) { if (G_VALUE_HOLDS_STRING (val)) {
check_formats (g_value_get_string (val), &has_420_8, &has_420_10, check_formats (g_value_get_string (val), &flags);
&has_422, &has_444);
} else if (GST_VALUE_HOLDS_LIST (val)) { } else if (GST_VALUE_HOLDS_LIST (val)) {
for (k = 0; k < gst_value_list_get_size (val); k++) { for (k = 0; k < gst_value_list_get_size (val); k++) {
const GValue *vlist = gst_value_list_get_value (val, k); const GValue *vlist = gst_value_list_get_value (val, k);
if (G_VALUE_HOLDS_STRING (vlist)) if (G_VALUE_HOLDS_STRING (vlist))
check_formats (g_value_get_string (vlist), &has_420_8, check_formats (g_value_get_string (vlist), &flags);
&has_420_10, &has_422, &has_444);
} }
} }
gst_x264_enc_add_x264_chroma_format (s, has_420_8, has_420_10, has_422, gst_x264_enc_add_x264_chroma_format (s, flags);
has_444);
} }
filter_caps = gst_caps_merge_structure (filter_caps, s); 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); "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
gst_x264_enc_add_x264_chroma_format (gst_caps_get_structure 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", sink_templ = gst_pad_template_new ("sink",
GST_PAD_SINK, GST_PAD_ALWAYS, supported_sinkcaps); 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) gst_x264_enc_gst_to_x264_video_format (GstVideoFormat format, gint * nplanes)
{ {
switch (format) { switch (format) {
case GST_VIDEO_FORMAT_GRAY8:
if (nplanes)
*nplanes = 1;
return X264_CSP_I400;
case GST_VIDEO_FORMAT_I420: case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12: case GST_VIDEO_FORMAT_YV12:
if (nplanes) if (nplanes)

View file

@ -148,6 +148,11 @@ check_caps (GstCaps * caps, const gchar * profile, gint profile_id)
fail_unless (!strcmp (caps_profile, profile)); 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[] = static const GstVideoFormat formats_420_8[] =
{ GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_NV12, { GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_YV12, GST_VIDEO_FORMAT_NV12,
GST_VIDEO_FORMAT_UNKNOWN GST_VIDEO_FORMAT_UNKNOWN
@ -358,8 +363,8 @@ GST_START_TEST (test_video_high)
{ {
gint i; gint i;
for (i = 0; formats_420_8[i] != GST_VIDEO_FORMAT_UNKNOWN; i++) for (i = 0; formats_420_8_and_400_8[i] != GST_VIDEO_FORMAT_UNKNOWN; i++)
test_video_profile ("high", 0x64, formats_420_8, i); test_video_profile ("high", 0x64, formats_420_8_and_400_8, i);
} }
GST_END_TEST; GST_END_TEST;