diff options
author | Ronald S. Bultje <rsbultje@gmail.com> | 2019-11-17 19:05:24 +0300 |
---|---|---|
committer | Ronald S. Bultje <rsbultje@gmail.com> | 2022-01-06 21:50:09 +0300 |
commit | 36beb8185dd9de5450c9a4cbcd0d969901a6b0cc (patch) | |
tree | b4e8531c680cab3a40aca3d872ee337e16793a92 /tools | |
parent | f9bddfff7bd93b875a86d4401bc699337706b8cf (diff) |
Add option to write each frame to separate output file
For per-file yuv/y4m writes, this can be automatically specified
using e.g. -o file_%w_%h_%5n.yuv/y4m. --muxer=framemd5 -o - --quiet
will accomplish the same for per-frame md5sums.
Addresses part of #310.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/dav1d_cli_parse.c | 4 | ||||
-rw-r--r-- | tools/output/output.c | 120 |
2 files changed, 117 insertions, 7 deletions
diff --git a/tools/dav1d_cli_parse.c b/tools/dav1d_cli_parse.c index a264b4f..533bb26 100644 --- a/tools/dav1d_cli_parse.c +++ b/tools/dav1d_cli_parse.c @@ -116,9 +116,9 @@ 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" + " --output/-o $file: output file (%%n, %%w or %%h will be filled in for per-frame files)\n" " --demuxer $name: force demuxer type ('ivf', 'section5' or 'annexb'; default: detect from content)\n" - " --muxer $name: force muxer type (" AVAILABLE_MUXERS "; default: detect from extension)\n" + " --muxer $name: force muxer type (" AVAILABLE_MUXERS "; use 'frame' as prefix to write per-frame files; default: detect from extension; if filename contains %%n, will default to writing per-frame files)\n" " --quiet/-q: disable status messages\n" " --frametimes $file: dump frame times to file\n" " --limit/-l $num: stop decoding after $num frames\n" diff --git a/tools/output/output.c b/tools/output/output.c index fe4e07e..cf66d2e 100644 --- a/tools/output/output.c +++ b/tools/output/output.c @@ -34,6 +34,7 @@ #include <string.h> #include "common/attributes.h" +#include "common/intops.h" #include "output/output.h" #include "output/muxer.h" @@ -41,6 +42,10 @@ struct MuxerContext { MuxerPriv *data; const Muxer *impl; + int one_file_per_frame; + unsigned fps[2]; + const char *filename; + int framenum; }; extern const Muxer null_muxer; @@ -84,10 +89,12 @@ int output_open(MuxerContext **const c_out, MuxerContext *c; unsigned i; int res; + int name_offset = 0; if (name) { + name_offset = 5 * !strncmp(name, "frame", 5); for (i = 0; muxers[i]; i++) { - if (!strcmp(muxers[i]->name, name)) { + if (!strcmp(muxers[i]->name, &name[name_offset])) { impl = muxers[i]; break; } @@ -122,7 +129,25 @@ int output_open(MuxerContext **const c_out, } c->impl = impl; c->data = (MuxerPriv *) &c[1]; - if (impl->write_header && (res = impl->write_header(c->data, filename, p, fps)) < 0) { + int have_num_pattern = 0; + for (const char *ptr = filename ? strchr(filename, '%') : NULL; + !have_num_pattern && ptr; ptr = strchr(ptr, '%')) + { + ptr++; // skip '%' + while (*ptr >= '0' && *ptr <= '9') + ptr++; // skip length indicators + have_num_pattern = *ptr == 'n'; + } + c->one_file_per_frame = name_offset || (!name && have_num_pattern); + + if (c->one_file_per_frame) { + c->fps[0] = fps[0]; + c->fps[1] = fps[1]; + c->filename = filename; + c->framenum = 0; + } else if (impl->write_header && + (res = impl->write_header(c->data, filename, p, fps)) < 0) + { free(c); return res; } @@ -131,13 +156,98 @@ int output_open(MuxerContext **const c_out, return 0; } +static void safe_strncat(char *const dst, const int dst_len, + const char *const src, const int src_len) +{ + if (!src_len) return; + const int dst_fill = (int) strlen(dst); + assert(dst_fill < dst_len); + const int to_copy = imin(src_len, dst_len - dst_fill - 1); + if (!to_copy) return; + memcpy(dst + dst_fill, src, to_copy); + dst[dst_fill + to_copy] = 0; +} + +static void assemble_field(char *const dst, const int dst_len, + const char *const fmt, const int fmt_len, + const int field) +{ + char fmt_copy[32]; + + assert(fmt[0] == '%'); + fmt_copy[0] = '%'; + if (fmt[1] >= '1' && fmt[1] <= '9') { + fmt_copy[1] = '0'; // pad with zeroes, not spaces + fmt_copy[2] = 0; + } else { + fmt_copy[1] = 0; + } + safe_strncat(fmt_copy, sizeof(fmt_copy), &fmt[1], fmt_len - 1); + safe_strncat(fmt_copy, sizeof(fmt_copy), "d", 1); + + char tmp[32]; + snprintf(tmp, sizeof(tmp), fmt_copy, field); + + safe_strncat(dst, dst_len, tmp, (int) strlen(tmp)); +} + +static void assemble_filename(MuxerContext *const ctx, char *const filename, + const int filename_size, + const Dav1dPictureParameters *const p) +{ + filename[0] = 0; + const int framenum = ctx->framenum++; + assert(ctx->filename); + const char *ptr = ctx->filename, *iptr; + while ((iptr = strchr(ptr, '%'))) { + safe_strncat(filename, filename_size, ptr, (int) (iptr - ptr)); + ptr = iptr; + + const char *iiptr = &iptr[1]; // skip '%' + while (*iiptr >= '0' && *iiptr <= '9') + iiptr++; // skip length indicators + + switch (*iiptr) { + case 'w': + assemble_field(filename, filename_size, ptr, (int) (iiptr - ptr), p->w); + break; + case 'h': + assemble_field(filename, filename_size, ptr, (int) (iiptr - ptr), p->h); + break; + case 'n': + assemble_field(filename, filename_size, ptr, (int) (iiptr - ptr), framenum); + break; + default: + safe_strncat(filename, filename_size, "%", 1); + ptr = &iptr[1]; + continue; + } + + ptr = &iiptr[1]; + } + safe_strncat(filename, filename_size, ptr, (int) strlen(ptr)); +} + int output_write(MuxerContext *const ctx, Dav1dPicture *const p) { - const int res = ctx->impl->write_picture(ctx->data, p); - return res < 0 ? res : 0; + int res; + + if (ctx->one_file_per_frame && ctx->impl->write_header) { + char filename[1024]; + assemble_filename(ctx, filename, sizeof(filename), &p->p); + res = ctx->impl->write_header(ctx->data, filename, &p->p, ctx->fps); + if (res < 0) + return res; + } + if ((res = ctx->impl->write_picture(ctx->data, p)) < 0) + return res; + if (ctx->one_file_per_frame && ctx->impl->write_trailer) + ctx->impl->write_trailer(ctx->data); + + return 0; } void output_close(MuxerContext *const ctx) { - if (ctx->impl->write_trailer) + if (!ctx->one_file_per_frame && ctx->impl->write_trailer) ctx->impl->write_trailer(ctx->data); free(ctx); } |