1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-10-30 23:18:11 +02:00

fftools/ffmpeg_opt: add helpers to match stream groups

Will be used to check for specifiers that match a given stream group and not
a stream within one.

Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
James Almer
2025-10-18 20:46:14 -03:00
parent 7b18beb477
commit ba0dc3d49e
4 changed files with 174 additions and 0 deletions

View File

@@ -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:
// <n-th> 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)

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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);