From 56b16e52322ed8b11bef0829b44778c71b6e6f69 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Fri, 26 Jan 2024 04:26:07 +0530 Subject: [PATCH] y4mdec: Parse extended headers written out by FFmpeg References: https://wiki.multimedia.cx/index.php/YUV4MPEG2 https://github.com/FFmpeg/FFmpeg/blob/eee3b7e2/libavformat/yuv4mpegenc.c#L74-L166 The primary purpose is to add high bit-depth y4m support, which is commonly used for testing codecs. Part-of: --- .../docs/plugins/gst_plugins_cache.json | 2 +- .../gst-plugins-bad/gst/y4m/gsty4mdec.c | 119 +++++++++++++----- 2 files changed, 87 insertions(+), 34 deletions(-) diff --git a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json index e7d5cfb1c9..4d61d9ccec 100644 --- a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json +++ b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json @@ -245783,7 +245783,7 @@ "presence": "always" }, "src": { - "caps": "video/x-raw:\n format: { I420, Y42B, Y444 }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n", + "caps": "video/x-raw:\n format: { I420, Y41B, Y42B, Y444, I420_10LE, I422_10LE, Y444_10LE, I420_12LE, I422_12LE, Y444_12LE, Y444_16LE, GRAY8, GRAY16_LE }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n", "direction": "src", "presence": "always" } diff --git a/subprojects/gst-plugins-bad/gst/y4m/gsty4mdec.c b/subprojects/gst-plugins-bad/gst/y4m/gsty4mdec.c index 6c0d9ce406..c0883e3941 100644 --- a/subprojects/gst-plugins-bad/gst/y4m/gsty4mdec.c +++ b/subprojects/gst-plugins-bad/gst/y4m/gsty4mdec.c @@ -86,8 +86,12 @@ static GstStaticPadTemplate gst_y4m_dec_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{I420,Y42B,Y444}")) - ); + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ \ + I420,Y41B,Y42B,Y444, \ + I420_10LE,I422_10LE,Y444_10LE, \ + I420_12LE,I422_12LE,Y444_12LE, \ + Y444_16LE,GRAY8,GRAY16_LE \ + }"))); /* class initialization */ #define gst_y4m_dec_parent_class parent_class @@ -287,38 +291,90 @@ gst_y4m_dec_bytes_to_timestamp (GstY4mDec * y4mdec, gint64 bytes) gst_y4m_dec_bytes_to_frames (y4mdec, bytes)); } -static gboolean -parse_colorspace (const char *param, GstVideoFormat * format) +static GstVideoFormat +parse_colorspace (const char *param) { - gulong iformat = strtoul (param, NULL, 10); - switch (iformat) { - case 420: - *format = GST_VIDEO_FORMAT_I420; - break; - case 422: - *format = GST_VIDEO_FORMAT_Y42B; - break; - case 444: - *format = GST_VIDEO_FORMAT_Y444; - break; - default: - return FALSE; + char *end; + guint iformat = g_ascii_strtoull (param, &end, 10); + + if (*end == '\0') { + switch (iformat) { + case 420: + return GST_VIDEO_FORMAT_I420; + case 411: + return GST_VIDEO_FORMAT_Y41B; + case 422: + return GST_VIDEO_FORMAT_Y42B; + case 444: + return GST_VIDEO_FORMAT_Y444; + } } - return TRUE; + + /* + * Parse non-standard (i.e., unknown to mjpegtools) streams that are + * generated by FFmpeg: + * https://wiki.multimedia.cx/index.php/YUV4MPEG2 + * https://github.com/FFmpeg/FFmpeg/blob/eee3b7e2/libavformat/yuv4mpegenc.c#L74-L166 + * Will assume little-endian because this is an on-disk serialization format. + */ + + // TODO: Differentiate between: + // * C420jpeg: biaxially-displaced chroma planes + // * C420paldv: coincident R and vertically-displaced B + // * C420mpeg2: vertically-displaced chroma planes + if (iformat == 420 && (g_strcmp0 (end, "jpeg") == 0 || + g_strcmp0 (end, "paldv") == 0 || g_strcmp0 (end, "mpeg2") == 0)) + return GST_VIDEO_FORMAT_I420; + + if (iformat == 0 && strncmp (end, "mono", 4) == 0) { + char *type = end + 4; + if (*type == '\0') + return GST_VIDEO_FORMAT_GRAY8; + if (g_strcmp0 (type, "16") == 0) + return GST_VIDEO_FORMAT_GRAY16_LE; + } + + if (*end == 'p') { + guint depth = g_ascii_strtoull (end + 1, NULL, 10); + if (depth == 10) { + switch (iformat) { + case 420: + return GST_VIDEO_FORMAT_I420_10LE; + case 422: + return GST_VIDEO_FORMAT_I422_10LE; + case 444: + return GST_VIDEO_FORMAT_Y444_10LE; + } + } else if (depth == 12) { + switch (iformat) { + case 420: + return GST_VIDEO_FORMAT_I420_12LE; + case 422: + return GST_VIDEO_FORMAT_I422_12LE; + case 444: + return GST_VIDEO_FORMAT_Y444_12LE; + } + } else if (depth == 16 && iformat == 444) { + return GST_VIDEO_FORMAT_Y444_16LE; + } + } + + GST_WARNING ("%s is not a supported format", param); + return GST_VIDEO_FORMAT_UNKNOWN; } static gboolean parse_ratio (const char *param, gulong * n, gulong * d) { char *end; - *n = strtoul (param, &end, 10); + *n = g_ascii_strtoull (param, &end, 10); if (end == param) return FALSE; param = end; if (param[0] != ':') return FALSE; param++; - *d = strtoul (param, &end, 10); + *d = g_ascii_strtoull (param, &end, 10); if (end == param) return FALSE; return TRUE; @@ -328,9 +384,7 @@ static gboolean gst_y4m_dec_parse_header (GstY4mDec * y4mdec, char *header) { guint len; - char *valid; char **params; - const char *end; guint interlaced_char = 0; gulong fps_n = 0, fps_d = 0; gulong par_n = 0, par_d = 0; @@ -343,16 +397,13 @@ gst_y4m_dec_parse_header (GstY4mDec * y4mdec, char *header) } header += 10; - g_utf8_validate (header, -1, &end); - if (header == end) { - GST_ERROR_OBJECT (y4mdec, "Empty y4m header"); + if (!g_str_is_ascii (header)) { + GST_ERROR_OBJECT (y4mdec, "Invalid non-ASCII y4m header: %s", header); return FALSE; } - valid = g_strndup (header, end - header); - GST_INFO_OBJECT (y4mdec, "Found header: %s", valid); - params = g_strsplit (valid, " ", -1); - g_free (valid); + GST_INFO_OBJECT (y4mdec, "Found header: %s", header); + params = g_strsplit (header, " ", -1); len = g_strv_length (params); for (int i = 0; i < len; i++) { @@ -361,19 +412,22 @@ gst_y4m_dec_parse_header (GstY4mDec * y4mdec, char *header) const char *param_value = param + 1; switch (param_type) { case 'C': - if (!parse_colorspace (param_value, &format)) { + format = parse_colorspace (param_value); + if (format == GST_VIDEO_FORMAT_UNKNOWN) { GST_ERROR_OBJECT (y4mdec, "Failed to parse colorspace: %s", param); return FALSE; } + GST_INFO_OBJECT (y4mdec, "Parsed format as %s", + gst_video_format_to_string (format)); continue; case 'W': - if ((width = strtoul (param_value, NULL, 10)) == 0) { + if ((width = g_ascii_strtoull (param_value, NULL, 10)) == 0) { GST_ERROR_OBJECT (y4mdec, "Failed to parse width: %s", param); return FALSE; } continue; case 'H': - if ((height = strtoul (param_value, NULL, 10)) == 0) { + if ((height = g_ascii_strtoull (param_value, NULL, 10)) == 0) { GST_ERROR_OBJECT (y4mdec, "Failed to parse height: %s", param); return FALSE; } @@ -449,7 +503,6 @@ gst_y4m_dec_parse_header (GstY4mDec * y4mdec, char *header) y4mdec->info.offset[2] + y4mdec->info.stride[2] * height; break; default: - g_assert_not_reached (); break; }