mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 11:41:09 +00:00
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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5997>
This commit is contained in:
parent
968ebd26ab
commit
56b16e5232
2 changed files with 87 additions and 34 deletions
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
char *end;
|
||||
guint iformat = g_ascii_strtoull (param, &end, 10);
|
||||
|
||||
if (*end == '\0') {
|
||||
switch (iformat) {
|
||||
case 420:
|
||||
*format = GST_VIDEO_FORMAT_I420;
|
||||
break;
|
||||
return GST_VIDEO_FORMAT_I420;
|
||||
case 411:
|
||||
return GST_VIDEO_FORMAT_Y41B;
|
||||
case 422:
|
||||
*format = GST_VIDEO_FORMAT_Y42B;
|
||||
break;
|
||||
return GST_VIDEO_FORMAT_Y42B;
|
||||
case 444:
|
||||
*format = GST_VIDEO_FORMAT_Y444;
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue