diff options
author | Pablo Stebler <pablo@stebler.xyz> | 2019-05-09 16:25:21 +0300 |
---|---|---|
committer | Jean-Baptiste Kempf <jb@videolan.org> | 2019-05-09 16:25:21 +0300 |
commit | d400361524ce739db30d552a9e54809d812710c6 (patch) | |
tree | 546501a7fb62715b7078a5d8ed5ceff6d1519d3c /tools | |
parent | 8f7af99687533d15a9b5d16abc7b9d7b0cd4dcd0 (diff) |
Add fps counter and --realtime, --frametimes and --realtimecache options
Fixes #262.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/dav1d.c | 109 | ||||
-rw-r--r-- | tools/dav1d_cli_parse.c | 78 | ||||
-rw-r--r-- | tools/dav1d_cli_parse.h | 8 |
3 files changed, 169 insertions, 26 deletions
diff --git a/tools/dav1d.c b/tools/dav1d.c index 63a658a..c744b54 100644 --- a/tools/dav1d.c +++ b/tools/dav1d.c @@ -30,15 +30,20 @@ #include <assert.h> #include <errno.h> +#include <inttypes.h> #include <math.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 #include "dav1d/dav1d.h" @@ -48,18 +53,70 @@ #include "dav1d_cli_parse.h" -static void print_stats(const int istty, const unsigned n, - const unsigned num) +static uint64_t get_time_nanos(void) { +#ifdef _WIN32 + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + LARGE_INTEGER t; + QueryPerformanceCounter(&t); + return 1000000000 * t.QuadPart / frequency.QuadPart; +#else + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return 1000000000ULL * ts.tv_sec + ts.tv_nsec; +#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 char *pre_string = istty ? "\r" : ""; - const char *post_string = istty ? "" : "\n"; + 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); + } +} - if (num == 0xFFFFFFFFU) { - fprintf(stderr, "%sDecoded %u frames%s", pre_string, n, post_string); +static void print_stats(const int istty, const unsigned n, const unsigned num, + const uint64_t elapsed, const double i_fps) +{ + if (istty) fputs("\r", stderr); + const double d_fps = 1e9 * n / elapsed; + const double speed = d_fps / i_fps; + if (num == 0xFFFFFFFF) { + fprintf(stderr, "Decoded %u frames", n); } else { - fprintf(stderr, "%sDecoded %u/%u frames (%.1lf%%)%s", - pre_string, n, num, 100.0 * n / num, post_string); + fprintf(stderr, "Decoded %u/%u frames (%.1lf%%)", n, num, + 100.0 * n / num); } + if (i_fps) + fprintf(stderr, " - %.2lf/%.2lf fps (%.2lfx)", d_fps, i_fps, speed); + if (!istty) fputs("\n", stderr); } int main(const int argc, char *const *const argv) { @@ -73,6 +130,9 @@ int main(const int argc, char *const *const argv) { Dav1dContext *c; Dav1dData data; unsigned n_out = 0, total, fps[2]; + uint64_t nspf, tfirst, elapsed; + double i_fps; + FILE *frametimes = NULL; const char *version = dav1d_version(); if (strcmp(version, DAV1D_VERSION)) { @@ -126,6 +186,23 @@ int main(const int argc, char *const *const argv) { if ((res = dav1d_open(&c, &lib_settings))) return res; + 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 = 1000000000.0 / cli_settings.realtime_fps; + } + tfirst = get_time_nanos(); + do { memset(&p, 0, sizeof(p)); if ((res = dav1d_send_data(c, &data)) < 0) { @@ -149,14 +226,19 @@ int main(const int argc, char *const *const argv) { cli_settings.outputfile, &p.p, fps)) < 0) { + if (frametimes) fclose(frametimes); return res; } } if ((res = output_write(out, &p)) < 0) break; n_out++; + if (nspf) { + synchronize(cli_settings.realtime, cli_settings.realtime_cache, + n_out, nspf, tfirst, &elapsed, frametimes); + } if (!cli_settings.quiet) - print_stats(istty, n_out, total); + print_stats(istty, n_out, total, elapsed, i_fps); } if (cli_settings.limit && n_out == cli_settings.limit) @@ -181,17 +263,24 @@ int main(const int argc, char *const *const argv) { cli_settings.outputfile, &p.p, fps)) < 0) { + if (frametimes) fclose(frametimes); return res; } } if ((res = output_write(out, &p)) < 0) break; n_out++; + if (nspf) { + synchronize(cli_settings.realtime, cli_settings.realtime_cache, + n_out, nspf, tfirst, &elapsed, frametimes); + } if (!cli_settings.quiet) - print_stats(istty, n_out, total); + print_stats(istty, n_out, total, elapsed, i_fps); } } + if (frametimes) fclose(frametimes); + input_close(in); if (out) { if (!cli_settings.quiet && istty) diff --git a/tools/dav1d_cli_parse.c b/tools/dav1d_cli_parse.c index b364ca3..00b1339 100644 --- a/tools/dav1d_cli_parse.c +++ b/tools/dav1d_cli_parse.c @@ -46,6 +46,9 @@ 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, @@ -62,8 +65,11 @@ static const struct option long_opts[] = { { "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 }, @@ -94,21 +100,24 @@ static void usage(const char *const app, const char *const reason, ...) { } 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' or 'annexb'; default: detect from extension)\n" - " --muxer $name: force muxer type ('md5', 'yuv', 'yuv4mpeg2' or 'null'; default: detect from extension)\n" - " --quiet/-q: disable status messages\n" - " --limit/-l $num: stop decoding after $num frames\n" - " --skip/-s $num: skip decoding of the first $num frames\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 - 32)\n" - " --alllayers $num: output all spatial layers of a scalable AV1 bitstream (default: 1)\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"); + " --input/-i $file: input file\n" + " --output/-o $file: output file\n" + " --demuxer $name: force demuxer type ('ivf' or 'annexb'; default: detect from extension)\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 - 32)\n" + " --alllayers $num: output all spatial layers of a scalable AV1 bitstream (default: 1)\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); } @@ -132,13 +141,31 @@ static void error(const char *const app, const char *const optarg, optarg, optname, shouldbe); } -static unsigned parse_unsigned(char *optarg, const int option, const char *app) { +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; @@ -238,6 +265,25 @@ void parse(const int argc, char *const *const argv, 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]); diff --git a/tools/dav1d_cli_parse.h b/tools/dav1d_cli_parse.h index 899f207..11e88e1 100644 --- a/tools/dav1d_cli_parse.h +++ b/tools/dav1d_cli_parse.h @@ -35,9 +35,17 @@ typedef struct { 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, |