diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index 5f79d269b2..35f786dba5 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -1350,6 +1350,77 @@ int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec) return ret; } +unsigned stream_group_specifier_match(const StreamSpecifier *ss, + const AVFormatContext *s, const AVStreamGroup *stg, + void *logctx) +{ + int start_stream_group = 0, nb_stream_groups; + int nb_matched = 0; + + if (ss->idx >= 0) + return 0; + + switch (ss->stream_list) { + case STREAM_LIST_STREAM_ID: + case STREAM_LIST_ALL: + case STREAM_LIST_PROGRAM: + return 0; + case STREAM_LIST_GROUP_ID: + // stream with given ID makes no sense and should be impossible to request + av_assert0(ss->idx < 0); + // return early if we know for sure the stream does not match + if (stg->id != ss->list_id) + return 0; + start_stream_group = stg->index; + nb_stream_groups = stg->index + 1; + break; + case STREAM_LIST_GROUP_IDX: + start_stream_group = ss->list_id >= 0 ? 0 : stg->index; + nb_stream_groups = stg->index + 1; + break; + default: av_assert0(0); + } + + for (int i = start_stream_group; i < nb_stream_groups; i++) { + const AVStreamGroup *candidate = s->stream_groups[i]; + + if (ss->meta_key) { + const AVDictionaryEntry *tag = av_dict_get(candidate->metadata, + ss->meta_key, NULL, 0); + + if (!tag) + continue; + if (ss->meta_val && strcmp(tag->value, ss->meta_val)) + continue; + } + + if (ss->usable_only) { + switch (candidate->type) { + case AV_STREAM_GROUP_PARAMS_TILE_GRID: { + const AVStreamGroupTileGrid *tg = candidate->params.tile_grid; + if (!tg->coded_width || !tg->coded_height || !tg->nb_tiles || + !tg->width || !tg->height || !tg->nb_tiles) + continue; + break; + } + default: + continue; + } + } + + if (ss->disposition && + (candidate->disposition & ss->disposition) != ss->disposition) + continue; + + if (stg == candidate) + return ss->list_id < 0 || ss->list_id == nb_matched; + + nb_matched++; + } + + return 0; +} + int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id, AVFormatContext *s, AVStream *st, const AVCodec *codec, AVDictionary **dst, AVDictionary **opts_used) diff --git a/fftools/cmdutils.h b/fftools/cmdutils.h index c4a3efcf52..93e05c7130 100644 --- a/fftools/cmdutils.h +++ b/fftools/cmdutils.h @@ -158,6 +158,10 @@ unsigned stream_specifier_match(const StreamSpecifier *ss, const AVFormatContext *s, const AVStream *st, void *logctx); +unsigned stream_group_specifier_match(const StreamSpecifier *ss, + const AVFormatContext *s, const AVStreamGroup *stg, + void *logctx); + void stream_specifier_uninit(StreamSpecifier *ss); typedef struct SpecifierOpt { diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 8f665f3432..155674d303 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -136,6 +136,7 @@ typedef struct StreamMap { int disabled; /* 1 is this mapping is disabled by a negative map */ int file_index; int stream_index; + int group_index; char *linklabel; /* name of an output link, for mapping lavfi outputs */ ViewSpecifier vs; @@ -932,6 +933,15 @@ void opt_match_per_stream_int64(void *logctx, const SpecifierOptList *sol, void opt_match_per_stream_dbl(void *logctx, const SpecifierOptList *sol, AVFormatContext *fc, AVStream *st, double *out); +void opt_match_per_stream_group_str(void *logctx, const SpecifierOptList *sol, + AVFormatContext *fc, AVStreamGroup *stg, const char **out); +void opt_match_per_stream_group_int(void *logctx, const SpecifierOptList *sol, + AVFormatContext *fc, AVStreamGroup *stg, int *out); +void opt_match_per_stream_group_int64(void *logctx, const SpecifierOptList *sol, + AVFormatContext *fc, AVStreamGroup *stg, int64_t *out); +void opt_match_per_stream_group_dbl(void *logctx, const SpecifierOptList *sol, + AVFormatContext *fc, AVStreamGroup *stg, double *out); + int view_specifier_parse(const char **pspec, ViewSpecifier *vs); int muxer_thread(void *arg); diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 4152bbf06f..381360c16a 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -242,6 +242,70 @@ OPT_MATCH_PER_STREAM(int, int, OPT_TYPE_INT, i); OPT_MATCH_PER_STREAM(int64, int64_t, OPT_TYPE_INT64, i64); OPT_MATCH_PER_STREAM(dbl, double, OPT_TYPE_DOUBLE, dbl); +static unsigned opt_match_per_stream_group(void *logctx, enum OptionType type, + const SpecifierOptList *sol, + AVFormatContext *fc, AVStreamGroup *stg) +{ + int matches = 0, match_idx = -1; + + av_assert0((type == sol->type) || !sol->nb_opt); + + for (int i = 0; i < sol->nb_opt; i++) { + const StreamSpecifier *ss = &sol->opt[i].stream_spec; + + if (stream_group_specifier_match(ss, fc, stg, logctx)) { + match_idx = i; + matches++; + } + } + + if (matches > 1 && sol->opt_canon) { + const SpecifierOpt *so = &sol->opt[match_idx]; + const char *spec = so->specifier && so->specifier[0] ? so->specifier : ""; + + char namestr[128] = ""; + char optval_buf[32]; + const char *optval = optval_buf; + + snprintf(namestr, sizeof(namestr), "-%s", sol->opt_canon->name); + if (sol->opt_canon->flags & OPT_HAS_ALT) { + const char * const *names_alt = sol->opt_canon->u1.names_alt; + for (int i = 0; names_alt[i]; i++) + av_strlcatf(namestr, sizeof(namestr), "/-%s", names_alt[i]); + } + + switch (sol->type) { + case OPT_TYPE_STRING: optval = so->u.str; break; + case OPT_TYPE_INT: snprintf(optval_buf, sizeof(optval_buf), "%d", so->u.i); break; + case OPT_TYPE_INT64: snprintf(optval_buf, sizeof(optval_buf), "%"PRId64, so->u.i64); break; + case OPT_TYPE_FLOAT: snprintf(optval_buf, sizeof(optval_buf), "%f", so->u.f); break; + case OPT_TYPE_DOUBLE: snprintf(optval_buf, sizeof(optval_buf), "%f", so->u.dbl); break; + default: av_assert0(0); + } + + av_log(logctx, AV_LOG_WARNING, "Multiple %s options specified for " + "stream group %d, only the last option '-%s%s%s %s' will be used.\n", + namestr, stg->index, sol->opt_canon->name, spec[0] ? ":" : "", + spec, optval); + } + + return match_idx + 1; +} + +#define OPT_MATCH_PER_STREAM_GROUP(name, type, opt_type, m) \ +void opt_match_per_stream_group_ ## name(void *logctx, const SpecifierOptList *sol, \ + AVFormatContext *fc, AVStreamGroup *stg, type *out) \ +{ \ + unsigned ret = opt_match_per_stream_group(logctx, opt_type, sol, fc, stg); \ + if (ret > 0) \ + *out = sol->opt[ret - 1].u.m; \ +} + +OPT_MATCH_PER_STREAM_GROUP(str, const char *, OPT_TYPE_STRING, str); +OPT_MATCH_PER_STREAM_GROUP(int, int, OPT_TYPE_INT, i); +OPT_MATCH_PER_STREAM_GROUP(int64, int64_t, OPT_TYPE_INT64, i64); +OPT_MATCH_PER_STREAM_GROUP(dbl, double, OPT_TYPE_DOUBLE, dbl); + int view_specifier_parse(const char **pspec, ViewSpecifier *vs) { const char *spec = *pspec; @@ -504,8 +568,10 @@ static int opt_map(void *optctx, const char *opt, const char *arg) } if (arg[0] == '[') { + ViewSpecifier vs; /* this mapping refers to lavfi output */ const char *c = arg + 1; + char *endptr; ret = GROW_ARRAY(o->stream_maps, o->nb_stream_maps); if (ret < 0) @@ -518,6 +584,27 @@ static int opt_map(void *optctx, const char *opt, const char *arg) ret = AVERROR(EINVAL); goto fail; } + + arg++; + + m->group_index = -1; + file_idx = strtol(arg, &endptr, 0); + if (file_idx >= nb_input_files || file_idx < 0) + goto end; + + arg = endptr; + ret = stream_specifier_parse(&ss, *arg == ':' ? arg + 1 : arg, 1, NULL); + if (ret < 0) + goto end; + + arg = ss.remainder ? ss.remainder : ""; + ret = view_specifier_parse(&arg, &vs); + if (ret < 0 || (*arg && strcmp(arg, "]"))) + goto end; + + m->file_index = file_idx; + m->stream_index = ss.idx; + m->group_index = ss.stream_list == STREAM_LIST_GROUP_IDX ? ss.list_id : -1; } else { ViewSpecifier vs; char *endptr; @@ -583,6 +670,7 @@ static int opt_map(void *optctx, const char *opt, const char *arg) m->file_index = file_idx; m->stream_index = i; + m->group_index = ss.stream_list == STREAM_LIST_GROUP_IDX ? ss.list_id : -1; m->vs = vs; } } @@ -602,6 +690,7 @@ static int opt_map(void *optctx, const char *opt, const char *arg) goto fail; } } +end: ret = 0; fail: stream_specifier_uninit(&ss);