static InputFile *input_files = NULL;
static int nb_input_files = 0;
+static OutputStream *output_streams = NULL;
+static int nb_output_streams = 0;
+static OutputFile *output_files = NULL;
+static int nb_output_files = 0;
+
+typedef struct OptionsContext {
+ /* input/output options */
+ int64_t start_time;
+ const char *format;
+
+ SpecifierOpt *codec_names;
+ int nb_codec_names;
+ SpecifierOpt *audio_channels;
+ int nb_audio_channels;
+ SpecifierOpt *audio_sample_rate;
+ int nb_audio_sample_rate;
+ SpecifierOpt *rematrix_volume;
+ int nb_rematrix_volume;
+ SpecifierOpt *frame_rates;
+ int nb_frame_rates;
+ SpecifierOpt *frame_sizes;
+ int nb_frame_sizes;
+ SpecifierOpt *frame_pix_fmts;
+ int nb_frame_pix_fmts;
+
+ /* input options */
+ int64_t input_ts_offset;
+ int rate_emu;
+
+ SpecifierOpt *ts_scale;
+ int nb_ts_scale;
+ SpecifierOpt *dump_attachment;
+ int nb_dump_attachment;
+
+ /* output options */
+ StreamMap *stream_maps;
+ int nb_stream_maps;
+ AudioChannelMap *audio_channel_maps; ///< one info entry per -map_channel
+ int nb_audio_channel_maps; ///< number of (valid) -map_channel settings
+ /* first item specifies output metadata, second is input */
+ MetadataMap (*meta_data_maps)[2];
+ int nb_meta_data_maps;
+ int metadata_global_manual;
+ int metadata_streams_manual;
+ int metadata_chapters_manual;
+ const char **attachments;
+ int nb_attachments;
+
+ int chapters_input_file;
+
+ int64_t recording_time;
+ uint64_t limit_filesize;
+ float mux_preload;
+ float mux_max_delay;
+
+ int video_disable;
+ int audio_disable;
+ int subtitle_disable;
+ int data_disable;
+
+ /* indexed by output file stream index */
+ int *streamid_map;
+ int nb_streamid_map;
+
+ SpecifierOpt *metadata;
+ int nb_metadata;
+ SpecifierOpt *max_frames;
+ int nb_max_frames;
+ SpecifierOpt *bitstream_filters;
+ int nb_bitstream_filters;
+ SpecifierOpt *codec_tags;
+ int nb_codec_tags;
+ SpecifierOpt *sample_fmts;
+ int nb_sample_fmts;
+ SpecifierOpt *qscale;
+ int nb_qscale;
+ SpecifierOpt *forced_key_frames;
+ int nb_forced_key_frames;
+ SpecifierOpt *force_fps;
+ int nb_force_fps;
+ SpecifierOpt *frame_aspect_ratios;
+ int nb_frame_aspect_ratios;
+ SpecifierOpt *rc_overrides;
+ int nb_rc_overrides;
+ SpecifierOpt *intra_matrices;
+ int nb_intra_matrices;
+ SpecifierOpt *inter_matrices;
+ int nb_inter_matrices;
+ SpecifierOpt *top_field_first;
+ int nb_top_field_first;
++ SpecifierOpt *metadata_map;
++ int nb_metadata_map;
+ SpecifierOpt *presets;
+ int nb_presets;
+ SpecifierOpt *copy_initial_nonkeyframes;
+ int nb_copy_initial_nonkeyframes;
+#if CONFIG_AVFILTER
+ SpecifierOpt *filters;
+ int nb_filters;
+#endif
+} OptionsContext;
+
+#define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\
+{\
+ int i, ret;\
+ for (i = 0; i < o->nb_ ## name; i++) {\
+ char *spec = o->name[i].specifier;\
+ if ((ret = check_stream_specifier(fmtctx, st, spec)) > 0)\
+ outvar = o->name[i].u.type;\
+ else if (ret < 0)\
+ exit_program(1);\
+ }\
+}
+
+static void reset_options(OptionsContext *o, int is_input)
+{
+ const OptionDef *po = options;
+ OptionsContext bak= *o;
+
+ /* all OPT_SPEC and OPT_STRING can be freed in generic way */
+ while (po->name) {
+ void *dst = (uint8_t*)o + po->u.off;
+
+ if (po->flags & OPT_SPEC) {
+ SpecifierOpt **so = dst;
+ int i, *count = (int*)(so + 1);
+ for (i = 0; i < *count; i++) {
+ av_freep(&(*so)[i].specifier);
+ if (po->flags & OPT_STRING)
+ av_freep(&(*so)[i].u.str);
+ }
+ av_freep(so);
+ *count = 0;
+ } else if (po->flags & OPT_OFFSET && po->flags & OPT_STRING)
+ av_freep(dst);
+ po++;
+ }
+
+ av_freep(&o->stream_maps);
+ av_freep(&o->audio_channel_maps);
+ av_freep(&o->meta_data_maps);
+ av_freep(&o->streamid_map);
+
+ memset(o, 0, sizeof(*o));
+
+ if(is_input) o->recording_time = bak.recording_time;
+ else o->recording_time = INT64_MAX;
+ o->mux_max_delay = 0.7;
+ o->limit_filesize = UINT64_MAX;
+ o->chapters_input_file = INT_MAX;
+
+ uninit_opts();
+ init_opts();
+}
+
#if CONFIG_AVFILTER
static int configure_video_filters(InputStream *ist, OutputStream *ost)
return 0;
}
- static void parse_meta_type(char *arg, char *type, int *index)
-static void parse_meta_type(char *arg, char *type, int *index, char **endptr)
++/**
++ * Parse a metadata specifier in arg.
++ * @param type metadata type is written here -- g(lobal)/s(tream)/c(hapter)/p(rogram)
++ * @param index for type c/p, chapter/program index is written here
++ * @param stream_spec for type s, the stream specifier is written here
++ */
++static void parse_meta_type(char *arg, char *type, int *index, const char **stream_spec)
{
- *endptr = arg;
- if (*arg == ',') {
- *type = *(++arg);
+ if (*arg) {
+ *type = *arg;
switch (*arg) {
case 'g':
break;
case 's':
++ if (*(++arg) && *arg != ':') {
++ av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", arg);
++ exit_program(1);
++ }
++ *stream_spec = *arg == ':' ? arg + 1 : "";
++ break;
case 'c':
case 'p':
- *index = strtol(++arg, endptr, 0);
+ if (*(++arg) == ':')
+ *index = strtol(++arg, NULL, 0);
break;
default:
- fprintf(stderr, "Invalid metadata type %c.\n", *arg);
+ av_log(NULL, AV_LOG_FATAL, "Invalid metadata type %c.\n", *arg);
exit_program(1);
}
} else
*type = 'g';
}
- static int opt_map_metadata(OptionsContext *o, const char *opt, const char *arg)
-static int opt_map_metadata(const char *opt, const char *arg)
++static int copy_metadata(char *outspec, char *inspec, AVFormatContext *oc, AVFormatContext *ic, OptionsContext *o)
{
-- MetadataMap *m, *m1;
-- char *p;
-
- o->meta_data_maps = grow_array(o->meta_data_maps, sizeof(*o->meta_data_maps),
- &o->nb_meta_data_maps, o->nb_meta_data_maps + 1);
-
- m = &o->meta_data_maps[o->nb_meta_data_maps - 1][1];
- m->file = strtol(arg, &p, 0);
- parse_meta_type(*p ? p + 1 : p, &m->type, &m->index);
++ AVDictionary **meta_in = NULL;
++ AVDictionary **meta_out;
++ int i, ret = 0;
++ char type_in, type_out;
++ const char *istream_spec = NULL, *ostream_spec = NULL;
++ int idx_in = 0, idx_out = 0;
+
- m1 = &o->meta_data_maps[o->nb_meta_data_maps - 1][0];
- if (p = strchr(opt, ':'))
- parse_meta_type(p + 1, &m1->type, &m1->index);
- else
- m1->type = 'g';
++ parse_meta_type(inspec, &type_in, &idx_in, &istream_spec);
++ parse_meta_type(outspec, &type_out, &idx_out, &ostream_spec);
+
- if (m->type == 'g' || m1->type == 'g')
++ if (type_in == 'g' || type_out == 'g')
+ o->metadata_global_manual = 1;
- if (m->type == 's' || m1->type == 's')
++ if (type_in == 's' || type_out == 's')
+ o->metadata_streams_manual = 1;
- if (m->type == 'c' || m1->type == 'c')
++ if (type_in == 'c' || type_out == 'c')
+ o->metadata_chapters_manual = 1;
- return 0;
- }
- meta_data_maps = grow_array(meta_data_maps, sizeof(*meta_data_maps),
- &nb_meta_data_maps, nb_meta_data_maps + 1);
-
- m = &meta_data_maps[nb_meta_data_maps - 1][0];
- m->file = strtol(arg, &p, 0);
- parse_meta_type(p, &m->type, &m->index, &p);
- if (*p)
- p++;
++#define METADATA_CHECK_INDEX(index, nb_elems, desc)\
++ if ((index) < 0 || (index) >= (nb_elems)) {\
++ av_log(NULL, AV_LOG_FATAL, "Invalid %s index %d while processing metadata maps.\n",\
++ (desc), (index));\
++ exit_program(1);\
++ }
+
- m1 = &meta_data_maps[nb_meta_data_maps - 1][1];
- m1->file = strtol(p, &p, 0);
- parse_meta_type(p, &m1->type, &m1->index, &p);
++#define SET_DICT(type, meta, context, index)\
++ switch (type) {\
++ case 'g':\
++ meta = &context->metadata;\
++ break;\
++ case 'c':\
++ METADATA_CHECK_INDEX(index, context->nb_chapters, "chapter")\
++ meta = &context->chapters[index]->metadata;\
++ break;\
++ case 'p':\
++ METADATA_CHECK_INDEX(index, context->nb_programs, "program")\
++ meta = &context->programs[index]->metadata;\
++ break;\
++ }\
++
++ SET_DICT(type_in, meta_in, ic, idx_in);
++ SET_DICT(type_out, meta_out, oc, idx_out);
++
++ /* for input streams choose first matching stream */
++ if (type_in == 's') {
++ for (i = 0; i < ic->nb_streams; i++) {
++ if ((ret = check_stream_specifier(ic, ic->streams[i], istream_spec)) > 0) {
++ meta_in = &ic->streams[i]->metadata;
++ break;
++ } else if (ret < 0)
++ exit_program(1);
++ }
++ if (!meta_in) {
++ av_log(NULL, AV_LOG_FATAL, "Stream specifier %s does not match any streams.\n", istream_spec);
++ exit_program(1);
++ }
++ }
- static int opt_map_meta_data(OptionsContext *o, const char *opt, const char *arg)
- {
- av_log(NULL, AV_LOG_WARNING, "-map_meta_data is deprecated and will be removed soon. "
- "Use -map_metadata instead.\n");
- return opt_map_metadata(o, opt, arg);
- if (m->type == 'g' || m1->type == 'g')
- metadata_global_autocopy = 0;
- if (m->type == 's' || m1->type == 's')
- metadata_streams_autocopy = 0;
- if (m->type == 'c' || m1->type == 'c')
- metadata_chapters_autocopy = 0;
++ if (type_out == 's') {
++ for (i = 0; i < oc->nb_streams; i++) {
++ if ((ret = check_stream_specifier(oc, oc->streams[i], ostream_spec)) > 0) {
++ meta_out = &oc->streams[i]->metadata;
++ av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE);
++ } else if (ret < 0)
++ exit_program(1);
++ }
++ } else
++ av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE);
+
+ return 0;
}
-static int opt_map_meta_data(const char *opt, const char *arg)
-{
- fprintf(stderr, "-map_meta_data is deprecated and will be removed soon. "
- "Use -map_metadata instead.\n");
- return opt_map_metadata(opt, arg);
-}
-
-static int opt_map_chapters(const char *opt, const char *arg)
+static int opt_recording_timestamp(OptionsContext *o, const char *opt, const char *arg)
{
- ChapterMap *c;
- char *p;
-
- chapter_maps = grow_array(chapter_maps, sizeof(*chapter_maps), &nb_chapter_maps,
- nb_chapter_maps + 1);
- c = &chapter_maps[nb_chapter_maps - 1];
- c->out_file = strtol(arg, &p, 0);
- if (*p)
- p++;
+ char buf[128];
+ int64_t recording_timestamp = parse_time_or_die(opt, arg, 0) / 1E6;
+ struct tm time = *gmtime((time_t*)&recording_timestamp);
+ strftime(buf, sizeof(buf), "creation_time=%FT%T%z", &time);
+ parse_option(o, "metadata", buf, options);
- c->in_file = strtol(p, &p, 0);
+ av_log(NULL, AV_LOG_WARNING, "%s is deprecated, set the 'creation_time' metadata "
+ "tag instead.\n", opt);
return 0;
}
av_log(NULL, AV_LOG_WARNING, "-loop_output is deprecated, use -loop\n");
oc->loop_output = loop_output;
}
- oc->flags |= AVFMT_FLAG_NONBLOCK;
- frame_rate = (AVRational){0, 0};
- frame_width = 0;
- frame_height = 0;
- audio_sample_rate = 0;
- audio_channels = 0;
- audio_sample_fmt = AV_SAMPLE_FMT_NONE;
++ /* copy metadata */
++ for (i = 0; i < o->nb_metadata_map; i++) {
++ char *p;
++ int in_file_index = strtol(o->metadata_map[i].u.str, &p, 0);
+
- av_freep(&forced_key_frames);
- uninit_opts();
- init_opts();
++ if (in_file_index < 0)
++ continue;
++ if (in_file_index >= nb_input_files) {
++ av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d while processing metadata maps\n", in_file_index);
++ exit_program(1);
++ }
++ copy_metadata(o->metadata_map[i].specifier, *p ? p + 1 : p, oc, input_files[in_file_index].ctx, o);
++ }
++
+ /* copy chapters */
+ if (o->chapters_input_file >= nb_input_files) {
+ if (o->chapters_input_file == INT_MAX) {
+ /* copy chapters from the first input file that has them*/
+ o->chapters_input_file = -1;
+ for (i = 0; i < nb_input_files; i++)
+ if (input_files[i].ctx->nb_chapters) {
+ o->chapters_input_file = i;
+ break;
+ }
+ } else {
+ av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d in chapter mapping.\n",
+ o->chapters_input_file);
+ exit_program(1);
+ }
+ }
+ if (o->chapters_input_file >= 0)
+ copy_chapters(&input_files[o->chapters_input_file], &output_files[nb_output_files - 1],
+ !o->metadata_chapters_manual);
+
- /* copy metadata */
- for (i = 0; i < o->nb_meta_data_maps; i++) {
- AVFormatContext *files[2];
- AVDictionary **meta[2];
- int j;
-
- #define METADATA_CHECK_INDEX(index, nb_elems, desc)\
- if ((index) < 0 || (index) >= (nb_elems)) {\
- av_log(NULL, AV_LOG_FATAL, "Invalid %s index %d while processing metadata maps\n",\
- (desc), (index));\
- exit_program(1);\
- }
-
- int in_file_index = o->meta_data_maps[i][1].file;
- if (in_file_index < 0)
- continue;
- METADATA_CHECK_INDEX(in_file_index, nb_input_files, "input file")
-
- files[0] = oc;
- files[1] = input_files[in_file_index].ctx;
-
- for (j = 0; j < 2; j++) {
- MetadataMap *map = &o->meta_data_maps[i][j];
-
- switch (map->type) {
- case 'g':
- meta[j] = &files[j]->metadata;
- break;
- case 's':
- METADATA_CHECK_INDEX(map->index, files[j]->nb_streams, "stream")
- meta[j] = &files[j]->streams[map->index]->metadata;
- break;
- case 'c':
- METADATA_CHECK_INDEX(map->index, files[j]->nb_chapters, "chapter")
- meta[j] = &files[j]->chapters[map->index]->metadata;
- break;
- case 'p':
- METADATA_CHECK_INDEX(map->index, files[j]->nb_programs, "program")
- meta[j] = &files[j]->programs[map->index]->metadata;
- break;
- default:
- abort();
- }
- }
-
- av_dict_copy(meta[0], *meta[1], AV_DICT_DONT_OVERWRITE);
- }
-
+ /* copy global metadata by default */
+ if (!o->metadata_global_manual && nb_input_files){
+ av_dict_copy(&oc->metadata, input_files[0].ctx->metadata,
+ AV_DICT_DONT_OVERWRITE);
+ if(o->recording_time != INT64_MAX)
+ av_dict_set(&oc->metadata, "duration", NULL, 0);
+ }
+ if (!o->metadata_streams_manual)
+ for (i = output_files[nb_output_files - 1].ost_index; i < nb_output_streams; i++) {
+ InputStream *ist;
+ if (output_streams[i].source_index < 0) /* this is true e.g. for attached files */
+ continue;
+ ist = &input_streams[output_streams[i].source_index];
+ av_dict_copy(&output_streams[i].st->metadata, ist->st->metadata, AV_DICT_DONT_OVERWRITE);
+ }
+
+ /* process manually set metadata */
+ for (i = 0; i < o->nb_metadata; i++) {
+ AVDictionary **m;
+ char type, *val;
- int index = 0;
++ const char *stream_spec;
++ int index = 0, j, ret;
+
+ val = strchr(o->metadata[i].u.str, '=');
+ if (!val) {
+ av_log(NULL, AV_LOG_FATAL, "No '=' character in metadata string %s.\n",
+ o->metadata[i].u.str);
+ exit_program(1);
+ }
+ *val++ = 0;
+
- parse_meta_type(o->metadata[i].specifier, &type, &index);
- switch (type) {
- case 'g':
- m = &oc->metadata;
- break;
- case 's':
- if (index < 0 || index >= oc->nb_streams) {
- av_log(NULL, AV_LOG_FATAL, "Invalid stream index %d in metadata specifier.\n", index);
- exit_program(1);
++ parse_meta_type(o->metadata[i].specifier, &type, &index, &stream_spec);
++ if (type == 's') {
++ for (j = 0; j < oc->nb_streams; j++) {
++ if ((ret = check_stream_specifier(oc, oc->streams[j], stream_spec)) > 0) {
++ av_dict_set(&oc->streams[j]->metadata, o->metadata[i].u.str, *val ? val : NULL, 0);
++ } else if (ret < 0)
++ exit_program(1);
+ }
- m = &oc->streams[index]->metadata;
- break;
- case 'c':
- if (index < 0 || index >= oc->nb_chapters) {
- av_log(NULL, AV_LOG_FATAL, "Invalid chapter index %d in metadata specifier.\n", index);
++ printf("ret %d, stream_spec %s\n", ret, stream_spec);
++ }
++ else {
++ switch (type) {
++ case 'g':
++ m = &oc->metadata;
++ break;
++ case 'c':
++ if (index < 0 || index >= oc->nb_chapters) {
++ av_log(NULL, AV_LOG_FATAL, "Invalid chapter index %d in metadata specifier.\n", index);
++ exit_program(1);
++ }
++ m = &oc->chapters[index]->metadata;
++ break;
++ default:
++ av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", o->metadata[i].specifier);
+ exit_program(1);
+ }
- m = &oc->chapters[index]->metadata;
- break;
- default:
- av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", o->metadata[i].specifier);
- exit_program(1);
++ av_dict_set(m, o->metadata[i].u.str, *val ? val : NULL, 0);
+ }
-
- av_dict_set(m, o->metadata[i].u.str, *val ? val : NULL, 0);
+ }
+
+ reset_options(o, 0);
}
/* same option as mencoder */
static const OptionDef options[] = {
/* main options */
#include "cmdutils_common_opts.h"
- { "f", HAS_ARG, {(void*)opt_format}, "force format", "fmt" },
- { "i", HAS_ARG, {(void*)opt_input_file}, "input file name", "filename" },
+ { "f", HAS_ARG | OPT_STRING | OPT_OFFSET, {.off = OFFSET(format)}, "force format", "fmt" },
+ { "i", HAS_ARG | OPT_FUNC2, {(void*)opt_input_file}, "input file name", "filename" },
{ "y", OPT_BOOL, {(void*)&file_overwrite}, "overwrite output files" },
- { "map", HAS_ARG | OPT_EXPERT, {(void*)opt_map}, "set input stream mapping", "file.stream[:syncfile.syncstream]" },
- { "map_meta_data", HAS_ARG | OPT_EXPERT, {(void*)opt_map_meta_data}, "DEPRECATED set meta data information of outfile from infile",
+ { "n", OPT_BOOL, {(void*)&no_file_overwrite}, "do not overwrite output files" },
+ { "c", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(codec_names)}, "codec name", "codec" },
+ { "codec", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(codec_names)}, "codec name", "codec" },
+ { "pre", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(presets)}, "preset name", "preset" },
+ { "map", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map}, "set input stream mapping", "[-]input_file_id[:stream_specifier][,sync_file_id[:stream_specifier]]" },
+ { "map_channel", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map_channel}, "map an audio channel from one stream to another", "file.stream.channel[:syncfile.syncstream]" },
- { "map_meta_data", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map_meta_data}, "DEPRECATED set meta data information of outfile from infile",
- "outfile[,metadata]:infile[,metadata]" },
- { "map_metadata", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map_metadata}, "set metadata information of outfile from infile",
++ { "map_metadata", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(metadata_map)}, "set metadata information of outfile from infile",
"outfile[,metadata]:infile[,metadata]" },
- { "map_metadata", HAS_ARG | OPT_EXPERT, {(void*)opt_map_metadata}, "set metadata information of outfile from infile",
- "outfile[,metadata]:infile[,metadata]" },
- { "map_chapters", HAS_ARG | OPT_EXPERT, {(void*)opt_map_chapters}, "set chapters mapping", "outfile:infile" },
- { "t", HAS_ARG, {(void*)opt_recording_time}, "record or transcode \"duration\" seconds of audio/video", "duration" },
- { "fs", HAS_ARG | OPT_INT64, {(void*)&limit_filesize}, "set the limit file size in bytes", "limit_size" }, //
- { "ss", HAS_ARG, {(void*)opt_start_time}, "set the start time offset", "time_off" },
- { "itsoffset", HAS_ARG, {(void*)opt_input_ts_offset}, "set the input ts offset", "time_off" },
- { "itsscale", HAS_ARG, {(void*)opt_input_ts_scale}, "set the input ts scale", "stream:scale" },
- { "timestamp", HAS_ARG, {(void*)opt_recording_timestamp}, "set the recording timestamp ('now' to set the current time)", "time" },
- { "metadata", HAS_ARG, {(void*)opt_metadata}, "add metadata", "string=string" },
- { "dframes", OPT_INT | HAS_ARG, {(void*)&max_frames[AVMEDIA_TYPE_DATA]}, "set the number of data frames to record", "number" },
+ { "map_chapters", OPT_INT | HAS_ARG | OPT_EXPERT | OPT_OFFSET, {.off = OFFSET(chapters_input_file)}, "set chapters mapping", "input_file_index" },
+ { "t", HAS_ARG | OPT_TIME | OPT_OFFSET, {.off = OFFSET(recording_time)}, "record or transcode \"duration\" seconds of audio/video", "duration" },
+ { "fs", HAS_ARG | OPT_INT64 | OPT_OFFSET, {.off = OFFSET(limit_filesize)}, "set the limit file size in bytes", "limit_size" }, //
+ { "ss", HAS_ARG | OPT_TIME | OPT_OFFSET, {.off = OFFSET(start_time)}, "set the start time offset", "time_off" },
+ { "itsoffset", HAS_ARG | OPT_TIME | OPT_OFFSET, {.off = OFFSET(input_ts_offset)}, "set the input ts offset", "time_off" },
+ { "itsscale", HAS_ARG | OPT_DOUBLE | OPT_SPEC, {.off = OFFSET(ts_scale)}, "set the input ts scale", "scale" },
+ { "timestamp", HAS_ARG | OPT_FUNC2, {(void*)opt_recording_timestamp}, "set the recording timestamp ('now' to set the current time)", "time" },
+ { "metadata", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(metadata)}, "add metadata", "string=string" },
+ { "dframes", HAS_ARG | OPT_FUNC2, {(void*)opt_data_frames}, "set the number of data frames to record", "number" },
{ "benchmark", OPT_BOOL | OPT_EXPERT, {(void*)&do_benchmark},
"add timings for benchmarking" },
{ "timelimit", HAS_ARG, {(void*)opt_timelimit}, "set max runtime in seconds", "limit" },