ffmpeg: add per-stream input option drop_changed

This is a replacement in ffmpeg for the deprecated avcodec flag AV_CODEC_FLAG_DROPCHANGED.

This option is meant to be used when the filtergraph should not be
reinited upon input parameter changes as that leads to loss of state
in the filtergraph potentially leading to broken or aborted output,
e.g. inserting of silence with first_pts specified in aresample.

Generally useful to avoid corrupted yet decodable packets in live
streaming inputs.

This option when enabled takes precedence over reinit_filters
This commit is contained in:
Gyan Doshi 2025-03-15 16:33:05 +05:30
parent 323cb8c61e
commit cbbc927a67
5 changed files with 36 additions and 1 deletions

View file

@ -232,6 +232,7 @@ typedef struct OptionsContext {
SpecifierOptList filter_scripts;
#endif
SpecifierOptList reinit_filters;
SpecifierOptList drop_changed;
SpecifierOptList fix_sub_duration;
SpecifierOptList fix_sub_duration_heartbeat;
SpecifierOptList canvas_sizes;
@ -262,6 +263,7 @@ enum IFilterFlags {
IFILTER_FLAG_REINIT = (1 << 1),
IFILTER_FLAG_CFR = (1 << 2),
IFILTER_FLAG_CROP = (1 << 3),
IFILTER_FLAG_DROPCHANGED = (1 << 4),
};
typedef struct InputFilterOptions {

View file

@ -67,6 +67,7 @@ typedef struct DemuxStream {
int reinit_filters;
int autorotate;
int apply_cropping;
int drop_changed;
int wrap_correction_done;
@ -1099,7 +1100,8 @@ int ist_filter_add(InputStream *ist, InputFilter *ifilter, int is_simple,
return AVERROR(ENOMEM);
opts->flags |= IFILTER_FLAG_AUTOROTATE * !!(ds->autorotate) |
IFILTER_FLAG_REINIT * !!(ds->reinit_filters);
IFILTER_FLAG_REINIT * !!(ds->reinit_filters) |
IFILTER_FLAG_DROPCHANGED* !!(ds->drop_changed);
return 0;
}
@ -1410,6 +1412,17 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona
ds->reinit_filters = -1;
opt_match_per_stream_int(ist, &o->reinit_filters, ic, st, &ds->reinit_filters);
ds->drop_changed = 0;
opt_match_per_stream_int(ist, &o->drop_changed, ic, st, &ds->drop_changed);
if (ds->drop_changed && ds->reinit_filters) {
if (ds->reinit_filters > 0) {
av_log(ist, AV_LOG_ERROR, "drop_changed and reinit_filters both enabled. These are mutually exclusive.\n");
return AVERROR(EINVAL);
}
ds->reinit_filters = 0;
}
ist->user_set_discard = AVDISCARD_NONE;
if ((o->video_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) ||

View file

@ -126,6 +126,8 @@ typedef struct InputFilterPriv {
int eof;
int bound;
int drop_warned;
uint64_t nb_dropped;
// parameters configured for this input
int format;
@ -2898,6 +2900,13 @@ static int send_frame(FilterGraph *fg, FilterGraphThread *fgt,
} else if (ifp->downmixinfo_present)
need_reinit |= DOWNMIX_CHANGED;
if (need_reinit && (ifp->opts.flags & IFILTER_FLAG_DROPCHANGED)) {
ifp->nb_dropped++;
av_log_once(fg, AV_LOG_WARNING, AV_LOG_DEBUG, &ifp->drop_warned, "Avoiding reinit; dropping frame pts: %s bound for %s\n", av_ts2str(frame->pts), ifilter->name);
av_frame_unref(frame);
return 0;
}
if (!(ifp->opts.flags & IFILTER_FLAG_REINIT) && fgt->graph)
need_reinit = 0;
@ -3140,6 +3149,8 @@ read_frames:
ret = read_frames(fg, &fgt, fgt.frame);
if (ret == AVERROR_EOF) {
av_log(fg, AV_LOG_VERBOSE, "All consumers returned EOF\n");
if (ifp->opts.flags & IFILTER_FLAG_DROPCHANGED)
av_log(fg, AV_LOG_INFO, "Total changed input frames dropped : %"PRId64"\n", ifp->nb_dropped);
break;
} else if (ret < 0) {
av_log(fg, AV_LOG_ERROR, "Error sending frames to consumers: %s\n",

View file

@ -1718,6 +1718,9 @@ const OptionDef options[] = {
{ "reinit_filter", OPT_TYPE_INT, OPT_PERSTREAM | OPT_INPUT | OPT_EXPERT,
{ .off = OFFSET(reinit_filters) },
"reinit filtergraph on input parameter changes", "" },
{ "drop_changed", OPT_TYPE_INT, OPT_PERSTREAM | OPT_INPUT | OPT_EXPERT,
{ .off = OFFSET(drop_changed) },
"drop frame instead of reiniting filtergraph on input parameter changes", "" },
{ "filter_complex", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT,
{ .func_arg = opt_filter_complex },
"create a complex filtergraph", "graph_description" },