diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/dav1d/tools | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/dav1d/tools')
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, + §ion5_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, +}; |