Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/videolan/dav1d.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorRonald S. Bultje <rsbultje@gmail.com>2019-11-17 19:05:24 +0300
committerRonald S. Bultje <rsbultje@gmail.com>2022-01-06 21:50:09 +0300
commit36beb8185dd9de5450c9a4cbcd0d969901a6b0cc (patch)
treeb4e8531c680cab3a40aca3d872ee337e16793a92 /tools
parentf9bddfff7bd93b875a86d4401bc699337706b8cf (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.c4
-rw-r--r--tools/output/output.c120
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);
}