summaryrefslogtreecommitdiffstats
path: root/third_party/aom/common
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/aom/common
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/aom/common')
-rw-r--r--third_party/aom/common/args.c248
-rw-r--r--third_party/aom/common/args.h40
-rw-r--r--third_party/aom/common/args_helper.c221
-rw-r--r--third_party/aom/common/args_helper.h79
-rw-r--r--third_party/aom/common/av1_config.c511
-rw-r--r--third_party/aom/common/av1_config.h86
-rw-r--r--third_party/aom/common/ivf_dec.cmake28
-rw-r--r--third_party/aom/common/ivfdec.c112
-rw-r--r--third_party/aom/common/ivfdec.h30
-rw-r--r--third_party/aom/common/ivfenc.c52
-rw-r--r--third_party/aom/common/ivfenc.h34
-rw-r--r--third_party/aom/common/md5_utils.c257
-rw-r--r--third_party/aom/common/md5_utils.h49
-rw-r--r--third_party/aom/common/obudec.c512
-rw-r--r--third_party/aom/common/obudec.h48
-rw-r--r--third_party/aom/common/rawenc.c99
-rw-r--r--third_party/aom/common/rawenc.h32
-rw-r--r--third_party/aom/common/tools_common.c636
-rw-r--r--third_party/aom/common/tools_common.h208
-rw-r--r--third_party/aom/common/video_common.h25
-rw-r--r--third_party/aom/common/video_reader.c135
-rw-r--r--third_party/aom/common/video_reader.h60
-rw-r--r--third_party/aom/common/video_writer.c83
-rw-r--r--third_party/aom/common/video_writer.h47
-rw-r--r--third_party/aom/common/warnings.c97
-rw-r--r--third_party/aom/common/warnings.h34
-rw-r--r--third_party/aom/common/webmdec.cc248
-rw-r--r--third_party/aom/common/webmdec.h71
-rw-r--r--third_party/aom/common/webmenc.cc242
-rw-r--r--third_party/aom/common/webmenc.h71
-rw-r--r--third_party/aom/common/y4menc.c108
-rw-r--r--third_party/aom/common/y4menc.h39
-rw-r--r--third_party/aom/common/y4minput.c1222
-rw-r--r--third_party/aom/common/y4minput.h82
34 files changed, 5846 insertions, 0 deletions
diff --git a/third_party/aom/common/args.c b/third_party/aom/common/args.c
new file mode 100644
index 0000000000..b5ede193b5
--- /dev/null
+++ b/third_party/aom/common/args.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include "common/args.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "aom/aom_integer.h"
+#include "aom_ports/msvc.h"
+#include "aom/aom_codec.h"
+#include "common/tools_common.h"
+
+static const char kSbSizeWarningString[] =
+ "super_block_size has to be 64 or 128.";
+static const char kMinpartWarningString[] =
+ "min_partition_size has to be smaller or equal to max_partition_size.";
+static const char kMaxpartWarningString[] =
+ "max_partition_size has to be smaller or equal to super_block_size.";
+
+static char *ignore_front_spaces(const char *str) {
+ while (str[0] == ' ' || str[0] == '\t') ++str;
+ return (char *)str;
+}
+
+static void ignore_end_spaces(char *str) {
+ char *end = str + strlen(str);
+ while (end > str && (end[0] == ' ' || end[0] == '\t' || end[0] == '\n' ||
+ end[0] == '\r' || end[0] == '\0'))
+ --end;
+ if (end >= str) end[1] = '\0';
+}
+
+int parse_cfg(const char *file, cfg_options_t *config) {
+ char line[1024 * 10];
+ FILE *f = fopen(file, "r");
+ if (!f) return 1;
+
+#define GET_PARAMS(field) \
+ if (strcmp(left, #field) == 0) { \
+ config->field = atoi(right); \
+ continue; \
+ }
+
+ while (fgets(line, sizeof(line) - 1, f)) {
+ char *actual_line = ignore_front_spaces(line);
+ char *left, *right, *comment;
+ size_t length = strlen(actual_line);
+
+ if (length == 0 || actual_line[0] == '#') continue;
+ right = strchr(actual_line, '=');
+ if (right == NULL) continue;
+ right[0] = '\0';
+
+ left = ignore_front_spaces(actual_line);
+ right = ignore_front_spaces(right + 1);
+
+ comment = strchr(right, '#');
+ if (comment != NULL) comment[0] = '\0';
+
+ ignore_end_spaces(left);
+ ignore_end_spaces(right);
+
+ GET_PARAMS(super_block_size)
+ GET_PARAMS(max_partition_size)
+ GET_PARAMS(min_partition_size)
+ GET_PARAMS(disable_ab_partition_type)
+ GET_PARAMS(disable_rect_partition_type)
+ GET_PARAMS(disable_1to4_partition_type)
+ GET_PARAMS(disable_flip_idtx)
+ GET_PARAMS(disable_cdef)
+ GET_PARAMS(disable_lr)
+ GET_PARAMS(disable_obmc)
+ GET_PARAMS(disable_warp_motion)
+ GET_PARAMS(disable_global_motion)
+ GET_PARAMS(disable_dist_wtd_comp)
+ GET_PARAMS(disable_diff_wtd_comp)
+ GET_PARAMS(disable_inter_intra_comp)
+ GET_PARAMS(disable_masked_comp)
+ GET_PARAMS(disable_one_sided_comp)
+ GET_PARAMS(disable_palette)
+ GET_PARAMS(disable_intrabc)
+ GET_PARAMS(disable_cfl)
+ GET_PARAMS(disable_smooth_intra)
+ GET_PARAMS(disable_filter_intra)
+ GET_PARAMS(disable_dual_filter)
+ GET_PARAMS(disable_intra_angle_delta)
+ GET_PARAMS(disable_intra_edge_filter)
+ GET_PARAMS(disable_tx_64x64)
+ GET_PARAMS(disable_smooth_inter_intra)
+ GET_PARAMS(disable_inter_inter_wedge)
+ GET_PARAMS(disable_inter_intra_wedge)
+ GET_PARAMS(disable_paeth_intra)
+ GET_PARAMS(disable_trellis_quant)
+ GET_PARAMS(disable_ref_frame_mv)
+ GET_PARAMS(reduced_reference_set)
+ GET_PARAMS(reduced_tx_type_set)
+
+ fprintf(stderr, "\nInvalid parameter: %s", left);
+ exit(-1);
+ }
+
+ if (config->super_block_size != 128 && config->super_block_size != 64) {
+ fprintf(stderr, "\n%s", kSbSizeWarningString);
+ exit(-1);
+ }
+ if (config->min_partition_size > config->max_partition_size) {
+ fprintf(stderr, "\n%s", kMinpartWarningString);
+ exit(-1);
+ }
+ if (config->max_partition_size > config->super_block_size) {
+ fprintf(stderr, "\n%s", kMaxpartWarningString);
+ exit(-1);
+ }
+
+ fclose(f);
+ config->init_by_cfg_file = 1;
+
+ return 0;
+}
+
+int arg_match(struct arg *arg_, const struct arg_def *def, char **argv) {
+ char err_msg[ARG_ERR_MSG_MAX_LEN];
+ int ret = arg_match_helper(arg_, def, argv, err_msg);
+ if (err_msg[0] != '\0') {
+ die("%s", err_msg);
+ }
+ return ret;
+}
+
+const char *arg_next(struct arg *arg) {
+ if (arg->argv[0]) arg->argv += arg->argv_step;
+
+ return *arg->argv;
+}
+
+char **argv_dup(int argc, const char **argv) {
+ char **new_argv = malloc((argc + 1) * sizeof(*argv));
+ if (!new_argv) return NULL;
+
+ memcpy(new_argv, argv, argc * sizeof(*argv));
+ new_argv[argc] = NULL;
+ return new_argv;
+}
+
+void arg_show_usage(FILE *fp, const struct arg_def *const *defs) {
+ for (; *defs; defs++) {
+ const struct arg_def *def = *defs;
+ char *short_val = def->has_val ? " <arg>" : "";
+ char *long_val = def->has_val ? "=<arg>" : "";
+ int n = 0;
+
+ // Short options are indented with two spaces. Long options are indented
+ // with 12 spaces.
+ if (def->short_name && def->long_name) {
+ char *comma = def->has_val ? "," : ", ";
+
+ n = fprintf(fp, " -%s%s%s --%s%s", def->short_name, short_val, comma,
+ def->long_name, long_val);
+ } else if (def->short_name)
+ n = fprintf(fp, " -%s%s", def->short_name, short_val);
+ else if (def->long_name)
+ n = fprintf(fp, " --%s%s", def->long_name, long_val);
+
+ // Descriptions are indented with 40 spaces. If an option is 40 characters
+ // or longer, its description starts on the next line.
+ if (n < 40)
+ for (int i = 0; i < 40 - n; i++) fputc(' ', fp);
+ else
+ fputs("\n ", fp);
+ fprintf(fp, "%s\n", def->desc);
+
+ if (def->enums) {
+ const struct arg_enum_list *listptr;
+
+ fprintf(fp, " %-37s\t ", "");
+
+ for (listptr = def->enums; listptr->name; listptr++)
+ fprintf(fp, "%s%s", listptr->name, listptr[1].name ? ", " : "\n");
+ }
+ }
+}
+
+unsigned int arg_parse_uint(const struct arg *arg) {
+ char err_msg[ARG_ERR_MSG_MAX_LEN];
+ unsigned int ret = arg_parse_uint_helper(arg, err_msg);
+ if (err_msg[0] != '\0') {
+ die("%s", err_msg);
+ }
+ return ret;
+}
+
+int arg_parse_int(const struct arg *arg) {
+ char err_msg[ARG_ERR_MSG_MAX_LEN];
+ int ret = arg_parse_int_helper(arg, err_msg);
+ if (err_msg[0] != '\0') {
+ die("%s", err_msg);
+ }
+ return ret;
+}
+
+struct aom_rational arg_parse_rational(const struct arg *arg) {
+ char err_msg[ARG_ERR_MSG_MAX_LEN];
+ struct aom_rational ret = arg_parse_rational_helper(arg, err_msg);
+ if (err_msg[0] != '\0') {
+ die("%s", err_msg);
+ }
+ return ret;
+}
+
+int arg_parse_enum(const struct arg *arg) {
+ char err_msg[ARG_ERR_MSG_MAX_LEN];
+ int ret = arg_parse_enum_helper(arg, err_msg);
+ if (err_msg[0] != '\0') {
+ die("%s", err_msg);
+ }
+ return ret;
+}
+
+int arg_parse_enum_or_int(const struct arg *arg) {
+ char err_msg[ARG_ERR_MSG_MAX_LEN];
+ int ret = arg_parse_enum_or_int_helper(arg, err_msg);
+ if (err_msg[0] != '\0') {
+ die("%s", err_msg);
+ }
+ return ret;
+}
+
+// parse a comma separated list of at most n integers
+// return the number of elements in the list
+int arg_parse_list(const struct arg *arg, int *list, int n) {
+ char err_msg[ARG_ERR_MSG_MAX_LEN];
+ int ret = arg_parse_list_helper(arg, list, n, err_msg);
+ if (err_msg[0] != '\0') {
+ die("%s", err_msg);
+ }
+ return ret;
+}
diff --git a/third_party/aom/common/args.h b/third_party/aom/common/args.h
new file mode 100644
index 0000000000..1c5c437632
--- /dev/null
+++ b/third_party/aom/common/args.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AOM_COMMON_ARGS_H_
+#define AOM_COMMON_ARGS_H_
+#include <stdio.h>
+
+#include "aom/aom_codec.h"
+#include "aom/aom_encoder.h"
+#include "common/args_helper.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int arg_match(struct arg *arg_, const struct arg_def *def, char **argv);
+int parse_cfg(const char *file, cfg_options_t *config);
+const char *arg_next(struct arg *arg);
+void arg_show_usage(FILE *fp, const struct arg_def *const *defs);
+char **argv_dup(int argc, const char **argv);
+
+unsigned int arg_parse_uint(const struct arg *arg);
+int arg_parse_int(const struct arg *arg);
+struct aom_rational arg_parse_rational(const struct arg *arg);
+int arg_parse_enum(const struct arg *arg);
+int arg_parse_enum_or_int(const struct arg *arg);
+int arg_parse_list(const struct arg *arg, int *list, int n);
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // AOM_COMMON_ARGS_H_
diff --git a/third_party/aom/common/args_helper.c b/third_party/aom/common/args_helper.c
new file mode 100644
index 0000000000..2201868335
--- /dev/null
+++ b/third_party/aom/common/args_helper.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#include "common/args_helper.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#define SET_ERR_STRING(...) \
+ if (err_msg) snprintf(err_msg, ARG_ERR_MSG_MAX_LEN, __VA_ARGS__)
+
+struct arg arg_init(char **argv) {
+ struct arg a;
+
+ a.argv = argv;
+ a.argv_step = 1;
+ a.name = NULL;
+ a.val = NULL;
+ a.def = NULL;
+ return a;
+}
+
+int arg_match_helper(struct arg *arg_, const struct arg_def *def, char **argv,
+ char *err_msg) {
+ struct arg arg;
+
+ if (err_msg) err_msg[0] = '\0';
+
+ assert(def->has_val == 0 || def->has_val == 1 || def->has_val == -1);
+
+ if (!argv[0] || argv[0][0] != '-') return 0;
+
+ arg = arg_init(argv);
+
+ if (def->short_name && !strcmp(arg.argv[0] + 1, def->short_name)) {
+ arg.name = arg.argv[0] + 1;
+ arg.val = def->has_val ? arg.argv[1] : NULL;
+ arg.argv_step = def->has_val ? 2 : 1;
+ } else if (def->long_name) {
+ const size_t name_len = strlen(def->long_name);
+
+ if (arg.argv[0][1] == '-' &&
+ !strncmp(arg.argv[0] + 2, def->long_name, name_len) &&
+ (arg.argv[0][name_len + 2] == '=' ||
+ arg.argv[0][name_len + 2] == '\0')) {
+ arg.name = arg.argv[0] + 2;
+ arg.val = arg.name[name_len] == '=' ? arg.name + name_len + 1 : NULL;
+ arg.argv_step = 1;
+ }
+ }
+
+ if (arg.name) {
+ if (def->has_val == -1) {
+ arg.def = def;
+ *arg_ = arg;
+ return 1;
+ }
+
+ if (!arg.val && def->has_val) {
+ SET_ERR_STRING("Error: option %s requires argument.\n", arg.name);
+ return 0;
+ }
+
+ if (arg.val && !def->has_val) {
+ SET_ERR_STRING("Error: option %s requires no argument.\n", arg.name);
+ return 0;
+ }
+
+ arg.def = def;
+ *arg_ = arg;
+ return 1;
+ }
+
+ return 0;
+}
+
+unsigned int arg_parse_uint_helper(const struct arg *arg, char *err_msg) {
+ char *endptr;
+ const unsigned long rawval = strtoul(arg->val, &endptr, 10); // NOLINT
+
+ if (err_msg) err_msg[0] = '\0';
+
+ if (arg->val[0] != '\0' && endptr[0] == '\0') {
+ if (rawval <= UINT_MAX) return (unsigned int)rawval;
+ SET_ERR_STRING("Option %s: Value %lu out of range for unsigned int\n",
+ arg->name, rawval);
+ return 0;
+ }
+ SET_ERR_STRING("Option %s: Invalid character '%c'\n", arg->name, *endptr);
+ return 0;
+}
+
+int arg_parse_int_helper(const struct arg *arg, char *err_msg) {
+ char *endptr;
+ const long rawval = strtol(arg->val, &endptr, 10); // NOLINT
+
+ if (err_msg) err_msg[0] = '\0';
+
+ if (arg->val[0] != '\0' && endptr[0] == '\0') {
+ if (rawval >= INT_MIN && rawval <= INT_MAX) return (int)rawval;
+ SET_ERR_STRING("Option %s: Value %ld out of range for signed int\n",
+ arg->name, rawval);
+ return 0;
+ }
+ SET_ERR_STRING("Option %s: Invalid character '%c'\n", arg->name, *endptr);
+ return 0;
+}
+
+struct aom_rational arg_parse_rational_helper(const struct arg *arg,
+ char *err_msg) {
+ long rawval; // NOLINT
+ char *endptr;
+ struct aom_rational rat = { 0, 1 };
+
+ if (err_msg) err_msg[0] = '\0';
+
+ /* parse numerator */
+ rawval = strtol(arg->val, &endptr, 10);
+
+ if (arg->val[0] != '\0' && endptr[0] == '/') {
+ if (rawval >= INT_MIN && rawval <= INT_MAX) {
+ rat.num = (int)rawval;
+ } else {
+ SET_ERR_STRING("Option %s: Value %ld out of range for signed int\n",
+ arg->name, rawval);
+ return rat;
+ }
+ } else {
+ SET_ERR_STRING("Option %s: Expected / at '%c'\n", arg->name, *endptr);
+ return rat;
+ }
+
+ /* parse denominator */
+ rawval = strtol(endptr + 1, &endptr, 10);
+
+ if (arg->val[0] != '\0' && endptr[0] == '\0') {
+ if (rawval >= INT_MIN && rawval <= INT_MAX) {
+ rat.den = (int)rawval;
+ } else {
+ SET_ERR_STRING("Option %s: Value %ld out of range for signed int\n",
+ arg->name, rawval);
+ return rat;
+ }
+ } else {
+ SET_ERR_STRING("Option %s: Invalid character '%c'\n", arg->name, *endptr);
+ return rat;
+ }
+
+ return rat;
+}
+
+int arg_parse_enum_helper(const struct arg *arg, char *err_msg) {
+ const struct arg_enum_list *listptr;
+ long rawval; // NOLINT
+ char *endptr;
+
+ if (err_msg) err_msg[0] = '\0';
+
+ /* First see if the value can be parsed as a raw value */
+ rawval = strtol(arg->val, &endptr, 10);
+ if (arg->val[0] != '\0' && endptr[0] == '\0') {
+ /* Got a raw value, make sure it's valid */
+ for (listptr = arg->def->enums; listptr->name; listptr++)
+ if (listptr->val == rawval) return (int)rawval;
+ }
+
+ /* Next see if it can be parsed as a string */
+ for (listptr = arg->def->enums; listptr->name; listptr++)
+ if (!strcmp(arg->val, listptr->name)) return listptr->val;
+
+ SET_ERR_STRING("Option %s: Invalid value '%s'\n", arg->name, arg->val);
+ return 0;
+}
+
+int arg_parse_enum_or_int_helper(const struct arg *arg, char *err_msg) {
+ if (arg->def->enums) return arg_parse_enum_helper(arg, err_msg);
+ return arg_parse_int_helper(arg, err_msg);
+}
+
+// parse a comma separated list of at most n integers
+// return the number of elements in the list
+int arg_parse_list_helper(const struct arg *arg, int *list, int n,
+ char *err_msg) {
+ const char *ptr = arg->val;
+ char *endptr;
+ int i = 0;
+
+ if (err_msg) err_msg[0] = '\0';
+
+ while (ptr[0] != '\0') {
+ long rawval = strtol(ptr, &endptr, 10); // NOLINT
+ if (rawval < INT_MIN || rawval > INT_MAX) {
+ SET_ERR_STRING("Option %s: Value %ld out of range for signed int\n",
+ arg->name, rawval);
+ return 0;
+ } else if (i >= n) {
+ SET_ERR_STRING("Option %s: List has more than %d entries\n", arg->name,
+ n);
+ return 0;
+ } else if (*endptr == ',') {
+ endptr++;
+ } else if (*endptr != '\0') {
+ SET_ERR_STRING("Option %s: Bad list separator '%c'\n", arg->name,
+ *endptr);
+ return 0;
+ }
+ list[i++] = (int)rawval;
+ ptr = endptr;
+ }
+ return i;
+}
diff --git a/third_party/aom/common/args_helper.h b/third_party/aom/common/args_helper.h
new file mode 100644
index 0000000000..c86a6128d3
--- /dev/null
+++ b/third_party/aom/common/args_helper.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AOM_COMMON_ARGS_HELPER_H_
+#define AOM_COMMON_ARGS_HELPER_H_
+
+#include "aom/aom_encoder.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Maximum length of the error messages for the helper functions.
+#define ARG_ERR_MSG_MAX_LEN 200
+
+struct arg {
+ char **argv;
+ const char *name;
+ const char *val;
+ unsigned int argv_step;
+ const struct arg_def *def;
+};
+
+struct arg_enum_list {
+ const char *name;
+ int val;
+};
+#define ARG_ENUM_LIST_END \
+ { 0 }
+
+typedef struct arg_def {
+ const char *short_name;
+ const char *long_name;
+ int has_val; // 0: The argument must not have a value.
+ // 1: The argument must have a value.
+ // -1: The argument may or may not have a value.
+ const char *desc;
+ const struct arg_enum_list *enums;
+} arg_def_t;
+#define ARG_DEF(s, l, v, d) \
+ { s, l, v, d, NULL }
+#define ARG_DEF_ENUM(s, l, v, d, e) \
+ { s, l, v, d, e }
+#define ARG_DEF_LIST_END \
+ { 0 }
+
+struct arg arg_init(char **argv);
+
+/*
+ * The helper functions below all take an optional parameter err_msg for
+ * error reporting. When err_msg is not NULL (must point to a buffer
+ * which is at least ARG_ERR_MSG_MAX_LEN bytes long), a related error message is
+ * stored in it if an error occurs. It will be set to an empty string if no
+ * error occurs.
+ */
+int arg_match_helper(struct arg *arg_, const struct arg_def *def, char **argv,
+ char *err_msg);
+unsigned int arg_parse_uint_helper(const struct arg *arg, char *err_msg);
+int arg_parse_int_helper(const struct arg *arg, char *err_msg);
+struct aom_rational arg_parse_rational_helper(const struct arg *arg,
+ char *err_msg);
+int arg_parse_enum_helper(const struct arg *arg, char *err_msg);
+int arg_parse_enum_or_int_helper(const struct arg *arg, char *err_msg);
+int arg_parse_list_helper(const struct arg *arg, int *list, int n,
+ char *err_msg);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // AOM_COMMON_ARGS_HELPER_H_
diff --git a/third_party/aom/common/av1_config.c b/third_party/aom/common/av1_config.c
new file mode 100644
index 0000000000..9f5b02015b
--- /dev/null
+++ b/third_party/aom/common/av1_config.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright (c) 2018, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include "aom/aom_image.h"
+#include "aom/aom_integer.h"
+#include "aom_dsp/bitreader_buffer.h"
+#include "aom_dsp/bitwriter_buffer.h"
+#include "av1/common/obu_util.h"
+#include "common/av1_config.h"
+#include "config/aom_config.h"
+
+// Helper macros to reduce verbosity required to check for read errors.
+//
+// Note that when using these macros, even single line if statements should use
+// curly braces to avoid unexpected behavior because all but the
+// AV1C_POP_ERROR_HANDLER_DATA() macro consist of multiple statements.
+#define AV1C_READ_BIT_OR_RETURN_ERROR(field) \
+ int field = 0; \
+ do { \
+ field = aom_rb_read_bit(reader); \
+ if (result == -1) { \
+ fprintf(stderr, \
+ "av1c: Error reading bit for " #field ", value=%d result=%d.\n", \
+ field, result); \
+ return -1; \
+ } \
+ } while (0)
+
+#define AV1C_READ_BITS_OR_RETURN_ERROR(field, length) \
+ int field = 0; \
+ do { \
+ field = aom_rb_read_literal(reader, (length)); \
+ if (result == -1) { \
+ fprintf(stderr, \
+ "av1c: Could not read bits for " #field \
+ ", value=%d result=%d.\n", \
+ field, result); \
+ return -1; \
+ } \
+ } while (0)
+
+// Helper macros for setting/restoring the error handler data in
+// aom_read_bit_buffer.
+#define AV1C_PUSH_ERROR_HANDLER_DATA(new_data) \
+ void *original_error_handler_data = NULL; \
+ do { \
+ original_error_handler_data = reader->error_handler_data; \
+ reader->error_handler_data = &new_data; \
+ } while (0)
+
+#define AV1C_POP_ERROR_HANDLER_DATA() \
+ do { \
+ reader->error_handler_data = original_error_handler_data; \
+ } while (0)
+
+static const size_t kAv1cSize = 4;
+
+static void bitreader_error_handler(void *data) {
+ int *error_val = (int *)data;
+ *error_val = -1;
+}
+
+// Parse the AV1 timing_info() structure:
+// timing_info( ) {
+// num_units_in_display_tick f(32)
+// time_scale f(32)
+// equal_picture_interval f(1)
+// if (equal_picture_interval)
+// num_ticks_per_picture_minus_1 uvlc()
+// }
+static int parse_timing_info(struct aom_read_bit_buffer *reader) {
+ int result = 0;
+ AV1C_PUSH_ERROR_HANDLER_DATA(result);
+
+ AV1C_READ_BITS_OR_RETURN_ERROR(num_units_in_display_tick, 32);
+ AV1C_READ_BITS_OR_RETURN_ERROR(time_scale, 32);
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(equal_picture_interval);
+ if (equal_picture_interval) {
+ uint32_t num_ticks_per_picture_minus_1 = aom_rb_read_uvlc(reader);
+ if (result == -1) {
+ fprintf(stderr,
+ "av1c: Could not read bits for "
+ "num_ticks_per_picture_minus_1, value=%u.\n",
+ num_ticks_per_picture_minus_1);
+ return result;
+ }
+ }
+
+ AV1C_POP_ERROR_HANDLER_DATA();
+ return result;
+}
+
+// Parse the AV1 decoder_model_info() structure:
+// decoder_model_info( ) {
+// buffer_delay_length_minus_1 f(5)
+// num_units_in_decoding_tick f(32)
+// buffer_removal_time_length_minus_1 f(5)
+// frame_presentation_time_length_minus_1 f(5)
+// }
+//
+// Returns -1 upon failure, or the value of buffer_delay_length_minus_1 + 1.
+static int parse_decoder_model_info(struct aom_read_bit_buffer *reader) {
+ int result = 0;
+ AV1C_PUSH_ERROR_HANDLER_DATA(result);
+
+ AV1C_READ_BITS_OR_RETURN_ERROR(buffer_delay_length_minus_1, 5);
+ AV1C_READ_BITS_OR_RETURN_ERROR(num_units_in_decoding_tick, 32);
+ AV1C_READ_BITS_OR_RETURN_ERROR(buffer_removal_time_length_minus_1, 5);
+ AV1C_READ_BITS_OR_RETURN_ERROR(frame_presentation_time_length_minus_1, 5);
+
+ AV1C_POP_ERROR_HANDLER_DATA();
+ return buffer_delay_length_minus_1 + 1;
+}
+
+// Parse the AV1 operating_parameters_info() structure:
+// operating_parameters_info( op ) {
+// n = buffer_delay_length_minus_1 + 1
+// decoder_buffer_delay[ op ] f(n)
+// encoder_buffer_delay[ op ] f(n)
+// low_delay_mode_flag[ op ] f(1)
+// }
+static int parse_operating_parameters_info(struct aom_read_bit_buffer *reader,
+ int buffer_delay_length_minus_1) {
+ int result = 0;
+ AV1C_PUSH_ERROR_HANDLER_DATA(result);
+
+ const int buffer_delay_length = buffer_delay_length_minus_1 + 1;
+ AV1C_READ_BITS_OR_RETURN_ERROR(decoder_buffer_delay, buffer_delay_length);
+ AV1C_READ_BITS_OR_RETURN_ERROR(encoder_buffer_delay, buffer_delay_length);
+ AV1C_READ_BIT_OR_RETURN_ERROR(low_delay_mode_flag);
+
+ AV1C_POP_ERROR_HANDLER_DATA();
+ return result;
+}
+
+// Parse the AV1 color_config() structure..See:
+// https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=44
+static int parse_color_config(struct aom_read_bit_buffer *reader,
+ Av1Config *config) {
+ int result = 0;
+ AV1C_PUSH_ERROR_HANDLER_DATA(result);
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(high_bitdepth);
+ config->high_bitdepth = high_bitdepth;
+
+ int bit_depth = 0;
+ if (config->seq_profile == 2 && config->high_bitdepth) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(twelve_bit);
+ config->twelve_bit = twelve_bit;
+ bit_depth = config->twelve_bit ? 12 : 10;
+ } else {
+ bit_depth = config->high_bitdepth ? 10 : 8;
+ }
+
+ if (config->seq_profile != 1) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(mono_chrome);
+ config->monochrome = mono_chrome;
+ }
+
+ int color_primaries = AOM_CICP_CP_UNSPECIFIED;
+ int transfer_characteristics = AOM_CICP_TC_UNSPECIFIED;
+ int matrix_coefficients = AOM_CICP_MC_UNSPECIFIED;
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(color_description_present_flag);
+ if (color_description_present_flag) {
+ AV1C_READ_BITS_OR_RETURN_ERROR(color_primaries_val, 8);
+ color_primaries = color_primaries_val;
+ AV1C_READ_BITS_OR_RETURN_ERROR(transfer_characteristics_val, 8);
+ transfer_characteristics = transfer_characteristics_val;
+ AV1C_READ_BITS_OR_RETURN_ERROR(matrix_coefficients_val, 8);
+ matrix_coefficients = matrix_coefficients_val;
+ }
+
+ if (config->monochrome) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(color_range);
+ config->chroma_subsampling_x = 1;
+ config->chroma_subsampling_y = 1;
+ } else if (color_primaries == AOM_CICP_CP_BT_709 &&
+ transfer_characteristics == AOM_CICP_TC_SRGB &&
+ matrix_coefficients == AOM_CICP_MC_IDENTITY) {
+ config->chroma_subsampling_x = 0;
+ config->chroma_subsampling_y = 0;
+ } else {
+ AV1C_READ_BIT_OR_RETURN_ERROR(color_range);
+ if (config->seq_profile == 0) {
+ config->chroma_subsampling_x = 1;
+ config->chroma_subsampling_y = 1;
+ } else if (config->seq_profile == 1) {
+ config->chroma_subsampling_x = 0;
+ config->chroma_subsampling_y = 0;
+ } else {
+ if (bit_depth == 12) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(subsampling_x);
+ config->chroma_subsampling_x = subsampling_x;
+ if (subsampling_x) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(subsampling_y);
+ config->chroma_subsampling_y = subsampling_y;
+ } else {
+ config->chroma_subsampling_y = 0;
+ }
+ } else {
+ config->chroma_subsampling_x = 1;
+ config->chroma_subsampling_y = 0;
+ }
+ }
+
+ if (config->chroma_subsampling_x && config->chroma_subsampling_y) {
+ AV1C_READ_BITS_OR_RETURN_ERROR(chroma_sample_position, 2);
+ config->chroma_sample_position = chroma_sample_position;
+ }
+ }
+
+ if (!config->monochrome) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(separate_uv_delta_q);
+ }
+
+ AV1C_POP_ERROR_HANDLER_DATA();
+ return result;
+}
+
+// Parse AV1 Sequence Header OBU. See:
+// https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=41
+static int parse_sequence_header(const uint8_t *const buffer, size_t length,
+ Av1Config *config) {
+ int result = 0;
+ // The reader instance is local to this function, but a pointer to the
+ // reader instance is used within this function and throughout this file to
+ // allow use of the helper macros that reduce parse error checking verbosity.
+ struct aom_read_bit_buffer reader_instance = { buffer, buffer + length, 0,
+ &result,
+ bitreader_error_handler };
+ struct aom_read_bit_buffer *reader = &reader_instance;
+
+ AV1C_READ_BITS_OR_RETURN_ERROR(seq_profile, 3);
+ config->seq_profile = seq_profile;
+ AV1C_READ_BIT_OR_RETURN_ERROR(still_picture);
+ AV1C_READ_BIT_OR_RETURN_ERROR(reduced_still_picture_header);
+ if (reduced_still_picture_header) {
+ config->initial_presentation_delay_present = 0;
+ AV1C_READ_BITS_OR_RETURN_ERROR(seq_level_idx_0, 5);
+ config->seq_level_idx_0 = seq_level_idx_0;
+ config->seq_tier_0 = 0;
+ } else {
+ int has_decoder_model = 0;
+ int buffer_delay_length = 0;
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(timing_info_present_flag);
+ if (timing_info_present_flag) {
+ if (parse_timing_info(reader) != 0) return -1;
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(decoder_model_info_present_flag);
+ if (decoder_model_info_present_flag &&
+ (buffer_delay_length = parse_decoder_model_info(reader)) == -1) {
+ return -1;
+ }
+ has_decoder_model = 1;
+ }
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(initial_presentation_delay_present);
+ config->initial_presentation_delay_present =
+ initial_presentation_delay_present;
+
+ AV1C_READ_BITS_OR_RETURN_ERROR(operating_points_cnt_minus_1, 5);
+ const int num_operating_points = operating_points_cnt_minus_1 + 1;
+
+ for (int op_index = 0; op_index < num_operating_points; ++op_index) {
+ AV1C_READ_BITS_OR_RETURN_ERROR(operating_point_idc, 12);
+ AV1C_READ_BITS_OR_RETURN_ERROR(seq_level_idx, 5);
+
+ int seq_tier = 0;
+ if (seq_level_idx > 7) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(seq_tier_this_op);
+ seq_tier = seq_tier_this_op;
+ }
+
+ if (has_decoder_model) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(decoder_model_present_for_op);
+ if (decoder_model_present_for_op) {
+ if (parse_operating_parameters_info(reader, buffer_delay_length) ==
+ -1) {
+ return -1;
+ }
+ }
+ }
+
+ if (config->initial_presentation_delay_present) {
+ // Skip the initial presentation delay bits if present since this
+ // function has no access to the data required to properly set the
+ // field.
+ AV1C_READ_BIT_OR_RETURN_ERROR(
+ initial_presentation_delay_present_for_this_op);
+ if (initial_presentation_delay_present_for_this_op) {
+ AV1C_READ_BITS_OR_RETURN_ERROR(initial_presentation_delay_minus_1, 4);
+ }
+ }
+
+ if (op_index == 0) {
+ // Av1Config needs only the values from the first operating point.
+ config->seq_level_idx_0 = seq_level_idx;
+ config->seq_tier_0 = seq_tier;
+ config->initial_presentation_delay_present = 0;
+ config->initial_presentation_delay_minus_one = 0;
+ }
+ }
+ }
+
+ AV1C_READ_BITS_OR_RETURN_ERROR(frame_width_bits_minus_1, 4);
+ AV1C_READ_BITS_OR_RETURN_ERROR(frame_height_bits_minus_1, 4);
+ AV1C_READ_BITS_OR_RETURN_ERROR(max_frame_width_minus_1,
+ frame_width_bits_minus_1 + 1);
+ AV1C_READ_BITS_OR_RETURN_ERROR(max_frame_height_minus_1,
+ frame_height_bits_minus_1 + 1);
+
+ uint8_t frame_id_numbers_present = 0;
+ if (!reduced_still_picture_header) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(frame_id_numbers_present_flag);
+ frame_id_numbers_present = frame_id_numbers_present_flag;
+ }
+
+ if (frame_id_numbers_present) {
+ AV1C_READ_BITS_OR_RETURN_ERROR(delta_frame_id_length_minus_2, 4);
+ AV1C_READ_BITS_OR_RETURN_ERROR(additional_frame_id_length_minus_1, 3);
+ }
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(use_128x128_superblock);
+ AV1C_READ_BIT_OR_RETURN_ERROR(enable_filter_intra);
+ AV1C_READ_BIT_OR_RETURN_ERROR(enable_intra_edge_filter);
+
+ if (!reduced_still_picture_header) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(enable_interintra_compound);
+ AV1C_READ_BIT_OR_RETURN_ERROR(enable_masked_compound);
+ AV1C_READ_BIT_OR_RETURN_ERROR(enable_warped_motion);
+ AV1C_READ_BIT_OR_RETURN_ERROR(enable_dual_filter);
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(enable_order_hint);
+ if (enable_order_hint) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(enable_dist_wtd_comp);
+ AV1C_READ_BIT_OR_RETURN_ERROR(enable_ref_frame_mvs);
+ }
+
+ const int SELECT_SCREEN_CONTENT_TOOLS = 2;
+ int seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS;
+ AV1C_READ_BIT_OR_RETURN_ERROR(seq_choose_screen_content_tools);
+ if (!seq_choose_screen_content_tools) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(seq_force_screen_content_tools_val);
+ seq_force_screen_content_tools = seq_force_screen_content_tools_val;
+ }
+
+ if (seq_force_screen_content_tools > 0) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(seq_choose_integer_mv);
+
+ if (!seq_choose_integer_mv) {
+ AV1C_READ_BIT_OR_RETURN_ERROR(seq_force_integer_mv);
+ }
+ }
+
+ if (enable_order_hint) {
+ AV1C_READ_BITS_OR_RETURN_ERROR(order_hint_bits_minus_1, 3);
+ }
+ }
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(enable_superres);
+ AV1C_READ_BIT_OR_RETURN_ERROR(enable_cdef);
+ AV1C_READ_BIT_OR_RETURN_ERROR(enable_restoration);
+
+ if (parse_color_config(reader, config) != 0) {
+ fprintf(stderr, "av1c: color_config() parse failed.\n");
+ return -1;
+ }
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(film_grain_params_present);
+ return 0;
+}
+
+int get_av1config_from_obu(const uint8_t *buffer, size_t length, int is_annexb,
+ Av1Config *config) {
+ if (!buffer || length == 0 || !config) {
+ return -1;
+ }
+
+ ObuHeader obu_header;
+ memset(&obu_header, 0, sizeof(obu_header));
+
+ size_t sequence_header_length = 0;
+ size_t obu_header_length = 0;
+ if (aom_read_obu_header_and_size(buffer, length, is_annexb, &obu_header,
+ &sequence_header_length,
+ &obu_header_length) != AOM_CODEC_OK ||
+ obu_header.type != OBU_SEQUENCE_HEADER ||
+ sequence_header_length + obu_header_length > length) {
+ return -1;
+ }
+
+ memset(config, 0, sizeof(*config));
+ config->marker = 1;
+ config->version = 1;
+ return parse_sequence_header(buffer + obu_header_length,
+ sequence_header_length, config);
+}
+
+int read_av1config(const uint8_t *buffer, size_t buffer_length,
+ size_t *bytes_read, Av1Config *config) {
+ if (!buffer || buffer_length < kAv1cSize || !bytes_read || !config) return -1;
+
+ *bytes_read = 0;
+
+ int result = 0;
+ struct aom_read_bit_buffer reader_instance = { buffer, buffer + buffer_length,
+ 0, &result,
+ bitreader_error_handler };
+ struct aom_read_bit_buffer *reader = &reader_instance;
+
+ memset(config, 0, sizeof(*config));
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(marker);
+ config->marker = marker;
+
+ AV1C_READ_BITS_OR_RETURN_ERROR(version, 7);
+ config->version = version;
+
+ AV1C_READ_BITS_OR_RETURN_ERROR(seq_profile, 3);
+ config->seq_profile = seq_profile;
+
+ AV1C_READ_BITS_OR_RETURN_ERROR(seq_level_idx_0, 5);
+ config->seq_level_idx_0 = seq_level_idx_0;
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(seq_tier_0);
+ config->seq_tier_0 = seq_tier_0;
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(high_bitdepth);
+ config->high_bitdepth = high_bitdepth;
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(twelve_bit);
+ config->twelve_bit = twelve_bit;
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(monochrome);
+ config->monochrome = monochrome;
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(chroma_subsampling_x);
+ config->chroma_subsampling_x = chroma_subsampling_x;
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(chroma_subsampling_y);
+ config->chroma_subsampling_y = chroma_subsampling_y;
+
+ AV1C_READ_BITS_OR_RETURN_ERROR(chroma_sample_position, 2);
+ config->chroma_sample_position = chroma_sample_position;
+
+ AV1C_READ_BITS_OR_RETURN_ERROR(reserved, 3);
+
+ AV1C_READ_BIT_OR_RETURN_ERROR(initial_presentation_delay_present);
+ config->initial_presentation_delay_present =
+ initial_presentation_delay_present;
+
+ AV1C_READ_BITS_OR_RETURN_ERROR(initial_presentation_delay_minus_one, 4);
+ config->initial_presentation_delay_minus_one =
+ initial_presentation_delay_minus_one;
+
+ *bytes_read = aom_rb_bytes_read(reader);
+
+ return 0;
+}
+
+int write_av1config(const Av1Config *config, size_t capacity,
+ size_t *bytes_written, uint8_t *buffer) {
+ if (!config || !buffer || capacity < kAv1cSize || !bytes_written) return -1;
+
+ *bytes_written = 0;
+ memset(buffer, 0, kAv1cSize);
+
+ struct aom_write_bit_buffer writer = { buffer, 0 };
+
+ aom_wb_write_bit(&writer, config->marker);
+ aom_wb_write_literal(&writer, config->version, 7);
+ aom_wb_write_literal(&writer, config->seq_profile, 3);
+ aom_wb_write_literal(&writer, config->seq_level_idx_0, 5);
+ aom_wb_write_bit(&writer, config->seq_tier_0);
+ aom_wb_write_bit(&writer, config->high_bitdepth);
+ aom_wb_write_bit(&writer, config->twelve_bit);
+ aom_wb_write_bit(&writer, config->monochrome);
+ aom_wb_write_bit(&writer, config->chroma_subsampling_x);
+ aom_wb_write_bit(&writer, config->chroma_subsampling_y);
+ aom_wb_write_literal(&writer, config->chroma_sample_position, 2);
+ aom_wb_write_literal(&writer, 0, 3); // reserved
+ aom_wb_write_bit(&writer, config->initial_presentation_delay_present);
+
+ if (config->initial_presentation_delay_present) {
+ aom_wb_write_literal(&writer, config->initial_presentation_delay_minus_one,
+ 4);
+ } else {
+ aom_wb_write_literal(&writer, 0, 4); // reserved
+ }
+
+ *bytes_written = aom_wb_bytes_written(&writer);
+ return 0;
+}
+
+#undef AV1C_READ_BIT_OR_RETURN_ERROR
+#undef AV1C_READ_BITS_OR_RETURN_ERROR
+#undef AV1C_PUSH_ERROR_HANDLER_DATA
+#undef AV1C_POP_ERROR_HANDLER_DATA
diff --git a/third_party/aom/common/av1_config.h b/third_party/aom/common/av1_config.h
new file mode 100644
index 0000000000..a15bedb305
--- /dev/null
+++ b/third_party/aom/common/av1_config.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2018, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#ifndef AOM_COMMON_AV1_CONFIG_H_
+#define AOM_COMMON_AV1_CONFIG_H_
+
+#include "aom/aom_integer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Struct representing ISOBMFF/Matroska AV1 config. See:
+// https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-syntax
+//
+// The AV1 config has the following format:
+//
+// unsigned int (1) marker = 1;
+// unsigned int (7) version = 1;
+// unsigned int (3) seq_profile;
+// unsigned int (5) seq_level_idx_0;
+// unsigned int (1) seq_tier_0;
+// unsigned int (1) high_bitdepth;
+// unsigned int (1) twelve_bit;
+// unsigned int (1) monochrome;
+// unsigned int (1) chroma_subsampling_x;
+// unsigned int (1) chroma_subsampling_y;
+// unsigned int (2) chroma_sample_position;
+// unsigned int (3) reserved = 0;
+//
+// unsigned int (1) initial_presentation_delay_present;
+// if (initial_presentation_delay_present) {
+// unsigned int (4) initial_presentation_delay_minus_one;
+// } else {
+// unsigned int (4) reserved = 0;
+// }
+//
+// unsigned int (8)[] configOBUs;
+//
+// Note: get_av1config_from_obu() does not currently store 'configOBUs' data, so
+// the field is omitted.
+typedef struct _Av1Config {
+ uint8_t marker;
+ uint8_t version;
+ uint8_t seq_profile;
+ uint8_t seq_level_idx_0;
+ uint8_t seq_tier_0;
+ uint8_t high_bitdepth;
+ uint8_t twelve_bit;
+ uint8_t monochrome;
+ uint8_t chroma_subsampling_x;
+ uint8_t chroma_subsampling_y;
+ uint8_t chroma_sample_position;
+ uint8_t initial_presentation_delay_present;
+ uint8_t initial_presentation_delay_minus_one;
+} Av1Config;
+
+// Attempts to parse a Sequence Header OBU and set the paramenters of 'config'.
+// Returns 0 upon success, and -1 upon failure. 'buffer' can contain multiple
+// OBUs, but the Sequence Header OBU must be the first OBU within the buffer.
+int get_av1config_from_obu(const uint8_t *buffer, size_t length, int is_annexb,
+ Av1Config *config);
+
+// Attempts to parse an AV1 config from 'buffer'. Returns 0 upon success.
+// Returns -1 when 'buffer_length' is less than 4, when passed NULL pointers, or
+// when parsing of 'buffer' fails.
+int read_av1config(const uint8_t *buffer, size_t buffer_length,
+ size_t *bytes_read, Av1Config *config);
+
+// Writes 'config' to 'buffer'. Returns 0 upon successful write to 'buffer'.
+// Returns -1 when passed NULL pointers or when 'capacity' insufficient.
+int write_av1config(const Av1Config *config, size_t capacity,
+ size_t *bytes_written, uint8_t *buffer);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif // AOM_COMMON_AV1_CONFIG_H_
diff --git a/third_party/aom/common/ivf_dec.cmake b/third_party/aom/common/ivf_dec.cmake
new file mode 100644
index 0000000000..fedeea7940
--- /dev/null
+++ b/third_party/aom/common/ivf_dec.cmake
@@ -0,0 +1,28 @@
+#
+# Copyright (c) 2021, Alliance for Open Media. All rights reserved
+#
+# This source code is subject to the terms of the BSD 2 Clause License and the
+# Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License was
+# not distributed with this source code in the LICENSE file, you can obtain it
+# at www.aomedia.org/license/software. If the Alliance for Open Media Patent
+# License 1.0 was not distributed with this source code in the PATENTS file, you
+# can obtain it at www.aomedia.org/license/patent.
+#
+if(AOM_COMMON_IVF_DEC_CMAKE_)
+ return()
+endif() # AOM_COMMON_AOM_COMMON_CMAKE_
+set(AOM_COMMON_IVF_DEC_CMAKE_ 1)
+
+list(APPEND IVF_DEC_SOURCES "${AOM_ROOT}/common/ivfdec.c"
+ "${AOM_ROOT}/common/ivfdec.h")
+
+# Creates the aom_common build target and makes libaom depend on it. The libaom
+# target must exist before this function is called.
+function(setup_ivf_dec_targets)
+ add_library(ivf_dec OBJECT ${IVF_DEC_SOURCES})
+ set(AOM_LIB_TARGETS ${AOM_LIB_TARGETS} ivf_dec PARENT_SCOPE)
+ target_sources(aom PRIVATE $<TARGET_OBJECTS:ivf_dec>)
+ if(BUILD_SHARED_LIBS)
+ target_sources(aom_static PRIVATE $<TARGET_OBJECTS:ivf_dec>)
+ endif()
+endfunction()
diff --git a/third_party/aom/common/ivfdec.c b/third_party/aom/common/ivfdec.c
new file mode 100644
index 0000000000..6e714d1cfe
--- /dev/null
+++ b/third_party/aom/common/ivfdec.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include "common/ivfdec.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "aom_ports/mem_ops.h"
+#include "aom_ports/sanitizer.h"
+#include "tools_common.h"
+
+static const char *IVF_SIGNATURE = "DKIF";
+
+static void fix_framerate(int *num, int *den) {
+ if (*den <= 0 || *den >= 1000000000 || *num <= 0 || *num >= 1000) {
+ // framerate seems to be invalid, just default to 30fps.
+ *num = 30;
+ *den = 1;
+ }
+}
+
+int file_is_ivf(struct AvxInputContext *input_ctx) {
+ unsigned char raw_hdr[32];
+ int is_ivf = 0;
+
+ if (buffer_input(input_ctx, 32, raw_hdr, /*buffered=*/true) == 32) {
+ if (memcmp(IVF_SIGNATURE, raw_hdr, 4) == 0) {
+ is_ivf = 1;
+
+ if (mem_get_le16(raw_hdr + 4) != 0) {
+ fprintf(stderr,
+ "Error: Unrecognized IVF version! This file may not"
+ " decode properly.\n");
+ }
+
+ input_ctx->fourcc = mem_get_le32(raw_hdr + 8);
+ input_ctx->width = mem_get_le16(raw_hdr + 12);
+ input_ctx->height = mem_get_le16(raw_hdr + 14);
+ input_ctx->framerate.numerator = mem_get_le32(raw_hdr + 16);
+ input_ctx->framerate.denominator = mem_get_le32(raw_hdr + 20);
+ fix_framerate(&input_ctx->framerate.numerator,
+ &input_ctx->framerate.denominator);
+ }
+ }
+
+ if (!is_ivf) {
+ rewind_detect(input_ctx);
+ }
+ return is_ivf;
+}
+
+int ivf_read_frame(struct AvxInputContext *input_ctx, uint8_t **buffer,
+ size_t *bytes_read, size_t *buffer_size,
+ aom_codec_pts_t *pts) {
+ unsigned char raw_header[IVF_FRAME_HDR_SZ] = { 0 };
+ size_t frame_size = 0;
+
+ if (read_from_input(input_ctx, IVF_FRAME_HDR_SZ, raw_header) !=
+ IVF_FRAME_HDR_SZ) {
+ if (!input_eof(input_ctx))
+ fprintf(stderr, "Warning: Failed to read frame size\n");
+ } else {
+ frame_size = mem_get_le32(raw_header);
+
+ if (frame_size > 256 * 1024 * 1024) {
+ fprintf(stderr, "Warning: Read invalid frame size (%u)\n",
+ (unsigned int)frame_size);
+ frame_size = 0;
+ }
+
+ if (frame_size > *buffer_size) {
+ uint8_t *new_buffer = (uint8_t *)realloc(*buffer, 2 * frame_size);
+
+ if (new_buffer) {
+ *buffer = new_buffer;
+ *buffer_size = 2 * frame_size;
+ } else {
+ fprintf(stderr, "Warning: Failed to allocate compressed data buffer\n");
+ frame_size = 0;
+ }
+ }
+
+ if (pts) {
+ *pts = mem_get_le32(&raw_header[4]);
+ *pts += ((aom_codec_pts_t)mem_get_le32(&raw_header[8]) << 32);
+ }
+ }
+
+ if (!input_eof(input_ctx)) {
+ ASAN_UNPOISON_MEMORY_REGION(*buffer, *buffer_size);
+ if (read_from_input(input_ctx, frame_size, *buffer) != frame_size) {
+ fprintf(stderr, "Warning: Failed to read full frame\n");
+ return 1;
+ }
+
+ ASAN_POISON_MEMORY_REGION(*buffer + frame_size, *buffer_size - frame_size);
+ *bytes_read = frame_size;
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/third_party/aom/common/ivfdec.h b/third_party/aom/common/ivfdec.h
new file mode 100644
index 0000000000..e8fe8d0c53
--- /dev/null
+++ b/third_party/aom/common/ivfdec.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#ifndef AOM_COMMON_IVFDEC_H_
+#define AOM_COMMON_IVFDEC_H_
+
+#include "aom/aom_codec.h"
+#include "common/tools_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int file_is_ivf(struct AvxInputContext *input);
+int ivf_read_frame(struct AvxInputContext *input_ctx, uint8_t **buffer,
+ size_t *bytes_read, size_t *buffer_size,
+ aom_codec_pts_t *pts);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif // AOM_COMMON_IVFDEC_H_
diff --git a/third_party/aom/common/ivfenc.c b/third_party/aom/common/ivfenc.c
new file mode 100644
index 0000000000..64715f4d74
--- /dev/null
+++ b/third_party/aom/common/ivfenc.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include "common/ivfenc.h"
+
+#include "aom/aom_encoder.h"
+#include "aom_ports/mem_ops.h"
+
+void ivf_write_file_header(FILE *outfile, const struct aom_codec_enc_cfg *cfg,
+ unsigned int fourcc, int frame_cnt) {
+ char header[32];
+
+ header[0] = 'D';
+ header[1] = 'K';
+ header[2] = 'I';
+ header[3] = 'F';
+ mem_put_le16(header + 4, 0); // version
+ mem_put_le16(header + 6, 32); // header size
+ mem_put_le32(header + 8, fourcc); // fourcc
+ mem_put_le16(header + 12, cfg->g_w); // width
+ mem_put_le16(header + 14, cfg->g_h); // height
+ mem_put_le32(header + 16, cfg->g_timebase.den); // rate
+ mem_put_le32(header + 20, cfg->g_timebase.num); // scale
+ mem_put_le32(header + 24, frame_cnt); // length
+ mem_put_le32(header + 28, 0); // unused
+
+ fwrite(header, 1, 32, outfile);
+}
+
+void ivf_write_frame_header(FILE *outfile, int64_t pts, size_t frame_size) {
+ char header[12];
+
+ mem_put_le32(header, (int)frame_size);
+ mem_put_le32(header + 4, (int)(pts & 0xFFFFFFFF));
+ mem_put_le32(header + 8, (int)(pts >> 32));
+ fwrite(header, 1, 12, outfile);
+}
+
+void ivf_write_frame_size(FILE *outfile, size_t frame_size) {
+ char header[4];
+
+ mem_put_le32(header, (int)frame_size);
+ fwrite(header, 1, 4, outfile);
+}
diff --git a/third_party/aom/common/ivfenc.h b/third_party/aom/common/ivfenc.h
new file mode 100644
index 0000000000..8f6d947d47
--- /dev/null
+++ b/third_party/aom/common/ivfenc.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#ifndef AOM_COMMON_IVFENC_H_
+#define AOM_COMMON_IVFENC_H_
+
+#include "common/tools_common.h"
+
+struct aom_codec_enc_cfg;
+struct aom_codec_cx_pkt;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ivf_write_file_header(FILE *outfile, const struct aom_codec_enc_cfg *cfg,
+ uint32_t fourcc, int frame_cnt);
+
+void ivf_write_frame_header(FILE *outfile, int64_t pts, size_t frame_size);
+
+void ivf_write_frame_size(FILE *outfile, size_t frame_size);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif // AOM_COMMON_IVFENC_H_
diff --git a/third_party/aom/common/md5_utils.c b/third_party/aom/common/md5_utils.c
new file mode 100644
index 0000000000..c69aa57a3b
--- /dev/null
+++ b/third_party/aom/common/md5_utils.c
@@ -0,0 +1,257 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h' header
+ * definitions
+ * - Ian Jackson <ian@chiark.greenend.org.uk>.
+ * Still in the public domain.
+ */
+
+#include <string.h> /* for memcpy() */
+
+#include "common/md5_utils.h"
+
+static void byteSwap(UWORD32 *buf, unsigned words) {
+ md5byte *p;
+
+ /* Only swap bytes for big endian machines */
+ int i = 1;
+
+ if (*(char *)&i == 1) return;
+
+ p = (md5byte *)buf;
+
+ do {
+ *buf++ = (UWORD32)((unsigned)p[3] << 8 | p[2]) << 16 |
+ ((unsigned)p[1] << 8 | p[0]);
+ p += 4;
+ } while (--words);
+}
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx) {
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bytes[0] = 0;
+ ctx->bytes[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) {
+ UWORD32 t;
+
+ /* Update byte count */
+
+ t = ctx->bytes[0];
+
+ if ((ctx->bytes[0] = t + len) < t)
+ ctx->bytes[1]++; /* Carry from low to high */
+
+ t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
+
+ if (t > len) {
+ memcpy((md5byte *)ctx->in + 64 - t, buf, len);
+ return;
+ }
+
+ /* First chunk is an odd size */
+ memcpy((md5byte *)ctx->in + 64 - t, buf, t);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += t;
+ len -= t;
+
+ /* Process data in 64-byte chunks */
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(md5byte digest[16], struct MD5Context *ctx) {
+ int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
+ md5byte *p = (md5byte *)ctx->in + count;
+
+ /* Set the first char of padding to 0x80. There is always room. */
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 56 bytes (-8..55) */
+ count = 56 - 1 - count;
+
+ if (count < 0) { /* Padding forces an extra block */
+ memset(p, 0, count + 8);
+ byteSwap(ctx->in, 16);
+ MD5Transform(ctx->buf, ctx->in);
+ p = (md5byte *)ctx->in;
+ count = 56;
+ }
+
+ memset(p, 0, count);
+ byteSwap(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ctx->in[14] = ctx->bytes[0] << 3;
+ ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
+ MD5Transform(ctx->buf, ctx->in);
+
+ byteSwap(ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, in, s) \
+ (w += f(x, y, z) + in, w = (w << s | w >> (32 - s)) + x)
+
+#if defined(__clang__) && defined(__has_attribute)
+#if __has_attribute(no_sanitize)
+#define AOM_NO_UNSIGNED_OVERFLOW_CHECK \
+ __attribute__((no_sanitize("unsigned-integer-overflow")))
+#endif
+#if __clang_major__ >= 12
+#define VPX_NO_UNSIGNED_SHIFT_CHECK \
+ __attribute__((no_sanitize("unsigned-shift-base")))
+#endif // __clang__ >= 12
+#endif // __clang__
+
+#ifndef AOM_NO_UNSIGNED_OVERFLOW_CHECK
+#define AOM_NO_UNSIGNED_OVERFLOW_CHECK
+#endif
+#ifndef AOM_NO_UNSIGNED_SHIFT_CHECK
+#define AOM_NO_UNSIGNED_SHIFT_CHECK
+#endif
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+AOM_NO_UNSIGNED_OVERFLOW_CHECK AOM_NO_UNSIGNED_SHIFT_CHECK void MD5Transform(
+ UWORD32 buf[4], UWORD32 const in[16]) {
+ register UWORD32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#undef AOM_NO_UNSIGNED_OVERFLOW_CHECK
+#undef AOM_NO_UNSIGNED_SHIFT_CHECK
+
+#endif
diff --git a/third_party/aom/common/md5_utils.h b/third_party/aom/common/md5_utils.h
new file mode 100644
index 0000000000..144fa3ad28
--- /dev/null
+++ b/third_party/aom/common/md5_utils.h
@@ -0,0 +1,49 @@
+/*
+ * This is the header file for the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h'
+ * header definitions
+ * - Ian Jackson <ian@chiark.greenend.org.uk>.
+ * Still in the public domain.
+ */
+
+#ifndef AOM_COMMON_MD5_UTILS_H_
+#define AOM_COMMON_MD5_UTILS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define md5byte unsigned char
+#define UWORD32 unsigned int
+
+typedef struct MD5Context MD5Context;
+struct MD5Context {
+ UWORD32 buf[4];
+ UWORD32 bytes[2];
+ UWORD32 in[16];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(UWORD32 buf[4], UWORD32 const in[16]);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // AOM_COMMON_MD5_UTILS_H_
diff --git a/third_party/aom/common/obudec.c b/third_party/aom/common/obudec.c
new file mode 100644
index 0000000000..8b7bd39a60
--- /dev/null
+++ b/third_party/aom/common/obudec.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 2017, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "common/obudec.h"
+
+#include "aom_dsp/aom_dsp_common.h"
+#include "aom_ports/mem_ops.h"
+#include "av1/common/common.h"
+#include "av1/common/obu_util.h"
+#include "tools_common.h"
+
+#define OBU_BUFFER_SIZE (500 * 1024)
+
+#define OBU_HEADER_SIZE 1
+#define OBU_EXTENSION_SIZE 1
+#define OBU_MAX_LENGTH_FIELD_SIZE 8
+
+#define OBU_MAX_HEADER_SIZE \
+ (OBU_HEADER_SIZE + OBU_EXTENSION_SIZE + 2 * OBU_MAX_LENGTH_FIELD_SIZE)
+
+#define OBU_DETECTION_SIZE \
+ (OBU_HEADER_SIZE + OBU_EXTENSION_SIZE + 4 * OBU_MAX_LENGTH_FIELD_SIZE)
+
+// Reads unsigned LEB128 integer and returns 0 upon successful read and decode.
+// Stores raw bytes in 'value_buffer', length of the number in 'value_length',
+// and decoded value in 'value'. If 'buffered' is true, it is buffered in the
+// detect buffer first.
+static int obudec_read_leb128(struct AvxInputContext *input_ctx,
+ uint8_t *value_buffer, size_t *value_length,
+ uint64_t *value, bool buffered) {
+ if (!input_ctx || !value_buffer || !value_length || !value) return -1;
+ size_t len;
+ for (len = 0; len < OBU_MAX_LENGTH_FIELD_SIZE; ++len) {
+ const size_t num_read =
+ buffer_input(input_ctx, 1, &value_buffer[len], buffered);
+ if (num_read == 0) {
+ if (len == 0 && input_eof(input_ctx)) {
+ *value_length = 0;
+ return 0;
+ }
+ // Ran out of data before completing read of value.
+ return -1;
+ }
+ if ((value_buffer[len] >> 7) == 0) {
+ ++len;
+ *value_length = len;
+ break;
+ }
+ }
+
+ return aom_uleb_decode(value_buffer, len, value, NULL);
+}
+
+// Reads OBU header from 'input_ctx'. The 'buffer_capacity' passed in must be
+// large enough to store an OBU header with extension (2 bytes). Raw OBU data is
+// written to 'obu_data', parsed OBU header values are written to 'obu_header',
+// and total bytes read from file are written to 'bytes_read'. Returns 0 for
+// success, and non-zero on failure. When end of file is reached, the return
+// value is 0 and the 'bytes_read' value is set to 0. If 'buffered' is true, it
+// is buffered in the detect buffer first.
+static int obudec_read_obu_header(struct AvxInputContext *input_ctx,
+ size_t buffer_capacity, int is_annexb,
+ uint8_t *obu_data, ObuHeader *obu_header,
+ size_t *bytes_read, bool buffered) {
+ if (!input_ctx || buffer_capacity < (OBU_HEADER_SIZE + OBU_EXTENSION_SIZE) ||
+ !obu_data || !obu_header || !bytes_read) {
+ return -1;
+ }
+ *bytes_read = buffer_input(input_ctx, 1, obu_data, buffered);
+
+ if (input_eof(input_ctx) && *bytes_read == 0) {
+ return 0;
+ } else if (*bytes_read != 1) {
+ fprintf(stderr, "obudec: Failure reading OBU header.\n");
+ return -1;
+ }
+
+ const int has_extension = (obu_data[0] >> 2) & 0x1;
+ if (has_extension) {
+ if (buffer_input(input_ctx, 1, &obu_data[1], buffered) != 1) {
+ fprintf(stderr, "obudec: Failure reading OBU extension.");
+ return -1;
+ }
+ ++*bytes_read;
+ }
+
+ size_t obu_bytes_parsed = 0;
+ const aom_codec_err_t parse_result = aom_read_obu_header(
+ obu_data, *bytes_read, &obu_bytes_parsed, obu_header, is_annexb);
+ if (parse_result != AOM_CODEC_OK || *bytes_read != obu_bytes_parsed) {
+ fprintf(stderr, "obudec: Error parsing OBU header.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+// Reads OBU payload from 'input_ctx' and returns 0 for success when all payload
+// bytes are read from the file. Payload data is written to 'obu_data', and
+// actual bytes read added to 'bytes_read'. If 'buffered' is true, it is
+// buffered in the detect buffer first.
+static int obudec_read_obu_payload(struct AvxInputContext *input_ctx,
+ size_t payload_length, uint8_t *obu_data,
+ size_t *bytes_read, bool buffered) {
+ if (!input_ctx || payload_length == 0 || !obu_data || !bytes_read) return -1;
+
+ if (buffer_input(input_ctx, payload_length, obu_data, buffered) !=
+ payload_length) {
+ fprintf(stderr, "obudec: Failure reading OBU payload.\n");
+ return -1;
+ }
+
+ *bytes_read += payload_length;
+ return 0;
+}
+
+static int obudec_read_obu_header_and_size(
+ struct AvxInputContext *input_ctx, size_t buffer_capacity, int is_annexb,
+ uint8_t *buffer, size_t *bytes_read, size_t *payload_length,
+ ObuHeader *obu_header, bool buffered) {
+ const size_t kMinimumBufferSize = OBU_MAX_HEADER_SIZE;
+ if (!input_ctx || !buffer || !bytes_read || !payload_length || !obu_header ||
+ buffer_capacity < kMinimumBufferSize) {
+ return -1;
+ }
+
+ size_t leb128_length_obu = 0;
+ size_t leb128_length_payload = 0;
+ uint64_t obu_size = 0;
+ if (is_annexb) {
+ if (obudec_read_leb128(input_ctx, &buffer[0], &leb128_length_obu, &obu_size,
+ buffered) != 0) {
+ fprintf(stderr, "obudec: Failure reading OBU size length.\n");
+ return -1;
+ } else if (leb128_length_obu == 0) {
+ *payload_length = 0;
+ return 0;
+ }
+ if (obu_size > UINT32_MAX) {
+ fprintf(stderr, "obudec: OBU payload length too large.\n");
+ return -1;
+ }
+ }
+
+ size_t header_size = 0;
+ if (obudec_read_obu_header(input_ctx, buffer_capacity - leb128_length_obu,
+ is_annexb, buffer + leb128_length_obu, obu_header,
+ &header_size, buffered) != 0) {
+ return -1;
+ } else if (header_size == 0) {
+ *payload_length = 0;
+ return 0;
+ }
+
+ if (!obu_header->has_size_field) {
+ assert(is_annexb);
+ if (obu_size < header_size) {
+ fprintf(stderr, "obudec: OBU size is too small.\n");
+ return -1;
+ }
+ *payload_length = (size_t)obu_size - header_size;
+ } else {
+ uint64_t u64_payload_length = 0;
+ if (obudec_read_leb128(input_ctx, &buffer[leb128_length_obu + header_size],
+ &leb128_length_payload, &u64_payload_length,
+ buffered) != 0) {
+ fprintf(stderr, "obudec: Failure reading OBU payload length.\n");
+ return -1;
+ }
+ if (u64_payload_length > UINT32_MAX) {
+ fprintf(stderr, "obudec: OBU payload length too large.\n");
+ return -1;
+ }
+
+ *payload_length = (size_t)u64_payload_length;
+ }
+
+ *bytes_read = leb128_length_obu + header_size + leb128_length_payload;
+ return 0;
+}
+
+static int obudec_grow_buffer(size_t growth_amount, uint8_t **obu_buffer,
+ size_t *obu_buffer_capacity) {
+ if (!*obu_buffer || !obu_buffer_capacity || growth_amount == 0) {
+ return -1;
+ }
+
+ const size_t capacity = *obu_buffer_capacity;
+ if (SIZE_MAX - growth_amount < capacity) {
+ fprintf(stderr, "obudec: cannot grow buffer, capacity will roll over.\n");
+ return -1;
+ }
+
+ const size_t new_capacity = capacity + growth_amount;
+
+#if defined AOM_MAX_ALLOCABLE_MEMORY
+ if (new_capacity > AOM_MAX_ALLOCABLE_MEMORY) {
+ fprintf(stderr, "obudec: OBU size exceeds max alloc size.\n");
+ return -1;
+ }
+#endif
+
+ uint8_t *new_buffer = (uint8_t *)realloc(*obu_buffer, new_capacity);
+ if (!new_buffer) {
+ fprintf(stderr, "obudec: Failed to allocate compressed data buffer.\n");
+ return -1;
+ }
+
+ *obu_buffer = new_buffer;
+ *obu_buffer_capacity = new_capacity;
+ return 0;
+}
+
+static int obudec_read_one_obu(struct AvxInputContext *input_ctx,
+ uint8_t **obu_buffer, size_t obu_bytes_buffered,
+ size_t *obu_buffer_capacity, size_t *obu_length,
+ ObuHeader *obu_header, int is_annexb,
+ bool buffered) {
+ if (!input_ctx || !(*obu_buffer) || !obu_buffer_capacity || !obu_length ||
+ !obu_header) {
+ return -1;
+ }
+
+ size_t bytes_read = 0;
+ size_t obu_payload_length = 0;
+ size_t available_buffer_capacity = *obu_buffer_capacity - obu_bytes_buffered;
+
+ if (available_buffer_capacity < OBU_MAX_HEADER_SIZE) {
+ if (obudec_grow_buffer(AOMMAX(*obu_buffer_capacity, OBU_MAX_HEADER_SIZE),
+ obu_buffer, obu_buffer_capacity) != 0) {
+ *obu_length = bytes_read;
+ return -1;
+ }
+ available_buffer_capacity +=
+ AOMMAX(*obu_buffer_capacity, OBU_MAX_HEADER_SIZE);
+ }
+
+ const int status = obudec_read_obu_header_and_size(
+ input_ctx, available_buffer_capacity, is_annexb,
+ *obu_buffer + obu_bytes_buffered, &bytes_read, &obu_payload_length,
+ obu_header, buffered);
+ if (status < 0) return status;
+
+ if (obu_payload_length > SIZE_MAX - bytes_read) return -1;
+
+ if (obu_payload_length > 256 * 1024 * 1024) {
+ fprintf(stderr, "obudec: Read invalid OBU size (%u)\n",
+ (unsigned int)obu_payload_length);
+ *obu_length = bytes_read + obu_payload_length;
+ return -1;
+ }
+
+ if (bytes_read + obu_payload_length > available_buffer_capacity &&
+ obudec_grow_buffer(AOMMAX(*obu_buffer_capacity, obu_payload_length),
+ obu_buffer, obu_buffer_capacity) != 0) {
+ *obu_length = bytes_read + obu_payload_length;
+ return -1;
+ }
+
+ if (obu_payload_length > 0 &&
+ obudec_read_obu_payload(input_ctx, obu_payload_length,
+ *obu_buffer + obu_bytes_buffered + bytes_read,
+ &bytes_read, buffered) != 0) {
+ return -1;
+ }
+
+ *obu_length = bytes_read;
+ return 0;
+}
+
+int file_is_obu(struct ObuDecInputContext *obu_ctx) {
+ if (!obu_ctx || !obu_ctx->avx_ctx) return 0;
+
+ struct AvxInputContext *avx_ctx = obu_ctx->avx_ctx;
+ uint8_t detect_buf[OBU_DETECTION_SIZE] = { 0 };
+ const int is_annexb = obu_ctx->is_annexb;
+ size_t payload_length = 0;
+ ObuHeader obu_header;
+ memset(&obu_header, 0, sizeof(obu_header));
+ size_t length_of_unit_size = 0;
+ size_t annexb_header_length = 0;
+ uint64_t unit_size = 0;
+
+ if (is_annexb) {
+ // read the size of first temporal unit
+ if (obudec_read_leb128(avx_ctx, &detect_buf[0], &length_of_unit_size,
+ &unit_size, /*buffered=*/true) != 0) {
+ fprintf(stderr, "obudec: Failure reading temporal unit header\n");
+ rewind_detect(avx_ctx);
+ return 0;
+ }
+
+ // read the size of first frame unit
+ if (obudec_read_leb128(avx_ctx, &detect_buf[length_of_unit_size],
+ &annexb_header_length, &unit_size,
+ /*buffered=*/true) != 0) {
+ fprintf(stderr, "obudec: Failure reading frame unit header\n");
+ rewind_detect(avx_ctx);
+ return 0;
+ }
+ annexb_header_length += length_of_unit_size;
+ }
+
+ size_t bytes_read = 0;
+ if (obudec_read_obu_header_and_size(
+ avx_ctx, OBU_DETECTION_SIZE - annexb_header_length, is_annexb,
+ &detect_buf[annexb_header_length], &bytes_read, &payload_length,
+ &obu_header, /*buffered=*/true) != 0) {
+ fprintf(stderr, "obudec: Failure reading first OBU.\n");
+ rewind_detect(avx_ctx);
+ return 0;
+ }
+
+ if (is_annexb) {
+ bytes_read += annexb_header_length;
+ }
+
+ if (obu_header.type != OBU_TEMPORAL_DELIMITER &&
+ obu_header.type != OBU_SEQUENCE_HEADER) {
+ rewind_detect(avx_ctx);
+ return 0;
+ }
+
+ if (obu_header.has_size_field) {
+ if (obu_header.type == OBU_TEMPORAL_DELIMITER && payload_length != 0) {
+ fprintf(
+ stderr,
+ "obudec: Invalid OBU_TEMPORAL_DELIMITER payload length (non-zero).");
+ rewind_detect(avx_ctx);
+ return 0;
+ }
+ } else if (!is_annexb) {
+ fprintf(stderr, "obudec: OBU size fields required, cannot decode input.\n");
+ rewind_detect(avx_ctx);
+ return 0;
+ }
+
+ // Appears that input is valid Section 5 AV1 stream.
+ obu_ctx->buffer = (uint8_t *)malloc(OBU_BUFFER_SIZE);
+ if (!obu_ctx->buffer) {
+ fprintf(stderr, "Out of memory.\n");
+ rewind_detect(avx_ctx);
+ return 0;
+ }
+ obu_ctx->buffer_capacity = OBU_BUFFER_SIZE;
+
+ memcpy(obu_ctx->buffer, &detect_buf[0], bytes_read);
+ obu_ctx->bytes_buffered = bytes_read;
+ // If the first OBU is a SEQUENCE_HEADER, then it will have a payload.
+ // We need to read this in so that our buffer only contains complete OBUs.
+ if (payload_length > 0) {
+ if (payload_length > (obu_ctx->buffer_capacity - bytes_read)) {
+ fprintf(stderr, "obudec: First OBU's payload is too large\n");
+ rewind_detect(avx_ctx);
+ obudec_free(obu_ctx);
+ return 0;
+ }
+
+ size_t payload_bytes = 0;
+ const int status = obudec_read_obu_payload(
+ avx_ctx, payload_length, &obu_ctx->buffer[bytes_read], &payload_bytes,
+ /*buffered=*/false);
+ if (status < 0) {
+ rewind_detect(avx_ctx);
+ obudec_free(obu_ctx);
+ return 0;
+ }
+ obu_ctx->bytes_buffered += payload_bytes;
+ }
+ return 1;
+}
+
+int obudec_read_temporal_unit(struct ObuDecInputContext *obu_ctx,
+ uint8_t **buffer, size_t *bytes_read,
+ size_t *buffer_size) {
+ FILE *f = obu_ctx->avx_ctx->file;
+ if (!f) return -1;
+
+ *buffer_size = 0;
+ *bytes_read = 0;
+
+ if (input_eof(obu_ctx->avx_ctx)) {
+ return 1;
+ }
+
+ size_t tu_size;
+ size_t obu_size = 0;
+ size_t length_of_temporal_unit_size = 0;
+ uint8_t tuheader[OBU_MAX_LENGTH_FIELD_SIZE] = { 0 };
+
+ if (obu_ctx->is_annexb) {
+ uint64_t size = 0;
+
+ if (obu_ctx->bytes_buffered == 0) {
+ if (obudec_read_leb128(obu_ctx->avx_ctx, &tuheader[0],
+ &length_of_temporal_unit_size, &size,
+ /*buffered=*/false) != 0) {
+ fprintf(stderr, "obudec: Failure reading temporal unit header\n");
+ return -1;
+ }
+ if (size == 0 && input_eof(obu_ctx->avx_ctx)) {
+ return 1;
+ }
+ } else {
+ // temporal unit size was already stored in buffer
+ if (aom_uleb_decode(obu_ctx->buffer, obu_ctx->bytes_buffered, &size,
+ &length_of_temporal_unit_size) != 0) {
+ fprintf(stderr, "obudec: Failure reading temporal unit header\n");
+ return -1;
+ }
+ }
+
+ if (size > UINT32_MAX || size + length_of_temporal_unit_size > UINT32_MAX) {
+ fprintf(stderr, "obudec: TU too large.\n");
+ return -1;
+ }
+
+ size += length_of_temporal_unit_size;
+ tu_size = (size_t)size;
+ } else {
+ while (1) {
+ ObuHeader obu_header;
+ memset(&obu_header, 0, sizeof(obu_header));
+
+ if (obudec_read_one_obu(obu_ctx->avx_ctx, &obu_ctx->buffer,
+ obu_ctx->bytes_buffered,
+ &obu_ctx->buffer_capacity, &obu_size, &obu_header,
+ 0, /*buffered=*/false) != 0) {
+ fprintf(stderr, "obudec: read_one_obu failed in TU loop\n");
+ return -1;
+ }
+
+ if (obu_header.type == OBU_TEMPORAL_DELIMITER || obu_size == 0) {
+ tu_size = obu_ctx->bytes_buffered;
+ break;
+ } else {
+ obu_ctx->bytes_buffered += obu_size;
+ }
+ }
+ }
+
+#if defined AOM_MAX_ALLOCABLE_MEMORY
+ if (tu_size > AOM_MAX_ALLOCABLE_MEMORY) {
+ fprintf(stderr, "obudec: Temporal Unit size exceeds max alloc size.\n");
+ return -1;
+ }
+#endif
+ if (tu_size > 0) {
+ uint8_t *new_buffer = (uint8_t *)realloc(*buffer, tu_size);
+ if (!new_buffer) {
+ free(*buffer);
+ fprintf(stderr, "obudec: Out of memory.\n");
+ return -1;
+ }
+ *buffer = new_buffer;
+ }
+ *bytes_read = tu_size;
+ *buffer_size = tu_size;
+
+ if (!obu_ctx->is_annexb) {
+ memcpy(*buffer, obu_ctx->buffer, tu_size);
+
+ // At this point, (obu_ctx->buffer + obu_ctx->bytes_buffered + obu_size)
+ // points to the end of the buffer.
+ memmove(obu_ctx->buffer, obu_ctx->buffer + obu_ctx->bytes_buffered,
+ obu_size);
+ obu_ctx->bytes_buffered = obu_size;
+ } else {
+ if (!input_eof(obu_ctx->avx_ctx)) {
+ size_t data_size;
+ size_t offset;
+ if (!obu_ctx->bytes_buffered) {
+ data_size = tu_size - length_of_temporal_unit_size;
+ memcpy(*buffer, &tuheader[0], length_of_temporal_unit_size);
+ offset = length_of_temporal_unit_size;
+ } else {
+ const size_t copy_size = AOMMIN(obu_ctx->bytes_buffered, tu_size);
+ memcpy(*buffer, obu_ctx->buffer, copy_size);
+ offset = copy_size;
+ data_size = tu_size - copy_size;
+ obu_ctx->bytes_buffered -= copy_size;
+ }
+
+ if (read_from_input(obu_ctx->avx_ctx, data_size, *buffer + offset) !=
+ data_size) {
+ fprintf(stderr, "obudec: Failed to read full temporal unit\n");
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+void obudec_free(struct ObuDecInputContext *obu_ctx) {
+ free(obu_ctx->buffer);
+ obu_ctx->buffer = NULL;
+ obu_ctx->buffer_capacity = 0;
+ obu_ctx->bytes_buffered = 0;
+}
diff --git a/third_party/aom/common/obudec.h b/third_party/aom/common/obudec.h
new file mode 100644
index 0000000000..b2adb1e3d7
--- /dev/null
+++ b/third_party/aom/common/obudec.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#ifndef AOM_COMMON_OBUDEC_H_
+#define AOM_COMMON_OBUDEC_H_
+
+#include "common/tools_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ObuDecInputContext {
+ struct AvxInputContext *avx_ctx;
+ uint8_t *buffer;
+ size_t buffer_capacity;
+ size_t bytes_buffered;
+ int is_annexb;
+};
+
+// Returns 1 when file data starts (if Annex B stream, after reading the
+// size of the OBU) with what appears to be a Temporal Delimiter
+// OBU as defined by Section 5 of the AV1 bitstream specification.
+int file_is_obu(struct ObuDecInputContext *obu_ctx);
+
+// Reads one Temporal Unit from the input file. Returns 0 when a TU is
+// successfully read, 1 when end of file is reached, and less than 0 when an
+// error occurs. Stores TU data in 'buffer'. Reallocs buffer to match TU size,
+// returns buffer capacity via 'buffer_size', and returns size of buffered data
+// via 'bytes_read'.
+int obudec_read_temporal_unit(struct ObuDecInputContext *obu_ctx,
+ uint8_t **buffer, size_t *bytes_read,
+ size_t *buffer_size);
+
+void obudec_free(struct ObuDecInputContext *obu_ctx);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif // AOM_COMMON_OBUDEC_H_
diff --git a/third_party/aom/common/rawenc.c b/third_party/aom/common/rawenc.c
new file mode 100644
index 0000000000..aa80d2cae3
--- /dev/null
+++ b/third_party/aom/common/rawenc.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include <stdbool.h>
+#include "common/rawenc.h"
+
+// Number of bytes to write per batch in write_greyscale.
+#define BATCH_SIZE 8
+
+// Interface to writing to either a file or MD5Context. Takes a pointer to
+// either the file or MD5Context, the buffer, the size of each element, and
+// number of elements to write. Note that size and nmemb (last two args) must
+// be unsigned int, as the interface to MD5Update requires that.
+typedef void (*WRITER)(void *, const uint8_t *, unsigned int, unsigned int);
+
+static void write_file(void *fp, const uint8_t *buffer, unsigned int size,
+ unsigned int nmemb) {
+ fwrite(buffer, size, nmemb, (FILE *)fp);
+}
+
+static void write_md5(void *md5, const uint8_t *buffer, unsigned int size,
+ unsigned int nmemb) {
+ MD5Update((MD5Context *)md5, buffer, size * nmemb);
+}
+
+// Writes out n neutral chroma samples (for greyscale).
+static void write_greyscale(const aom_image_t *img, int n, WRITER writer_func,
+ void *file_or_md5) {
+ // Batch 8 writes for low bit-depth, 4 writes for high bit-depth.
+ int bytes_per_sample;
+ union {
+ uint8_t u8[BATCH_SIZE];
+ uint16_t u16[BATCH_SIZE / 2];
+ } batched;
+ if (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) {
+ bytes_per_sample = 2;
+ for (int i = 0; i < BATCH_SIZE / 2; ++i) {
+ batched.u16[i] = 1 << (img->bit_depth - 1);
+ }
+ } else {
+ bytes_per_sample = 1;
+ for (int i = 0; i < BATCH_SIZE; ++i) {
+ batched.u8[i] = 0x80;
+ }
+ }
+ const int samples_per_batch = BATCH_SIZE / bytes_per_sample;
+ const int num_batched_writes = n / samples_per_batch;
+ for (int i = 0; i < num_batched_writes; ++i) {
+ writer_func(file_or_md5, batched.u8, sizeof(uint8_t), BATCH_SIZE);
+ }
+ const int remaining = n % samples_per_batch;
+ for (int i = 0; i < remaining; ++i) {
+ writer_func(file_or_md5, batched.u8, sizeof(uint8_t), bytes_per_sample);
+ }
+}
+
+// Encapsulates the logic for writing raw data to either an image file or
+// to an MD5 context.
+static void raw_write_image_file_or_md5(const aom_image_t *img,
+ const int *planes, const int num_planes,
+ void *file_or_md5, WRITER writer_func) {
+ const bool high_bitdepth = img->fmt & AOM_IMG_FMT_HIGHBITDEPTH;
+ const int bytes_per_sample = high_bitdepth ? 2 : 1;
+ for (int i = 0; i < num_planes; ++i) {
+ const int plane = planes[i];
+ const int w = aom_img_plane_width(img, plane);
+ const int h = aom_img_plane_height(img, plane);
+ // If we're on a color plane and the output is monochrome, write a greyscale
+ // value. Since there are only YUV planes, compare against Y.
+ if (img->monochrome && plane != AOM_PLANE_Y) {
+ write_greyscale(img, w * h, writer_func, file_or_md5);
+ continue;
+ }
+ const unsigned char *buf = img->planes[plane];
+ const int stride = img->stride[plane];
+ for (int y = 0; y < h; ++y) {
+ writer_func(file_or_md5, buf, bytes_per_sample, w);
+ buf += stride;
+ }
+ }
+}
+
+void raw_write_image_file(const aom_image_t *img, const int *planes,
+ const int num_planes, FILE *file) {
+ raw_write_image_file_or_md5(img, planes, num_planes, file, write_file);
+}
+
+void raw_update_image_md5(const aom_image_t *img, const int *planes,
+ const int num_planes, MD5Context *md5) {
+ raw_write_image_file_or_md5(img, planes, num_planes, md5, write_md5);
+}
diff --git a/third_party/aom/common/rawenc.h b/third_party/aom/common/rawenc.h
new file mode 100644
index 0000000000..cf5e00e6fd
--- /dev/null
+++ b/third_party/aom/common/rawenc.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AOM_COMMON_RAWENC_H_
+#define AOM_COMMON_RAWENC_H_
+
+#include "aom/aom_decoder.h"
+#include "common/md5_utils.h"
+#include "common/tools_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void raw_write_image_file(const aom_image_t *img, const int *planes,
+ const int num_planes, FILE *file);
+void raw_update_image_md5(const aom_image_t *img, const int *planes,
+ const int num_planes, MD5Context *md5);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // AOM_COMMON_RAWENC_H_
diff --git a/third_party/aom/common/tools_common.c b/third_party/aom/common/tools_common.c
new file mode 100644
index 0000000000..4d77a1b427
--- /dev/null
+++ b/third_party/aom/common/tools_common.c
@@ -0,0 +1,636 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include <assert.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common/tools_common.h"
+
+#if CONFIG_AV1_ENCODER
+#include "aom/aomcx.h"
+#endif
+
+#if CONFIG_AV1_DECODER
+#include "aom/aomdx.h"
+#endif
+
+#if defined(_WIN32)
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+#define LOG_ERROR(label) \
+ do { \
+ const char *l = label; \
+ va_list ap; \
+ va_start(ap, fmt); \
+ if (l) fprintf(stderr, "%s: ", l); \
+ vfprintf(stderr, fmt, ap); \
+ fprintf(stderr, "\n"); \
+ va_end(ap); \
+ } while (0)
+
+FILE *set_binary_mode(FILE *stream) {
+ (void)stream;
+#if defined(_WIN32)
+ _setmode(_fileno(stream), _O_BINARY);
+#endif
+ return stream;
+}
+
+void die(const char *fmt, ...) {
+ LOG_ERROR(NULL);
+ usage_exit();
+}
+
+void fatal(const char *fmt, ...) {
+ LOG_ERROR("Fatal");
+ exit(EXIT_FAILURE);
+}
+
+void aom_tools_warn(const char *fmt, ...) { LOG_ERROR("Warning"); }
+
+void die_codec(aom_codec_ctx_t *ctx, const char *s) {
+ const char *detail = aom_codec_error_detail(ctx);
+
+ fprintf(stderr, "%s: %s\n", s, aom_codec_error(ctx));
+ if (detail) fprintf(stderr, " %s\n", detail);
+ exit(EXIT_FAILURE);
+}
+
+const char *image_format_to_string(aom_img_fmt_t fmt) {
+ switch (fmt) {
+ case AOM_IMG_FMT_I420: return "I420";
+ case AOM_IMG_FMT_I422: return "I422";
+ case AOM_IMG_FMT_I444: return "I444";
+ case AOM_IMG_FMT_YV12: return "YV12";
+ case AOM_IMG_FMT_NV12: return "NV12";
+ case AOM_IMG_FMT_YV1216: return "YV1216";
+ case AOM_IMG_FMT_I42016: return "I42016";
+ case AOM_IMG_FMT_I42216: return "I42216";
+ case AOM_IMG_FMT_I44416: return "I44416";
+ default: return "Other";
+ }
+}
+
+int read_yuv_frame(struct AvxInputContext *input_ctx, aom_image_t *yuv_frame) {
+ FILE *f = input_ctx->file;
+ struct FileTypeDetectionBuffer *detect = &input_ctx->detect;
+ int plane = 0;
+ int shortread = 0;
+ const int bytespp = (yuv_frame->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
+
+ for (plane = 0; plane < 3; ++plane) {
+ uint8_t *ptr;
+ int w = aom_img_plane_width(yuv_frame, plane);
+ const int h = aom_img_plane_height(yuv_frame, plane);
+ int r;
+ // Assuming that for nv12 we read all chroma data at one time
+ if (yuv_frame->fmt == AOM_IMG_FMT_NV12 && plane > 1) break;
+ if (yuv_frame->fmt == AOM_IMG_FMT_NV12 && plane == 1) w *= 2;
+ /* Determine the correct plane based on the image format. The for-loop
+ * always counts in Y,U,V order, but this may not match the order of
+ * the data on disk.
+ */
+ switch (plane) {
+ case 1:
+ ptr =
+ yuv_frame->planes[yuv_frame->fmt == AOM_IMG_FMT_YV12 ? AOM_PLANE_V
+ : AOM_PLANE_U];
+ break;
+ case 2:
+ ptr =
+ yuv_frame->planes[yuv_frame->fmt == AOM_IMG_FMT_YV12 ? AOM_PLANE_U
+ : AOM_PLANE_V];
+ break;
+ default: ptr = yuv_frame->planes[plane];
+ }
+
+ for (r = 0; r < h; ++r) {
+ size_t needed = w * bytespp;
+ size_t buf_position = 0;
+ const size_t left = detect->buf_read - detect->position;
+ if (left > 0) {
+ const size_t more = (left < needed) ? left : needed;
+ memcpy(ptr, detect->buf + detect->position, more);
+ buf_position = more;
+ needed -= more;
+ detect->position += more;
+ }
+ if (needed > 0) {
+ shortread |= (fread(ptr + buf_position, 1, needed, f) < needed);
+ }
+
+ ptr += yuv_frame->stride[plane];
+ }
+ }
+
+ return shortread;
+}
+
+struct CodecInfo {
+ // Pointer to a function of zero arguments that returns an aom_codec_iface_t.
+ aom_codec_iface_t *(*interface)(void);
+ const char *short_name;
+ uint32_t fourcc;
+};
+
+#if CONFIG_AV1_ENCODER
+static const struct CodecInfo aom_encoders[] = {
+ { &aom_codec_av1_cx, "av1", AV1_FOURCC },
+};
+
+int get_aom_encoder_count(void) {
+ return sizeof(aom_encoders) / sizeof(aom_encoders[0]);
+}
+
+aom_codec_iface_t *get_aom_encoder_by_index(int i) {
+ assert(i >= 0 && i < get_aom_encoder_count());
+ return aom_encoders[i].interface();
+}
+
+aom_codec_iface_t *get_aom_encoder_by_short_name(const char *name) {
+ for (int i = 0; i < get_aom_encoder_count(); ++i) {
+ const struct CodecInfo *info = &aom_encoders[i];
+ if (strcmp(info->short_name, name) == 0) return info->interface();
+ }
+ return NULL;
+}
+
+uint32_t get_fourcc_by_aom_encoder(aom_codec_iface_t *iface) {
+ for (int i = 0; i < get_aom_encoder_count(); ++i) {
+ const struct CodecInfo *info = &aom_encoders[i];
+ if (info->interface() == iface) {
+ return info->fourcc;
+ }
+ }
+ return 0;
+}
+
+const char *get_short_name_by_aom_encoder(aom_codec_iface_t *iface) {
+ for (int i = 0; i < get_aom_encoder_count(); ++i) {
+ const struct CodecInfo *info = &aom_encoders[i];
+ if (info->interface() == iface) {
+ return info->short_name;
+ }
+ }
+ return NULL;
+}
+
+#endif // CONFIG_AV1_ENCODER
+
+#if CONFIG_AV1_DECODER
+static const struct CodecInfo aom_decoders[] = {
+ { &aom_codec_av1_dx, "av1", AV1_FOURCC },
+};
+
+int get_aom_decoder_count(void) {
+ return sizeof(aom_decoders) / sizeof(aom_decoders[0]);
+}
+
+aom_codec_iface_t *get_aom_decoder_by_index(int i) {
+ assert(i >= 0 && i < get_aom_decoder_count());
+ return aom_decoders[i].interface();
+}
+
+aom_codec_iface_t *get_aom_decoder_by_short_name(const char *name) {
+ for (int i = 0; i < get_aom_decoder_count(); ++i) {
+ const struct CodecInfo *info = &aom_decoders[i];
+ if (strcmp(info->short_name, name) == 0) return info->interface();
+ }
+ return NULL;
+}
+
+aom_codec_iface_t *get_aom_decoder_by_fourcc(uint32_t fourcc) {
+ for (int i = 0; i < get_aom_decoder_count(); ++i) {
+ const struct CodecInfo *info = &aom_decoders[i];
+ if (info->fourcc == fourcc) return info->interface();
+ }
+ return NULL;
+}
+
+const char *get_short_name_by_aom_decoder(aom_codec_iface_t *iface) {
+ for (int i = 0; i < get_aom_decoder_count(); ++i) {
+ const struct CodecInfo *info = &aom_decoders[i];
+ if (info->interface() == iface) {
+ return info->short_name;
+ }
+ }
+ return NULL;
+}
+
+uint32_t get_fourcc_by_aom_decoder(aom_codec_iface_t *iface) {
+ for (int i = 0; i < get_aom_decoder_count(); ++i) {
+ const struct CodecInfo *info = &aom_decoders[i];
+ if (info->interface() == iface) {
+ return info->fourcc;
+ }
+ }
+ return 0;
+}
+
+#endif // CONFIG_AV1_DECODER
+
+void aom_img_write(const aom_image_t *img, FILE *file) {
+ int plane;
+
+ for (plane = 0; plane < 3; ++plane) {
+ const unsigned char *buf = img->planes[plane];
+ const int stride = img->stride[plane];
+ const int w = aom_img_plane_width(img, plane) *
+ ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
+ const int h = aom_img_plane_height(img, plane);
+ int y;
+
+ for (y = 0; y < h; ++y) {
+ fwrite(buf, 1, w, file);
+ buf += stride;
+ }
+ }
+}
+
+bool aom_img_read(aom_image_t *img, FILE *file) {
+ int plane;
+ const int bytespp = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
+
+ for (plane = 0; plane < 3; ++plane) {
+ unsigned char *buf = img->planes[plane];
+ const int stride = img->stride[plane];
+ const int w = aom_img_plane_width(img, plane) * bytespp;
+ const int h = aom_img_plane_height(img, plane);
+ int y;
+
+ for (y = 0; y < h; ++y) {
+ if (fread(buf, 1, w, file) != (size_t)w) return false;
+ buf += stride;
+ }
+ }
+
+ return true;
+}
+
+// TODO(dkovalev) change sse_to_psnr signature: double -> int64_t
+double sse_to_psnr(double samples, double peak, double sse) {
+ static const double kMaxPSNR = 100.0;
+
+ if (sse > 0.0) {
+ const double psnr = 10.0 * log10(samples * peak * peak / sse);
+ return psnr > kMaxPSNR ? kMaxPSNR : psnr;
+ } else {
+ return kMaxPSNR;
+ }
+}
+
+// TODO(debargha): Consolidate the functions below into a separate file.
+static void highbd_img_upshift(aom_image_t *dst, const aom_image_t *src,
+ int input_shift) {
+ // Note the offset is 1 less than half.
+ const int offset = input_shift > 0 ? (1 << (input_shift - 1)) - 1 : 0;
+ int plane;
+ if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
+ dst->x_chroma_shift != src->x_chroma_shift ||
+ dst->y_chroma_shift != src->y_chroma_shift || dst->fmt != src->fmt ||
+ input_shift < 0) {
+ fatal("Unsupported image conversion");
+ }
+ switch (src->fmt) {
+ case AOM_IMG_FMT_I42016:
+ case AOM_IMG_FMT_I42216:
+ case AOM_IMG_FMT_I44416: break;
+ default: fatal("Unsupported image conversion");
+ }
+ for (plane = 0; plane < 3; plane++) {
+ int w = src->d_w;
+ int h = src->d_h;
+ int x, y;
+ if (plane) {
+ w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
+ h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
+ }
+ for (y = 0; y < h; y++) {
+ const uint16_t *p_src =
+ (const uint16_t *)(src->planes[plane] + y * src->stride[plane]);
+ uint16_t *p_dst =
+ (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]);
+ for (x = 0; x < w; x++) *p_dst++ = (*p_src++ << input_shift) + offset;
+ }
+ }
+}
+
+static void lowbd_img_upshift(aom_image_t *dst, const aom_image_t *src,
+ int input_shift) {
+ // Note the offset is 1 less than half.
+ const int offset = input_shift > 0 ? (1 << (input_shift - 1)) - 1 : 0;
+ int plane;
+ if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
+ dst->x_chroma_shift != src->x_chroma_shift ||
+ dst->y_chroma_shift != src->y_chroma_shift ||
+ dst->fmt != src->fmt + AOM_IMG_FMT_HIGHBITDEPTH || input_shift < 0) {
+ fatal("Unsupported image conversion");
+ }
+ switch (src->fmt) {
+ case AOM_IMG_FMT_YV12:
+ case AOM_IMG_FMT_I420:
+ case AOM_IMG_FMT_I422:
+ case AOM_IMG_FMT_I444: break;
+ default: fatal("Unsupported image conversion");
+ }
+ for (plane = 0; plane < 3; plane++) {
+ int w = src->d_w;
+ int h = src->d_h;
+ int x, y;
+ if (plane) {
+ w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
+ h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
+ }
+ for (y = 0; y < h; y++) {
+ const uint8_t *p_src = src->planes[plane] + y * src->stride[plane];
+ uint16_t *p_dst =
+ (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]);
+ for (x = 0; x < w; x++) {
+ *p_dst++ = (*p_src++ << input_shift) + offset;
+ }
+ }
+ }
+}
+
+void aom_img_upshift(aom_image_t *dst, const aom_image_t *src,
+ int input_shift) {
+ if (src->fmt & AOM_IMG_FMT_HIGHBITDEPTH) {
+ highbd_img_upshift(dst, src, input_shift);
+ } else {
+ lowbd_img_upshift(dst, src, input_shift);
+ }
+}
+
+void aom_img_truncate_16_to_8(aom_image_t *dst, const aom_image_t *src) {
+ int plane;
+ if (dst->fmt + AOM_IMG_FMT_HIGHBITDEPTH != src->fmt || dst->d_w != src->d_w ||
+ dst->d_h != src->d_h || dst->x_chroma_shift != src->x_chroma_shift ||
+ dst->y_chroma_shift != src->y_chroma_shift) {
+ fatal("Unsupported image conversion");
+ }
+ switch (dst->fmt) {
+ case AOM_IMG_FMT_I420:
+ case AOM_IMG_FMT_I422:
+ case AOM_IMG_FMT_I444: break;
+ default: fatal("Unsupported image conversion");
+ }
+ for (plane = 0; plane < 3; plane++) {
+ int w = src->d_w;
+ int h = src->d_h;
+ int x, y;
+ if (plane) {
+ w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
+ h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
+ }
+ for (y = 0; y < h; y++) {
+ const uint16_t *p_src =
+ (const uint16_t *)(src->planes[plane] + y * src->stride[plane]);
+ uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane];
+ for (x = 0; x < w; x++) {
+ *p_dst++ = (uint8_t)(*p_src++);
+ }
+ }
+ }
+}
+
+static void highbd_img_downshift(aom_image_t *dst, const aom_image_t *src,
+ int down_shift) {
+ int plane;
+ if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
+ dst->x_chroma_shift != src->x_chroma_shift ||
+ dst->y_chroma_shift != src->y_chroma_shift || dst->fmt != src->fmt ||
+ down_shift < 0) {
+ fatal("Unsupported image conversion");
+ }
+ switch (src->fmt) {
+ case AOM_IMG_FMT_I42016:
+ case AOM_IMG_FMT_I42216:
+ case AOM_IMG_FMT_I44416: break;
+ default: fatal("Unsupported image conversion");
+ }
+ for (plane = 0; plane < 3; plane++) {
+ int w = src->d_w;
+ int h = src->d_h;
+ int x, y;
+ if (plane) {
+ w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
+ h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
+ }
+ for (y = 0; y < h; y++) {
+ const uint16_t *p_src =
+ (const uint16_t *)(src->planes[plane] + y * src->stride[plane]);
+ uint16_t *p_dst =
+ (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]);
+ for (x = 0; x < w; x++) *p_dst++ = *p_src++ >> down_shift;
+ }
+ }
+}
+
+static void lowbd_img_downshift(aom_image_t *dst, const aom_image_t *src,
+ int down_shift) {
+ int plane;
+ if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
+ dst->x_chroma_shift != src->x_chroma_shift ||
+ dst->y_chroma_shift != src->y_chroma_shift ||
+ src->fmt != dst->fmt + AOM_IMG_FMT_HIGHBITDEPTH || down_shift < 0) {
+ fatal("Unsupported image conversion");
+ }
+ switch (dst->fmt) {
+ case AOM_IMG_FMT_I420:
+ case AOM_IMG_FMT_I422:
+ case AOM_IMG_FMT_I444: break;
+ default: fatal("Unsupported image conversion");
+ }
+ for (plane = 0; plane < 3; plane++) {
+ int w = src->d_w;
+ int h = src->d_h;
+ int x, y;
+ if (plane) {
+ w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
+ h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
+ }
+ for (y = 0; y < h; y++) {
+ const uint16_t *p_src =
+ (const uint16_t *)(src->planes[plane] + y * src->stride[plane]);
+ uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane];
+ for (x = 0; x < w; x++) {
+ *p_dst++ = *p_src++ >> down_shift;
+ }
+ }
+ }
+}
+
+void aom_img_downshift(aom_image_t *dst, const aom_image_t *src,
+ int down_shift) {
+ if (dst->fmt & AOM_IMG_FMT_HIGHBITDEPTH) {
+ highbd_img_downshift(dst, src, down_shift);
+ } else {
+ lowbd_img_downshift(dst, src, down_shift);
+ }
+}
+
+static int img_shifted_realloc_required(const aom_image_t *img,
+ const aom_image_t *shifted,
+ aom_img_fmt_t required_fmt) {
+ return img->d_w != shifted->d_w || img->d_h != shifted->d_h ||
+ required_fmt != shifted->fmt;
+}
+
+bool aom_shift_img(unsigned int output_bit_depth, aom_image_t **img_ptr,
+ aom_image_t **img_shifted_ptr) {
+ aom_image_t *img = *img_ptr;
+ aom_image_t *img_shifted = *img_shifted_ptr;
+
+ const aom_img_fmt_t shifted_fmt = output_bit_depth == 8
+ ? img->fmt & ~AOM_IMG_FMT_HIGHBITDEPTH
+ : img->fmt | AOM_IMG_FMT_HIGHBITDEPTH;
+
+ if (shifted_fmt != img->fmt || output_bit_depth != img->bit_depth) {
+ if (img_shifted &&
+ img_shifted_realloc_required(img, img_shifted, shifted_fmt)) {
+ aom_img_free(img_shifted);
+ img_shifted = NULL;
+ }
+ if (img_shifted) {
+ img_shifted->monochrome = img->monochrome;
+ }
+ if (!img_shifted) {
+ img_shifted = aom_img_alloc(NULL, shifted_fmt, img->d_w, img->d_h, 16);
+ if (!img_shifted) {
+ *img_shifted_ptr = NULL;
+ return false;
+ }
+ img_shifted->bit_depth = output_bit_depth;
+ img_shifted->monochrome = img->monochrome;
+ img_shifted->csp = img->csp;
+ }
+ if (output_bit_depth > img->bit_depth) {
+ aom_img_upshift(img_shifted, img, output_bit_depth - img->bit_depth);
+ } else {
+ aom_img_downshift(img_shifted, img, img->bit_depth - output_bit_depth);
+ }
+ *img_shifted_ptr = img_shifted;
+ *img_ptr = img_shifted;
+ }
+
+ return true;
+}
+
+// Related to I420, NV12 format has one luma "luminance" plane Y and one plane
+// with U and V values interleaved.
+void aom_img_write_nv12(const aom_image_t *img, FILE *file) {
+ // Y plane
+ const unsigned char *buf = img->planes[0];
+ int stride = img->stride[0];
+ int w = aom_img_plane_width(img, 0) *
+ ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
+ int h = aom_img_plane_height(img, 0);
+ int x, y;
+
+ for (y = 0; y < h; ++y) {
+ fwrite(buf, 1, w, file);
+ buf += stride;
+ }
+
+ // Interleaved U and V plane
+ const unsigned char *ubuf = img->planes[1];
+ const unsigned char *vbuf = img->planes[2];
+ const size_t size = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
+ stride = img->stride[1];
+ w = aom_img_plane_width(img, 1);
+ h = aom_img_plane_height(img, 1);
+
+ for (y = 0; y < h; ++y) {
+ for (x = 0; x < w; ++x) {
+ fwrite(ubuf, size, 1, file);
+ fwrite(vbuf, size, 1, file);
+ ubuf += size;
+ vbuf += size;
+ }
+ ubuf += (stride - w * size);
+ vbuf += (stride - w * size);
+ }
+}
+
+size_t read_from_input(struct AvxInputContext *input_ctx, size_t n,
+ unsigned char *buf) {
+ const size_t buffered_bytes =
+ input_ctx->detect.buf_read - input_ctx->detect.position;
+ size_t read_n;
+ if (buffered_bytes == 0) {
+ read_n = fread(buf, 1, n, input_ctx->file);
+ } else if (n <= buffered_bytes) {
+ memcpy(buf, input_ctx->detect.buf + input_ctx->detect.position, n);
+ input_ctx->detect.position += n;
+ read_n = n;
+ } else {
+ memcpy(buf, input_ctx->detect.buf + input_ctx->detect.position,
+ buffered_bytes);
+ input_ctx->detect.position += buffered_bytes;
+ read_n = buffered_bytes;
+ read_n +=
+ fread(buf + buffered_bytes, 1, n - buffered_bytes, input_ctx->file);
+ }
+ return read_n;
+}
+
+size_t input_to_detect_buf(struct AvxInputContext *input_ctx, size_t n) {
+ if (n + input_ctx->detect.position > DETECT_BUF_SZ) {
+ die("Failed to store in the detect buffer, maximum size exceeded.");
+ }
+ const size_t buffered_bytes =
+ input_ctx->detect.buf_read - input_ctx->detect.position;
+ size_t read_n;
+ if (buffered_bytes == 0) {
+ read_n = fread(input_ctx->detect.buf + input_ctx->detect.buf_read, 1, n,
+ input_ctx->file);
+ input_ctx->detect.buf_read += read_n;
+ } else if (n <= buffered_bytes) {
+ // In this case, don't need to do anything as the data is already in
+ // the detect buffer
+ read_n = n;
+ } else {
+ read_n = fread(input_ctx->detect.buf + input_ctx->detect.buf_read, 1,
+ n - buffered_bytes, input_ctx->file);
+ input_ctx->detect.buf_read += read_n;
+ read_n += buffered_bytes;
+ }
+ return read_n;
+}
+
+// Read from detect buffer to a buffer. If not enough, read from input and also
+// buffer them first.
+size_t buffer_input(struct AvxInputContext *input_ctx, size_t n,
+ unsigned char *buf, bool buffered) {
+ if (!buffered) {
+ return read_from_input(input_ctx, n, buf);
+ }
+ const size_t buf_n = input_to_detect_buf(input_ctx, n);
+ if (buf_n < n) {
+ return buf_n;
+ }
+ return read_from_input(input_ctx, n, buf);
+}
+
+void rewind_detect(struct AvxInputContext *input_ctx) {
+ input_ctx->detect.position = 0;
+}
+
+bool input_eof(struct AvxInputContext *input_ctx) {
+ return feof(input_ctx->file) &&
+ input_ctx->detect.position == input_ctx->detect.buf_read;
+}
diff --git a/third_party/aom/common/tools_common.h b/third_party/aom/common/tools_common.h
new file mode 100644
index 0000000000..b31371c670
--- /dev/null
+++ b/third_party/aom/common/tools_common.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#ifndef AOM_COMMON_TOOLS_COMMON_H_
+#define AOM_COMMON_TOOLS_COMMON_H_
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "config/aom_config.h"
+
+#include "aom/aom_codec.h"
+#include "aom/aom_image.h"
+#include "aom/aom_integer.h"
+#include "aom_ports/mem.h"
+#include "aom_ports/msvc.h"
+
+#if CONFIG_AV1_ENCODER
+#include "common/y4minput.h"
+#endif
+
+#if defined(_MSC_VER)
+/* MSVS uses _f{seek,tell}i64. */
+#define fseeko _fseeki64
+#define ftello _ftelli64
+typedef int64_t FileOffset;
+#elif defined(_WIN32)
+#include <sys/types.h> /* NOLINT*/
+/* MinGW uses f{seek,tell}o64 for large files. */
+#define fseeko fseeko64
+#define ftello ftello64
+typedef off64_t FileOffset;
+#elif CONFIG_OS_SUPPORT
+#include <sys/types.h> /* NOLINT*/
+typedef off_t FileOffset;
+/* Use 32-bit file operations in WebM file format when building ARM
+ * executables (.axf) with RVCT. */
+#else
+#define fseeko fseek
+#define ftello ftell
+typedef long FileOffset; /* NOLINT */
+#endif /* CONFIG_OS_SUPPORT */
+
+#if CONFIG_OS_SUPPORT
+#if defined(_MSC_VER)
+#include <io.h> /* NOLINT */
+#define isatty _isatty
+#define fileno _fileno
+#else
+#include <unistd.h> /* NOLINT */
+#endif /* _MSC_VER */
+#endif /* CONFIG_OS_SUPPORT */
+
+#define LITERALU64(hi, lo) ((((uint64_t)hi) << 32) | lo)
+
+#ifndef PATH_MAX
+#define PATH_MAX 512
+#endif
+
+#define IVF_FRAME_HDR_SZ (4 + 8) /* 4 byte size + 8 byte timestamp */
+#define IVF_FILE_HDR_SZ 32
+
+#define RAW_FRAME_HDR_SZ sizeof(uint32_t)
+#define OBU_DETECTION_SZ 34 // See common/obudec.c
+
+#define DETECT_BUF_SZ 34 // Max of the above header sizes
+
+#define AV1_FOURCC 0x31305641
+
+enum VideoFileType {
+ FILE_TYPE_OBU,
+ FILE_TYPE_RAW,
+ FILE_TYPE_IVF,
+ FILE_TYPE_Y4M,
+ FILE_TYPE_WEBM
+};
+
+// The fourcc for large_scale_tile encoding is "LSTC".
+#define LST_FOURCC 0x4354534c
+
+struct FileTypeDetectionBuffer {
+ char buf[DETECT_BUF_SZ];
+ size_t buf_read;
+ size_t position;
+};
+
+struct AvxRational {
+ int numerator;
+ int denominator;
+};
+
+struct AvxInputContext {
+ const char *filename;
+ FILE *file;
+ int64_t length;
+ struct FileTypeDetectionBuffer detect;
+ enum VideoFileType file_type;
+ uint32_t width;
+ uint32_t height;
+ struct AvxRational pixel_aspect_ratio;
+ aom_img_fmt_t fmt;
+ aom_bit_depth_t bit_depth;
+ int only_i420;
+ uint32_t fourcc;
+ struct AvxRational framerate;
+#if CONFIG_AV1_ENCODER
+ y4m_input y4m;
+#endif
+ aom_color_range_t color_range;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__GNUC__)
+#define AOM_NO_RETURN __attribute__((noreturn))
+#elif defined(_MSC_VER)
+#define AOM_NO_RETURN __declspec(noreturn)
+#else
+#define AOM_NO_RETURN
+#endif
+
+// Tells the compiler to perform `printf` format string checking if the
+// compiler supports it; see the 'format' attribute in
+// <https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html>.
+#define AOM_TOOLS_FORMAT_PRINTF(string_index, first_to_check)
+#if defined(__has_attribute)
+#if __has_attribute(format)
+#undef AOM_TOOLS_FORMAT_PRINTF
+#define AOM_TOOLS_FORMAT_PRINTF(string_index, first_to_check) \
+ __attribute__((__format__(__printf__, string_index, first_to_check)))
+#endif
+#endif
+
+/* Sets a stdio stream into binary mode */
+FILE *set_binary_mode(FILE *stream);
+
+AOM_NO_RETURN void die(const char *fmt, ...) AOM_TOOLS_FORMAT_PRINTF(1, 2);
+AOM_NO_RETURN void fatal(const char *fmt, ...) AOM_TOOLS_FORMAT_PRINTF(1, 2);
+void aom_tools_warn(const char *fmt, ...) AOM_TOOLS_FORMAT_PRINTF(1, 2);
+
+AOM_NO_RETURN void die_codec(aom_codec_ctx_t *ctx, const char *s);
+
+/* The tool including this file must define usage_exit() */
+AOM_NO_RETURN void usage_exit(void);
+
+#undef AOM_NO_RETURN
+
+// The AOM library can support different encoders / decoders. These
+// functions provide different ways to lookup / iterate through them.
+// The return result may be NULL to indicate no codec was found.
+int get_aom_encoder_count(void);
+aom_codec_iface_t *get_aom_encoder_by_index(int i);
+aom_codec_iface_t *get_aom_encoder_by_short_name(const char *name);
+// If the interface is unknown, returns NULL.
+const char *get_short_name_by_aom_encoder(aom_codec_iface_t *encoder);
+// If the interface is unknown, returns 0.
+uint32_t get_fourcc_by_aom_encoder(aom_codec_iface_t *iface);
+
+int get_aom_decoder_count(void);
+aom_codec_iface_t *get_aom_decoder_by_index(int i);
+aom_codec_iface_t *get_aom_decoder_by_short_name(const char *name);
+aom_codec_iface_t *get_aom_decoder_by_fourcc(uint32_t fourcc);
+const char *get_short_name_by_aom_decoder(aom_codec_iface_t *decoder);
+// If the interface is unknown, returns 0.
+uint32_t get_fourcc_by_aom_decoder(aom_codec_iface_t *iface);
+
+const char *image_format_to_string(aom_img_fmt_t fmt);
+
+int read_yuv_frame(struct AvxInputContext *input_ctx, aom_image_t *yuv_frame);
+
+void aom_img_write(const aom_image_t *img, FILE *file);
+// Returns true on success, false on failure.
+bool aom_img_read(aom_image_t *img, FILE *file);
+
+double sse_to_psnr(double samples, double peak, double mse);
+void aom_img_upshift(aom_image_t *dst, const aom_image_t *src, int input_shift);
+void aom_img_downshift(aom_image_t *dst, const aom_image_t *src,
+ int down_shift);
+// Returns true on success, false on failure.
+bool aom_shift_img(unsigned int output_bit_depth, aom_image_t **img_ptr,
+ aom_image_t **img_shifted_ptr);
+void aom_img_truncate_16_to_8(aom_image_t *dst, const aom_image_t *src);
+
+// Output in NV12 format.
+void aom_img_write_nv12(const aom_image_t *img, FILE *file);
+
+size_t read_from_input(struct AvxInputContext *input_ctx, size_t n,
+ unsigned char *buf);
+size_t input_to_detect_buf(struct AvxInputContext *input_ctx, size_t n);
+size_t buffer_input(struct AvxInputContext *input_ctx, size_t n,
+ unsigned char *buf, bool buffered);
+void rewind_detect(struct AvxInputContext *input_ctx);
+bool input_eof(struct AvxInputContext *input_ctx);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif // AOM_COMMON_TOOLS_COMMON_H_
diff --git a/third_party/aom/common/video_common.h b/third_party/aom/common/video_common.h
new file mode 100644
index 0000000000..bf95031be6
--- /dev/null
+++ b/third_party/aom/common/video_common.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AOM_COMMON_VIDEO_COMMON_H_
+#define AOM_COMMON_VIDEO_COMMON_H_
+
+#include "common/tools_common.h"
+
+typedef struct {
+ uint32_t codec_fourcc;
+ int frame_width;
+ int frame_height;
+ struct AvxRational time_base;
+ unsigned int is_annexb;
+} AvxVideoInfo;
+
+#endif // AOM_COMMON_VIDEO_COMMON_H_
diff --git a/third_party/aom/common/video_reader.c b/third_party/aom/common/video_reader.c
new file mode 100644
index 0000000000..27f69a9672
--- /dev/null
+++ b/third_party/aom/common/video_reader.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "aom_ports/mem_ops.h"
+#include "common/ivfdec.h"
+#include "common/obudec.h"
+#include "common/tools_common.h"
+#include "common/video_reader.h"
+#include "common/webmdec.h"
+
+struct AvxVideoReaderStruct {
+ AvxVideoInfo info;
+ struct AvxInputContext input_ctx;
+ struct ObuDecInputContext obu_ctx;
+ struct WebmInputContext webm_ctx;
+ uint8_t *buffer;
+ size_t buffer_size;
+ size_t frame_size;
+ aom_codec_pts_t pts;
+};
+
+AvxVideoReader *aom_video_reader_open(const char *filename) {
+ AvxVideoReader *reader = NULL;
+ const bool using_file = strcmp(filename, "-") != 0;
+ FILE *const file =
+ using_file ? fopen(filename, "rb") : set_binary_mode(stdin);
+ if (!file) return NULL; // Can't open file
+
+ reader = (AvxVideoReader *)calloc(1, sizeof(*reader));
+ if (!reader) {
+ fclose(file);
+ return NULL; // Can't allocate AvxVideoReader
+ }
+
+ reader->input_ctx.filename = filename;
+ reader->input_ctx.file = file;
+ reader->obu_ctx.avx_ctx = &reader->input_ctx;
+ reader->obu_ctx.is_annexb = 1;
+
+ // TODO(https://crbug.com/aomedia/1706): webm type does not support reading
+ // from stdin yet, and file_is_webm is not using the detect buffer when
+ // determining the type. Therefore it should only be checked when using a file
+ // and needs to be checked prior to other types.
+ if (false) {
+#if CONFIG_WEBM_IO
+ } else if (using_file &&
+ file_is_webm(&reader->webm_ctx, &reader->input_ctx)) {
+ reader->input_ctx.file_type = FILE_TYPE_WEBM;
+ reader->info.codec_fourcc = reader->input_ctx.fourcc;
+ reader->info.frame_width = reader->input_ctx.width;
+ reader->info.frame_height = reader->input_ctx.height;
+#endif
+ } else if (file_is_ivf(&reader->input_ctx)) {
+ reader->input_ctx.file_type = FILE_TYPE_IVF;
+ reader->info.codec_fourcc = reader->input_ctx.fourcc;
+ reader->info.frame_width = reader->input_ctx.width;
+ reader->info.frame_height = reader->input_ctx.height;
+ } else if (file_is_obu(&reader->obu_ctx)) {
+ reader->input_ctx.file_type = FILE_TYPE_OBU;
+ // assume AV1
+ reader->info.codec_fourcc = AV1_FOURCC;
+ reader->info.is_annexb = reader->obu_ctx.is_annexb;
+ } else {
+ fclose(file);
+ free(reader);
+ return NULL; // Unknown file type
+ }
+
+ return reader;
+}
+
+void aom_video_reader_close(AvxVideoReader *reader) {
+ if (reader) {
+ fclose(reader->input_ctx.file);
+ if (reader->input_ctx.file_type == FILE_TYPE_OBU) {
+ obudec_free(&reader->obu_ctx);
+ }
+ free(reader->buffer);
+ free(reader);
+ }
+}
+
+int aom_video_reader_read_frame(AvxVideoReader *reader) {
+ if (reader->input_ctx.file_type == FILE_TYPE_IVF) {
+ return !ivf_read_frame(&reader->input_ctx, &reader->buffer,
+ &reader->frame_size, &reader->buffer_size,
+ &reader->pts);
+ } else if (reader->input_ctx.file_type == FILE_TYPE_OBU) {
+ return !obudec_read_temporal_unit(&reader->obu_ctx, &reader->buffer,
+ &reader->frame_size,
+ &reader->buffer_size);
+#if CONFIG_WEBM_IO
+ } else if (reader->input_ctx.file_type == FILE_TYPE_WEBM) {
+ return !webm_read_frame(&reader->webm_ctx, &reader->buffer,
+ &reader->frame_size, &reader->buffer_size);
+#endif
+ } else {
+ assert(0);
+ return 0;
+ }
+}
+
+const uint8_t *aom_video_reader_get_frame(AvxVideoReader *reader,
+ size_t *size) {
+ if (size) *size = reader->frame_size;
+
+ return reader->buffer;
+}
+
+int64_t aom_video_reader_get_frame_pts(AvxVideoReader *reader) {
+ return (int64_t)reader->pts;
+}
+
+FILE *aom_video_reader_get_file(AvxVideoReader *reader) {
+ return reader->input_ctx.file;
+}
+
+const AvxVideoInfo *aom_video_reader_get_info(AvxVideoReader *reader) {
+ return &reader->info;
+}
+
+void aom_video_reader_set_fourcc(AvxVideoReader *reader, uint32_t fourcc) {
+ reader->info.codec_fourcc = fourcc;
+}
diff --git a/third_party/aom/common/video_reader.h b/third_party/aom/common/video_reader.h
new file mode 100644
index 0000000000..9ab439e8af
--- /dev/null
+++ b/third_party/aom/common/video_reader.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AOM_COMMON_VIDEO_READER_H_
+#define AOM_COMMON_VIDEO_READER_H_
+
+#include "common/video_common.h"
+
+// The following code is work in progress. It is going to support transparent
+// reading of input files. Right now only IVF format is supported for
+// simplicity. The main goal the API is to be simple and easy to use in example
+// code and in aomenc/aomdec later. All low-level details like memory
+// buffer management are hidden from API users.
+struct AvxVideoReaderStruct;
+typedef struct AvxVideoReaderStruct AvxVideoReader;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Opens the input file for reading and inspects it to determine file type.
+// Returns an opaque AvxVideoReader* upon success, or NULL upon failure.
+// Right now only IVF format is supported.
+AvxVideoReader *aom_video_reader_open(const char *filename);
+
+// Frees all resources associated with AvxVideoReader* returned from
+// aom_video_reader_open() call.
+void aom_video_reader_close(AvxVideoReader *reader);
+
+// Reads frame from the file and stores it in internal buffer.
+int aom_video_reader_read_frame(AvxVideoReader *reader);
+
+// Returns the pointer to memory buffer with frame data read by last call to
+// aom_video_reader_read_frame().
+const uint8_t *aom_video_reader_get_frame(AvxVideoReader *reader, size_t *size);
+
+// Returns the pts of the frame.
+int64_t aom_video_reader_get_frame_pts(AvxVideoReader *reader);
+// Return the reader file.
+FILE *aom_video_reader_get_file(AvxVideoReader *reader);
+
+// Fills AvxVideoInfo with information from opened video file.
+const AvxVideoInfo *aom_video_reader_get_info(AvxVideoReader *reader);
+
+// Set fourcc.
+void aom_video_reader_set_fourcc(AvxVideoReader *reader, uint32_t fourcc);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // AOM_COMMON_VIDEO_READER_H_
diff --git a/third_party/aom/common/video_writer.c b/third_party/aom/common/video_writer.c
new file mode 100644
index 0000000000..1d4328ae1e
--- /dev/null
+++ b/third_party/aom/common/video_writer.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#include "common/video_writer.h"
+
+#include <stdlib.h>
+
+#include "aom/aom_encoder.h"
+#include "common/ivfenc.h"
+
+struct AvxVideoWriterStruct {
+ AvxVideoInfo info;
+ FILE *file;
+ int frame_count;
+};
+
+static void write_header(FILE *file, const AvxVideoInfo *info,
+ int frame_count) {
+ struct aom_codec_enc_cfg cfg;
+ cfg.g_w = info->frame_width;
+ cfg.g_h = info->frame_height;
+ cfg.g_timebase.num = info->time_base.numerator;
+ cfg.g_timebase.den = info->time_base.denominator;
+
+ ivf_write_file_header(file, &cfg, info->codec_fourcc, frame_count);
+}
+
+AvxVideoWriter *aom_video_writer_open(const char *filename,
+ AvxContainer container,
+ const AvxVideoInfo *info) {
+ if (container == kContainerIVF) {
+ AvxVideoWriter *writer = NULL;
+ FILE *const file = fopen(filename, "wb");
+ if (!file) return NULL;
+
+ writer = malloc(sizeof(*writer));
+ if (!writer) {
+ fclose(file);
+ return NULL;
+ }
+ writer->frame_count = 0;
+ writer->info = *info;
+ writer->file = file;
+
+ write_header(writer->file, info, 0);
+
+ return writer;
+ }
+
+ return NULL;
+}
+
+void aom_video_writer_close(AvxVideoWriter *writer) {
+ if (writer) {
+ // Rewriting frame header with real frame count
+ rewind(writer->file);
+ write_header(writer->file, &writer->info, writer->frame_count);
+
+ fclose(writer->file);
+ free(writer);
+ }
+}
+
+int aom_video_writer_write_frame(AvxVideoWriter *writer, const uint8_t *buffer,
+ size_t size, int64_t pts) {
+ ivf_write_frame_header(writer->file, pts, size);
+ if (fwrite(buffer, 1, size, writer->file) != size) return 0;
+
+ ++writer->frame_count;
+
+ return 1;
+}
+
+void aom_video_writer_set_fourcc(AvxVideoWriter *writer, uint32_t fourcc) {
+ writer->info.codec_fourcc = fourcc;
+}
diff --git a/third_party/aom/common/video_writer.h b/third_party/aom/common/video_writer.h
new file mode 100644
index 0000000000..8712d47a58
--- /dev/null
+++ b/third_party/aom/common/video_writer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AOM_COMMON_VIDEO_WRITER_H_
+#define AOM_COMMON_VIDEO_WRITER_H_
+
+#include "common/video_common.h"
+
+enum { kContainerIVF } UENUM1BYTE(AvxContainer);
+
+struct AvxVideoWriterStruct;
+typedef struct AvxVideoWriterStruct AvxVideoWriter;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Finds and opens writer for specified container format.
+// Returns an opaque AvxVideoWriter* upon success, or NULL upon failure.
+// Right now only IVF format is supported.
+AvxVideoWriter *aom_video_writer_open(const char *filename,
+ AvxContainer container,
+ const AvxVideoInfo *info);
+
+// Frees all resources associated with AvxVideoWriter* returned from
+// aom_video_writer_open() call.
+void aom_video_writer_close(AvxVideoWriter *writer);
+
+// Writes frame bytes to the file.
+int aom_video_writer_write_frame(AvxVideoWriter *writer, const uint8_t *buffer,
+ size_t size, int64_t pts);
+// Set fourcc.
+void aom_video_writer_set_fourcc(AvxVideoWriter *writer, uint32_t fourcc);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // AOM_COMMON_VIDEO_WRITER_H_
diff --git a/third_party/aom/common/warnings.c b/third_party/aom/common/warnings.c
new file mode 100644
index 0000000000..a20531cb8b
--- /dev/null
+++ b/third_party/aom/common/warnings.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include "common/warnings.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "aom/aom_encoder.h"
+#include "apps/aomenc.h"
+#include "common/tools_common.h"
+
+static const char quantizer_warning_string[] =
+ "Bad quantizer values. Quantizer values should not be equal, and should "
+ "differ by at least 8.";
+
+struct WarningListNode {
+ const char *warning_string;
+ struct WarningListNode *next_warning;
+};
+
+struct WarningList {
+ struct WarningListNode *warning_node;
+};
+
+static void add_warning(const char *warning_string,
+ struct WarningList *warning_list) {
+ struct WarningListNode **node = &warning_list->warning_node;
+
+ struct WarningListNode *new_node = malloc(sizeof(*new_node));
+ if (new_node == NULL) {
+ fatal("Unable to allocate warning node.");
+ }
+
+ new_node->warning_string = warning_string;
+ new_node->next_warning = NULL;
+
+ while (*node != NULL) node = &(*node)->next_warning;
+
+ *node = new_node;
+}
+
+static void free_warning_list(struct WarningList *warning_list) {
+ while (warning_list->warning_node != NULL) {
+ struct WarningListNode *const node = warning_list->warning_node;
+ warning_list->warning_node = node->next_warning;
+ free(node);
+ }
+}
+
+static int continue_prompt(int num_warnings) {
+ int c;
+ fprintf(stderr,
+ "%d encoder configuration warning(s). Continue? (y to continue) ",
+ num_warnings);
+ c = getchar();
+ return c == 'y';
+}
+
+static void check_quantizer(int min_q, int max_q,
+ struct WarningList *warning_list) {
+ const int lossless = min_q == 0 && max_q == 0;
+ if (!lossless && (min_q == max_q || abs(max_q - min_q) < 8))
+ add_warning(quantizer_warning_string, warning_list);
+}
+
+void check_encoder_config(int disable_prompt,
+ const struct AvxEncoderConfig *global_config,
+ const struct aom_codec_enc_cfg *stream_config) {
+ int num_warnings = 0;
+ struct WarningListNode *warning = NULL;
+ struct WarningList warning_list = { 0 };
+ (void)global_config;
+ check_quantizer(stream_config->rc_min_quantizer,
+ stream_config->rc_max_quantizer, &warning_list);
+ /* Count and print warnings. */
+ for (warning = warning_list.warning_node; warning != NULL;
+ warning = warning->next_warning, ++num_warnings) {
+ aom_tools_warn("%s", warning->warning_string);
+ }
+
+ free_warning_list(&warning_list);
+
+ if (num_warnings) {
+ if (!disable_prompt && !continue_prompt(num_warnings)) exit(EXIT_FAILURE);
+ }
+}
diff --git a/third_party/aom/common/warnings.h b/third_party/aom/common/warnings.h
new file mode 100644
index 0000000000..36f1fe0706
--- /dev/null
+++ b/third_party/aom/common/warnings.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#ifndef AOM_COMMON_WARNINGS_H_
+#define AOM_COMMON_WARNINGS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct aom_codec_enc_cfg;
+struct AvxEncoderConfig;
+
+/*
+ * Checks config for improperly used settings. Warns user upon encountering
+ * settings that will lead to poor output quality. Prompts user to continue
+ * when warnings are issued.
+ */
+void check_encoder_config(int disable_prompt,
+ const struct AvxEncoderConfig *global_config,
+ const struct aom_codec_enc_cfg *stream_config);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // AOM_COMMON_WARNINGS_H_
diff --git a/third_party/aom/common/webmdec.cc b/third_party/aom/common/webmdec.cc
new file mode 100644
index 0000000000..33bda59021
--- /dev/null
+++ b/third_party/aom/common/webmdec.cc
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include "common/webmdec.h"
+
+#include <cassert>
+#include <cstring>
+#include <cstdio>
+
+#include "third_party/libwebm/mkvparser/mkvparser.h"
+#include "third_party/libwebm/mkvparser/mkvreader.h"
+
+namespace {
+
+void reset(struct WebmInputContext *const webm_ctx) {
+ if (webm_ctx->reader != NULL) {
+ mkvparser::MkvReader *const reader =
+ reinterpret_cast<mkvparser::MkvReader *>(webm_ctx->reader);
+ delete reader;
+ }
+ if (webm_ctx->segment != NULL) {
+ mkvparser::Segment *const segment =
+ reinterpret_cast<mkvparser::Segment *>(webm_ctx->segment);
+ delete segment;
+ }
+ if (webm_ctx->buffer != NULL) {
+ delete[] webm_ctx->buffer;
+ }
+ webm_ctx->reader = NULL;
+ webm_ctx->segment = NULL;
+ webm_ctx->buffer = NULL;
+ webm_ctx->cluster = NULL;
+ webm_ctx->block_entry = NULL;
+ webm_ctx->block = NULL;
+ webm_ctx->block_frame_index = 0;
+ webm_ctx->video_track_index = 0;
+ webm_ctx->timestamp_ns = 0;
+ webm_ctx->is_key_frame = false;
+}
+
+void get_first_cluster(struct WebmInputContext *const webm_ctx) {
+ mkvparser::Segment *const segment =
+ reinterpret_cast<mkvparser::Segment *>(webm_ctx->segment);
+ const mkvparser::Cluster *const cluster = segment->GetFirst();
+ webm_ctx->cluster = cluster;
+}
+
+void rewind_and_reset(struct WebmInputContext *const webm_ctx,
+ struct AvxInputContext *const aom_ctx) {
+ rewind(aom_ctx->file);
+ reset(webm_ctx);
+}
+
+} // namespace
+
+int file_is_webm(struct WebmInputContext *webm_ctx,
+ struct AvxInputContext *aom_ctx) {
+ mkvparser::MkvReader *const reader = new mkvparser::MkvReader(aom_ctx->file);
+ webm_ctx->reader = reader;
+ webm_ctx->reached_eos = 0;
+
+ mkvparser::EBMLHeader header;
+ long long pos = 0;
+ if (header.Parse(reader, pos) < 0) {
+ rewind_and_reset(webm_ctx, aom_ctx);
+ return 0;
+ }
+
+ mkvparser::Segment *segment;
+ if (mkvparser::Segment::CreateInstance(reader, pos, segment)) {
+ rewind_and_reset(webm_ctx, aom_ctx);
+ return 0;
+ }
+ webm_ctx->segment = segment;
+ if (segment->Load() < 0) {
+ rewind_and_reset(webm_ctx, aom_ctx);
+ return 0;
+ }
+
+ const mkvparser::Tracks *const tracks = segment->GetTracks();
+ const mkvparser::VideoTrack *video_track = NULL;
+ for (unsigned long i = 0; i < tracks->GetTracksCount(); ++i) {
+ const mkvparser::Track *const track = tracks->GetTrackByIndex(i);
+ if (track->GetType() == mkvparser::Track::kVideo) {
+ video_track = static_cast<const mkvparser::VideoTrack *>(track);
+ webm_ctx->video_track_index = static_cast<int>(track->GetNumber());
+ break;
+ }
+ }
+
+ if (video_track == NULL || video_track->GetCodecId() == NULL) {
+ rewind_and_reset(webm_ctx, aom_ctx);
+ return 0;
+ }
+
+ if (!strncmp(video_track->GetCodecId(), "V_AV1", 5)) {
+ aom_ctx->fourcc = AV1_FOURCC;
+ } else {
+ rewind_and_reset(webm_ctx, aom_ctx);
+ return 0;
+ }
+
+ aom_ctx->framerate.denominator = 0;
+ aom_ctx->framerate.numerator = 0;
+ aom_ctx->width = static_cast<uint32_t>(video_track->GetWidth());
+ aom_ctx->height = static_cast<uint32_t>(video_track->GetHeight());
+
+ get_first_cluster(webm_ctx);
+
+ return 1;
+}
+
+int webm_read_frame(struct WebmInputContext *webm_ctx, uint8_t **buffer,
+ size_t *bytes_read, size_t *buffer_size) {
+ assert(webm_ctx->buffer == *buffer);
+ // This check is needed for frame parallel decoding, in which case this
+ // function could be called even after it has reached end of input stream.
+ if (webm_ctx->reached_eos) {
+ return 1;
+ }
+ mkvparser::Segment *const segment =
+ reinterpret_cast<mkvparser::Segment *>(webm_ctx->segment);
+ const mkvparser::Cluster *cluster =
+ reinterpret_cast<const mkvparser::Cluster *>(webm_ctx->cluster);
+ const mkvparser::Block *block =
+ reinterpret_cast<const mkvparser::Block *>(webm_ctx->block);
+ const mkvparser::BlockEntry *block_entry =
+ reinterpret_cast<const mkvparser::BlockEntry *>(webm_ctx->block_entry);
+ bool block_entry_eos = false;
+ do {
+ long status = 0;
+ bool get_new_block = false;
+ if (block_entry == NULL && !block_entry_eos) {
+ status = cluster->GetFirst(block_entry);
+ get_new_block = true;
+ } else if (block_entry_eos || block_entry->EOS()) {
+ cluster = segment->GetNext(cluster);
+ if (cluster == NULL || cluster->EOS()) {
+ *bytes_read = 0;
+ webm_ctx->reached_eos = 1;
+ return 1;
+ }
+ status = cluster->GetFirst(block_entry);
+ block_entry_eos = false;
+ get_new_block = true;
+ } else if (block == NULL ||
+ webm_ctx->block_frame_index == block->GetFrameCount() ||
+ block->GetTrackNumber() != webm_ctx->video_track_index) {
+ status = cluster->GetNext(block_entry, block_entry);
+ if (block_entry == NULL || block_entry->EOS()) {
+ block_entry_eos = true;
+ continue;
+ }
+ get_new_block = true;
+ }
+ if (status || block_entry == NULL) {
+ return -1;
+ }
+ if (get_new_block) {
+ block = block_entry->GetBlock();
+ if (block == NULL) return -1;
+ webm_ctx->block_frame_index = 0;
+ }
+ } while (block_entry_eos ||
+ block->GetTrackNumber() != webm_ctx->video_track_index);
+
+ webm_ctx->cluster = cluster;
+ webm_ctx->block_entry = block_entry;
+ webm_ctx->block = block;
+
+ const mkvparser::Block::Frame &frame =
+ block->GetFrame(webm_ctx->block_frame_index);
+ ++webm_ctx->block_frame_index;
+ if (frame.len > static_cast<long>(*buffer_size)) {
+ delete[] * buffer;
+ *buffer = new uint8_t[frame.len];
+ webm_ctx->buffer = *buffer;
+ if (*buffer == NULL) {
+ return -1;
+ }
+ *buffer_size = frame.len;
+ }
+ *bytes_read = frame.len;
+ webm_ctx->timestamp_ns = block->GetTime(cluster);
+ webm_ctx->is_key_frame = block->IsKey();
+
+ mkvparser::MkvReader *const reader =
+ reinterpret_cast<mkvparser::MkvReader *>(webm_ctx->reader);
+ return frame.Read(reader, *buffer) ? -1 : 0;
+}
+
+// Calculate the greatest common divisor between two numbers.
+static int gcd(int a, int b) {
+ int remainder;
+ while (b > 0) {
+ remainder = a % b;
+ a = b;
+ b = remainder;
+ }
+ return a;
+}
+
+int webm_guess_framerate(struct WebmInputContext *webm_ctx,
+ struct AvxInputContext *aom_ctx) {
+ uint32_t i = 0;
+ uint8_t *buffer = NULL;
+ size_t buffer_size = 0;
+ size_t bytes_read = 0;
+ assert(webm_ctx->buffer == NULL);
+ while (webm_ctx->timestamp_ns < 1000000000 && i < 50) {
+ if (webm_read_frame(webm_ctx, &buffer, &bytes_read, &buffer_size)) {
+ break;
+ }
+ ++i;
+ }
+ aom_ctx->framerate.numerator = (i - 1) * 1000000;
+ aom_ctx->framerate.denominator =
+ static_cast<int>(webm_ctx->timestamp_ns / 1000);
+ // Fraction might be represented in large numbers, like 49000000/980000
+ // for 50fps. Simplify as much as possible.
+ int g = gcd(aom_ctx->framerate.numerator, aom_ctx->framerate.denominator);
+ if (g != 0) {
+ aom_ctx->framerate.numerator /= g;
+ aom_ctx->framerate.denominator /= g;
+ }
+
+ delete[] buffer;
+ webm_ctx->buffer = NULL;
+
+ get_first_cluster(webm_ctx);
+ webm_ctx->block = NULL;
+ webm_ctx->block_entry = NULL;
+ webm_ctx->block_frame_index = 0;
+ webm_ctx->timestamp_ns = 0;
+ webm_ctx->reached_eos = 0;
+
+ return 0;
+}
+
+void webm_free(struct WebmInputContext *webm_ctx) { reset(webm_ctx); }
diff --git a/third_party/aom/common/webmdec.h b/third_party/aom/common/webmdec.h
new file mode 100644
index 0000000000..fcbdeffe4d
--- /dev/null
+++ b/third_party/aom/common/webmdec.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#ifndef AOM_COMMON_WEBMDEC_H_
+#define AOM_COMMON_WEBMDEC_H_
+
+#include "common/tools_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AvxInputContext;
+
+struct WebmInputContext {
+ void *reader;
+ void *segment;
+ uint8_t *buffer;
+ const void *cluster;
+ const void *block_entry;
+ const void *block;
+ int block_frame_index;
+ int video_track_index;
+ int64_t timestamp_ns;
+ int is_key_frame;
+ int reached_eos;
+};
+
+// Checks if the input is a WebM file. If so, initializes WebMInputContext so
+// that webm_read_frame can be called to retrieve a video frame.
+// Returns 1 on success and 0 on failure or input is not WebM file.
+// TODO(vigneshv): Refactor this function into two smaller functions specific
+// to their task.
+int file_is_webm(struct WebmInputContext *webm_ctx,
+ struct AvxInputContext *aom_ctx);
+
+// Reads a WebM Video Frame. Memory for the buffer is created, owned and managed
+// by this function. For the first call, |buffer| should be NULL and
+// |*buffer_size| should be 0. Once all the frames are read and used,
+// webm_free() should be called, otherwise there will be a leak.
+// Parameters:
+// webm_ctx - WebmInputContext object
+// buffer - pointer where the frame data will be filled.
+// bytes_read - pointer to bytes read.
+// buffer_size - pointer to buffer size.
+// Return values:
+// 0 - Success
+// 1 - End of Stream
+// -1 - Error
+int webm_read_frame(struct WebmInputContext *webm_ctx, uint8_t **buffer,
+ size_t *bytes_read, size_t *buffer_size);
+
+// Guesses the frame rate of the input file based on the container timestamps.
+int webm_guess_framerate(struct WebmInputContext *webm_ctx,
+ struct AvxInputContext *aom_ctx);
+
+// Resets the WebMInputContext.
+void webm_free(struct WebmInputContext *webm_ctx);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // AOM_COMMON_WEBMDEC_H_
diff --git a/third_party/aom/common/webmenc.cc b/third_party/aom/common/webmenc.cc
new file mode 100644
index 0000000000..bb754e8119
--- /dev/null
+++ b/third_party/aom/common/webmenc.cc
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include "common/webmenc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <memory>
+#include <new>
+#include <string>
+
+#include "common/av1_config.h"
+#include "third_party/libwebm/mkvmuxer/mkvmuxer.h"
+#include "third_party/libwebm/mkvmuxer/mkvmuxerutil.h"
+#include "third_party/libwebm/mkvmuxer/mkvwriter.h"
+
+namespace {
+const uint64_t kDebugTrackUid = 0xDEADBEEF;
+const int kVideoTrackNumber = 1;
+
+// Simplistic mechanism to detect if an argv parameter refers to
+// an input or output file. Returns the total number of arguments that
+// should be skipped.
+int skip_input_output_arg(const char *arg, const char *input_fname) {
+ if (strcmp(arg, input_fname) == 0) {
+ return 1;
+ }
+ if (strcmp(arg, "-o") == 0 || strcmp(arg, "--output") == 0) {
+ return 2;
+ }
+ if (strncmp(arg, "--output=", strlen("--output=")) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+} // namespace
+
+char *extract_encoder_settings(const char *version, const char **argv, int argc,
+ const char *input_fname) {
+ // + 9 for "version:" prefix and for null terminator.
+ size_t total_size = strlen(version) + 9;
+ int i = 1;
+ while (i < argc) {
+ int num_skip = skip_input_output_arg(argv[i], input_fname);
+ i += num_skip;
+ if (num_skip == 0) {
+ total_size += strlen(argv[i]) + 1; // + 1 is for space separator.
+ ++i;
+ }
+ }
+ char *result = static_cast<char *>(malloc(total_size));
+ if (result == nullptr) {
+ return nullptr;
+ }
+ char *cur = result;
+ cur += snprintf(cur, total_size, "version:%s", version);
+ i = 1;
+ while (i < argc) {
+ int num_skip = skip_input_output_arg(argv[i], input_fname);
+ i += num_skip;
+ if (num_skip == 0) {
+ cur += snprintf(cur, total_size, " %s", argv[i]);
+ ++i;
+ }
+ }
+ *cur = '\0';
+ return result;
+}
+
+int write_webm_file_header(struct WebmOutputContext *webm_ctx,
+ aom_codec_ctx_t *encoder_ctx,
+ const aom_codec_enc_cfg_t *cfg,
+ stereo_format_t stereo_fmt, unsigned int fourcc,
+ const struct AvxRational *par,
+ const char *encoder_settings) {
+ std::unique_ptr<mkvmuxer::MkvWriter> writer(
+ new (std::nothrow) mkvmuxer::MkvWriter(webm_ctx->stream));
+ std::unique_ptr<mkvmuxer::Segment> segment(new (std::nothrow)
+ mkvmuxer::Segment());
+ if (writer == nullptr || segment == nullptr) {
+ fprintf(stderr, "webmenc> mkvmuxer objects alloc failed, out of memory?\n");
+ return -1;
+ }
+
+ bool ok = segment->Init(writer.get());
+ if (!ok) {
+ fprintf(stderr, "webmenc> mkvmuxer Init failed.\n");
+ return -1;
+ }
+
+ segment->set_mode(mkvmuxer::Segment::kFile);
+ segment->OutputCues(true);
+
+ mkvmuxer::SegmentInfo *const info = segment->GetSegmentInfo();
+ if (!info) {
+ fprintf(stderr, "webmenc> Cannot retrieve Segment Info.\n");
+ return -1;
+ }
+
+ const uint64_t kTimecodeScale = 1000000;
+ info->set_timecode_scale(kTimecodeScale);
+ std::string version = "aomenc";
+ if (!webm_ctx->debug) {
+ version.append(std::string(" ") + aom_codec_version_str());
+ }
+ info->set_writing_app(version.c_str());
+
+ const uint64_t video_track_id =
+ segment->AddVideoTrack(static_cast<int>(cfg->g_w),
+ static_cast<int>(cfg->g_h), kVideoTrackNumber);
+ mkvmuxer::VideoTrack *const video_track = static_cast<mkvmuxer::VideoTrack *>(
+ segment->GetTrackByNumber(video_track_id));
+
+ if (!video_track) {
+ fprintf(stderr, "webmenc> Video track creation failed.\n");
+ return -1;
+ }
+
+ ok = false;
+ aom_fixed_buf_t *obu_sequence_header =
+ aom_codec_get_global_headers(encoder_ctx);
+ if (obu_sequence_header) {
+ Av1Config av1_config;
+ if (get_av1config_from_obu(
+ reinterpret_cast<const uint8_t *>(obu_sequence_header->buf),
+ obu_sequence_header->sz, false, &av1_config) == 0) {
+ uint8_t av1_config_buffer[4] = { 0 };
+ size_t bytes_written = 0;
+ if (write_av1config(&av1_config, sizeof(av1_config_buffer),
+ &bytes_written, av1_config_buffer) == 0) {
+ ok = video_track->SetCodecPrivate(av1_config_buffer,
+ sizeof(av1_config_buffer));
+ }
+ }
+ free(obu_sequence_header->buf);
+ free(obu_sequence_header);
+ }
+ if (!ok) {
+ fprintf(stderr, "webmenc> Unable to set AV1 config.\n");
+ return -1;
+ }
+
+ ok = video_track->SetStereoMode(stereo_fmt);
+ if (!ok) {
+ fprintf(stderr, "webmenc> Unable to set stereo mode.\n");
+ return -1;
+ }
+
+ if (fourcc != AV1_FOURCC) {
+ fprintf(stderr, "webmenc> Unsupported codec (unknown 4 CC).\n");
+ return -1;
+ }
+ video_track->set_codec_id("V_AV1");
+
+ if (par->numerator > 1 || par->denominator > 1) {
+ // TODO(fgalligan): Add support of DisplayUnit, Display Aspect Ratio type
+ // to WebM format.
+ const uint64_t display_width = static_cast<uint64_t>(
+ ((cfg->g_w * par->numerator * 1.0) / par->denominator) + .5);
+ video_track->set_display_width(display_width);
+ video_track->set_display_height(cfg->g_h);
+ }
+
+ if (encoder_settings != nullptr) {
+ mkvmuxer::Tag *tag = segment->AddTag();
+ if (tag == nullptr) {
+ fprintf(stderr,
+ "webmenc> Unable to allocate memory for encoder settings tag.\n");
+ return -1;
+ }
+ ok = tag->add_simple_tag("ENCODER_SETTINGS", encoder_settings);
+ if (!ok) {
+ fprintf(stderr,
+ "webmenc> Unable to allocate memory for encoder settings tag.\n");
+ return -1;
+ }
+ }
+
+ if (webm_ctx->debug) {
+ video_track->set_uid(kDebugTrackUid);
+ }
+
+ webm_ctx->writer = writer.release();
+ webm_ctx->segment = segment.release();
+ return 0;
+}
+
+int write_webm_block(struct WebmOutputContext *webm_ctx,
+ const aom_codec_enc_cfg_t *cfg,
+ const aom_codec_cx_pkt_t *pkt) {
+ if (!webm_ctx->segment) {
+ fprintf(stderr, "webmenc> segment is NULL.\n");
+ return -1;
+ }
+ mkvmuxer::Segment *const segment =
+ reinterpret_cast<mkvmuxer::Segment *>(webm_ctx->segment);
+ int64_t pts_ns = pkt->data.frame.pts * 1000000000ll * cfg->g_timebase.num /
+ cfg->g_timebase.den;
+ if (pts_ns <= webm_ctx->last_pts_ns) pts_ns = webm_ctx->last_pts_ns + 1000000;
+ webm_ctx->last_pts_ns = pts_ns;
+
+ if (!segment->AddFrame(static_cast<uint8_t *>(pkt->data.frame.buf),
+ pkt->data.frame.sz, kVideoTrackNumber, pts_ns,
+ pkt->data.frame.flags & AOM_FRAME_IS_KEY)) {
+ fprintf(stderr, "webmenc> AddFrame failed.\n");
+ return -1;
+ }
+ return 0;
+}
+
+int write_webm_file_footer(struct WebmOutputContext *webm_ctx) {
+ if (!webm_ctx->writer || !webm_ctx->segment) {
+ fprintf(stderr, "webmenc> segment or writer NULL.\n");
+ return -1;
+ }
+ mkvmuxer::MkvWriter *const writer =
+ reinterpret_cast<mkvmuxer::MkvWriter *>(webm_ctx->writer);
+ mkvmuxer::Segment *const segment =
+ reinterpret_cast<mkvmuxer::Segment *>(webm_ctx->segment);
+ const bool ok = segment->Finalize();
+ delete segment;
+ delete writer;
+ webm_ctx->writer = NULL;
+ webm_ctx->segment = NULL;
+
+ if (!ok) {
+ fprintf(stderr, "webmenc> Segment::Finalize failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/third_party/aom/common/webmenc.h b/third_party/aom/common/webmenc.h
new file mode 100644
index 0000000000..c912208b45
--- /dev/null
+++ b/third_party/aom/common/webmenc.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#ifndef AOM_COMMON_WEBMENC_H_
+#define AOM_COMMON_WEBMENC_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tools_common.h"
+#include "aom/aom_encoder.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct WebmOutputContext {
+ int debug;
+ FILE *stream;
+ int64_t last_pts_ns;
+ void *writer;
+ void *segment;
+};
+
+/* Stereo 3D packed frame format */
+enum {
+ STEREO_FORMAT_MONO = 0,
+ STEREO_FORMAT_LEFT_RIGHT = 1,
+ STEREO_FORMAT_BOTTOM_TOP = 2,
+ STEREO_FORMAT_TOP_BOTTOM = 3,
+ STEREO_FORMAT_RIGHT_LEFT = 11
+} UENUM1BYTE(stereo_format_t);
+
+// Simplistic mechanism to extract encoder settings, without having
+// to re-invoke the entire flag-parsing logic. It lists the codec version
+// and then copies the arguments as-is from argv, but skips the binary name,
+// any arguments that match the input filename, and the output flags "-o"
+// and "--output" (and the following argument for those flags). The caller
+// is responsible for free-ing the returned string. If there is insufficient
+// memory, it returns nullptr.
+char *extract_encoder_settings(const char *version, const char **argv, int argc,
+ const char *input_fname);
+
+// The following functions wrap libwebm's mkvmuxer. All functions return 0 upon
+// success, or -1 upon failure.
+
+int write_webm_file_header(struct WebmOutputContext *webm_ctx,
+ aom_codec_ctx_t *encoder_ctx,
+ const aom_codec_enc_cfg_t *cfg,
+ stereo_format_t stereo_fmt, unsigned int fourcc,
+ const struct AvxRational *par,
+ const char *encoder_settings);
+
+int write_webm_block(struct WebmOutputContext *webm_ctx,
+ const aom_codec_enc_cfg_t *cfg,
+ const aom_codec_cx_pkt_t *pkt);
+
+int write_webm_file_footer(struct WebmOutputContext *webm_ctx);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // AOM_COMMON_WEBMENC_H_
diff --git a/third_party/aom/common/y4menc.c b/third_party/aom/common/y4menc.c
new file mode 100644
index 0000000000..25086a91d0
--- /dev/null
+++ b/third_party/aom/common/y4menc.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include <assert.h>
+
+#include "common/rawenc.h"
+#include "common/y4menc.h"
+
+// Returns the Y4M name associated with the monochrome colorspace.
+static const char *monochrome_colorspace(unsigned int bit_depth) {
+ switch (bit_depth) {
+ case 8: return "Cmono";
+ case 9: return "Cmono9";
+ case 10: return "Cmono10";
+ case 12: return "Cmono12";
+ case 16: return "Cmono16";
+ default: assert(0); return NULL;
+ }
+}
+
+// Return the Y4M name of the 8-bit colorspace, given the chroma position and
+// image format.
+static const char *colorspace8(aom_chroma_sample_position_t csp,
+ aom_img_fmt_t fmt) {
+ switch (fmt) {
+ case AOM_IMG_FMT_I444: return "C444";
+ case AOM_IMG_FMT_I422: return "C422";
+ default:
+ if (csp == AOM_CSP_VERTICAL) {
+ return "C420mpeg2 XYSCSS=420MPEG2";
+ } else if (csp == AOM_CSP_COLOCATED) {
+ // Note that Y4M does not have a dedicated header for colocated chroma,
+ // and that FFMPEG interprets C420 as C420jpeg.
+ return "C420";
+ } else {
+ return "C420jpeg";
+ }
+ }
+}
+
+// Return the Y4M name of the colorspace, given the bit depth and image format.
+static const char *colorspace(unsigned int bit_depth,
+ aom_chroma_sample_position_t csp,
+ aom_img_fmt_t fmt) {
+ switch (bit_depth) {
+ case 8: return colorspace8(csp, fmt);
+ case 9:
+ return fmt == AOM_IMG_FMT_I44416 ? "C444p9 XYSCSS=444P9"
+ : fmt == AOM_IMG_FMT_I42216 ? "C422p9 XYSCSS=422P9"
+ : "C420p9 XYSCSS=420P9";
+ case 10:
+ return fmt == AOM_IMG_FMT_I44416 ? "C444p10 XYSCSS=444P10"
+ : fmt == AOM_IMG_FMT_I42216 ? "C422p10 XYSCSS=422P10"
+ : "C420p10 XYSCSS=420P10";
+ case 12:
+ return fmt == AOM_IMG_FMT_I44416 ? "C444p12 XYSCSS=444P12"
+ : fmt == AOM_IMG_FMT_I42216 ? "C422p12 XYSCSS=422P12"
+ : "C420p12 XYSCSS=420P12";
+ case 14:
+ return fmt == AOM_IMG_FMT_I44416 ? "C444p14 XYSCSS=444P14"
+ : fmt == AOM_IMG_FMT_I42216 ? "C422p14 XYSCSS=422P14"
+ : "C420p14 XYSCSS=420P14";
+ case 16:
+ return fmt == AOM_IMG_FMT_I44416 ? "C444p16 XYSCSS=444P16"
+ : fmt == AOM_IMG_FMT_I42216 ? "C422p16 XYSCSS=422P16"
+ : "C420p16 XYSCSS=420P16";
+ default: assert(0); return NULL;
+ }
+}
+
+int y4m_write_file_header(char *buf, size_t len, int width, int height,
+ const struct AvxRational *framerate, int monochrome,
+ aom_chroma_sample_position_t csp, aom_img_fmt_t fmt,
+ unsigned int bit_depth, aom_color_range_t range) {
+ const char *color = monochrome ? monochrome_colorspace(bit_depth)
+ : colorspace(bit_depth, csp, fmt);
+ const char *color_range = ""; // Default assumption is studio range.
+ if (range == AOM_CR_FULL_RANGE) {
+ color_range = " XCOLORRANGE=FULL";
+ }
+ return snprintf(buf, len, "YUV4MPEG2 W%d H%d F%d:%d Ip %s%s\n", width, height,
+ framerate->numerator, framerate->denominator, color,
+ color_range);
+}
+
+int y4m_write_frame_header(char *buf, size_t len) {
+ return snprintf(buf, len, "FRAME\n");
+}
+
+void y4m_write_image_file(const aom_image_t *img, const int *planes,
+ FILE *file) {
+ int num_planes = img->monochrome ? 1 : 3;
+ raw_write_image_file(img, planes, num_planes, file);
+}
+
+void y4m_update_image_md5(const aom_image_t *img, const int *planes,
+ MD5Context *md5) {
+ int num_planes = img->monochrome ? 1 : 3;
+ raw_update_image_md5(img, planes, num_planes, md5);
+}
diff --git a/third_party/aom/common/y4menc.h b/third_party/aom/common/y4menc.h
new file mode 100644
index 0000000000..6484efcc50
--- /dev/null
+++ b/third_party/aom/common/y4menc.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AOM_COMMON_Y4MENC_H_
+#define AOM_COMMON_Y4MENC_H_
+
+#include "aom/aom_decoder.h"
+#include "common/md5_utils.h"
+#include "common/tools_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define Y4M_BUFFER_SIZE 256
+
+int y4m_write_file_header(char *buf, size_t len, int width, int height,
+ const struct AvxRational *framerate, int monochrome,
+ aom_chroma_sample_position_t csp, aom_img_fmt_t fmt,
+ unsigned int bit_depth, aom_color_range_t range);
+int y4m_write_frame_header(char *buf, size_t len);
+void y4m_write_image_file(const aom_image_t *img, const int *planes,
+ FILE *file);
+void y4m_update_image_md5(const aom_image_t *img, const int *planes,
+ MD5Context *md5);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // AOM_COMMON_Y4MENC_H_
diff --git a/third_party/aom/common/y4minput.c b/third_party/aom/common/y4minput.c
new file mode 100644
index 0000000000..1974d76f1f
--- /dev/null
+++ b/third_party/aom/common/y4minput.c
@@ -0,0 +1,1222 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ *
+ * Based on code from the OggTheora software codec source code,
+ * Copyright (C) 2002-2010 The Xiph.Org Foundation and contributors.
+ */
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "aom/aom_integer.h"
+#include "aom_ports/msvc.h"
+#include "y4minput.h"
+
+// Reads 'size' bytes from 'file' into 'buf' with some fault tolerance.
+// Returns true on success.
+static int file_read(void *buf, size_t size, FILE *file) {
+ const int kMaxTries = 5;
+ int try_count = 0;
+ int file_error = 0;
+ size_t len = 0;
+ while (!feof(file) && len < size && try_count < kMaxTries) {
+ const size_t n = fread((uint8_t *)buf + len, 1, size - len, file);
+ ++try_count;
+ len += n;
+ file_error = ferror(file);
+ if (file_error) {
+ if (errno == EINTR || errno == EAGAIN) {
+ clearerr(file);
+ continue;
+ } else {
+ fprintf(stderr, "Error reading file: %u of %u bytes read, %d: %s\n",
+ (uint32_t)len, (uint32_t)size, errno, strerror(errno));
+ return 0;
+ }
+ }
+ }
+
+ if (!feof(file) && len != size) {
+ fprintf(stderr,
+ "Error reading file: %u of %u bytes read,"
+ " error: %d, tries: %d, %d: %s\n",
+ (uint32_t)len, (uint32_t)size, file_error, try_count, errno,
+ strerror(errno));
+ }
+ return len == size;
+}
+
+// Stores the color range in 'y4m_ctx', returning 1 if successfully parsed,
+// 0 otherwise.
+static int parse_color_range(y4m_input *y4m_ctx, const char *buf) {
+ // Note that default is studio range.
+ if (strcmp(buf, "LIMITED") == 0) {
+ return 1;
+ }
+ if (strcmp(buf, "FULL") == 0) {
+ y4m_ctx->color_range = AOM_CR_FULL_RANGE;
+ return 1;
+ }
+ fprintf(stderr, "Unknown color range value: %s\n", buf);
+ return 0;
+}
+
+static int parse_metadata(y4m_input *y4m_ctx, const char *buf) {
+ if (strncmp(buf, "COLORRANGE=", 11) == 0) {
+ return parse_color_range(y4m_ctx, buf + 11);
+ }
+ return 1; // No support for other metadata, just ignore them.
+}
+
+static int y4m_parse_tags(y4m_input *_y4m, char *_tags) {
+ char *p;
+ char *q;
+ for (p = _tags;; p = q) {
+ /*Skip any leading spaces.*/
+ while (*p == ' ') p++;
+ /*If that's all we have, stop.*/
+ if (p[0] == '\0') break;
+ /*Find the end of this tag.*/
+ for (q = p + 1; *q != '\0' && *q != ' '; q++) {
+ }
+ /*Process the tag.*/
+ switch (p[0]) {
+ case 'W': {
+ if (sscanf(p + 1, "%d", &_y4m->pic_w) != 1) return -1;
+ } break;
+ case 'H': {
+ if (sscanf(p + 1, "%d", &_y4m->pic_h) != 1) return -1;
+ } break;
+ case 'F': {
+ if (sscanf(p + 1, "%d:%d", &_y4m->fps_n, &_y4m->fps_d) != 2) {
+ return -1;
+ }
+ } break;
+ case 'I': {
+ _y4m->interlace = p[1];
+ } break;
+ case 'A': {
+ if (sscanf(p + 1, "%d:%d", &_y4m->par_n, &_y4m->par_d) != 2) {
+ return -1;
+ }
+ } break;
+ case 'C': {
+ if (q - p > 16) return -1;
+ memcpy(_y4m->chroma_type, p + 1, q - p - 1);
+ _y4m->chroma_type[q - p - 1] = '\0';
+ } break;
+ case 'X': {
+ if (!parse_metadata(_y4m, p + 1)) return -1;
+ } break;
+ default: break; /*Ignore unknown tags.*/
+ }
+ }
+ return 0;
+}
+
+// Copy a single tag into the buffer, along with a null character.
+// Returns 0 if any file IO errors occur.
+static int copy_tag(char *buf, size_t buf_len, char *end_tag, FILE *file) {
+ size_t i;
+ assert(buf_len >= 1);
+ // Skip leading space characters.
+ do {
+ if (!file_read(buf, 1, file)) {
+ return 0;
+ }
+ } while (buf[0] == ' ');
+
+ // If we hit the newline, treat this as the "empty" tag.
+ if (buf[0] == '\n') {
+ buf[0] = '\0';
+ *end_tag = '\n';
+ return 1;
+ }
+
+ // Copy over characters until a space is hit, or the buffer is exhausted.
+ for (i = 1; i < buf_len; ++i) {
+ if (!file_read(buf + i, 1, file)) {
+ return 0;
+ }
+ if (buf[i] == ' ' || buf[i] == '\n') {
+ break;
+ }
+ }
+ if (i == buf_len) {
+ fprintf(stderr, "Error: Y4M header tags must be less than %lu characters\n",
+ (unsigned long)i);
+ return 0;
+ }
+ *end_tag = buf[i];
+ buf[i] = '\0';
+ return 1;
+}
+
+// Returns 1 if tags were parsed successfully, 0 otherwise.
+static int parse_tags(y4m_input *y4m_ctx, FILE *file) {
+ char tag[256];
+ char end; // Character denoting the end of the tag, ' ' or '\n'.
+ // 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.
+ 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 = '?';
+ y4m_ctx->color_range = AOM_CR_STUDIO_RANGE;
+ snprintf(y4m_ctx->chroma_type, sizeof(y4m_ctx->chroma_type), "420");
+
+ // Find one tag at a time.
+ do {
+ if (!copy_tag(tag, sizeof(tag), &end, file)) {
+ return 0;
+ }
+ // y4m_parse_tags returns 0 on success.
+ if (y4m_parse_tags(y4m_ctx, tag)) {
+ return 0;
+ }
+ } while (end != '\n');
+
+ // 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):
+ sinc(\pi*t)*sinc(\pi*t/3), |t|<3 (sinc(t)==sin(t)/t)
+ 0, |t|>=3
+ The 4-tap Mitchell window (for up-sampling):
+ 7|t|^3-12|t|^2+16/3, |t|<1
+ -(7/3)|x|^3+12|x|^2-20|x|+32/3, |t|<2
+ 0, |t|>=2
+ The number of taps is intentionally kept small to reduce computational
+ overhead and limit ringing.
+
+ The taps from these filters are scaled so that their sum is 1, and the
+ result is scaled by 128 and rounded to integers to create a filter whose
+ intermediate values fit inside 16 bits.
+ Coefficients are rounded in such a way as to ensure their sum is still 128,
+ which is usually equivalent to normal rounding.
+
+ Conversions which require both horizontal and vertical filtering could
+ have these steps pipelined, for less memory consumption and better cache
+ performance, but we do them separately for simplicity.*/
+#define OC_MINI(_a, _b) ((_a) > (_b) ? (_b) : (_a))
+#define OC_MAXI(_a, _b) ((_a) < (_b) ? (_b) : (_a))
+#define OC_CLAMPI(_a, _b, _c) (OC_MAXI(_a, OC_MINI(_b, _c)))
+
+/*420jpeg chroma samples are sited like:
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ 420mpeg2 chroma samples are sited like:
+ Y-------Y-------Y-------Y-------
+ | | | |
+ BR | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ BR | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ We use a resampling filter to shift the site locations one quarter pixel (at
+ the chroma plane's resolution) to the right.
+ The 4:2:2 modes look exactly the same, except there are twice as many chroma
+ lines, and they are vertically co-sited with the luma samples in both the
+ mpeg2 and jpeg cases (thus requiring no vertical resampling).*/
+static void y4m_42xmpeg2_42xjpeg_helper(unsigned char *_dst,
+ const unsigned char *_src, int _c_w,
+ int _c_h) {
+ int y;
+ int x;
+ for (y = 0; y < _c_h; y++) {
+ /*Filter: [4 -17 114 35 -9 1]/128, derived from a 6-tap Lanczos
+ window.*/
+ for (x = 0; x < OC_MINI(_c_w, 2); x++) {
+ _dst[x] = (unsigned char)OC_CLAMPI(
+ 0,
+ (4 * _src[0] - 17 * _src[OC_MAXI(x - 1, 0)] + 114 * _src[x] +
+ 35 * _src[OC_MINI(x + 1, _c_w - 1)] -
+ 9 * _src[OC_MINI(x + 2, _c_w - 1)] + _src[OC_MINI(x + 3, _c_w - 1)] +
+ 64) >>
+ 7,
+ 255);
+ }
+ for (; x < _c_w - 3; x++) {
+ _dst[x] = (unsigned char)OC_CLAMPI(
+ 0,
+ (4 * _src[x - 2] - 17 * _src[x - 1] + 114 * _src[x] +
+ 35 * _src[x + 1] - 9 * _src[x + 2] + _src[x + 3] + 64) >>
+ 7,
+ 255);
+ }
+ for (; x < _c_w; x++) {
+ _dst[x] = (unsigned char)OC_CLAMPI(
+ 0,
+ (4 * _src[x - 2] - 17 * _src[x - 1] + 114 * _src[x] +
+ 35 * _src[OC_MINI(x + 1, _c_w - 1)] -
+ 9 * _src[OC_MINI(x + 2, _c_w - 1)] + _src[_c_w - 1] + 64) >>
+ 7,
+ 255);
+ }
+ _dst += _c_w;
+ _src += _c_w;
+ }
+}
+
+/*This format is only used for interlaced content, but is included for
+ completeness.
+
+ 420jpeg chroma samples are sited like:
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ 420paldv chroma samples are sited like:
+ YR------Y-------YR------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YB------Y-------YB------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YR------Y-------YR------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YB------Y-------YB------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ We use a resampling filter to shift the site locations one quarter pixel (at
+ the chroma plane's resolution) to the right.
+ Then we use another filter to move the C_r location down one quarter pixel,
+ and the C_b location up one quarter pixel.*/
+static void y4m_convert_42xpaldv_42xjpeg(y4m_input *_y4m, unsigned char *_dst,
+ unsigned char *_aux) {
+ unsigned char *tmp;
+ int c_w;
+ int c_h;
+ int c_sz;
+ int pli;
+ int y;
+ int x;
+ /*Skip past the luma data.*/
+ _dst += _y4m->pic_w * _y4m->pic_h;
+ /*Compute the size of each chroma plane.*/
+ c_w = (_y4m->pic_w + 1) / 2;
+ c_h = (_y4m->pic_h + _y4m->dst_c_dec_h - 1) / _y4m->dst_c_dec_h;
+ c_sz = c_w * c_h;
+ tmp = _aux + 2 * c_sz;
+ for (pli = 1; pli < 3; pli++) {
+ /*First do the horizontal re-sampling.
+ This is the same as the mpeg2 case, except that after the horizontal
+ case, we need to apply a second vertical filter.*/
+ y4m_42xmpeg2_42xjpeg_helper(tmp, _aux, c_w, c_h);
+ _aux += c_sz;
+ switch (pli) {
+ case 1: {
+ /*Slide C_b up a quarter-pel.
+ This is the same filter used above, but in the other order.*/
+ for (x = 0; x < c_w; x++) {
+ for (y = 0; y < OC_MINI(c_h, 3); y++) {
+ _dst[y * c_w] = (unsigned char)OC_CLAMPI(
+ 0,
+ (tmp[0] - 9 * tmp[OC_MAXI(y - 2, 0) * c_w] +
+ 35 * tmp[OC_MAXI(y - 1, 0) * c_w] + 114 * tmp[y * c_w] -
+ 17 * tmp[OC_MINI(y + 1, c_h - 1) * c_w] +
+ 4 * tmp[OC_MINI(y + 2, c_h - 1) * c_w] + 64) >>
+ 7,
+ 255);
+ }
+ for (; y < c_h - 2; y++) {
+ _dst[y * c_w] = (unsigned char)OC_CLAMPI(
+ 0,
+ (tmp[(y - 3) * c_w] - 9 * tmp[(y - 2) * c_w] +
+ 35 * tmp[(y - 1) * c_w] + 114 * tmp[y * c_w] -
+ 17 * tmp[(y + 1) * c_w] + 4 * tmp[(y + 2) * c_w] + 64) >>
+ 7,
+ 255);
+ }
+ for (; y < c_h; y++) {
+ _dst[y * c_w] = (unsigned char)OC_CLAMPI(
+ 0,
+ (tmp[(y - 3) * c_w] - 9 * tmp[(y - 2) * c_w] +
+ 35 * tmp[(y - 1) * c_w] + 114 * tmp[y * c_w] -
+ 17 * tmp[OC_MINI(y + 1, c_h - 1) * c_w] +
+ 4 * tmp[(c_h - 1) * c_w] + 64) >>
+ 7,
+ 255);
+ }
+ _dst++;
+ tmp++;
+ }
+ _dst += c_sz - c_w;
+ tmp -= c_w;
+ } break;
+ case 2: {
+ /*Slide C_r down a quarter-pel.
+ This is the same as the horizontal filter.*/
+ for (x = 0; x < c_w; x++) {
+ for (y = 0; y < OC_MINI(c_h, 2); y++) {
+ _dst[y * c_w] = (unsigned char)OC_CLAMPI(
+ 0,
+ (4 * tmp[0] - 17 * tmp[OC_MAXI(y - 1, 0) * c_w] +
+ 114 * tmp[y * c_w] + 35 * tmp[OC_MINI(y + 1, c_h - 1) * c_w] -
+ 9 * tmp[OC_MINI(y + 2, c_h - 1) * c_w] +
+ tmp[OC_MINI(y + 3, c_h - 1) * c_w] + 64) >>
+ 7,
+ 255);
+ }
+ for (; y < c_h - 3; y++) {
+ _dst[y * c_w] = (unsigned char)OC_CLAMPI(
+ 0,
+ (4 * tmp[(y - 2) * c_w] - 17 * tmp[(y - 1) * c_w] +
+ 114 * tmp[y * c_w] + 35 * tmp[(y + 1) * c_w] -
+ 9 * tmp[(y + 2) * c_w] + tmp[(y + 3) * c_w] + 64) >>
+ 7,
+ 255);
+ }
+ for (; y < c_h; y++) {
+ _dst[y * c_w] = (unsigned char)OC_CLAMPI(
+ 0,
+ (4 * tmp[(y - 2) * c_w] - 17 * tmp[(y - 1) * c_w] +
+ 114 * tmp[y * c_w] + 35 * tmp[OC_MINI(y + 1, c_h - 1) * c_w] -
+ 9 * tmp[OC_MINI(y + 2, c_h - 1) * c_w] + tmp[(c_h - 1) * c_w] +
+ 64) >>
+ 7,
+ 255);
+ }
+ _dst++;
+ tmp++;
+ }
+ } break;
+ }
+ /*For actual interlaced material, this would have to be done separately on
+ each field, and the shift amounts would be different.
+ C_r moves down 1/8, C_b up 3/8 in the top field, and C_r moves down 3/8,
+ C_b up 1/8 in the bottom field.
+ The corresponding filters would be:
+ Down 1/8 (reverse order for up): [3 -11 125 15 -4 0]/128
+ Down 3/8 (reverse order for up): [4 -19 98 56 -13 2]/128*/
+ }
+}
+
+/*Perform vertical filtering to reduce a single plane from 4:2:2 to 4:2:0.
+ This is used as a helper by several conversion routines.*/
+static void y4m_422jpeg_420jpeg_helper(unsigned char *_dst,
+ const unsigned char *_src, int _c_w,
+ int _c_h) {
+ int y;
+ int x;
+ /*Filter: [3 -17 78 78 -17 3]/128, derived from a 6-tap Lanczos window.*/
+ for (x = 0; x < _c_w; x++) {
+ for (y = 0; y < OC_MINI(_c_h, 2); y += 2) {
+ _dst[(y >> 1) * _c_w] =
+ OC_CLAMPI(0,
+ (64 * _src[0] + 78 * _src[OC_MINI(1, _c_h - 1) * _c_w] -
+ 17 * _src[OC_MINI(2, _c_h - 1) * _c_w] +
+ 3 * _src[OC_MINI(3, _c_h - 1) * _c_w] + 64) >>
+ 7,
+ 255);
+ }
+ for (; y < _c_h - 3; y += 2) {
+ _dst[(y >> 1) * _c_w] =
+ OC_CLAMPI(0,
+ (3 * (_src[(y - 2) * _c_w] + _src[(y + 3) * _c_w]) -
+ 17 * (_src[(y - 1) * _c_w] + _src[(y + 2) * _c_w]) +
+ 78 * (_src[y * _c_w] + _src[(y + 1) * _c_w]) + 64) >>
+ 7,
+ 255);
+ }
+ for (; y < _c_h; y += 2) {
+ _dst[(y >> 1) * _c_w] = OC_CLAMPI(
+ 0,
+ (3 * (_src[(y - 2) * _c_w] + _src[(_c_h - 1) * _c_w]) -
+ 17 * (_src[(y - 1) * _c_w] + _src[OC_MINI(y + 2, _c_h - 1) * _c_w]) +
+ 78 * (_src[y * _c_w] + _src[OC_MINI(y + 1, _c_h - 1) * _c_w]) +
+ 64) >>
+ 7,
+ 255);
+ }
+ _src++;
+ _dst++;
+ }
+}
+
+/*420jpeg chroma samples are sited like:
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ 422jpeg chroma samples are sited like:
+ Y---BR--Y-------Y---BR--Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y---BR--Y-------Y---BR--Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y---BR--Y-------Y---BR--Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y---BR--Y-------Y---BR--Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ We use a resampling filter to decimate the chroma planes by two in the
+ vertical direction.*/
+static void y4m_convert_422jpeg_420jpeg(y4m_input *_y4m, unsigned char *_dst,
+ unsigned char *_aux) {
+ int c_w;
+ int c_h;
+ int c_sz;
+ int dst_c_w;
+ int dst_c_h;
+ int dst_c_sz;
+ int pli;
+ /*Skip past the luma data.*/
+ _dst += _y4m->pic_w * _y4m->pic_h;
+ /*Compute the size of each chroma plane.*/
+ c_w = (_y4m->pic_w + _y4m->src_c_dec_h - 1) / _y4m->src_c_dec_h;
+ c_h = _y4m->pic_h;
+ dst_c_w = (_y4m->pic_w + _y4m->dst_c_dec_h - 1) / _y4m->dst_c_dec_h;
+ dst_c_h = (_y4m->pic_h + _y4m->dst_c_dec_v - 1) / _y4m->dst_c_dec_v;
+ c_sz = c_w * c_h;
+ dst_c_sz = dst_c_w * dst_c_h;
+ for (pli = 1; pli < 3; pli++) {
+ y4m_422jpeg_420jpeg_helper(_dst, _aux, c_w, c_h);
+ _aux += c_sz;
+ _dst += dst_c_sz;
+ }
+}
+
+/*420jpeg chroma samples are sited like:
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ 422 chroma samples are sited like:
+ YBR-----Y-------YBR-----Y-------
+ | | | |
+ | | | |
+ | | | |
+ YBR-----Y-------YBR-----Y-------
+ | | | |
+ | | | |
+ | | | |
+ YBR-----Y-------YBR-----Y-------
+ | | | |
+ | | | |
+ | | | |
+ YBR-----Y-------YBR-----Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ We use a resampling filter to shift the original site locations one quarter
+ pixel (at the original chroma resolution) to the right.
+ Then we use a second resampling filter to decimate the chroma planes by two
+ in the vertical direction.*/
+static void y4m_convert_422_420jpeg(y4m_input *_y4m, unsigned char *_dst,
+ unsigned char *_aux) {
+ unsigned char *tmp;
+ int c_w;
+ int c_h;
+ int c_sz;
+ int dst_c_h;
+ int dst_c_sz;
+ int pli;
+ /*Skip past the luma data.*/
+ _dst += _y4m->pic_w * _y4m->pic_h;
+ /*Compute the size of each chroma plane.*/
+ c_w = (_y4m->pic_w + _y4m->src_c_dec_h - 1) / _y4m->src_c_dec_h;
+ c_h = _y4m->pic_h;
+ dst_c_h = (_y4m->pic_h + _y4m->dst_c_dec_v - 1) / _y4m->dst_c_dec_v;
+ c_sz = c_w * c_h;
+ dst_c_sz = c_w * dst_c_h;
+ tmp = _aux + 2 * c_sz;
+ for (pli = 1; pli < 3; pli++) {
+ /*In reality, the horizontal and vertical steps could be pipelined, for
+ less memory consumption and better cache performance, but we do them
+ separately for simplicity.*/
+ /*First do horizontal filtering (convert to 422jpeg)*/
+ y4m_42xmpeg2_42xjpeg_helper(tmp, _aux, c_w, c_h);
+ /*Now do the vertical filtering.*/
+ y4m_422jpeg_420jpeg_helper(_dst, tmp, c_w, c_h);
+ _aux += c_sz;
+ _dst += dst_c_sz;
+ }
+}
+
+/*420jpeg chroma samples are sited like:
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | BR | | BR |
+ | | | |
+ Y-------Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ 411 chroma samples are sited like:
+ YBR-----Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YBR-----Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YBR-----Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+ YBR-----Y-------Y-------Y-------
+ | | | |
+ | | | |
+ | | | |
+
+ We use a filter to resample at site locations one eighth pixel (at the source
+ chroma plane's horizontal resolution) and five eighths of a pixel to the
+ right.
+ Then we use another filter to decimate the planes by 2 in the vertical
+ direction.*/
+static void y4m_convert_411_420jpeg(y4m_input *_y4m, unsigned char *_dst,
+ unsigned char *_aux) {
+ unsigned char *tmp;
+ int c_w;
+ int c_h;
+ int c_sz;
+ int dst_c_w;
+ int dst_c_h;
+ int dst_c_sz;
+ int tmp_sz;
+ int pli;
+ int y;
+ int x;
+ /*Skip past the luma data.*/
+ _dst += _y4m->pic_w * _y4m->pic_h;
+ /*Compute the size of each chroma plane.*/
+ c_w = (_y4m->pic_w + _y4m->src_c_dec_h - 1) / _y4m->src_c_dec_h;
+ c_h = _y4m->pic_h;
+ dst_c_w = (_y4m->pic_w + _y4m->dst_c_dec_h - 1) / _y4m->dst_c_dec_h;
+ dst_c_h = (_y4m->pic_h + _y4m->dst_c_dec_v - 1) / _y4m->dst_c_dec_v;
+ c_sz = c_w * c_h;
+ dst_c_sz = dst_c_w * dst_c_h;
+ tmp_sz = dst_c_w * c_h;
+ tmp = _aux + 2 * c_sz;
+ for (pli = 1; pli < 3; pli++) {
+ /*In reality, the horizontal and vertical steps could be pipelined, for
+ less memory consumption and better cache performance, but we do them
+ separately for simplicity.*/
+ /*First do horizontal filtering (convert to 422jpeg)*/
+ for (y = 0; y < c_h; y++) {
+ /*Filters: [1 110 18 -1]/128 and [-3 50 86 -5]/128, both derived from a
+ 4-tap Mitchell window.*/
+ for (x = 0; x < OC_MINI(c_w, 1); x++) {
+ tmp[x << 1] = (unsigned char)OC_CLAMPI(
+ 0,
+ (111 * _aux[0] + 18 * _aux[OC_MINI(1, c_w - 1)] -
+ _aux[OC_MINI(2, c_w - 1)] + 64) >>
+ 7,
+ 255);
+ tmp[x << 1 | 1] = (unsigned char)OC_CLAMPI(
+ 0,
+ (47 * _aux[0] + 86 * _aux[OC_MINI(1, c_w - 1)] -
+ 5 * _aux[OC_MINI(2, c_w - 1)] + 64) >>
+ 7,
+ 255);
+ }
+ for (; x < c_w - 2; x++) {
+ tmp[x << 1] =
+ (unsigned char)OC_CLAMPI(0,
+ (_aux[x - 1] + 110 * _aux[x] +
+ 18 * _aux[x + 1] - _aux[x + 2] + 64) >>
+ 7,
+ 255);
+ tmp[x << 1 | 1] = (unsigned char)OC_CLAMPI(
+ 0,
+ (-3 * _aux[x - 1] + 50 * _aux[x] + 86 * _aux[x + 1] -
+ 5 * _aux[x + 2] + 64) >>
+ 7,
+ 255);
+ }
+ for (; x < c_w; x++) {
+ tmp[x << 1] = (unsigned char)OC_CLAMPI(
+ 0,
+ (_aux[x - 1] + 110 * _aux[x] + 18 * _aux[OC_MINI(x + 1, c_w - 1)] -
+ _aux[c_w - 1] + 64) >>
+ 7,
+ 255);
+ if ((x << 1 | 1) < dst_c_w) {
+ tmp[x << 1 | 1] = (unsigned char)OC_CLAMPI(
+ 0,
+ (-3 * _aux[x - 1] + 50 * _aux[x] +
+ 86 * _aux[OC_MINI(x + 1, c_w - 1)] - 5 * _aux[c_w - 1] + 64) >>
+ 7,
+ 255);
+ }
+ }
+ tmp += dst_c_w;
+ _aux += c_w;
+ }
+ tmp -= tmp_sz;
+ /*Now do the vertical filtering.*/
+ y4m_422jpeg_420jpeg_helper(_dst, tmp, dst_c_w, c_h);
+ _dst += dst_c_sz;
+ }
+}
+
+/*Convert 444 to 420jpeg.*/
+static void y4m_convert_444_420jpeg(y4m_input *_y4m, unsigned char *_dst,
+ unsigned char *_aux) {
+ unsigned char *tmp;
+ int c_w;
+ int c_h;
+ int c_sz;
+ int dst_c_w;
+ int dst_c_h;
+ int dst_c_sz;
+ int tmp_sz;
+ int pli;
+ int y;
+ int x;
+ /*Skip past the luma data.*/
+ _dst += _y4m->pic_w * _y4m->pic_h;
+ /*Compute the size of each chroma plane.*/
+ c_w = (_y4m->pic_w + _y4m->src_c_dec_h - 1) / _y4m->src_c_dec_h;
+ c_h = _y4m->pic_h;
+ dst_c_w = (_y4m->pic_w + _y4m->dst_c_dec_h - 1) / _y4m->dst_c_dec_h;
+ dst_c_h = (_y4m->pic_h + _y4m->dst_c_dec_v - 1) / _y4m->dst_c_dec_v;
+ c_sz = c_w * c_h;
+ dst_c_sz = dst_c_w * dst_c_h;
+ tmp_sz = dst_c_w * c_h;
+ tmp = _aux + 2 * c_sz;
+ for (pli = 1; pli < 3; pli++) {
+ /*Filter: [3 -17 78 78 -17 3]/128, derived from a 6-tap Lanczos window.*/
+ for (y = 0; y < c_h; y++) {
+ for (x = 0; x < OC_MINI(c_w, 2); x += 2) {
+ tmp[x >> 1] = OC_CLAMPI(0,
+ (64 * _aux[0] + 78 * _aux[OC_MINI(1, c_w - 1)] -
+ 17 * _aux[OC_MINI(2, c_w - 1)] +
+ 3 * _aux[OC_MINI(3, c_w - 1)] + 64) >>
+ 7,
+ 255);
+ }
+ for (; x < c_w - 3; x += 2) {
+ tmp[x >> 1] = OC_CLAMPI(0,
+ (3 * (_aux[x - 2] + _aux[x + 3]) -
+ 17 * (_aux[x - 1] + _aux[x + 2]) +
+ 78 * (_aux[x] + _aux[x + 1]) + 64) >>
+ 7,
+ 255);
+ }
+ for (; x < c_w; x += 2) {
+ tmp[x >> 1] =
+ OC_CLAMPI(0,
+ (3 * (_aux[x - 2] + _aux[c_w - 1]) -
+ 17 * (_aux[x - 1] + _aux[OC_MINI(x + 2, c_w - 1)]) +
+ 78 * (_aux[x] + _aux[OC_MINI(x + 1, c_w - 1)]) + 64) >>
+ 7,
+ 255);
+ }
+ tmp += dst_c_w;
+ _aux += c_w;
+ }
+ tmp -= tmp_sz;
+ /*Now do the vertical filtering.*/
+ y4m_422jpeg_420jpeg_helper(_dst, tmp, dst_c_w, c_h);
+ _dst += dst_c_sz;
+ }
+}
+
+/*The image is padded with empty chroma components at 4:2:0.*/
+static void y4m_convert_mono_420jpeg(y4m_input *_y4m, unsigned char *_dst,
+ unsigned char *_aux) {
+ int c_sz;
+ (void)_aux;
+ _dst += _y4m->pic_w * _y4m->pic_h;
+ c_sz = ((_y4m->pic_w + _y4m->dst_c_dec_h - 1) / _y4m->dst_c_dec_h) *
+ ((_y4m->pic_h + _y4m->dst_c_dec_v - 1) / _y4m->dst_c_dec_v);
+ memset(_dst, 128, c_sz * 2);
+}
+
+/*No conversion function needed.*/
+static void y4m_convert_null(y4m_input *_y4m, unsigned char *_dst,
+ unsigned char *_aux) {
+ (void)_y4m;
+ (void)_dst;
+ (void)_aux;
+}
+
+static const char TAG[] = "YUV4MPEG2";
+
+int y4m_input_open(y4m_input *y4m_ctx, FILE *file, char *skip_buffer,
+ int num_skip, aom_chroma_sample_position_t csp,
+ int only_420) {
+ // File must start with |TAG|.
+ char tag_buffer[9]; // 9 == strlen(TAG)
+ // Read as much as possible from |skip_buffer|, which were characters
+ // that were previously read from the file to do input-type detection.
+ assert(num_skip >= 0 && num_skip <= 8);
+ if (num_skip > 0) {
+ memcpy(tag_buffer, skip_buffer, num_skip);
+ }
+ // Start reading from the file now that the |skip_buffer| is depleted.
+ if (!file_read(tag_buffer + num_skip, 9 - num_skip, file)) {
+ return -1;
+ }
+ if (memcmp(TAG, tag_buffer, 9) != 0) {
+ fprintf(stderr, "Error parsing header: must start with %s\n", TAG);
+ return -1;
+ }
+ // Next character must be a space.
+ if (!file_read(tag_buffer, 1, file) || tag_buffer[0] != ' ') {
+ fprintf(stderr, "Error parsing header: space must follow %s\n", TAG);
+ return -1;
+ }
+ if (!parse_tags(y4m_ctx, file)) {
+ fprintf(stderr, "Error parsing %s header.\n", TAG);
+ return -1;
+ }
+ if (y4m_ctx->interlace == '?') {
+ fprintf(stderr,
+ "Warning: Input video interlacing format unknown; "
+ "assuming progressive scan.\n");
+ } else if (y4m_ctx->interlace != 'p') {
+ fprintf(stderr,
+ "Input video is interlaced; "
+ "Only progressive scan handled.\n");
+ return -1;
+ }
+ /* Only support vertical chroma sample position if the input format is
+ * already 420mpeg2. Colocated is not supported in Y4M.
+ */
+ if (csp == AOM_CSP_VERTICAL &&
+ strcmp(y4m_ctx->chroma_type, "420mpeg2") != 0) {
+ fprintf(stderr,
+ "Vertical chroma sample position only supported "
+ "for 420mpeg2 input\n");
+ return -1;
+ }
+ if (csp == AOM_CSP_COLOCATED) {
+ // TODO(any): check the right way to handle this in y4m
+ fprintf(stderr,
+ "Ignoring colocated chroma sample position for reading in Y4M\n");
+ }
+ y4m_ctx->aom_fmt = AOM_IMG_FMT_I420;
+ y4m_ctx->bps = 12;
+ y4m_ctx->bit_depth = 8;
+ y4m_ctx->aux_buf = NULL;
+ y4m_ctx->dst_buf = NULL;
+ if (strcmp(y4m_ctx->chroma_type, "420") == 0 ||
+ strcmp(y4m_ctx->chroma_type, "420jpeg") == 0 ||
+ strcmp(y4m_ctx->chroma_type, "420mpeg2") == 0) {
+ y4m_ctx->src_c_dec_h = y4m_ctx->dst_c_dec_h = y4m_ctx->src_c_dec_v =
+ y4m_ctx->dst_c_dec_v = 2;
+ y4m_ctx->dst_buf_read_sz =
+ y4m_ctx->pic_w * y4m_ctx->pic_h +
+ 2 * ((y4m_ctx->pic_w + 1) / 2) * ((y4m_ctx->pic_h + 1) / 2);
+ /* Natively supported: no conversion required. */
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz = 0;
+ y4m_ctx->convert = y4m_convert_null;
+ } else if (strcmp(y4m_ctx->chroma_type, "420p10") == 0) {
+ y4m_ctx->src_c_dec_h = 2;
+ y4m_ctx->dst_c_dec_h = 2;
+ y4m_ctx->src_c_dec_v = 2;
+ y4m_ctx->dst_c_dec_v = 2;
+ y4m_ctx->dst_buf_read_sz =
+ 2 * (y4m_ctx->pic_w * y4m_ctx->pic_h +
+ 2 * ((y4m_ctx->pic_w + 1) / 2) * ((y4m_ctx->pic_h + 1) / 2));
+ /* Natively supported: no conversion required. */
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz = 0;
+ y4m_ctx->convert = y4m_convert_null;
+ y4m_ctx->bit_depth = 10;
+ y4m_ctx->bps = 15;
+ y4m_ctx->aom_fmt = AOM_IMG_FMT_I42016;
+ if (only_420) {
+ fprintf(stderr, "Unsupported conversion from 420p10 to 420jpeg\n");
+ return -1;
+ }
+ } else if (strcmp(y4m_ctx->chroma_type, "420p12") == 0) {
+ y4m_ctx->src_c_dec_h = 2;
+ y4m_ctx->dst_c_dec_h = 2;
+ y4m_ctx->src_c_dec_v = 2;
+ y4m_ctx->dst_c_dec_v = 2;
+ y4m_ctx->dst_buf_read_sz =
+ 2 * (y4m_ctx->pic_w * y4m_ctx->pic_h +
+ 2 * ((y4m_ctx->pic_w + 1) / 2) * ((y4m_ctx->pic_h + 1) / 2));
+ /* Natively supported: no conversion required. */
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz = 0;
+ y4m_ctx->convert = y4m_convert_null;
+ y4m_ctx->bit_depth = 12;
+ y4m_ctx->bps = 18;
+ y4m_ctx->aom_fmt = AOM_IMG_FMT_I42016;
+ if (only_420) {
+ fprintf(stderr, "Unsupported conversion from 420p12 to 420jpeg\n");
+ return -1;
+ }
+ } else if (strcmp(y4m_ctx->chroma_type, "420paldv") == 0) {
+ y4m_ctx->src_c_dec_h = y4m_ctx->dst_c_dec_h = y4m_ctx->src_c_dec_v =
+ y4m_ctx->dst_c_dec_v = 2;
+ y4m_ctx->dst_buf_read_sz = y4m_ctx->pic_w * y4m_ctx->pic_h;
+ /*Chroma filter required: read into the aux buf first.
+ We need to make two filter passes, so we need some extra space in the
+ aux buffer.*/
+ y4m_ctx->aux_buf_sz =
+ 3 * ((y4m_ctx->pic_w + 1) / 2) * ((y4m_ctx->pic_h + 1) / 2);
+ y4m_ctx->aux_buf_read_sz =
+ 2 * ((y4m_ctx->pic_w + 1) / 2) * ((y4m_ctx->pic_h + 1) / 2);
+ y4m_ctx->convert = y4m_convert_42xpaldv_42xjpeg;
+ } else if (strcmp(y4m_ctx->chroma_type, "422jpeg") == 0) {
+ y4m_ctx->src_c_dec_h = y4m_ctx->dst_c_dec_h = 2;
+ y4m_ctx->src_c_dec_v = 1;
+ y4m_ctx->dst_c_dec_v = 2;
+ y4m_ctx->dst_buf_read_sz = y4m_ctx->pic_w * y4m_ctx->pic_h;
+ /*Chroma filter required: read into the aux buf first.*/
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz =
+ 2 * ((y4m_ctx->pic_w + 1) / 2) * y4m_ctx->pic_h;
+ y4m_ctx->convert = y4m_convert_422jpeg_420jpeg;
+ } else if (strcmp(y4m_ctx->chroma_type, "422") == 0) {
+ y4m_ctx->src_c_dec_h = 2;
+ y4m_ctx->src_c_dec_v = 1;
+ if (only_420) {
+ y4m_ctx->dst_c_dec_h = 2;
+ y4m_ctx->dst_c_dec_v = 2;
+ y4m_ctx->dst_buf_read_sz = y4m_ctx->pic_w * y4m_ctx->pic_h;
+ /*Chroma filter required: read into the aux buf first.
+ We need to make two filter passes, so we need some extra space in the
+ aux buffer.*/
+ y4m_ctx->aux_buf_read_sz =
+ 2 * ((y4m_ctx->pic_w + 1) / 2) * y4m_ctx->pic_h;
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz +
+ ((y4m_ctx->pic_w + 1) / 2) * y4m_ctx->pic_h;
+ y4m_ctx->convert = y4m_convert_422_420jpeg;
+ } else {
+ y4m_ctx->aom_fmt = AOM_IMG_FMT_I422;
+ y4m_ctx->bps = 16;
+ y4m_ctx->dst_c_dec_h = y4m_ctx->src_c_dec_h;
+ y4m_ctx->dst_c_dec_v = y4m_ctx->src_c_dec_v;
+ y4m_ctx->dst_buf_read_sz =
+ y4m_ctx->pic_w * y4m_ctx->pic_h +
+ 2 * ((y4m_ctx->pic_w + 1) / 2) * y4m_ctx->pic_h;
+ /*Natively supported: no conversion required.*/
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz = 0;
+ y4m_ctx->convert = y4m_convert_null;
+ }
+ } else if (strcmp(y4m_ctx->chroma_type, "422p10") == 0) {
+ y4m_ctx->src_c_dec_h = 2;
+ y4m_ctx->src_c_dec_v = 1;
+ y4m_ctx->aom_fmt = AOM_IMG_FMT_I42216;
+ y4m_ctx->bps = 20;
+ y4m_ctx->bit_depth = 10;
+ y4m_ctx->dst_c_dec_h = y4m_ctx->src_c_dec_h;
+ y4m_ctx->dst_c_dec_v = y4m_ctx->src_c_dec_v;
+ y4m_ctx->dst_buf_read_sz =
+ 2 * (y4m_ctx->pic_w * y4m_ctx->pic_h +
+ 2 * ((y4m_ctx->pic_w + 1) / 2) * y4m_ctx->pic_h);
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz = 0;
+ y4m_ctx->convert = y4m_convert_null;
+ if (only_420) {
+ fprintf(stderr, "Unsupported conversion from 422p10 to 420jpeg\n");
+ return -1;
+ }
+ } else if (strcmp(y4m_ctx->chroma_type, "422p12") == 0) {
+ y4m_ctx->src_c_dec_h = 2;
+ y4m_ctx->src_c_dec_v = 1;
+ y4m_ctx->aom_fmt = AOM_IMG_FMT_I42216;
+ y4m_ctx->bps = 24;
+ y4m_ctx->bit_depth = 12;
+ y4m_ctx->dst_c_dec_h = y4m_ctx->src_c_dec_h;
+ y4m_ctx->dst_c_dec_v = y4m_ctx->src_c_dec_v;
+ y4m_ctx->dst_buf_read_sz =
+ 2 * (y4m_ctx->pic_w * y4m_ctx->pic_h +
+ 2 * ((y4m_ctx->pic_w + 1) / 2) * y4m_ctx->pic_h);
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz = 0;
+ y4m_ctx->convert = y4m_convert_null;
+ if (only_420) {
+ fprintf(stderr, "Unsupported conversion from 422p12 to 420jpeg\n");
+ return -1;
+ }
+ } else if (strcmp(y4m_ctx->chroma_type, "411") == 0) {
+ y4m_ctx->src_c_dec_h = 4;
+ y4m_ctx->dst_c_dec_h = 2;
+ y4m_ctx->src_c_dec_v = 1;
+ y4m_ctx->dst_c_dec_v = 2;
+ y4m_ctx->dst_buf_read_sz = y4m_ctx->pic_w * y4m_ctx->pic_h;
+ /*Chroma filter required: read into the aux buf first.
+ We need to make two filter passes, so we need some extra space in the
+ aux buffer.*/
+ y4m_ctx->aux_buf_read_sz = 2 * ((y4m_ctx->pic_w + 3) / 4) * y4m_ctx->pic_h;
+ y4m_ctx->aux_buf_sz =
+ y4m_ctx->aux_buf_read_sz + ((y4m_ctx->pic_w + 1) / 2) * y4m_ctx->pic_h;
+ y4m_ctx->convert = y4m_convert_411_420jpeg;
+ } else if (strcmp(y4m_ctx->chroma_type, "444") == 0) {
+ y4m_ctx->src_c_dec_h = 1;
+ y4m_ctx->src_c_dec_v = 1;
+ if (only_420) {
+ y4m_ctx->dst_c_dec_h = 2;
+ y4m_ctx->dst_c_dec_v = 2;
+ y4m_ctx->dst_buf_read_sz = y4m_ctx->pic_w * y4m_ctx->pic_h;
+ /*Chroma filter required: read into the aux buf first.
+ We need to make two filter passes, so we need some extra space in the
+ aux buffer.*/
+ y4m_ctx->aux_buf_read_sz = 2 * y4m_ctx->pic_w * y4m_ctx->pic_h;
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz +
+ ((y4m_ctx->pic_w + 1) / 2) * y4m_ctx->pic_h;
+ y4m_ctx->convert = y4m_convert_444_420jpeg;
+ } else {
+ y4m_ctx->aom_fmt = AOM_IMG_FMT_I444;
+ y4m_ctx->bps = 24;
+ y4m_ctx->dst_c_dec_h = y4m_ctx->src_c_dec_h;
+ y4m_ctx->dst_c_dec_v = y4m_ctx->src_c_dec_v;
+ y4m_ctx->dst_buf_read_sz = 3 * y4m_ctx->pic_w * y4m_ctx->pic_h;
+ /*Natively supported: no conversion required.*/
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz = 0;
+ y4m_ctx->convert = y4m_convert_null;
+ }
+ } else if (strcmp(y4m_ctx->chroma_type, "444p10") == 0) {
+ y4m_ctx->src_c_dec_h = 1;
+ y4m_ctx->src_c_dec_v = 1;
+ y4m_ctx->aom_fmt = AOM_IMG_FMT_I44416;
+ y4m_ctx->bps = 30;
+ y4m_ctx->bit_depth = 10;
+ y4m_ctx->dst_c_dec_h = y4m_ctx->src_c_dec_h;
+ y4m_ctx->dst_c_dec_v = y4m_ctx->src_c_dec_v;
+ y4m_ctx->dst_buf_read_sz = 2 * 3 * y4m_ctx->pic_w * y4m_ctx->pic_h;
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz = 0;
+ y4m_ctx->convert = y4m_convert_null;
+ if (only_420) {
+ fprintf(stderr, "Unsupported conversion from 444p10 to 420jpeg\n");
+ return -1;
+ }
+ } else if (strcmp(y4m_ctx->chroma_type, "444p12") == 0) {
+ y4m_ctx->src_c_dec_h = 1;
+ y4m_ctx->src_c_dec_v = 1;
+ y4m_ctx->aom_fmt = AOM_IMG_FMT_I44416;
+ y4m_ctx->bps = 36;
+ y4m_ctx->bit_depth = 12;
+ y4m_ctx->dst_c_dec_h = y4m_ctx->src_c_dec_h;
+ y4m_ctx->dst_c_dec_v = y4m_ctx->src_c_dec_v;
+ y4m_ctx->dst_buf_read_sz = 2 * 3 * y4m_ctx->pic_w * y4m_ctx->pic_h;
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz = 0;
+ y4m_ctx->convert = y4m_convert_null;
+ if (only_420) {
+ fprintf(stderr, "Unsupported conversion from 444p12 to 420jpeg\n");
+ return -1;
+ }
+ } else if (strcmp(y4m_ctx->chroma_type, "444alpha") == 0) {
+ y4m_ctx->src_c_dec_h = 1;
+ y4m_ctx->src_c_dec_v = 1;
+ if (only_420) {
+ y4m_ctx->dst_c_dec_h = 2;
+ y4m_ctx->dst_c_dec_v = 2;
+ y4m_ctx->dst_buf_read_sz = y4m_ctx->pic_w * y4m_ctx->pic_h;
+ /*Chroma filter required: read into the aux buf first.
+ We need to make two filter passes, so we need some extra space in the
+ aux buffer.
+ The extra plane also gets read into the aux buf.
+ It will be discarded.*/
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz =
+ 3 * y4m_ctx->pic_w * y4m_ctx->pic_h;
+ y4m_ctx->convert = y4m_convert_444_420jpeg;
+ } else {
+ fprintf(stderr, "Unsupported format: 444A\n");
+ return -1;
+ }
+ } else if (strcmp(y4m_ctx->chroma_type, "mono") == 0) {
+ y4m_ctx->src_c_dec_h = y4m_ctx->src_c_dec_v = 0;
+ y4m_ctx->dst_c_dec_h = y4m_ctx->dst_c_dec_v = 2;
+ y4m_ctx->dst_buf_read_sz = y4m_ctx->pic_w * y4m_ctx->pic_h;
+ /*No extra space required, but we need to clear the chroma planes.*/
+ y4m_ctx->aux_buf_sz = y4m_ctx->aux_buf_read_sz = 0;
+ y4m_ctx->convert = y4m_convert_mono_420jpeg;
+ } else {
+ fprintf(stderr, "Unknown chroma sampling type: %s\n", y4m_ctx->chroma_type);
+ return -1;
+ }
+ /*The size of the final frame buffers is always computed from the
+ destination chroma decimation type.*/
+ y4m_ctx->dst_buf_sz =
+ y4m_ctx->pic_w * y4m_ctx->pic_h +
+ 2 * ((y4m_ctx->pic_w + y4m_ctx->dst_c_dec_h - 1) / y4m_ctx->dst_c_dec_h) *
+ ((y4m_ctx->pic_h + y4m_ctx->dst_c_dec_v - 1) / y4m_ctx->dst_c_dec_v);
+ if (y4m_ctx->bit_depth == 8)
+ y4m_ctx->dst_buf = (unsigned char *)malloc(y4m_ctx->dst_buf_sz);
+ else
+ y4m_ctx->dst_buf = (unsigned char *)malloc(2 * y4m_ctx->dst_buf_sz);
+ if (!y4m_ctx->dst_buf) return -1;
+
+ if (y4m_ctx->aux_buf_sz > 0) {
+ y4m_ctx->aux_buf = (unsigned char *)malloc(y4m_ctx->aux_buf_sz);
+ if (!y4m_ctx->aux_buf) {
+ free(y4m_ctx->dst_buf);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void y4m_input_close(y4m_input *_y4m) {
+ free(_y4m->dst_buf);
+ free(_y4m->aux_buf);
+}
+
+int y4m_input_fetch_frame(y4m_input *_y4m, FILE *_fin, aom_image_t *_img) {
+ char frame[6];
+ int pic_sz;
+ int c_w;
+ int c_h;
+ int c_sz;
+ int bytes_per_sample = _y4m->bit_depth > 8 ? 2 : 1;
+ /*Read and skip the frame header.*/
+ if (!file_read(frame, 6, _fin)) return 0;
+ if (memcmp(frame, "FRAME", 5)) {
+ fprintf(stderr, "Loss of framing in Y4M input data\n");
+ return -1;
+ }
+ if (frame[5] != '\n') {
+ char c;
+ int j;
+ for (j = 0; j < 79 && file_read(&c, 1, _fin) && c != '\n'; j++) {
+ }
+ if (j == 79) {
+ fprintf(stderr, "Error parsing Y4M frame header\n");
+ return -1;
+ }
+ }
+ /*Read the frame data that needs no conversion.*/
+ if (!file_read(_y4m->dst_buf, _y4m->dst_buf_read_sz, _fin)) {
+ fprintf(stderr, "Error reading Y4M frame data.\n");
+ return -1;
+ }
+ /*Read the frame data that does need conversion.*/
+ if (!file_read(_y4m->aux_buf, _y4m->aux_buf_read_sz, _fin)) {
+ fprintf(stderr, "Error reading Y4M frame data.\n");
+ return -1;
+ }
+ /*Now convert the just read frame.*/
+ (*_y4m->convert)(_y4m, _y4m->dst_buf, _y4m->aux_buf);
+ /*Fill in the frame buffer pointers.
+ We don't use aom_img_wrap() because it forces padding for odd picture
+ sizes, which would require a separate fread call for every row.*/
+ memset(_img, 0, sizeof(*_img));
+ /*Y4M has the planes in Y'CbCr order, which libaom calls Y, U, and V.*/
+ _img->fmt = _y4m->aom_fmt;
+ _img->w = _img->d_w = _y4m->pic_w;
+ _img->h = _img->d_h = _y4m->pic_h;
+ _img->bit_depth = _y4m->bit_depth;
+ _img->x_chroma_shift = _y4m->dst_c_dec_h >> 1;
+ _img->y_chroma_shift = _y4m->dst_c_dec_v >> 1;
+ _img->bps = _y4m->bps;
+
+ /*Set up the buffer pointers.*/
+ pic_sz = _y4m->pic_w * _y4m->pic_h * bytes_per_sample;
+ c_w = (_y4m->pic_w + _y4m->dst_c_dec_h - 1) / _y4m->dst_c_dec_h;
+ c_w *= bytes_per_sample;
+ c_h = (_y4m->pic_h + _y4m->dst_c_dec_v - 1) / _y4m->dst_c_dec_v;
+ c_sz = c_w * c_h;
+ _img->stride[AOM_PLANE_Y] = _y4m->pic_w * bytes_per_sample;
+ _img->stride[AOM_PLANE_U] = _img->stride[AOM_PLANE_V] = c_w;
+ _img->planes[AOM_PLANE_Y] = _y4m->dst_buf;
+ _img->planes[AOM_PLANE_U] = _y4m->dst_buf + pic_sz;
+ _img->planes[AOM_PLANE_V] = _y4m->dst_buf + pic_sz + c_sz;
+ return 1;
+}
diff --git a/third_party/aom/common/y4minput.h b/third_party/aom/common/y4minput.h
new file mode 100644
index 0000000000..2472007b67
--- /dev/null
+++ b/third_party/aom/common/y4minput.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ *
+ * Based on code from the OggTheora software codec source code,
+ * Copyright (C) 2002-2010 The Xiph.Org Foundation and contributors.
+ */
+
+#ifndef AOM_COMMON_Y4MINPUT_H_
+#define AOM_COMMON_Y4MINPUT_H_
+
+#include <stdio.h>
+#include "aom/aom_image.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct y4m_input y4m_input;
+
+/*The function used to perform chroma conversion.*/
+typedef void (*y4m_convert_func)(y4m_input *_y4m, unsigned char *_dst,
+ unsigned char *_src);
+
+struct y4m_input {
+ int pic_w;
+ int pic_h;
+ int fps_n;
+ int fps_d;
+ int par_n;
+ int par_d;
+ char interlace;
+ int src_c_dec_h;
+ int src_c_dec_v;
+ int dst_c_dec_h;
+ int dst_c_dec_v;
+ char chroma_type[16];
+ /*The size of each converted frame buffer.*/
+ size_t dst_buf_sz;
+ /*The amount to read directly into the converted frame buffer.*/
+ size_t dst_buf_read_sz;
+ /*The size of the auxilliary buffer.*/
+ size_t aux_buf_sz;
+ /*The amount to read into the auxilliary buffer.*/
+ size_t aux_buf_read_sz;
+ y4m_convert_func convert;
+ unsigned char *dst_buf;
+ unsigned char *aux_buf;
+ enum aom_img_fmt aom_fmt;
+ int bps;
+ unsigned int bit_depth;
+ aom_color_range_t color_range;
+};
+
+/**
+ * Open the input file, treating it as Y4M. |y4m_ctx| is filled in after
+ * reading it. Note that |csp| should only be set for 420 input, and the input
+ * chroma is shifted if necessary. The code does not support the conversion
+ * from co-located to vertical. The |skip_buffer| indicates bytes that were
+ * previously read from |file|, to do input-type detection; this buffer will
+ * be read before the |file| is read. It is of size |num_skip|, which *must*
+ * be 8 or less.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int y4m_input_open(y4m_input *y4m_ctx, FILE *file, char *skip_buffer,
+ int num_skip, aom_chroma_sample_position_t csp,
+ int only_420);
+void y4m_input_close(y4m_input *_y4m);
+int y4m_input_fetch_frame(y4m_input *_y4m, FILE *_fin, aom_image_t *img);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // AOM_COMMON_Y4MINPUT_H_