summaryrefslogtreecommitdiffstats
path: root/third_party/dav1d/tools
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/dav1d/tools')
-rw-r--r--third_party/dav1d/tools/compat/getopt.c562
-rw-r--r--third_party/dav1d/tools/dav1d.c317
-rw-r--r--third_party/dav1d/tools/dav1d.manifest10
-rw-r--r--third_party/dav1d/tools/dav1d.rc.in33
-rw-r--r--third_party/dav1d/tools/dav1d_cli_parse.c362
-rw-r--r--third_party/dav1d/tools/dav1d_cli_parse.h54
-rw-r--r--third_party/dav1d/tools/input/annexb.c195
-rw-r--r--third_party/dav1d/tools/input/demuxer.h45
-rw-r--r--third_party/dav1d/tools/input/input.c134
-rw-r--r--third_party/dav1d/tools/input/input.h41
-rw-r--r--third_party/dav1d/tools/input/ivf.c158
-rw-r--r--third_party/dav1d/tools/input/parse.h109
-rw-r--r--third_party/dav1d/tools/input/section5.c185
-rw-r--r--third_party/dav1d/tools/meson.build104
-rw-r--r--third_party/dav1d/tools/output/md5.c317
-rw-r--r--third_party/dav1d/tools/output/muxer.h52
-rw-r--r--third_party/dav1d/tools/output/null.c44
-rw-r--r--third_party/dav1d/tools/output/output.c145
-rw-r--r--third_party/dav1d/tools/output/output.h48
-rw-r--r--third_party/dav1d/tools/output/y4m2.c151
-rw-r--r--third_party/dav1d/tools/output/yuv.c104
21 files changed, 3170 insertions, 0 deletions
diff --git a/third_party/dav1d/tools/compat/getopt.c b/third_party/dav1d/tools/compat/getopt.c
new file mode 100644
index 0000000000..ac1fda426e
--- /dev/null
+++ b/third_party/dav1d/tools/compat/getopt.c
@@ -0,0 +1,562 @@
+/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */
+/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
+
+/*
+ * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <windows.h>
+
+#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
+
+#ifdef REPLACE_GETOPT
+int opterr = 1; /* if error message should be printed */
+int optind = 1; /* index into parent argv vector */
+int optopt = '?'; /* character checked for validity */
+#undef optreset /* see getopt.h */
+#define optreset __mingw_optreset
+int optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+#endif
+
+#define PRINT_ERROR ((opterr) && (*options != ':'))
+
+#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
+#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
+#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
+
+/* return values */
+#define BADCH (int)'?'
+#define BADARG ((*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#ifndef __CYGWIN__
+#define __progname __argv[0]
+#else
+extern char __declspec(dllimport) *__progname;
+#endif
+
+#ifdef __CYGWIN__
+static char EMSG[] = "";
+#else
+#define EMSG ""
+#endif
+
+static int getopt_internal(int, char * const *, const char *,
+ const struct option *, int *, int);
+static int parse_long_options(char * const *, const char *,
+ const struct option *, int *, int);
+static int gcd(int, int);
+static void permute_args(int, int, int, char * const *);
+
+static char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1; /* first option after non options (for permute) */
+
+/* Error messages */
+static const char recargchar[] = "option requires an argument -- %c";
+static const char recargstring[] = "option requires an argument -- %s";
+static const char ambig[] = "ambiguous option -- %.*s";
+static const char noarg[] = "option doesn't take an argument -- %.*s";
+static const char illoptchar[] = "unknown option -- %c";
+static const char illoptstring[] = "unknown option -- %s";
+
+static void
+_vwarnx(const char *fmt,va_list ap)
+{
+ (void)fprintf(stderr,"%s: ",__progname);
+ if (fmt != NULL)
+ (void)vfprintf(stderr,fmt,ap);
+ (void)fprintf(stderr,"\n");
+}
+
+static void
+warnx(const char *fmt,...)
+{
+ va_list ap;
+ va_start(ap,fmt);
+ _vwarnx(fmt,ap);
+ va_end(ap);
+}
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(int a, int b)
+{
+ int c;
+
+ c = a % b;
+ while (c != 0) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+
+ return (b);
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(int panonopt_start, int panonopt_end, int opt_end,
+ char * const *nargv)
+{
+ int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+ char *swap;
+
+ /*
+ * compute lengths of blocks and number and size of cycles
+ */
+ nnonopts = panonopt_end - panonopt_start;
+ nopts = opt_end - panonopt_end;
+ ncycle = gcd(nnonopts, nopts);
+ cyclelen = (opt_end - panonopt_start) / ncycle;
+
+ for (i = 0; i < ncycle; i++) {
+ cstart = panonopt_end+i;
+ pos = cstart;
+ for (j = 0; j < cyclelen; j++) {
+ if (pos >= panonopt_end)
+ pos -= nnonopts;
+ else
+ pos += nopts;
+ swap = nargv[pos];
+ /* LINTED const cast */
+ ((char **) nargv)[pos] = nargv[cstart];
+ /* LINTED const cast */
+ ((char **)nargv)[cstart] = swap;
+ }
+ }
+}
+
+/*
+ * parse_long_options --
+ * Parse long options in argc/argv argument vector.
+ * Returns -1 if short_too is set and the option does not match long_options.
+ */
+static int
+parse_long_options(char * const *nargv, const char *options,
+ const struct option *long_options, int *idx, int short_too)
+{
+ char *current_argv, *has_equal;
+ size_t current_argv_len;
+ int i, ambiguous, match;
+
+#define IDENTICAL_INTERPRETATION(_x, _y) \
+ (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
+ long_options[(_x)].flag == long_options[(_y)].flag && \
+ long_options[(_x)].val == long_options[(_y)].val)
+
+ current_argv = place;
+ match = -1;
+ ambiguous = 0;
+
+ optind++;
+
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
+ /* argument found (--option=arg) */
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else
+ current_argv_len = strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ /* find matching long option */
+ if (strncmp(current_argv, long_options[i].name,
+ current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == current_argv_len) {
+ /* exact match */
+ match = i;
+ ambiguous = 0;
+ break;
+ }
+ /*
+ * If this is a known short option, don't allow
+ * a partial match of a single character.
+ */
+ if (short_too && current_argv_len == 1)
+ continue;
+
+ if (match == -1) /* partial match */
+ match = i;
+ else if (!IDENTICAL_INTERPRETATION(i, match))
+ ambiguous = 1;
+ }
+ if (ambiguous) {
+ /* ambiguous abbreviation */
+ if (PRINT_ERROR)
+ warnx(ambig, (int)current_argv_len,
+ current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ if (match != -1) { /* option found */
+ if (long_options[match].has_arg == no_argument
+ && has_equal) {
+ if (PRINT_ERROR)
+ warnx(noarg, (int)current_argv_len,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ return (BADARG);
+ }
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal)
+ optarg = has_equal;
+ else if (long_options[match].has_arg ==
+ required_argument) {
+ /*
+ * optional argument doesn't use next nargv
+ */
+ optarg = nargv[optind++];
+ }
+ }
+ if ((long_options[match].has_arg == required_argument)
+ && (optarg == NULL)) {
+ /*
+ * Missing argument; leading ':' indicates no error
+ * should be generated.
+ */
+ if (PRINT_ERROR)
+ warnx(recargstring,
+ current_argv);
+ /*
+ * XXX: GNU sets optopt to val regardless of flag
+ */
+ if (long_options[match].flag == NULL)
+ optopt = long_options[match].val;
+ else
+ optopt = 0;
+ --optind;
+ return (BADARG);
+ }
+ } else { /* unknown option */
+ if (short_too) {
+ --optind;
+ return (-1);
+ }
+ if (PRINT_ERROR)
+ warnx(illoptstring, current_argv);
+ optopt = 0;
+ return (BADCH);
+ }
+ if (idx)
+ *idx = match;
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ return (0);
+ } else
+ return (long_options[match].val);
+#undef IDENTICAL_INTERPRETATION
+}
+
+/*
+ * getopt_internal --
+ * Parse argc/argv argument vector. Called by user level routines.
+ */
+static int
+getopt_internal(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx, int flags)
+{
+ char *oli; /* option letter list index */
+ int optchar, short_too;
+ static int posixly_correct = -1;
+
+ if (options == NULL)
+ return (-1);
+
+ /*
+ * XXX Some GNU programs (like cvs) set optind to 0 instead of
+ * XXX using optreset. Work around this braindamage.
+ */
+ if (optind == 0)
+ optind = optreset = 1;
+
+ /*
+ * Disable GNU extensions if POSIXLY_CORRECT is set or options
+ * string begins with a '+'.
+ *
+ * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or
+ * optreset != 0 for GNU compatibility.
+ */
+ if (posixly_correct == -1 || optreset != 0)
+ posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
+ if (*options == '-')
+ flags |= FLAG_ALLARGS;
+ else if (posixly_correct || *options == '+')
+ flags &= ~FLAG_PERMUTE;
+ if (*options == '+' || *options == '-')
+ options++;
+
+ optarg = NULL;
+ if (optreset)
+ nonopt_start = nonopt_end = -1;
+start:
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc) { /* end of argument vector */
+ place = EMSG;
+ if (nonopt_end != -1) {
+ /* do permutation, if we have to */
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ else if (nonopt_start != -1) {
+ /*
+ * If we skipped non-options, set optind
+ * to the first of them.
+ */
+ optind = nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ if (*(place = nargv[optind]) != '-' ||
+ (place[1] == '\0' && strchr(options, '-') == NULL)) {
+ place = EMSG; /* found non-option */
+ if (flags & FLAG_ALLARGS) {
+ /*
+ * GNU extension:
+ * return non-option as argument to option 1
+ */
+ optarg = nargv[optind++];
+ return (INORDER);
+ }
+ if (!(flags & FLAG_PERMUTE)) {
+ /*
+ * If no permutation wanted, stop parsing
+ * at first non-option.
+ */
+ return (-1);
+ }
+ /* do permutation */
+ if (nonopt_start == -1)
+ nonopt_start = optind;
+ else if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ nonopt_start = optind -
+ (nonopt_end - nonopt_start);
+ nonopt_end = -1;
+ }
+ optind++;
+ /* process next argument */
+ goto start;
+ }
+ if (nonopt_start != -1 && nonopt_end == -1)
+ nonopt_end = optind;
+
+ /*
+ * If we have "-" do nothing, if "--" we are done.
+ */
+ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
+ optind++;
+ place = EMSG;
+ /*
+ * We found an option (--), so if we skipped
+ * non-options, we have to permute.
+ */
+ if (nonopt_end != -1) {
+ permute_args(nonopt_start, nonopt_end,
+ optind, nargv);
+ optind -= nonopt_end - nonopt_start;
+ }
+ nonopt_start = nonopt_end = -1;
+ return (-1);
+ }
+ }
+
+ /*
+ * Check long options if:
+ * 1) we were passed some
+ * 2) the arg is not just "-"
+ * 3) either the arg starts with -- we are getopt_long_only()
+ */
+ if (long_options != NULL && place != nargv[optind] &&
+ (*place == '-' || (flags & FLAG_LONGONLY))) {
+ short_too = 0;
+ if (*place == '-')
+ place++; /* --foo long option */
+ else if (*place != ':' && strchr(options, *place) != NULL)
+ short_too = 1; /* could be short option too */
+
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, short_too);
+ if (optchar != -1) {
+ place = EMSG;
+ return (optchar);
+ }
+ }
+
+ if ((optchar = (int)*place++) == (int)':' ||
+ (optchar == (int)'-' && *place != '\0') ||
+ (oli = strchr(options, optchar)) == NULL) {
+ /*
+ * If the user specified "-" and '-' isn't listed in
+ * options, return -1 (non-option) as per POSIX.
+ * Otherwise, it is an unknown option character (or ':').
+ */
+ if (optchar == (int)'-' && *place == '\0')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (PRINT_ERROR)
+ warnx(illoptchar, optchar);
+ optopt = optchar;
+ return (BADCH);
+ }
+ if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
+ /* -W long-option */
+ if (*place) /* no space */
+ /* NOTHING */;
+ else if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else /* white space */
+ place = nargv[optind];
+ optchar = parse_long_options(nargv, options, long_options,
+ idx, 0);
+ place = EMSG;
+ return (optchar);
+ }
+ if (*++oli != ':') { /* doesn't take argument */
+ if (!*place)
+ ++optind;
+ } else { /* takes (optional) argument */
+ optarg = NULL;
+ if (*place) /* no white space */
+ optarg = place;
+ else if (oli[1] != ':') { /* arg not optional */
+ if (++optind >= nargc) { /* no arg */
+ place = EMSG;
+ if (PRINT_ERROR)
+ warnx(recargchar, optchar);
+ optopt = optchar;
+ return (BADARG);
+ } else
+ optarg = nargv[optind];
+ }
+ place = EMSG;
+ ++optind;
+ }
+ /* dump back option letter */
+ return (optchar);
+}
+
+#ifdef REPLACE_GETOPT
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ *
+ * [eventually this will replace the BSD getopt]
+ */
+int
+getopt(int nargc, char * const *nargv, const char *options)
+{
+
+ /*
+ * We don't pass FLAG_PERMUTE to getopt_internal() since
+ * the BSD getopt(3) (unlike GNU) has never done this.
+ *
+ * Furthermore, since many privileged programs call getopt()
+ * before dropping privileges it makes sense to keep things
+ * as simple (and bug-free) as possible.
+ */
+ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
+}
+#endif /* REPLACE_GETOPT */
+
+/*
+ * getopt_long --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE));
+}
+
+/*
+ * getopt_long_only --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long_only(int nargc, char * const *nargv, const char *options,
+ const struct option *long_options, int *idx)
+{
+
+ return (getopt_internal(nargc, nargv, options, long_options, idx,
+ FLAG_PERMUTE|FLAG_LONGONLY));
+}
diff --git a/third_party/dav1d/tools/dav1d.c b/third_party/dav1d/tools/dav1d.c
new file mode 100644
index 0000000000..907af3f8ef
--- /dev/null
+++ b/third_party/dav1d/tools/dav1d.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "vcs_version.h"
+#include "cli_config.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_IO_H
+# include <io.h>
+#endif
+#ifdef _WIN32
+# include <windows.h>
+#endif
+#ifdef __APPLE__
+#include <mach/mach_time.h>
+#endif
+
+#include "dav1d/dav1d.h"
+
+#include "input/input.h"
+
+#include "output/output.h"
+
+#include "dav1d_cli_parse.h"
+
+static uint64_t get_time_nanos(void) {
+#ifdef _WIN32
+ LARGE_INTEGER frequency;
+ QueryPerformanceFrequency(&frequency);
+ LARGE_INTEGER t;
+ QueryPerformanceCounter(&t);
+ uint64_t seconds = t.QuadPart / frequency.QuadPart;
+ uint64_t fractions = t.QuadPart % frequency.QuadPart;
+ return 1000000000 * seconds + 1000000000 * fractions / frequency.QuadPart;
+#elif defined(HAVE_CLOCK_GETTIME)
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return 1000000000ULL * ts.tv_sec + ts.tv_nsec;
+#elif defined(__APPLE__)
+ mach_timebase_info_data_t info;
+ mach_timebase_info(&info);
+ return mach_absolute_time() * info.numer / info.denom;
+#endif
+}
+
+static void sleep_nanos(uint64_t d) {
+#ifdef _WIN32
+ Sleep((unsigned)(d / 1000000));
+#else
+ const struct timespec ts = {
+ .tv_sec = (time_t)(d / 1000000000),
+ .tv_nsec = d % 1000000000,
+ };
+ nanosleep(&ts, NULL);
+#endif
+}
+
+static void synchronize(const int realtime, const unsigned cache,
+ const unsigned n_out, const uint64_t nspf,
+ const uint64_t tfirst, uint64_t *const elapsed,
+ FILE *const frametimes)
+{
+ const uint64_t tcurr = get_time_nanos();
+ const uint64_t last = *elapsed;
+ *elapsed = tcurr - tfirst;
+ if (realtime) {
+ const uint64_t deadline = nspf * n_out;
+ if (*elapsed < deadline) {
+ const uint64_t remaining = deadline - *elapsed;
+ if (remaining > nspf * cache) sleep_nanos(remaining - nspf * cache);
+ *elapsed = deadline;
+ }
+ }
+ if (frametimes) {
+ const uint64_t frametime = *elapsed - last;
+ fprintf(frametimes, "%" PRIu64 "\n", frametime);
+ fflush(frametimes);
+ }
+}
+
+static void print_stats(const int istty, const unsigned n, const unsigned num,
+ const uint64_t elapsed, const double i_fps)
+{
+ char buf[80], *b = buf, *const end = buf + 80;
+
+ if (istty)
+ *b++ = '\r';
+ if (num == 0xFFFFFFFF)
+ b += snprintf(b, end - b, "Decoded %u frames", n);
+ else
+ b += snprintf(b, end - b, "Decoded %u/%u frames (%.1lf%%)",
+ n, num, 100.0 * n / num);
+ if (b < end) {
+ const double d_fps = 1e9 * n / elapsed;
+ if (i_fps) {
+ const double speed = d_fps / i_fps;
+ b += snprintf(b, end - b, " - %.2lf/%.2lf fps (%.2lfx)",
+ d_fps, i_fps, speed);
+ } else {
+ b += snprintf(b, end - b, " - %.2lf fps", d_fps);
+ }
+ }
+ if (!istty)
+ strcpy(b > end - 2 ? end - 2 : b, "\n");
+ fputs(buf, stderr);
+}
+
+int main(const int argc, char *const *const argv) {
+ const int istty = isatty(fileno(stderr));
+ int res = 0;
+ CLISettings cli_settings;
+ Dav1dSettings lib_settings;
+ DemuxerContext *in;
+ MuxerContext *out = NULL;
+ Dav1dPicture p;
+ Dav1dContext *c;
+ Dav1dData data;
+ unsigned n_out = 0, total, fps[2], timebase[2];
+ uint64_t nspf, tfirst, elapsed;
+ double i_fps;
+ FILE *frametimes = NULL;
+ const char *version = dav1d_version();
+
+ if (strcmp(version, DAV1D_VERSION)) {
+ fprintf(stderr, "Version mismatch (library: %s, executable: %s)\n",
+ version, DAV1D_VERSION);
+ return EXIT_FAILURE;
+ }
+
+ parse(argc, argv, &cli_settings, &lib_settings);
+
+ if ((res = input_open(&in, cli_settings.demuxer,
+ cli_settings.inputfile,
+ fps, &total, timebase)) < 0)
+ {
+ return EXIT_FAILURE;
+ }
+ for (unsigned i = 0; i <= cli_settings.skip; i++) {
+ if ((res = input_read(in, &data)) < 0) {
+ input_close(in);
+ return EXIT_FAILURE;
+ }
+ if (i < cli_settings.skip) dav1d_data_unref(&data);
+ }
+
+ if (!cli_settings.quiet)
+ fprintf(stderr, "dav1d %s - by VideoLAN\n", dav1d_version());
+
+ // skip frames until a sequence header is found
+ if (cli_settings.skip) {
+ Dav1dSequenceHeader seq;
+ unsigned seq_skip = 0;
+ while (dav1d_parse_sequence_header(&seq, data.data, data.sz)) {
+ if ((res = input_read(in, &data)) < 0) {
+ input_close(in);
+ return EXIT_FAILURE;
+ }
+ seq_skip++;
+ }
+ if (seq_skip && !cli_settings.quiet)
+ fprintf(stderr,
+ "skipped %u packets due to missing sequence header\n",
+ seq_skip);
+ }
+
+ //getc(stdin);
+ if (cli_settings.limit != 0 && cli_settings.limit < total)
+ total = cli_settings.limit;
+
+ if ((res = dav1d_open(&c, &lib_settings)))
+ return EXIT_FAILURE;
+
+ if (cli_settings.frametimes)
+ frametimes = fopen(cli_settings.frametimes, "w");
+
+ if (cli_settings.realtime != REALTIME_CUSTOM) {
+ if (fps[1] == 0) {
+ i_fps = 0;
+ nspf = 0;
+ } else {
+ i_fps = (double)fps[0] / fps[1];
+ nspf = 1000000000ULL * fps[1] / fps[0];
+ }
+ } else {
+ i_fps = cli_settings.realtime_fps;
+ nspf = (uint64_t)(1000000000.0 / cli_settings.realtime_fps);
+ }
+ tfirst = get_time_nanos();
+
+ do {
+ memset(&p, 0, sizeof(p));
+ if ((res = dav1d_send_data(c, &data)) < 0) {
+ if (res != DAV1D_ERR(EAGAIN)) {
+ fprintf(stderr, "Error decoding frame: %s\n",
+ strerror(DAV1D_ERR(res)));
+ break;
+ }
+ }
+
+ if ((res = dav1d_get_picture(c, &p)) < 0) {
+ if (res != DAV1D_ERR(EAGAIN)) {
+ fprintf(stderr, "Error decoding frame: %s\n",
+ strerror(DAV1D_ERR(res)));
+ break;
+ }
+ res = 0;
+ } else {
+ if (!n_out) {
+ if ((res = output_open(&out, cli_settings.muxer,
+ cli_settings.outputfile,
+ &p.p, fps)) < 0)
+ {
+ if (frametimes) fclose(frametimes);
+ return EXIT_FAILURE;
+ }
+ }
+ if ((res = output_write(out, &p)) < 0)
+ break;
+ n_out++;
+ if (nspf || !cli_settings.quiet) {
+ synchronize(cli_settings.realtime, cli_settings.realtime_cache,
+ n_out, nspf, tfirst, &elapsed, frametimes);
+ }
+ if (!cli_settings.quiet)
+ print_stats(istty, n_out, total, elapsed, i_fps);
+ }
+
+ if (cli_settings.limit && n_out == cli_settings.limit)
+ break;
+ } while (data.sz > 0 || !input_read(in, &data));
+
+ if (data.sz > 0) dav1d_data_unref(&data);
+
+ // flush
+ if (res == 0) while (!cli_settings.limit || n_out < cli_settings.limit) {
+ if ((res = dav1d_get_picture(c, &p)) < 0) {
+ if (res != DAV1D_ERR(EAGAIN)) {
+ fprintf(stderr, "Error decoding frame: %s\n",
+ strerror(DAV1D_ERR(res)));
+ } else {
+ res = 0;
+ break;
+ }
+ } else {
+ if (!n_out) {
+ if ((res = output_open(&out, cli_settings.muxer,
+ cli_settings.outputfile,
+ &p.p, fps)) < 0)
+ {
+ if (frametimes) fclose(frametimes);
+ return EXIT_FAILURE;
+ }
+ }
+ if ((res = output_write(out, &p)) < 0)
+ break;
+ n_out++;
+ if (nspf || !cli_settings.quiet) {
+ synchronize(cli_settings.realtime, cli_settings.realtime_cache,
+ n_out, nspf, tfirst, &elapsed, frametimes);
+ }
+ if (!cli_settings.quiet)
+ print_stats(istty, n_out, total, elapsed, i_fps);
+ }
+ }
+
+ if (frametimes) fclose(frametimes);
+
+ input_close(in);
+ if (out) {
+ if (!cli_settings.quiet && istty)
+ fprintf(stderr, "\n");
+ if (cli_settings.verify)
+ res |= output_verify(out, cli_settings.verify);
+ else
+ output_close(out);
+ } else {
+ fprintf(stderr, "No data decoded\n");
+ res = 1;
+ }
+ dav1d_close(&c);
+
+ return (res == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/third_party/dav1d/tools/dav1d.manifest b/third_party/dav1d/tools/dav1d.manifest
new file mode 100644
index 0000000000..68cd1856d7
--- /dev/null
+++ b/third_party/dav1d/tools/dav1d.manifest
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity type="win32" name="VideoLAN.dav1d" version="1.0.0.0"/>
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings>
+ <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
+ <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
+ </windowsSettings>
+ </application>
+</assembly>
diff --git a/third_party/dav1d/tools/dav1d.rc.in b/third_party/dav1d/tools/dav1d.rc.in
new file mode 100644
index 0000000000..a4b4900992
--- /dev/null
+++ b/third_party/dav1d/tools/dav1d.rc.in
@@ -0,0 +1,33 @@
+#define API_VERSION_NUMBER @API_VERSION_MAJOR@,@API_VERSION_MINOR@,@API_VERSION_REVISION@,0
+#define API_VERSION_NUMBER_STR "@API_VERSION_MAJOR@.@API_VERSION_MINOR@.@API_VERSION_REVISION@"
+#define PROJECT_VERSION_NUMBER @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_REVISION@,0
+#define PROJECT_VERSION_NUMBER_STR "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_REVISION@"
+
+#include <windows.h>
+
+1 RT_MANIFEST "dav1d.manifest"
+1 VERSIONINFO
+FILETYPE VFT_APP
+FILEOS VOS_NT_WINDOWS32
+PRODUCTVERSION PROJECT_VERSION_NUMBER
+FILEVERSION API_VERSION_NUMBER
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4"
+ BEGIN
+ VALUE "CompanyName", "VideoLAN"
+ VALUE "ProductName", "dav1d"
+ VALUE "ProductVersion", PROJECT_VERSION_NUMBER_STR
+ VALUE "FileVersion", API_VERSION_NUMBER_STR
+ VALUE "FileDescription", "dav1d " PROJECT_VERSION_NUMBER_STR " - AV1 decoder"
+ VALUE "InternalName", "dav1d"
+ VALUE "OriginalFilename", "dav1d.exe"
+ VALUE "LegalCopyright", "Copyright \251 @COPYRIGHT_YEARS@ VideoLAN and dav1d Authors"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
diff --git a/third_party/dav1d/tools/dav1d_cli_parse.c b/third_party/dav1d/tools/dav1d_cli_parse.c
new file mode 100644
index 0000000000..96f81e07df
--- /dev/null
+++ b/third_party/dav1d/tools/dav1d_cli_parse.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <getopt.h>
+#include <limits.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "dav1d_cli_parse.h"
+#include "src/cpu.h"
+
+static const char short_opts[] = "i:o:vql:s:";
+
+enum {
+ ARG_DEMUXER = 256,
+ ARG_MUXER,
+ ARG_FRAME_TIMES,
+ ARG_REALTIME,
+ ARG_REALTIME_CACHE,
+ ARG_FRAME_THREADS,
+ ARG_TILE_THREADS,
+ ARG_VERIFY,
+ ARG_FILM_GRAIN,
+ ARG_OPPOINT,
+ ARG_ALL_LAYERS,
+ ARG_SIZE_LIMIT,
+ ARG_CPU_MASK,
+};
+
+static const struct option long_opts[] = {
+ { "input", 1, NULL, 'i' },
+ { "output", 1, NULL, 'o' },
+ { "quiet", 0, NULL, 'q' },
+ { "demuxer", 1, NULL, ARG_DEMUXER },
+ { "muxer", 1, NULL, ARG_MUXER },
+ { "version", 0, NULL, 'v' },
+ { "frametimes", 1, NULL, ARG_FRAME_TIMES },
+ { "limit", 1, NULL, 'l' },
+ { "skip", 1, NULL, 's' },
+ { "realtime", 2, NULL, ARG_REALTIME },
+ { "realtimecache", 1, NULL, ARG_REALTIME_CACHE },
+ { "framethreads", 1, NULL, ARG_FRAME_THREADS },
+ { "tilethreads", 1, NULL, ARG_TILE_THREADS },
+ { "verify", 1, NULL, ARG_VERIFY },
+ { "filmgrain", 1, NULL, ARG_FILM_GRAIN },
+ { "oppoint", 1, NULL, ARG_OPPOINT },
+ { "alllayers", 1, NULL, ARG_ALL_LAYERS },
+ { "sizelimit", 1, NULL, ARG_SIZE_LIMIT },
+ { "cpumask", 1, NULL, ARG_CPU_MASK },
+ { NULL, 0, NULL, 0 },
+};
+
+#if ARCH_AARCH64 || ARCH_ARM
+#define ALLOWED_CPU_MASKS " or 'neon'"
+#elif ARCH_PPC64LE
+#define ALLOWED_CPU_MASKS " or 'vsx'"
+#elif ARCH_X86
+#define ALLOWED_CPU_MASKS \
+ ", 'sse2', 'ssse3', 'sse41', 'avx2' or 'avx512icl'"
+#else
+#define ALLOWED_CPU_MASKS "not yet implemented for this architecture"
+#endif
+
+static void usage(const char *const app, const char *const reason, ...) {
+ if (reason) {
+ va_list args;
+
+ va_start(args, reason);
+ vfprintf(stderr, reason, args);
+ va_end(args);
+ fprintf(stderr, "\n\n");
+ }
+ fprintf(stderr, "Usage: %s [options]\n\n", app);
+ fprintf(stderr, "Supported options:\n"
+ " --input/-i $file: input file\n"
+ " --output/-o $file: output file\n"
+ " --demuxer $name: force demuxer type ('ivf', 'section5' or 'annexb'; default: detect from content)\n"
+ " --muxer $name: force muxer type ('md5', 'yuv', 'yuv4mpeg2' or 'null'; default: detect from extension)\n"
+ " --quiet/-q: disable status messages\n"
+ " --frametimes $file: dump frame times to file\n"
+ " --limit/-l $num: stop decoding after $num frames\n"
+ " --skip/-s $num: skip decoding of the first $num frames\n"
+ " --realtime [$fract]: limit framerate, optional argument to override input framerate\n"
+ " --realtimecache $num: set the size of the cache in realtime mode (default: 0)\n"
+ " --version/-v: print version and exit\n"
+ " --framethreads $num: number of frame threads (default: 1)\n"
+ " --tilethreads $num: number of tile threads (default: 1)\n"
+ " --filmgrain $num: enable film grain application (default: 1, except if muxer is md5)\n"
+ " --oppoint $num: select an operating point of a scalable AV1 bitstream (0 - 31)\n"
+ " --alllayers $num: output all spatial layers of a scalable AV1 bitstream (default: 1)\n"
+ " --sizelimit $num: stop decoding if the frame size exceeds the specified limit\n"
+ " --verify $md5: verify decoded md5. implies --muxer md5, no output\n"
+ " --cpumask $mask: restrict permitted CPU instruction sets (0" ALLOWED_CPU_MASKS "; default: -1)\n");
+ exit(1);
+}
+
+static void error(const char *const app, const char *const optarg,
+ const int option, const char *const shouldbe)
+{
+ char optname[256];
+ int n;
+
+ for (n = 0; long_opts[n].name; n++)
+ if (long_opts[n].val == option)
+ break;
+ assert(long_opts[n].name);
+ if (long_opts[n].val < 256) {
+ sprintf(optname, "-%c/--%s", long_opts[n].val, long_opts[n].name);
+ } else {
+ sprintf(optname, "--%s", long_opts[n].name);
+ }
+
+ usage(app, "Invalid argument \"%s\" for option %s; should be %s",
+ optarg, optname, shouldbe);
+}
+
+static unsigned parse_unsigned(const char *const optarg, const int option,
+ const char *const app)
+{
+ char *end;
+ const unsigned res = (unsigned) strtoul(optarg, &end, 0);
+ if (*end || end == optarg) error(app, optarg, option, "an integer");
+ return res;
+}
+
+static int parse_optional_fraction(const char *const optarg, const int option,
+ const char *const app, double *value)
+{
+ if (optarg == NULL) return 0;
+ char *end;
+ *value = strtod(optarg, &end);
+ if (*end == '/' && end != optarg) {
+ const char *optarg2 = end + 1;
+ *value /= strtod(optarg2, &end);
+ if (*end || end == optarg2) error(app, optarg, option, "a fraction");
+ } else if (*end || end == optarg) {
+ error(app, optarg, option, "a fraction");
+ }
+ return 1;
+}
+
+typedef struct EnumParseTable {
+ const char *str;
+ const int val;
+} EnumParseTable;
+
+#if ARCH_X86
+enum CpuMask {
+ X86_CPU_MASK_SSE2 = DAV1D_X86_CPU_FLAG_SSE2,
+ X86_CPU_MASK_SSSE3 = DAV1D_X86_CPU_FLAG_SSSE3 | X86_CPU_MASK_SSE2,
+ X86_CPU_MASK_SSE41 = DAV1D_X86_CPU_FLAG_SSE41 | X86_CPU_MASK_SSSE3,
+ X86_CPU_MASK_AVX2 = DAV1D_X86_CPU_FLAG_AVX2 | X86_CPU_MASK_SSE41,
+ X86_CPU_MASK_AVX512ICL = DAV1D_X86_CPU_FLAG_AVX512ICL | X86_CPU_MASK_AVX2,
+};
+#endif
+
+static const EnumParseTable cpu_mask_tbl[] = {
+#if ARCH_AARCH64 || ARCH_ARM
+ { "neon", DAV1D_ARM_CPU_FLAG_NEON },
+#elif ARCH_PPC64LE
+ { "vsx", DAV1D_PPC_CPU_FLAG_VSX },
+#elif ARCH_X86
+ { "sse2", X86_CPU_MASK_SSE2 },
+ { "ssse3", X86_CPU_MASK_SSSE3 },
+ { "sse41", X86_CPU_MASK_SSE41 },
+ { "avx2", X86_CPU_MASK_AVX2 },
+ { "avx512icl", X86_CPU_MASK_AVX512ICL },
+#endif
+ { 0 },
+};
+
+static unsigned parse_enum(char *optarg, const EnumParseTable *const tbl,
+ const int option, const char *app)
+{
+ char str[1024];
+
+ strcpy(str, "any of ");
+ for (int n = 0; tbl[n].str; n++) {
+ if (!strcmp(tbl[n].str, optarg))
+ return tbl[n].val;
+
+ if (n) {
+ if (!tbl[n + 1].str)
+ strcat(str, " or ");
+ else
+ strcat(str, ", ");
+ }
+ strcat(str, tbl[n].str);
+ }
+
+ char *end;
+ unsigned res;
+ if (!strncmp(optarg, "0x", 2)) {
+ res = (unsigned) strtoul(&optarg[2], &end, 16);
+ } else {
+ res = (unsigned) strtoul(optarg, &end, 0);
+ }
+
+ if (*end || end == optarg) {
+ strcat(str, ", a hexadecimal (starting with 0x), or an integer");
+ error(app, optarg, option, str);
+ }
+
+ return res;
+}
+
+void parse(const int argc, char *const *const argv,
+ CLISettings *const cli_settings, Dav1dSettings *const lib_settings)
+{
+ int o;
+
+ memset(cli_settings, 0, sizeof(*cli_settings));
+ dav1d_default_settings(lib_settings);
+ int grain_specified = 0;
+
+ while ((o = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
+ switch (o) {
+ case 'o':
+ cli_settings->outputfile = optarg;
+ break;
+ case 'i':
+ cli_settings->inputfile = optarg;
+ break;
+ case 'q':
+ cli_settings->quiet = 1;
+ break;
+ case 'l':
+ cli_settings->limit = parse_unsigned(optarg, 'l', argv[0]);
+ break;
+ case 's':
+ cli_settings->skip = parse_unsigned(optarg, 's', argv[0]);
+ break;
+ case ARG_DEMUXER:
+ cli_settings->demuxer = optarg;
+ break;
+ case ARG_MUXER:
+ cli_settings->muxer = optarg;
+ break;
+ case ARG_FRAME_TIMES:
+ cli_settings->frametimes = optarg;
+ break;
+ case ARG_REALTIME:
+ // workaround to parse an optional argument of the form `--a b`
+ // (getopt only allows `--a=b`)
+ if (optarg == NULL && optind < argc && argv[optind] != NULL &&
+ argv[optind][0] != '-')
+ {
+ optarg = argv[optind];
+ optind++;
+ }
+ cli_settings->realtime = 1 + parse_optional_fraction(optarg,
+ ARG_REALTIME, argv[0], &cli_settings->realtime_fps);
+ break;
+ case ARG_REALTIME_CACHE:
+ cli_settings->realtime_cache =
+ parse_unsigned(optarg, ARG_REALTIME_CACHE, argv[0]);
+ break;
+ case ARG_FRAME_THREADS:
+ lib_settings->n_frame_threads =
+ parse_unsigned(optarg, ARG_FRAME_THREADS, argv[0]);
+ break;
+ case ARG_TILE_THREADS:
+ lib_settings->n_tile_threads =
+ parse_unsigned(optarg, ARG_TILE_THREADS, argv[0]);
+ break;
+ case ARG_VERIFY:
+ cli_settings->verify = optarg;
+ break;
+ case ARG_FILM_GRAIN:
+ lib_settings->apply_grain =
+ !!parse_unsigned(optarg, ARG_FILM_GRAIN, argv[0]);
+ grain_specified = 1;
+ break;
+ case ARG_OPPOINT:
+ lib_settings->operating_point =
+ parse_unsigned(optarg, ARG_OPPOINT, argv[0]);
+ break;
+ case ARG_ALL_LAYERS:
+ lib_settings->all_layers =
+ !!parse_unsigned(optarg, ARG_ALL_LAYERS, argv[0]);
+ break;
+ case ARG_SIZE_LIMIT: {
+ char *arg = optarg, *end;
+ uint64_t res = strtoul(arg, &end, 0);
+ if (*end == 'x') // NxM
+ res *= strtoul((arg = end + 1), &end, 0);
+ if (*end || end == arg || res >= UINT_MAX)
+ error(argv[0], optarg, ARG_SIZE_LIMIT, "an integer or dimension");
+ lib_settings->frame_size_limit = (unsigned) res;
+ break;
+ }
+ case 'v':
+ fprintf(stderr, "%s\n", dav1d_version());
+ exit(0);
+ case ARG_CPU_MASK:
+ dav1d_set_cpu_flags_mask(parse_enum(optarg, cpu_mask_tbl,
+ ARG_CPU_MASK, argv[0]));
+ break;
+ default:
+ usage(argv[0], NULL);
+ }
+ }
+
+ if (optind < argc)
+ usage(argv[0], "Extra/unused arguments found, e.g. '%s'\n", argv[optind]);
+ if (cli_settings->verify) {
+ if (cli_settings->outputfile)
+ usage(argv[0], "Verification (--verify) requires output file (-o/--output) to not set");
+ if (cli_settings->muxer && !strcmp(cli_settings->muxer, "md5"))
+ usage(argv[0], "Verification (--verify) requires the md5 muxer (--muxer md5)");
+
+ cli_settings->outputfile = "-";
+ if (!cli_settings->muxer)
+ cli_settings->muxer = "md5";
+ }
+
+ if (!grain_specified && cli_settings->muxer &&
+ !strcmp(cli_settings->muxer, "md5"))
+ {
+ lib_settings->apply_grain = 0;
+ }
+
+ if (!cli_settings->inputfile)
+ usage(argv[0], "Input file (-i/--input) is required");
+ if ((!cli_settings->muxer || strcmp(cli_settings->muxer, "null")) &&
+ !cli_settings->outputfile)
+ {
+ usage(argv[0], "Output file (-o/--output) is required");
+ }
+}
diff --git a/third_party/dav1d/tools/dav1d_cli_parse.h b/third_party/dav1d/tools/dav1d_cli_parse.h
new file mode 100644
index 0000000000..11e88e15d2
--- /dev/null
+++ b/third_party/dav1d/tools/dav1d_cli_parse.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DAV1D_CLI_PARSE_H
+#define DAV1D_CLI_PARSE_H
+
+#include "dav1d/dav1d.h"
+
+typedef struct {
+ const char *outputfile;
+ const char *inputfile;
+ const char *demuxer;
+ const char *muxer;
+ const char *frametimes;
+ const char *verify;
+ unsigned limit, skip;
+ int quiet;
+ enum {
+ REALTIME_DISABLE = 0,
+ REALTIME_INPUT,
+ REALTIME_CUSTOM,
+ } realtime;
+ double realtime_fps;
+ unsigned realtime_cache;
+} CLISettings;
+
+void parse(const int argc, char *const *const argv,
+ CLISettings *const cli_settings, Dav1dSettings *const lib_settings);
+
+#endif /* DAV1D_CLI_PARSE_H */
diff --git a/third_party/dav1d/tools/input/annexb.c b/third_party/dav1d/tools/input/annexb.c
new file mode 100644
index 0000000000..032480d00c
--- /dev/null
+++ b/third_party/dav1d/tools/input/annexb.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * Copyright © 2019, James Almer <jamrial@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common/intops.h"
+
+#include "dav1d/headers.h"
+
+#include "input/demuxer.h"
+#include "input/parse.h"
+
+// these functions are based on an implementation from FFmpeg, and relicensed
+// with author's permission
+
+#define PROBE_SIZE 1024
+
+static int annexb_probe(const uint8_t *data) {
+ int ret, cnt = 0;
+
+ size_t temporal_unit_size;
+ ret = leb(data + cnt, PROBE_SIZE - cnt, &temporal_unit_size);
+ if (ret < 0)
+ return 0;
+ cnt += ret;
+
+ size_t frame_unit_size;
+ ret = leb(data + cnt, PROBE_SIZE - cnt, &frame_unit_size);
+ if (ret < 0 || ((uint64_t)frame_unit_size + ret) > temporal_unit_size)
+ return 0;
+ cnt += ret;
+
+ temporal_unit_size -= ret;
+
+ size_t obu_unit_size;
+ ret = leb(data + cnt, PROBE_SIZE - cnt, &obu_unit_size);
+ if (ret < 0 || ((uint64_t)obu_unit_size + ret) >= frame_unit_size)
+ return 0;
+ cnt += ret;
+
+ temporal_unit_size -= obu_unit_size + ret;
+ frame_unit_size -= obu_unit_size + ret;
+
+ // Check that the first OBU is a Temporal Delimiter.
+ size_t obu_size;
+ enum Dav1dObuType type;
+ ret = parse_obu_header(data + cnt, imin(PROBE_SIZE - cnt, (int) obu_unit_size),
+ &obu_size, &type, 1);
+ if (ret < 0 || type != DAV1D_OBU_TD || obu_size > 0)
+ return 0;
+ cnt += (int)obu_unit_size;
+
+ // look for first frame and accompanying sequence header
+ int seq = 0;
+ while (cnt < PROBE_SIZE) {
+ ret = leb(data + cnt, PROBE_SIZE - cnt, &obu_unit_size);
+ if (ret < 0 || ((uint64_t)obu_unit_size + ret) > frame_unit_size)
+ return 0;
+ cnt += ret;
+ temporal_unit_size -= ret;
+ frame_unit_size -= ret;
+
+ ret = parse_obu_header(data + cnt, imin(PROBE_SIZE - cnt, (int) obu_unit_size),
+ &obu_size, &type, 1);
+ if (ret < 0)
+ return 0;
+ cnt += (int)obu_unit_size;
+
+ switch (type) {
+ case DAV1D_OBU_SEQ_HDR:
+ seq = 1;
+ break;
+ case DAV1D_OBU_FRAME:
+ case DAV1D_OBU_FRAME_HDR:
+ return seq;
+ case DAV1D_OBU_TD:
+ case DAV1D_OBU_TILE_GRP:
+ return 0;
+ default:
+ break;
+ }
+
+ temporal_unit_size -= obu_unit_size;
+ frame_unit_size -= obu_unit_size;
+ if (frame_unit_size <= 0)
+ break;
+ }
+
+ return 0;
+}
+
+typedef struct DemuxerPriv {
+ FILE *f;
+ size_t temporal_unit_size;
+ size_t frame_unit_size;
+} AnnexbInputContext;
+
+static int annexb_open(AnnexbInputContext *const c, const char *const file,
+ unsigned fps[2], unsigned *const num_frames, unsigned timebase[2])
+{
+ int res;
+ size_t len;
+
+ if (!(c->f = fopen(file, "rb"))) {
+ fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ return -1;
+ }
+
+ // TODO: Parse sequence header and read timing info if any.
+ fps[0] = 25;
+ fps[1] = 1;
+ timebase[0] = 25;
+ timebase[1] = 1;
+ for (*num_frames = 0;; (*num_frames)++) {
+ res = leb128(c->f, &len);
+ if (res < 0)
+ break;
+ fseeko(c->f, len, SEEK_CUR);
+ }
+ fseeko(c->f, 0, SEEK_SET);
+
+ return 0;
+}
+
+static int annexb_read(AnnexbInputContext *const c, Dav1dData *const data) {
+ size_t len;
+ int res;
+
+ if (!c->temporal_unit_size) {
+ res = leb128(c->f, &c->temporal_unit_size);
+ if (res < 0) return -1;
+ }
+ if (!c->frame_unit_size) {
+ res = leb128(c->f, &c->frame_unit_size);
+ if (res < 0 || (c->frame_unit_size + res) > c->temporal_unit_size) return -1;
+ c->temporal_unit_size -= res;
+ }
+ res = leb128(c->f, &len);
+ if (res < 0 || (len + res) > c->frame_unit_size) return -1;
+ uint8_t *ptr = dav1d_data_create(data, len);
+ if (!ptr) return -1;
+ c->temporal_unit_size -= len + res;
+ c->frame_unit_size -= len + res;
+ if (fread(ptr, len, 1, c->f) != 1) {
+ fprintf(stderr, "Failed to read frame data: %s\n", strerror(errno));
+ dav1d_data_unref(data);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void annexb_close(AnnexbInputContext *const c) {
+ fclose(c->f);
+}
+
+const Demuxer annexb_demuxer = {
+ .priv_data_size = sizeof(AnnexbInputContext),
+ .name = "annexb",
+ .probe = annexb_probe,
+ .probe_sz = PROBE_SIZE,
+ .open = annexb_open,
+ .read = annexb_read,
+ .close = annexb_close,
+};
diff --git a/third_party/dav1d/tools/input/demuxer.h b/third_party/dav1d/tools/input/demuxer.h
new file mode 100644
index 0000000000..c2b88e1480
--- /dev/null
+++ b/third_party/dav1d/tools/input/demuxer.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DAV1D_INPUT_DEMUXER_H
+#define DAV1D_INPUT_DEMUXER_H
+
+#include "data.h"
+
+typedef struct DemuxerPriv DemuxerPriv;
+typedef struct Demuxer {
+ int priv_data_size;
+ const char *name;
+ int probe_sz;
+ int (*probe)(const uint8_t *data);
+ int (*open)(DemuxerPriv *ctx, const char *filename,
+ unsigned fps[2], unsigned *num_frames, unsigned timebase[2]);
+ int (*read)(DemuxerPriv *ctx, Dav1dData *data);
+ void (*close)(DemuxerPriv *ctx);
+} Demuxer;
+
+#endif /* DAV1D_INPUT_DEMUXER_H */
diff --git a/third_party/dav1d/tools/input/input.c b/third_party/dav1d/tools/input/input.c
new file mode 100644
index 0000000000..3ed6983ace
--- /dev/null
+++ b/third_party/dav1d/tools/input/input.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common/attributes.h"
+#include "common/intops.h"
+
+#include "input/input.h"
+#include "input/demuxer.h"
+
+struct DemuxerContext {
+ DemuxerPriv *data;
+ const Demuxer *impl;
+};
+
+extern const Demuxer ivf_demuxer;
+extern const Demuxer annexb_demuxer;
+extern const Demuxer section5_demuxer;
+static const Demuxer *const demuxers[] = {
+ &ivf_demuxer,
+ &annexb_demuxer,
+ &section5_demuxer,
+ NULL
+};
+
+int input_open(DemuxerContext **const c_out,
+ const char *const name, const char *const filename,
+ unsigned fps[2], unsigned *const num_frames, unsigned timebase[2])
+{
+ const Demuxer *impl;
+ DemuxerContext *c;
+ int res, i;
+
+ if (name) {
+ for (i = 0; demuxers[i]; i++) {
+ if (!strcmp(demuxers[i]->name, name)) {
+ impl = demuxers[i];
+ break;
+ }
+ }
+ if (!demuxers[i]) {
+ fprintf(stderr, "Failed to find demuxer named \"%s\"\n", name);
+ return DAV1D_ERR(ENOPROTOOPT);
+ }
+ } else {
+ int probe_sz = 0;
+ for (i = 0; demuxers[i]; i++)
+ probe_sz = imax(probe_sz, demuxers[i]->probe_sz);
+ uint8_t *const probe_data = malloc(probe_sz);
+ if (!probe_data) {
+ fprintf(stderr, "Failed to allocate memory\n");
+ return DAV1D_ERR(ENOMEM);
+ }
+ FILE *f = fopen(filename, "rb");
+ if (!f) {
+ fprintf(stderr, "Failed to open input file %s: %s\n", filename, strerror(errno));
+ return errno ? DAV1D_ERR(errno) : DAV1D_ERR(EIO);
+ }
+ res = !!fread(probe_data, 1, probe_sz, f);
+ fclose(f);
+ if (!res) {
+ free(probe_data);
+ fprintf(stderr, "Failed to read probe data\n");
+ return errno ? DAV1D_ERR(errno) : DAV1D_ERR(EIO);
+ }
+
+ for (i = 0; demuxers[i]; i++) {
+ if (demuxers[i]->probe(probe_data)) {
+ impl = demuxers[i];
+ break;
+ }
+ }
+ free(probe_data);
+ if (!demuxers[i]) {
+ fprintf(stderr,
+ "Failed to probe demuxer for file %s\n",
+ filename);
+ return DAV1D_ERR(ENOPROTOOPT);
+ }
+ }
+
+ if (!(c = calloc(1, sizeof(DemuxerContext) + impl->priv_data_size))) {
+ fprintf(stderr, "Failed to allocate memory\n");
+ return DAV1D_ERR(ENOMEM);
+ }
+ c->impl = impl;
+ c->data = (DemuxerPriv *) &c[1];
+ if ((res = impl->open(c->data, filename, fps, num_frames, timebase)) < 0) {
+ free(c);
+ return res;
+ }
+ *c_out = c;
+
+ return 0;
+}
+
+int input_read(DemuxerContext *const ctx, Dav1dData *const data) {
+ return ctx->impl->read(ctx->data, data);
+}
+
+void input_close(DemuxerContext *const ctx) {
+ ctx->impl->close(ctx->data);
+ free(ctx);
+}
diff --git a/third_party/dav1d/tools/input/input.h b/third_party/dav1d/tools/input/input.h
new file mode 100644
index 0000000000..7b2fdc94c4
--- /dev/null
+++ b/third_party/dav1d/tools/input/input.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DAV1D_INPUT_INPUT_H
+#define DAV1D_INPUT_INPUT_H
+
+#include "data.h"
+
+typedef struct DemuxerContext DemuxerContext;
+
+int input_open(DemuxerContext **const c_out,
+ const char *const name, const char *const filename,
+ unsigned fps[2], unsigned *num_frames, unsigned timebase[2]);
+int input_read(DemuxerContext *ctx, Dav1dData *data);
+void input_close(DemuxerContext *ctx);
+
+#endif /* DAV1D_INPUT_INPUT_H */
diff --git a/third_party/dav1d/tools/input/ivf.c b/third_party/dav1d/tools/input/ivf.c
new file mode 100644
index 0000000000..7b572ee73c
--- /dev/null
+++ b/third_party/dav1d/tools/input/ivf.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "input/demuxer.h"
+
+typedef struct DemuxerPriv {
+ FILE *f;
+} IvfInputContext;
+
+static const uint8_t probe_data[] = {
+ 'D', 'K', 'I', 'F',
+ 0, 0, 0x20, 0,
+ 'A', 'V', '0', '1',
+};
+
+static int ivf_probe(const uint8_t *const data) {
+ return !memcmp(data, probe_data, sizeof(probe_data));
+}
+
+static unsigned rl32(const uint8_t *const p) {
+ return ((uint32_t)p[3] << 24U) | (p[2] << 16U) | (p[1] << 8U) | p[0];
+}
+
+static int64_t rl64(const uint8_t *const p) {
+ return (((uint64_t) rl32(&p[4])) << 32) | rl32(p);
+}
+
+static int ivf_open(IvfInputContext *const c, const char *const file,
+ unsigned fps[2], unsigned *const num_frames, unsigned timebase[2])
+{
+ size_t res;
+ uint8_t hdr[32];
+
+ if (!(c->f = fopen(file, "rb"))) {
+ fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ return -1;
+ } else if ((res = fread(hdr, 32, 1, c->f)) != 1) {
+ fprintf(stderr, "Failed to read stream header: %s\n", strerror(errno));
+ fclose(c->f);
+ return -1;
+ } else if (memcmp(hdr, "DKIF", 4)) {
+ fprintf(stderr, "%s is not an IVF file [tag=%.4s|0x%02x%02x%02x%02x]\n",
+ file, hdr, hdr[0], hdr[1], hdr[2], hdr[3]);
+ fclose(c->f);
+ return -1;
+ } else if (memcmp(&hdr[8], "AV01", 4)) {
+ fprintf(stderr, "%s is not an AV1 file [tag=%.4s|0x%02x%02x%02x%02x]\n",
+ file, &hdr[8], hdr[8], hdr[9], hdr[10], hdr[11]);
+ fclose(c->f);
+ return -1;
+ }
+
+ timebase[0] = rl32(&hdr[16]);
+ timebase[1] = rl32(&hdr[20]);
+ const unsigned duration = rl32(&hdr[24]);
+
+ uint8_t data[4];
+ for (*num_frames = 0;; (*num_frames)++) {
+ if ((res = fread(data, 4, 1, c->f)) != 1)
+ break; // EOF
+ fseeko(c->f, rl32(data) + 8, SEEK_CUR);
+ }
+
+ uint64_t fps_num = (uint64_t) timebase[0] * *num_frames;
+ uint64_t fps_den = (uint64_t) timebase[1] * duration;
+ if (fps_num && fps_den) { /* Reduce fraction */
+ uint64_t gcd = fps_num;
+ for (uint64_t a = fps_den, b; (b = a % gcd); a = gcd, gcd = b);
+ fps_num /= gcd;
+ fps_den /= gcd;
+
+ while ((fps_num | fps_den) > UINT_MAX) {
+ fps_num >>= 1;
+ fps_den >>= 1;
+ }
+ }
+ if (fps_num && fps_den) {
+ fps[0] = (unsigned) fps_num;
+ fps[1] = (unsigned) fps_den;
+ } else {
+ fps[0] = fps[1] = 0;
+ }
+
+ fseeko(c->f, 32, SEEK_SET);
+
+ return 0;
+}
+
+static int ivf_read(IvfInputContext *const c, Dav1dData *const buf) {
+ uint8_t data[8];
+ uint8_t *ptr;
+ size_t res;
+
+ const int64_t off = ftello(c->f);
+ if ((res = fread(data, 4, 1, c->f)) != 1)
+ return -1; // EOF
+ const ptrdiff_t sz = rl32(data);
+ if ((res = fread(data, 8, 1, c->f)) != 1)
+ return -1; // EOF
+ ptr = dav1d_data_create(buf, sz);
+ if (!ptr) return -1;
+ buf->m.offset = off;
+ buf->m.timestamp = rl64(data);
+ if ((res = fread(ptr, sz, 1, c->f)) != 1) {
+ fprintf(stderr, "Failed to read frame data: %s\n", strerror(errno));
+ dav1d_data_unref(buf);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ivf_close(IvfInputContext *const c) {
+ fclose(c->f);
+}
+
+const Demuxer ivf_demuxer = {
+ .priv_data_size = sizeof(IvfInputContext),
+ .name = "ivf",
+ .probe = ivf_probe,
+ .probe_sz = sizeof(probe_data),
+ .open = ivf_open,
+ .read = ivf_read,
+ .close = ivf_close,
+};
diff --git a/third_party/dav1d/tools/input/parse.h b/third_party/dav1d/tools/input/parse.h
new file mode 100644
index 0000000000..f5805e8ca4
--- /dev/null
+++ b/third_party/dav1d/tools/input/parse.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * Copyright © 2019, James Almer <jamrial@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DAV1D_INPUT_PARSE_H
+#define DAV1D_INPUT_PARSE_H
+
+#include <limits.h>
+
+#include "dav1d/headers.h"
+
+static int leb128(FILE *const f, size_t *const len) {
+ uint64_t val = 0;
+ unsigned i = 0, more;
+ do {
+ uint8_t v;
+ if (fread(&v, 1, 1, f) < 1)
+ return -1;
+ more = v & 0x80;
+ val |= ((uint64_t) (v & 0x7F)) << (i * 7);
+ i++;
+ } while (more && i < 8);
+ if (val > UINT_MAX || more)
+ return -1;
+ *len = (size_t) val;
+ return i;
+}
+
+// these functions are based on an implementation from FFmpeg, and relicensed
+// with author's permission
+
+static int leb(const uint8_t *ptr, int sz, size_t *const len) {
+ uint64_t val = 0;
+ unsigned i = 0, more;
+ do {
+ if (!sz--) return -1;
+ const int v = *ptr++;
+ more = v & 0x80;
+ val |= ((uint64_t) (v & 0x7F)) << (i * 7);
+ i++;
+ } while (more && i < 8);
+ if (val > UINT_MAX || more)
+ return -1;
+ *len = (size_t) val;
+ return i;
+}
+
+static inline int parse_obu_header(const uint8_t *buf, int buf_size,
+ size_t *const obu_size,
+ enum Dav1dObuType *const type,
+ const int allow_implicit_size)
+{
+ int ret, extension_flag, has_size_flag;
+
+ if (!buf_size)
+ return -1;
+ if (*buf & 0x80) // obu_forbidden_bit
+ return -1;
+
+ *type = (*buf & 0x78) >> 3;
+ extension_flag = (*buf & 0x4) >> 2;
+ has_size_flag = (*buf & 0x2) >> 1;
+ // ignore obu_reserved_1bit
+ buf++;
+ buf_size--;
+
+ if (extension_flag) {
+ buf++;
+ buf_size--;
+ // ignore fields
+ }
+
+ if (has_size_flag) {
+ ret = leb(buf, buf_size, obu_size);
+ if (ret < 0)
+ return -1;
+ return (int) *obu_size + ret + 1 + extension_flag;
+ } else if (!allow_implicit_size)
+ return -1;
+
+ *obu_size = buf_size;
+ return buf_size + 1 + extension_flag;
+}
+
+#endif /* DAV1D_INPUT_PARSE_H */
diff --git a/third_party/dav1d/tools/input/section5.c b/third_party/dav1d/tools/input/section5.c
new file mode 100644
index 0000000000..0c2ce28c63
--- /dev/null
+++ b/third_party/dav1d/tools/input/section5.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright © 2019, VideoLAN and dav1d authors
+ * Copyright © 2019, Two Orioles, LLC
+ * Copyright © 2019, James Almer <jamrial@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "dav1d/headers.h"
+
+#include "input/demuxer.h"
+#include "input/parse.h"
+
+#define PROBE_SIZE 1024
+
+static int section5_probe(const uint8_t *data) {
+ int ret, cnt = 0;
+
+ // Check that the first OBU is a Temporal Delimiter.
+ size_t obu_size;
+ enum Dav1dObuType type;
+ ret = parse_obu_header(data + cnt, PROBE_SIZE - cnt,
+ &obu_size, &type, 0);
+ if (ret < 0 || type != DAV1D_OBU_TD || obu_size > 0)
+ return 0;
+ cnt += ret;
+
+ // look for first frame and accompanying sequence header
+ int seq = 0;
+ while (cnt < PROBE_SIZE) {
+ ret = parse_obu_header(data + cnt, PROBE_SIZE - cnt,
+ &obu_size, &type, 0);
+ if (ret < 0)
+ return 0;
+ cnt += ret;
+
+ switch (type) {
+ case DAV1D_OBU_SEQ_HDR:
+ seq = 1;
+ break;
+ case DAV1D_OBU_FRAME:
+ case DAV1D_OBU_FRAME_HDR:
+ return seq;
+ case DAV1D_OBU_TD:
+ case DAV1D_OBU_TILE_GRP:
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+typedef struct DemuxerPriv {
+ FILE *f;
+} Section5InputContext;
+
+static int section5_open(Section5InputContext *const c, const char *const file,
+ unsigned fps[2], unsigned *const num_frames, unsigned timebase[2])
+{
+ if (!(c->f = fopen(file, "rb"))) {
+ fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ return -1;
+ }
+
+ // TODO: Parse sequence header and read timing info if any.
+ fps[0] = 25;
+ fps[1] = 1;
+ timebase[0] = 25;
+ timebase[1] = 1;
+ *num_frames = 0;
+ for (;;) {
+ uint8_t byte[2];
+
+ if (fread(&byte[0], 1, 1, c->f) < 1)
+ break;
+ const enum Dav1dObuType obu_type = (byte[0] >> 3) & 0xf;
+ if (obu_type == DAV1D_OBU_TD)
+ (*num_frames)++;
+ const int has_length_field = byte[0] & 0x2;
+ if (!has_length_field)
+ return -1;
+ const int has_extension = byte[0] & 0x4;
+ if (has_extension && fread(&byte[1], 1, 1, c->f) < 1)
+ return -1;
+ size_t len;
+ const int res = leb128(c->f, &len);
+ if (res < 0)
+ return -1;
+ fseeko(c->f, len, SEEK_CUR); // skip packet
+ }
+ fseeko(c->f, 0, SEEK_SET);
+
+ return 0;
+}
+
+static int section5_read(Section5InputContext *const c, Dav1dData *const data) {
+ size_t total_bytes = 0;
+
+ for (int first = 1;; first = 0) {
+ uint8_t byte[2];
+
+ if (fread(&byte[0], 1, 1, c->f) < 1) {
+ if (!first && feof(c->f)) break;
+ return -1;
+ }
+ const enum Dav1dObuType obu_type = (byte[0] >> 3) & 0xf;
+ if (first) {
+ if (obu_type != DAV1D_OBU_TD)
+ return -1;
+ } else {
+ if (obu_type == DAV1D_OBU_TD) {
+ // include TD in next packet
+ fseeko(c->f, -1, SEEK_CUR);
+ break;
+ }
+ }
+ const int has_length_field = byte[0] & 0x2;
+ if (!has_length_field)
+ return -1;
+ const int has_extension = !!(byte[0] & 0x4);
+ if (has_extension && fread(&byte[1], 1, 1, c->f) < 1)
+ return -1;
+ size_t len;
+ const int res = leb128(c->f, &len);
+ if (res < 0)
+ return -1;
+ total_bytes += 1 + has_extension + res + len;
+ fseeko(c->f, len, SEEK_CUR); // skip packet, we'll read it below
+ }
+
+ fseeko(c->f, -(off_t)total_bytes, SEEK_CUR);
+ uint8_t *ptr = dav1d_data_create(data, total_bytes);
+ if (!ptr) return -1;
+ if (fread(ptr, total_bytes, 1, c->f) != 1) {
+ fprintf(stderr, "Failed to read frame data: %s\n", strerror(errno));
+ dav1d_data_unref(data);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void section5_close(Section5InputContext *const c) {
+ fclose(c->f);
+}
+
+const Demuxer section5_demuxer = {
+ .priv_data_size = sizeof(Section5InputContext),
+ .name = "section5",
+ .probe = section5_probe,
+ .probe_sz = PROBE_SIZE,
+ .open = section5_open,
+ .read = section5_read,
+ .close = section5_close,
+};
diff --git a/third_party/dav1d/tools/meson.build b/third_party/dav1d/tools/meson.build
new file mode 100644
index 0000000000..76fa1e0bb5
--- /dev/null
+++ b/third_party/dav1d/tools/meson.build
@@ -0,0 +1,104 @@
+# Copyright © 2018, VideoLAN and dav1d authors
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Common source files used by tools and examples
+
+dav1d_input_sources = files(
+ 'input/input.c',
+ 'input/annexb.c',
+ 'input/ivf.c',
+ 'input/section5.c',
+)
+
+dav1d_output_sources = files(
+ 'output/md5.c',
+ 'output/null.c',
+ 'output/output.c',
+ 'output/y4m2.c',
+ 'output/yuv.c',
+)
+
+dav1d_input_objs = static_library('dav1d_input',
+ dav1d_input_sources,
+
+ include_directories : dav1d_inc_dirs,
+ install : false,
+ build_by_default : false,
+)
+
+dav1d_output_objs = static_library('dav1d_output',
+ dav1d_output_sources,
+
+ include_directories : dav1d_inc_dirs,
+ install : false,
+ build_by_default : false,
+)
+
+
+# Leave subdir if tools are disabled
+if not get_option('enable_tools')
+ subdir_done()
+endif
+
+
+#
+# Build definition for the dav1d tools
+#
+
+# Configuratin data for cli_config.h
+cli_cdata = configuration_data()
+
+cli_config_h_target = configure_file(output: 'cli_config.h', configuration: cli_cdata)
+
+# dav1d cli tool sources
+dav1d_sources = files(
+ 'dav1d.c',
+ 'dav1d_cli_parse.c',
+)
+
+if host_machine.system() == 'windows'
+ rc_file = configure_file(
+ input : 'dav1d.rc.in',
+ output : 'dav1d.rc',
+ configuration : rc_data
+ )
+
+ dav1d_rc_obj = winmod.compile_resources(rc_file,
+ depend_files : files('dav1d.manifest'),
+ include_directories : include_directories('.')
+ )
+else
+ dav1d_rc_obj = []
+endif
+
+dav1d = executable('dav1d',
+ dav1d_sources,
+ dav1d_rc_obj,
+ rev_target, cli_config_h_target,
+
+ link_with : [libdav1d, dav1d_input_objs, dav1d_output_objs],
+ include_directories : [dav1d_inc_dirs],
+ dependencies : [getopt_dependency, thread_dependency, rt_dependency],
+ install : true,
+)
diff --git a/third_party/dav1d/tools/output/md5.c b/third_party/dav1d/tools/output/md5.c
new file mode 100644
index 0000000000..6555de83ad
--- /dev/null
+++ b/third_party/dav1d/tools/output/md5.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "common/intops.h"
+
+#include "output/muxer.h"
+
+static const uint8_t s[][4] = {
+ { 7, 12, 17, 22, },
+ { 5, 9, 14, 20, },
+ { 4, 11, 16, 23, },
+ { 6, 10, 15, 21, },
+};
+
+static const unsigned k[] = {
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+ 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
+};
+
+
+#if ENDIANNESS_BIG
+#define NE2LE_32(x) (((x & 0x00ff) << 24) |\
+ ((x & 0xff00) << 8) |\
+ ((x >> 8) & 0xff00) |\
+ ((x >> 24) & 0x00ff))
+
+#define NE2LE_64(x) (((x & 0x000000ff) << 56) |\
+ ((x & 0x0000ff00) << 40) |\
+ ((x & 0x00ff0000) << 24) |\
+ ((x & 0xff000000) << 8) |\
+ ((x >> 8) & 0xff000000) |\
+ ((x >> 24) & 0x00ff0000) |\
+ ((x >> 40) & 0x0000ff00) |\
+ ((x >> 56) & 0x000000ff))
+
+#else
+#define NE2LE_32(x) (x)
+#define NE2LE_64(x) (x)
+#endif
+
+typedef struct MuxerPriv {
+ unsigned abcd[4];
+ uint8_t data[64];
+ uint64_t len;
+ FILE *f;
+#if ENDIANNESS_BIG
+ uint8_t *bswap;
+ int bswap_w;
+#endif
+} MD5Context;
+
+static int md5_open(MD5Context *const md5, const char *const file,
+ const Dav1dPictureParameters *const p,
+ const unsigned fps[2])
+{
+ if (!strcmp(file, "-")) {
+ md5->f = stdout;
+ } else if (!(md5->f = fopen(file, "wb"))) {
+ fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ return -1;
+ }
+
+#if ENDIANNESS_BIG
+ md5->bswap = NULL;
+ md5->bswap_w = 0;
+#endif
+
+ md5->abcd[0] = 0x67452301;
+ md5->abcd[1] = 0xefcdab89;
+ md5->abcd[2] = 0x98badcfe;
+ md5->abcd[3] = 0x10325476;
+ md5->len = 0;
+
+ return 0;
+}
+
+static inline unsigned leftrotate(const unsigned x, const unsigned c) {
+ return (x << c) | (x >> (32 - c));
+}
+
+static void md5_body(MD5Context *md5, const uint8_t *const _data) {
+ const uint32_t *data = (uint32_t *) _data;
+
+ unsigned a = md5->abcd[0];
+ unsigned b = md5->abcd[1];
+ unsigned c = md5->abcd[2];
+ unsigned d = md5->abcd[3];
+ unsigned i;
+
+ for (i = 0; i < 64; i++) {
+ unsigned f, g, tmp;
+
+ if (i < 16) {
+ f = (b & c) | (~b & d);
+ g = i;
+ } else if (i < 32) {
+ f = (d & b) | (~d & c);
+ g = (5 * i + 1) & 15;
+ } else if (i < 48) {
+ f = b ^ c ^ d;
+ g = (3 * i + 5) & 15;
+ } else {
+ f = c ^ (b | ~d);
+ g = (7 * i) & 15;
+ }
+
+ tmp = d;
+ d = c;
+ c = b;
+ b += leftrotate(a + f + k[i] + NE2LE_32(data[g]), s[i >> 4][i & 3]);
+ a = tmp;
+ }
+
+ md5->abcd[0] += a;
+ md5->abcd[1] += b;
+ md5->abcd[2] += c;
+ md5->abcd[3] += d;
+}
+
+static void md5_update(MD5Context *const md5, const uint8_t *data, unsigned len) {
+ if (!len) return;
+
+ if (md5->len & 63) {
+ const unsigned tmp = imin(len, 64 - (md5->len & 63));
+
+ memcpy(&md5->data[md5->len & 63], data, tmp);
+ len -= tmp;
+ data += tmp;
+ md5->len += tmp;
+ if (!(md5->len & 63))
+ md5_body(md5, md5->data);
+ }
+
+ while (len >= 64) {
+ memcpy(md5->data, data, 64);
+ md5_body(md5, md5->data);
+ md5->len += 64;
+ data += 64;
+ len -= 64;
+ }
+
+ if (len) {
+ memcpy(md5->data, data, len);
+ md5->len += len;
+ }
+}
+
+static int md5_write(MD5Context *const md5, Dav1dPicture *const p) {
+ const int hbd = p->p.bpc > 8;
+ const int w = p->p.w, h = p->p.h;
+ uint8_t *yptr = p->data[0];
+
+#if ENDIANNESS_BIG
+ if (hbd && (!md5->bswap || md5->bswap_w < p->p.w)) {
+ free(md5->bswap);
+ md5->bswap_w = 0;
+ md5->bswap = malloc(p->p.w << 1);
+ if (!md5->bswap) return -1;
+ md5->bswap_w = p->p.w;
+ }
+#endif
+
+ for (int y = 0; y < h; y++) {
+#if ENDIANNESS_BIG
+ if (hbd) {
+ for (int x = 0; x < w; x++) {
+ md5->bswap[2 * x + 1] = yptr[2 * x];
+ md5->bswap[2 * x] = yptr[2 * x + 1];
+ }
+ md5_update(md5, md5->bswap, w << hbd);
+ } else
+#endif
+ md5_update(md5, yptr, w << hbd);
+ yptr += p->stride[0];
+ }
+
+ if (p->p.layout != DAV1D_PIXEL_LAYOUT_I400) {
+ const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420;
+ const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444;
+ const int cw = (w + ss_hor) >> ss_hor;
+ const int ch = (h + ss_ver) >> ss_ver;
+ for (int pl = 1; pl <= 2; pl++) {
+ uint8_t *uvptr = p->data[pl];
+
+ for (int y = 0; y < ch; y++) {
+#if ENDIANNESS_BIG
+ if (hbd) {
+ for (int x = 0; x < cw; x++){
+ md5->bswap[2 * x + 1] = uvptr[2 * x];
+ md5->bswap[2 * x] = uvptr[2 * x + 1];
+ }
+ md5_update(md5, md5->bswap, cw << hbd);
+ } else
+#endif
+ md5_update(md5, uvptr, cw << hbd);
+ uvptr += p->stride[1];
+ }
+ }
+ }
+
+ dav1d_picture_unref(p);
+
+ return 0;
+}
+
+static void md5_finish(MD5Context *const md5) {
+ static const uint8_t bit[2] = { 0x80, 0x00 };
+ uint64_t len = NE2LE_64(md5->len << 3);
+
+ md5_update(md5, &bit[0], 1);
+ while ((md5->len & 63) != 56)
+ md5_update(md5, &bit[1], 1);
+ md5_update(md5, (uint8_t *) &len, 8);
+}
+
+static void md5_close(MD5Context *const md5) {
+ md5_finish(md5);
+ for (int i = 0; i < 4; i++)
+ fprintf(md5->f, "%2.2x%2.2x%2.2x%2.2x",
+ md5->abcd[i] & 0xff,
+ (md5->abcd[i] >> 8) & 0xff,
+ (md5->abcd[i] >> 16) & 0xff,
+ md5->abcd[i] >> 24);
+ fprintf(md5->f, "\n");
+
+#if ENDIANNESS_BIG
+ free(md5->bswap);
+ md5->bswap_w = 0;
+#endif
+
+ if (md5->f != stdout)
+ fclose(md5->f);
+}
+
+static int md5_verify(MD5Context *const md5, const char *const md5_str) {
+ md5_finish(md5);
+
+ if (strlen(md5_str) < 32)
+ return 0;
+
+ const char *p = md5_str;
+ unsigned abcd[4] = { 0 };
+ char t[3] = { 0 };
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ unsigned val;
+ char *ignore;
+ memcpy(t, p, 2);
+ p += 2;
+ val = (unsigned) strtoul(t, &ignore, 16);
+ abcd[i] |= val << (8 * j);
+ }
+ }
+
+#if ENDIANNESS_BIG
+ free(md5->bswap);
+ md5->bswap_w = 0;
+#endif
+
+ return !!memcmp(abcd, md5->abcd, sizeof(abcd));
+}
+
+const Muxer md5_muxer = {
+ .priv_data_size = sizeof(MD5Context),
+ .name = "md5",
+ .extension = "md5",
+ .write_header = md5_open,
+ .write_picture = md5_write,
+ .write_trailer = md5_close,
+ .verify = md5_verify,
+};
diff --git a/third_party/dav1d/tools/output/muxer.h b/third_party/dav1d/tools/output/muxer.h
new file mode 100644
index 0000000000..54b3f6aa13
--- /dev/null
+++ b/third_party/dav1d/tools/output/muxer.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DAV1D_OUTPUT_MUXER_H
+#define DAV1D_OUTPUT_MUXER_H
+
+#include "picture.h"
+
+typedef struct MuxerPriv MuxerPriv;
+typedef struct Muxer {
+ int priv_data_size;
+ const char *name;
+ const char *extension;
+ int (*write_header)(MuxerPriv *ctx, const char *filename,
+ const Dav1dPictureParameters *p, const unsigned fps[2]);
+ int (*write_picture)(MuxerPriv *ctx, Dav1dPicture *p);
+ void (*write_trailer)(MuxerPriv *ctx);
+ /**
+ * Verifies the muxed data (for example in the md5 muxer). Replaces write_trailer.
+ *
+ * @param hash_string Muxer specific reference value.
+ *
+ * @return 0 on success.
+ */
+ int (*verify)(MuxerPriv *ctx, const char *hash_string);
+} Muxer;
+
+#endif /* DAV1D_OUTPUT_MUXER_H */
diff --git a/third_party/dav1d/tools/output/null.c b/third_party/dav1d/tools/output/null.c
new file mode 100644
index 0000000000..f8633f3e67
--- /dev/null
+++ b/third_party/dav1d/tools/output/null.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "output/muxer.h"
+
+typedef struct MuxerPriv NullOutputContext;
+
+static int null_write(NullOutputContext *const c, Dav1dPicture *const p) {
+ dav1d_picture_unref(p);
+ return 0;
+}
+
+const Muxer null_muxer = {
+ .priv_data_size = 0,
+ .name = "null",
+ .extension = "null",
+ .write_picture = null_write,
+};
diff --git a/third_party/dav1d/tools/output/output.c b/third_party/dav1d/tools/output/output.c
new file mode 100644
index 0000000000..368d079156
--- /dev/null
+++ b/third_party/dav1d/tools/output/output.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common/attributes.h"
+
+#include "output/output.h"
+#include "output/muxer.h"
+
+struct MuxerContext {
+ MuxerPriv *data;
+ const Muxer *impl;
+};
+
+extern const Muxer null_muxer;
+extern const Muxer md5_muxer;
+extern const Muxer yuv_muxer;
+extern const Muxer y4m2_muxer;
+static const Muxer *muxers[] = {
+ &null_muxer,
+ &md5_muxer,
+ &yuv_muxer,
+ &y4m2_muxer,
+ NULL
+};
+
+static const char *find_extension(const char *const f) {
+ const size_t l = strlen(f);
+
+ if (l == 0) return NULL;
+
+ const char *const end = &f[l - 1], *step = end;
+ while ((*step >= 'a' && *step <= 'z') ||
+ (*step >= 'A' && *step <= 'Z') ||
+ (*step >= '0' && *step <= '9'))
+ {
+ step--;
+ }
+
+ return (step < end && step > f && *step == '.' && step[-1] != '/') ?
+ &step[1] : NULL;
+}
+
+int output_open(MuxerContext **const c_out,
+ const char *const name, const char *const filename,
+ const Dav1dPictureParameters *const p, const unsigned fps[2])
+{
+ const Muxer *impl;
+ MuxerContext *c;
+ unsigned i;
+ int res;
+
+ if (name) {
+ for (i = 0; muxers[i]; i++) {
+ if (!strcmp(muxers[i]->name, name)) {
+ impl = muxers[i];
+ break;
+ }
+ }
+ if (!muxers[i]) {
+ fprintf(stderr, "Failed to find muxer named \"%s\"\n", name);
+ return DAV1D_ERR(ENOPROTOOPT);
+ }
+ } else if (!strcmp(filename, "/dev/null")) {
+ impl = muxers[0];
+ } else {
+ const char *const ext = find_extension(filename);
+ if (!ext) {
+ fprintf(stderr, "No extension found for file %s\n", filename);
+ return -1;
+ }
+ for (i = 0; muxers[i]; i++) {
+ if (!strcmp(muxers[i]->extension, ext)) {
+ impl = muxers[i];
+ break;
+ }
+ }
+ if (!muxers[i]) {
+ fprintf(stderr, "Failed to find muxer for extension \"%s\"\n", ext);
+ return DAV1D_ERR(ENOPROTOOPT);
+ }
+ }
+
+ if (!(c = malloc(sizeof(MuxerContext) + impl->priv_data_size))) {
+ fprintf(stderr, "Failed to allocate memory\n");
+ return DAV1D_ERR(ENOMEM);
+ }
+ c->impl = impl;
+ c->data = (MuxerPriv *) &c[1];
+ if (impl->write_header && (res = impl->write_header(c->data, filename, p, fps)) < 0) {
+ free(c);
+ return res;
+ }
+ *c_out = c;
+
+ return 0;
+}
+
+int output_write(MuxerContext *const ctx, Dav1dPicture *const p) {
+ const int res = ctx->impl->write_picture(ctx->data, p);
+ return res < 0 ? res : 0;
+}
+
+void output_close(MuxerContext *const ctx) {
+ if (ctx->impl->write_trailer)
+ ctx->impl->write_trailer(ctx->data);
+ free(ctx);
+}
+
+int output_verify(MuxerContext *const ctx, const char *const md5_str) {
+ const int res = ctx->impl->verify ?
+ ctx->impl->verify(ctx->data, md5_str) : 0;
+ free(ctx);
+ return res;
+}
diff --git a/third_party/dav1d/tools/output/output.h b/third_party/dav1d/tools/output/output.h
new file mode 100644
index 0000000000..6111c8682b
--- /dev/null
+++ b/third_party/dav1d/tools/output/output.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DAV1D_OUTPUT_OUTPUT_H
+#define DAV1D_OUTPUT_OUTPUT_H
+
+#include "picture.h"
+
+typedef struct MuxerContext MuxerContext;
+
+int output_open(MuxerContext **c, const char *name, const char *filename,
+ const Dav1dPictureParameters *p, const unsigned fps[2]);
+int output_write(MuxerContext *ctx, Dav1dPicture *pic);
+void output_close(MuxerContext *ctx);
+/**
+ * Verifies the muxed data (for example in the md5 muxer). Replaces output_close.
+ *
+ * @param hash_string Muxer specific reference value.
+ *
+ * @return 0 on success.
+ */
+int output_verify(MuxerContext *ctx, const char *hash_string);
+
+#endif /* DAV1D_OUTPUT_OUTPUT_H */
diff --git a/third_party/dav1d/tools/output/y4m2.c b/third_party/dav1d/tools/output/y4m2.c
new file mode 100644
index 0000000000..8766f64868
--- /dev/null
+++ b/third_party/dav1d/tools/output/y4m2.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "output/muxer.h"
+
+typedef struct MuxerPriv {
+ FILE *f;
+ int first;
+ unsigned fps[2];
+} Y4m2OutputContext;
+
+static int y4m2_open(Y4m2OutputContext *const c, const char *const file,
+ const Dav1dPictureParameters *p, const unsigned fps[2])
+{
+ if (!strcmp(file, "-")) {
+ c->f = stdout;
+ } else if (!(c->f = fopen(file, "wb"))) {
+ fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ return -1;
+ }
+
+ c->first = 1;
+ c->fps[0] = fps[0];
+ c->fps[1] = fps[1];
+
+ return 0;
+}
+
+static int write_header(Y4m2OutputContext *const c, const Dav1dPicture *const p) {
+ static const char *const ss_names[][3] = {
+ [DAV1D_PIXEL_LAYOUT_I400] = { "mono", "mono10", "mono12" },
+ [DAV1D_PIXEL_LAYOUT_I420] = { NULL, "420p10", "420p12" },
+ [DAV1D_PIXEL_LAYOUT_I422] = { "422", "422p10", "422p12" },
+ [DAV1D_PIXEL_LAYOUT_I444] = { "444", "444p10", "444p12" }
+ };
+
+ static const char *const chr_names_8bpc_i420[] = {
+ [DAV1D_CHR_UNKNOWN] = "420jpeg",
+ [DAV1D_CHR_VERTICAL] = "420mpeg2",
+ [DAV1D_CHR_COLOCATED] = "420"
+ };
+
+ const char *const ss_name =
+ p->p.layout == DAV1D_PIXEL_LAYOUT_I420 && p->p.bpc == 8 ?
+ chr_names_8bpc_i420[p->seq_hdr->chr > 2 ? DAV1D_CHR_UNKNOWN : p->seq_hdr->chr] :
+ ss_names[p->p.layout][p->seq_hdr->hbd];
+
+ const unsigned fw = p->p.w;
+ const unsigned fh = p->p.h;
+ uint64_t aw = (uint64_t)fh * p->frame_hdr->render_width;
+ uint64_t ah = (uint64_t)fw * p->frame_hdr->render_height;
+ uint64_t gcd = ah;
+ for (uint64_t a = aw, b; (b = a % gcd); a = gcd, gcd = b);
+ aw /= gcd;
+ ah /= gcd;
+
+ fprintf(c->f, "YUV4MPEG2 W%u H%u F%u:%u Ip A%"PRIu64":%"PRIu64" C%s\n",
+ fw, fh, c->fps[0], c->fps[1], aw, ah, ss_name);
+
+ return 0;
+}
+
+static int y4m2_write(Y4m2OutputContext *const c, Dav1dPicture *const p) {
+ if (c->first) {
+ c->first = 0;
+ const int res = write_header(c, p);
+ if (res < 0) return res;
+ }
+ fprintf(c->f, "FRAME\n");
+
+ uint8_t *ptr;
+ const int hbd = p->p.bpc > 8;
+
+ ptr = p->data[0];
+ for (int y = 0; y < p->p.h; y++) {
+ if (fwrite(ptr, p->p.w << hbd, 1, c->f) != 1)
+ goto error;
+ ptr += p->stride[0];
+ }
+
+ if (p->p.layout != DAV1D_PIXEL_LAYOUT_I400) {
+ // u/v
+ const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420;
+ const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444;
+ const int cw = (p->p.w + ss_hor) >> ss_hor;
+ const int ch = (p->p.h + ss_ver) >> ss_ver;
+ for (int pl = 1; pl <= 2; pl++) {
+ ptr = p->data[pl];
+ for (int y = 0; y < ch; y++) {
+ if (fwrite(ptr, cw << hbd, 1, c->f) != 1)
+ goto error;
+ ptr += p->stride[1];
+ }
+ }
+ }
+
+ dav1d_picture_unref(p);
+ return 0;
+
+error:
+ dav1d_picture_unref(p);
+ fprintf(stderr, "Failed to write frame data: %s\n", strerror(errno));
+ return -1;
+}
+
+static void y4m2_close(Y4m2OutputContext *const c) {
+ if (c->f != stdout)
+ fclose(c->f);
+}
+
+const Muxer y4m2_muxer = {
+ .priv_data_size = sizeof(Y4m2OutputContext),
+ .name = "yuv4mpeg2",
+ .extension = "y4m",
+ .write_header = y4m2_open,
+ .write_picture = y4m2_write,
+ .write_trailer = y4m2_close,
+};
diff --git a/third_party/dav1d/tools/output/yuv.c b/third_party/dav1d/tools/output/yuv.c
new file mode 100644
index 0000000000..406f284188
--- /dev/null
+++ b/third_party/dav1d/tools/output/yuv.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright © 2018, VideoLAN and dav1d authors
+ * Copyright © 2018, Two Orioles, LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "output/muxer.h"
+
+typedef struct MuxerPriv {
+ FILE *f;
+} YuvOutputContext;
+
+static int yuv_open(YuvOutputContext *const c, const char *const file,
+ const Dav1dPictureParameters *const p,
+ const unsigned fps[2])
+{
+ if (!strcmp(file, "-")) {
+ c->f = stdout;
+ } else if (!(c->f = fopen(file, "wb"))) {
+ fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int yuv_write(YuvOutputContext *const c, Dav1dPicture *const p) {
+ uint8_t *ptr;
+ const int hbd = p->p.bpc > 8;
+
+ ptr = p->data[0];
+ for (int y = 0; y < p->p.h; y++) {
+ if (fwrite(ptr, p->p.w << hbd, 1, c->f) != 1)
+ goto error;
+ ptr += p->stride[0];
+ }
+
+ if (p->p.layout != DAV1D_PIXEL_LAYOUT_I400) {
+ // u/v
+ const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420;
+ const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444;
+ const int cw = (p->p.w + ss_hor) >> ss_hor;
+ const int ch = (p->p.h + ss_ver) >> ss_ver;
+ for (int pl = 1; pl <= 2; pl++) {
+ ptr = p->data[pl];
+ for (int y = 0; y < ch; y++) {
+ if (fwrite(ptr, cw << hbd, 1, c->f) != 1)
+ goto error;
+ ptr += p->stride[1];
+ }
+ }
+ }
+
+ dav1d_picture_unref(p);
+ return 0;
+
+error:
+ dav1d_picture_unref(p);
+ fprintf(stderr, "Failed to write frame data: %s\n", strerror(errno));
+ return -1;
+}
+
+static void yuv_close(YuvOutputContext *const c) {
+ if (c->f != stdout)
+ fclose(c->f);
+}
+
+const Muxer yuv_muxer = {
+ .priv_data_size = sizeof(YuvOutputContext),
+ .name = "yuv",
+ .extension = "yuv",
+ .write_header = yuv_open,
+ .write_picture = yuv_write,
+ .write_trailer = yuv_close,
+};