[PR] CLI: fix matching of non-apic streams and setting of default disposition (PR #24247)
Gyan Doshi via ffmpeg-devel <[email protected]>
| Newsgroups | gmane.comp.video.ffmpeg.devel |
|---|---|
| Message-ID | <[email protected]> |
PR #24247 opened by Gyan Doshi (GyanD) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24247 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24247.patch Two fixes for the ffmpeg CLI. **First commit** The capital V specifier as in '-c:V' is supposed to match only non-attached-pic video streams. However, a user reported that apic streams were matched as well. Confirmed by testing. Apparently a regression since before v3.4 (earliest ver I could test). This happens because encoder selection occurs before stream dispositions are set. Fixed by setting disposition before other spec options are applied. ---- **Second commit** The current implementation of fn set_dispositions lets multiple streams of the same media type be marked as default streams. The function is reworked to give precedence to user-assigned default disposition. Singular default disposition per media type is enforced. Function is renamed to make its primary role clearer. >From 1e61b20a06d4cde5715b517afe5dbaadce10067a Mon Sep 17 00:00:00 2001 From: Gyan Doshi <[email protected]> Date: Tue, 18 Aug 2026 19:16:49 +0530 Subject: [PATCH 1/2] ffmpeg: fix matching of non-apic stream specifiers. The capital V specifier as in '-c:V' is supposed to match only non-attached-pic video streams. However, a user reported that apic streams were matched as well. Confirmed by testing. Apparently a regression since before 3.4 (earliest ver I could test). This happens because encoder selection occurs before stream dispositions are set. Fixed by setting disposition before other spec options are applied. --- fftools/ffmpeg_mux_init.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c index f80b4427b4..9f3037f831 100644 --- a/fftools/ffmpeg_mux_init.c +++ b/fftools/ffmpeg_mux_init.c @@ -1147,7 +1147,7 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, int threads_manual = 0; AVRational enc_tb = { 0, 0 }; enum VideoSyncMethod vsync_method = VSYNC_AUTO; - const char *bsfs = NULL, *time_base = NULL, *codec_tag = NULL; + const char *bsfs = NULL, *time_base = NULL, *codec_tag = NULL, *manual_disp = NULL; char *next; double qscale = -1; @@ -1204,6 +1204,13 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, ms->par_in->codec_type = type; st->codecpar->codec_type = type; + if (ost->ist) + ost->st->disposition = ost->ist->st->disposition; + + opt_match_per_stream_str(ost, &o->disposition, oc, st, &manual_disp); + if (manual_disp) + ret = av_opt_set(ost->st, "disposition", manual_disp, 0); + ret = choose_encoder(o, oc, ms, &enc); if (ret < 0) { av_log(ost, AV_LOG_FATAL, "Error selecting an encoder\n"); -- 2.52.0 >From f152f098f622af4681c9ce5ce63754e7190882d3 Mon Sep 17 00:00:00 2001 From: Gyan Doshi <[email protected]> Date: Fri, 21 Aug 2026 18:44:36 +0530 Subject: [PATCH 2/2] ffmpeg: fix setting of default stream disposition. The current implementation of fn set_dispositions lets multiple streams of the same media type be marked as default streams. The function is reworked to give precedence to user assigned default disposition. Singular default disposition per media type is enforced. Function is renamed to make its primary role clearer. --- fftools/ffmpeg_mux_init.c | 109 ++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c index 9f3037f831..af917af4d6 100644 --- a/fftools/ffmpeg_mux_init.c +++ b/fftools/ffmpeg_mux_init.c @@ -3248,73 +3248,80 @@ static int copy_meta(Muxer *mux, const OptionsContext *o) return 0; } -static int set_dispositions(Muxer *mux, const OptionsContext *o) +static int set_default_dispositions(Muxer *mux, const OptionsContext *o) { OutputFile *of = &mux->of; AVFormatContext *ctx = mux->fc; // indexed by type+1, because AVMEDIA_TYPE_UNKNOWN=-1 - int nb_streams[AVMEDIA_TYPE_NB + 1] = { 0 }; - int have_default[AVMEDIA_TYPE_NB + 1] = { 0 }; - int have_manual = 0; + int nb_streams[AVMEDIA_TYPE_NB + 1] = { 0 }; + int inherited_default[AVMEDIA_TYPE_NB + 1] = { 0 }; + int manual_default[AVMEDIA_TYPE_NB + 1] = { 0 }; + int default_assigned[AVMEDIA_TYPE_NB + 1] = { 0 }; int ret = 0; - const char **dispositions; - - dispositions = av_calloc(ctx->nb_streams, sizeof(*dispositions)); - if (!dispositions) - return AVERROR(ENOMEM); - - // first, copy the input dispositions + // apply disposition again as metadata is now available & + // track all defaults for (int i = 0; i < ctx->nb_streams; i++) { OutputStream *ost = of->streams[i]; + const char *disposition = NULL; nb_streams[ost->type + 1]++; - opt_match_per_stream_str(ost, &o->disposition, ctx, ost->st, &dispositions[i]); - - have_manual |= !!dispositions[i]; - - if (ost->ist) { - ost->st->disposition = ost->ist->st->disposition; - - if (ost->st->disposition & AV_DISPOSITION_DEFAULT) - have_default[ost->type + 1] = 1; - } - } - - if (have_manual) { - // process manually set dispositions - they override the above copy - for (int i = 0; i < ctx->nb_streams; i++) { - OutputStream *ost = of->streams[i]; - const char *disp = dispositions[i]; - - if (!disp) - continue; - - ret = av_opt_set(ost->st, "disposition", disp, 0); + opt_match_per_stream_str(ost, &o->disposition, ctx, ost->st, &disposition); + if (disposition) { + ost->st->disposition = 0; + ret = av_opt_set(ost->st, "disposition", disposition, 0); if (ret < 0) - goto finish; + return ret; + if (ost->st->disposition & AV_DISPOSITION_DEFAULT) { + if (manual_default[ost->type + 1]) { + av_log(mux, AV_LOG_ERROR, "Default disposition manually specified for multiple streams of type %s\n", av_get_media_type_string(ost->type)); + return AVERROR_INVALIDDATA; + } + manual_default[ost->type + 1] = i+1; + } } - } else { - // For each media type with more than one stream, find a suitable stream to - // mark as default, unless one is already marked default. - // "Suitable" means the first of that type, skipping attached pictures. - for (int i = 0; i < ctx->nb_streams; i++) { - OutputStream *ost = of->streams[i]; - enum AVMediaType type = ost->type; - if (nb_streams[type + 1] < 2 || have_default[type + 1] || - ost->st->disposition & AV_DISPOSITION_ATTACHED_PIC) - continue; - - ost->st->disposition |= AV_DISPOSITION_DEFAULT; - have_default[type + 1] = 1; + if (ost->ist && ost->ist->st->disposition) { + if (disposition) { + ost->st->disposition = ost->ist->st->disposition; + ret = av_opt_set(ost->st, "disposition", disposition, 0); + if (ret < 0) + return ret; + } } + + if (ost->st->disposition & AV_DISPOSITION_ATTACHED_PIC) { + if (manual_default[ost->type + 1] == i+1) + manual_default[ost->type + 1] = 0; + continue; + } + + if (ost->st->disposition & AV_DISPOSITION_DEFAULT && + manual_default[ost->type + 1] != i+1 && + !inherited_default[ost->type + 1]) + inherited_default[ost->type + 1] = i+1; } -finish: - av_freep(&dispositions); + for (int i = 0; i < ctx->nb_streams; i++) { + OutputStream *ost = of->streams[i]; + int def_idx = -1; + + if (manual_default[ost->type + 1]) + def_idx = manual_default[ost->type + 1] - 1; + else if (inherited_default[ost->type + 1]) + def_idx = inherited_default[ost->type + 1] - 1; + else if (nb_streams[ost->type + 1] > 1 && !default_assigned[ost->type + 1]) { + default_assigned[ost->type + 1] = i+1; + def_idx = i; + } + + if (def_idx == i) + ost->st->disposition |= AV_DISPOSITION_DEFAULT; + else + ost->st->disposition &= ~AV_DISPOSITION_DEFAULT; + } return ret; } @@ -3629,9 +3636,9 @@ int of_open(const OptionsContext *o, const char *filename, Scheduler *sch) if (err < 0) return err; - err = set_dispositions(mux, o); + err = set_default_dispositions(mux, o); if (err < 0) { - av_log(mux, AV_LOG_FATAL, "Error setting output stream dispositions\n"); + av_log(mux, AV_LOG_FATAL, "Error setting output stream default dispositions\n"); return err; } -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]