ref: ebb5ffc1d462c70dfb2283a5c7afcb75288c7692
parent: deff7ddd27f1135d59b2d7ca524928654230174c
author: Elliott Karpilovsky <elliottk@google.com>
date: Thu Jan 28 06:22:58 EST 2021
Relax constraints on Y4M header parsing Previous parser assumed that the header would not exceed 80 characters. However, with latest FFMPEG changes, the header of Y4M files can exceed this limit. New parser can parse up to ~200 characters. Arbitrary parsing in future commit. BUG=aomedia:2876 Change-Id: I2ab8a7930cb5b76004e6731321d0ea20ddf333c1
--- a/test/y4m_test.cc
+++ b/test/y4m_test.cc
@@ -213,4 +213,30 @@
y4m_input_close(&y4m);
}
+// Testing that headers over 100 characters can be parsed.
+static const char kY4MLongHeader[] =
+ "YUV4MPEG2 W4 H4 F30:1 Ip A0:0 C420jpeg XYSCSS=420JPEG "
+ "XCOLORRANGE=LIMITED XSOME_UNKNOWN_METADATA XOTHER_UNKNOWN_METADATA\n"
+ "FRAME\n"
+ "012345678912345601230123";
+
+TEST(Y4MHeaderTest, LongHeader) {
+ libvpx_test::TempOutFile f;
+ fwrite(kY4MLongHeader, 1, sizeof(kY4MLongHeader), f.file());
+ fflush(f.file());
+ EXPECT_EQ(fseek(f.file(), 0, 0), 0);
+
+ y4m_input y4m;
+ EXPECT_EQ(y4m_input_open(&y4m, f.file(), /*skip_buffer=*/NULL,
+ /*num_skip=*/0, /*only_420=*/0),
+ 0);
+ EXPECT_EQ(y4m.pic_w, 4);
+ EXPECT_EQ(y4m.pic_h, 4);
+ EXPECT_EQ(y4m.fps_n, 30);
+ EXPECT_EQ(y4m.fps_d, 1);
+ EXPECT_EQ(y4m.interlace, 'p');
+ EXPECT_EQ(strcmp("420jpeg", y4m.chroma_type), 0);
+ y4m_input_close(&y4m);
+}
+
} // namespace
--- a/y4minput.c
+++ b/y4minput.c
@@ -52,15 +52,8 @@
}
static int y4m_parse_tags(y4m_input *_y4m, char *_tags) {
- int got_w;
- int got_h;
- int got_fps;
- int got_interlace;
- int got_par;
- int got_chroma;
char *p;
char *q;
- got_w = got_h = got_fps = got_interlace = got_par = got_chroma = 0;
for (p = _tags;; p = q) {
/*Skip any leading spaces.*/
while (*p == ' ') p++;
@@ -73,12 +66,10 @@
switch (p[0]) {
case 'W': {
if (sscanf(p + 1, "%d", &_y4m->pic_w) != 1) return -1;
- got_w = 1;
break;
}
case 'H': {
if (sscanf(p + 1, "%d", &_y4m->pic_h) != 1) return -1;
- got_h = 1;
break;
}
case 'F': {
@@ -85,12 +76,10 @@
if (sscanf(p + 1, "%d:%d", &_y4m->fps_n, &_y4m->fps_d) != 2) {
return -1;
}
- got_fps = 1;
break;
}
case 'I': {
_y4m->interlace = p[1];
- got_interlace = 1;
break;
}
case 'A': {
@@ -97,7 +86,6 @@
if (sscanf(p + 1, "%d:%d", &_y4m->par_n, &_y4m->par_d) != 2) {
return -1;
}
- got_par = 1;
break;
}
case 'C': {
@@ -104,21 +92,48 @@
if (q - p > 16) return -1;
memcpy(_y4m->chroma_type, p + 1, q - p - 1);
_y4m->chroma_type[q - p - 1] = '\0';
- got_chroma = 1;
break;
}
/*Ignore unknown tags.*/
}
}
- if (!got_w || !got_h || !got_fps) return -1;
- if (!got_interlace) _y4m->interlace = '?';
- if (!got_par) _y4m->par_n = _y4m->par_d = 0;
- /*Chroma-type is not specified in older files, e.g., those generated by
- mplayer.*/
- if (!got_chroma) strcpy(_y4m->chroma_type, "420");
return 0;
}
+/* Returns 1 if tags were parsed successfully, 0 otherwise. */
+static int parse_tags(y4m_input *y4m_ctx, char *buffer) {
+ /* Set Y4M tags to defaults, updating them as processing occurs. Mandatory
+ fields are marked with -1 and will be checked after the tags are parsed. */
+ int ret;
+ y4m_ctx->pic_w = -1;
+ y4m_ctx->pic_h = -1;
+ y4m_ctx->fps_n = -1; /* Also serves as marker for fps_d */
+ y4m_ctx->par_n = 0;
+ y4m_ctx->par_d = 0;
+ y4m_ctx->interlace = '?';
+ snprintf(y4m_ctx->chroma_type, sizeof(y4m_ctx->chroma_type), "420");
+
+ ret = y4m_parse_tags(y4m_ctx, buffer);
+ if (ret < 0) {
+ return 0;
+ }
+
+ /* Check the mandatory fields. */
+ if (y4m_ctx->pic_w == -1) {
+ fprintf(stderr, "Width field missing\n");
+ return 0;
+ }
+ if (y4m_ctx->pic_h == -1) {
+ fprintf(stderr, "Height field missing\n");
+ return 0;
+ }
+ if (y4m_ctx->fps_n == -1) {
+ fprintf(stderr, "FPS field missing\n");
+ return 0;
+ }
+ return 1;
+}
+
/*All anti-aliasing filters in the following conversion functions are based on
one of two window functions:
The 6-tap Lanczos window (for down-sampling and shifts):
@@ -786,13 +801,14 @@
}
static const char TAG[] = "YUV4MPEG2";
+/* Temporary until arbitrary header parsing submitted. */
+#define Y4M_HEADER_BUF_SIZE 200
int y4m_input_open(y4m_input *y4m_ctx, FILE *file, char *skip_buffer,
int num_skip, int only_420) {
// File must start with |TAG|.
- char tag_buffer[9]; // 9 == strlen(TAG)
- char buffer[80] = { 0 };
- int ret;
+ char tag_buffer[9]; // 9 == strlen(TAG)
+ char buffer[Y4M_HEADER_BUF_SIZE] = { 0 }; // Rest of header.
int i;
// Read as much as possible from |skip_buffer|, which were characters
// that were previously read from the file to do input-type detection.
@@ -813,21 +829,19 @@
fprintf(stderr, "Error parsing header: space must follow %s\n", TAG);
return -1;
}
- /*Read until newline, or 80 cols, whichever happens first.*/
- for (i = 0; i < 79; i++) {
+ /*Read until newline, or Y4M_HEADER_BUF_SIZE cols, whichever happens first.*/
+ for (i = 0; i < Y4M_HEADER_BUF_SIZE - 1; i++) {
if (!file_read(buffer + i, 1, file)) return -1;
if (buffer[i] == '\n') break;
}
- /*We skipped too much header data.*/
- if (i == 79) {
- fprintf(stderr, "Error parsing header; not a YUV4MPEG2 file?\n");
+ if (i == Y4M_HEADER_BUF_SIZE - 1) {
+ fprintf(stderr, "Error parsing header; not a %s file?\n", TAG);
return -1;
}
buffer[i] = '\0';
- ret = y4m_parse_tags(y4m_ctx, buffer);
- if (ret < 0) {
- fprintf(stderr, "Error parsing YUV4MPEG2 header.\n");
- return ret;
+ if (!parse_tags(y4m_ctx, buffer)) {
+ fprintf(stderr, "Error parsing %s header.\n", TAG);
+ return -1;
}
if (y4m_ctx->interlace == '?') {
fprintf(stderr,