> On Mar 24, 2025, at 10:28, yangyalei050 via ffmpeg-devel > <ffmpeg-devel@ffmpeg.org> wrote: > > From: yangyalei050 <269032...@qq.com> > > 1. Support reuse the graph to play different audio. > 2. Support config part of the graph. > > Signed-off-by: yangyalei050 <269032...@qq.com> > --- > libavfilter/avfilter.c | 8 +- > libavfilter/avfilter.h | 9 ++ > libavfilter/avfiltergraph.c | 217 +++++++++++++++++++++++++++++++++--- > 3 files changed, 215 insertions(+), 19 deletions(-) > > diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c > index e732556ffa..71396674fe 100644 > --- a/libavfilter/avfilter.c > +++ b/libavfilter/avfilter.c > @@ -345,7 +345,8 @@ int ff_filter_config_links(AVFilterContext *filter) > FilterLinkInternal *li = ff_link_internal(link); > FilterLinkInternal *li_in; > > - if (!link) continue; > + if (!link || !link->incfg.formats || !link->outcfg.formats) > + continue; > if (!link->src || !link->dst) { > av_log(filter, AV_LOG_ERROR, > "Not all input and output are properly linked (%d).\n", i); > @@ -359,6 +360,11 @@ int ff_filter_config_links(AVFilterContext *filter) > > switch (li->init_state) { > case AVLINK_INIT: > + /* for part graph re-negotiation. > + * For example: output-filter link has config_props, > + * but the input-filter still need config_props. */ > + if ((ret = ff_filter_config_links(link->src)) < 0) > + return ret; > continue; > case AVLINK_STARTINIT: > av_log(filter, AV_LOG_INFO, "circular filter chain detected\n"); > diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h > index 4520d5f978..9727b14231 100644 > --- a/libavfilter/avfilter.h > +++ b/libavfilter/avfilter.h > @@ -715,6 +715,15 @@ enum { > */ > int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx); > > +/** > + * Check validity and reconfigure all the links and formats in the graph. > + * > + * @param graphctx the filter graph > + * @param log_ctx context used for logging > + * @return >= 0 in case of success, a negative AVERROR code otherwise > + */ > +int ff_avfilter_graph_reconfig(AVFilterGraph *graphctx, void *log_ctx); > + > /** > * Free a graph, destroy its links, and set *graph to NULL. > * If *graph is NULL, do nothing. > diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c > index 5e93f93aab..15730063d0 100644 > --- a/libavfilter/avfiltergraph.c > +++ b/libavfilter/avfiltergraph.c > @@ -23,6 +23,7 @@ > #include "config.h" > > #include <string.h> > +#include <stdbool.h> > > #include "libavutil/avassert.h" > #include "libavutil/bprint.h" > @@ -348,7 +349,7 @@ static int filter_query_formats(AVFilterContext *ctx) > > if (filter->formats_state == FF_FILTER_FORMATS_QUERY_FUNC) { > if ((ret = filter->formats.query_func(ctx)) < 0) { > - if (ret != AVERROR(EAGAIN)) > + if (ret != AVERROR(EAGAIN) && ret != FFERROR_NOT_READY) > av_log(ctx, AV_LOG_ERROR, "Query format failed for '%s': > %s\n", > ctx->name, av_err2str(ret)); > return ret; > @@ -390,7 +391,7 @@ static int filter_query_formats(AVFilterContext *ctx) > av_freep(&cfg_in_dyn); > av_freep(&cfg_out_dyn); > if (ret < 0) { > - if (ret != AVERROR(EAGAIN)) > + if (ret != AVERROR(EAGAIN) && ret != FFERROR_NOT_READY) > av_log(ctx, AV_LOG_ERROR, "Query format failed for '%s': > %s\n", > ctx->name, av_err2str(ret)); > return ret; > @@ -438,6 +439,85 @@ static int formats_declared(AVFilterContext *f) > return 1; > } > > +static void ff_avfilter_link_unref_formats(AVFilterLink *link) > +{ > + ff_formats_unref(&link->incfg.formats); > + ff_formats_unref(&link->outcfg.formats); > + ff_formats_unref(&link->incfg.samplerates); > + ff_formats_unref(&link->outcfg.samplerates); > + ff_channel_layouts_unref(&link->incfg.channel_layouts); > + ff_channel_layouts_unref(&link->outcfg.channel_layouts); > +} > + > +static bool formats_useless(AVFilterContext *f) > +{ > + bool in, out; > + int i; > + > + if (!f->nb_inputs || !f->nb_outputs) > + return false; > + > + in = out = true; > + > + for (i = 0; i < f->nb_inputs; i++) > + if (f->inputs[i]->outcfg.formats) { > + in = false; > + break; > + } > + > + for (i = 0; i < f->nb_outputs; i++) > + if (f->outputs[i]->incfg.formats) { > + out = false; > + break; > + } > + > + if (!strcmp(f->name, "amix@a2dp")) { > + av_log(f, AV_LOG_ERROR, "check amix@a2dp, in:%d, out:%d, ret:%d\n", > in, out, in ^ out); > + } > + > + return in ^ out; > +} > + > + > +static bool sanitize_formats(AVFilterGraph *graph) > +{ > + AVFilterContext *f; > + bool changed = false; > + unsigned i, j; > + > + for (i = 0; i < graph->nb_filters; i++) { > + f = graph->filters[i]; > + > + for (j = 0; j < f->nb_inputs; j++) { > + if ((!f->inputs[j]->outcfg.formats && > f->inputs[j]->incfg.formats) > + || (f->inputs[j]->outcfg.formats && > !f->inputs[j]->incfg.formats)) { > + ff_avfilter_link_unref_formats(f->inputs[j]); > + changed = true; > + } > + } > + > + for (j = 0; j < f->nb_outputs; j++) { > + if ((!f->outputs[j]->incfg.formats && > f->outputs[j]->outcfg.formats) > + || (f->outputs[j]->incfg.formats && > !f->outputs[j]->outcfg.formats)) { > + ff_avfilter_link_unref_formats(f->outputs[j]); > + changed = true; > + } > + } > + > + if (formats_useless(f)) { > + for (j = 0; j < f->nb_inputs; j++) > + ff_avfilter_link_unref_formats(f->inputs[j]); > + > + for (j = 0; j < f->nb_outputs; j++) > + ff_avfilter_link_unref_formats(f->outputs[j]); > + > + changed = true; > + } > + } > + > + return changed; > +} > + > /** > * Perform one round of query_formats() and merging formats lists on the > * filter graph. > @@ -462,12 +542,14 @@ static int query_formats(AVFilterGraph *graph, void > *log_ctx) > if (formats_declared(f)) > continue; > ret = filter_query_formats(f); > - if (ret < 0 && ret != AVERROR(EAGAIN)) > + if (ret < 0 && ret != AVERROR(EAGAIN) && ret != FFERROR_NOT_READY) > return ret; > /* note: EAGAIN could indicate a partial success, not counted yet */ > count_queried += ret >= 0; > } > > + while (sanitize_formats(graph)); > + > /* go through and merge as many format lists as possible */ > for (i = 0; i < graph->nb_filters; i++) { > AVFilterContext *filter = graph->filters[i]; > @@ -478,7 +560,7 @@ static int query_formats(AVFilterGraph *graph, void > *log_ctx) > unsigned neg_step; > int convert_needed = 0; > > - if (!link) > + if (!link || !link->incfg.formats) > continue; > > neg = ff_filter_get_negotiation(link); > @@ -492,6 +574,7 @@ static int query_formats(AVFilterGraph *graph, void > *log_ctx) > break; > } > } > + > for (neg_step = 0; neg_step < neg->nb_mergers; neg_step++) { > const AVFilterFormatsMerger *m = &neg->mergers[neg_step]; > void *a = FF_FIELD_AT(void *, m->offset, link->incfg); > @@ -789,17 +872,6 @@ static int pick_format(AVFilterLink *link, AVFilterLink > *ref) > return ret; > } > > - ff_formats_unref(&link->incfg.formats); > - ff_formats_unref(&link->outcfg.formats); > - ff_formats_unref(&link->incfg.samplerates); > - ff_formats_unref(&link->outcfg.samplerates); > - ff_channel_layouts_unref(&link->incfg.channel_layouts); > - ff_channel_layouts_unref(&link->outcfg.channel_layouts); > - ff_formats_unref(&link->incfg.color_spaces); > - ff_formats_unref(&link->outcfg.color_spaces); > - ff_formats_unref(&link->incfg.color_ranges); > - ff_formats_unref(&link->outcfg.color_ranges); > - > return 0; > } > > @@ -818,6 +890,7 @@ do { > \ > list_type *fmts; \ > \ > if (link->type != out_link->type || \ > + !out_link->incfg.list || \ > out_link->incfg.list->nb == 1) \ > continue; \ > fmts = out_link->incfg.list; \ > @@ -925,6 +998,7 @@ static void swap_samplerates_on_filter(AVFilterContext > *filter) > link = filter->inputs[i]; > > if (link->type == AVMEDIA_TYPE_AUDIO && > + link->outcfg.samplerates && > link->outcfg.samplerates->nb_formats== 1) > break; > } > @@ -938,6 +1012,7 @@ static void swap_samplerates_on_filter(AVFilterContext > *filter) > int best_idx, best_diff = INT_MAX; > > if (outlink->type != AVMEDIA_TYPE_AUDIO || > + !outlink->incfg.samplerates || > outlink->incfg.samplerates->nb_formats < 2) > continue; > > @@ -1007,6 +1082,7 @@ static void > swap_channel_layouts_on_filter(AVFilterContext *filter) > link = filter->inputs[i]; > > if (link->type == AVMEDIA_TYPE_AUDIO && > + link->outcfg.channel_layouts && > link->outcfg.channel_layouts->nb_channel_layouts == 1) > break; > } > @@ -1018,6 +1094,7 @@ static void > swap_channel_layouts_on_filter(AVFilterContext *filter) > int best_idx = -1, best_score = INT_MIN, best_count_diff = INT_MAX; > > if (outlink->type != AVMEDIA_TYPE_AUDIO || > + !outlink->incfg.channel_layouts || > outlink->incfg.channel_layouts->nb_channel_layouts < 2) > continue; > > @@ -1110,6 +1187,7 @@ static void swap_sample_fmts_on_filter(AVFilterContext > *filter) > link = filter->inputs[i]; > > if (link->type == AVMEDIA_TYPE_AUDIO && > + link->outcfg.formats && > link->outcfg.formats->nb_formats == 1) > break; > } > @@ -1124,6 +1202,7 @@ static void swap_sample_fmts_on_filter(AVFilterContext > *filter) > int best_idx = -1, best_score = INT_MIN; > > if (outlink->type != AVMEDIA_TYPE_AUDIO || > + !outlink->incfg.formats || > outlink->incfg.formats->nb_formats < 2) > continue; > > @@ -1180,7 +1259,8 @@ static int pick_formats(AVFilterGraph *graph) > AVFilterContext *filter = graph->filters[i]; > if (filter->nb_inputs){ > for (j = 0; j < filter->nb_inputs; j++){ > - if (filter->inputs[j]->incfg.formats && > filter->inputs[j]->incfg.formats->nb_formats == 1) { > + if (filter->inputs[j]->format < 0 && > + filter->inputs[j]->incfg.formats && > filter->inputs[j]->incfg.formats->nb_formats == 1) { > if ((ret = pick_format(filter->inputs[j], NULL)) < 0) > return ret; > change = 1; > @@ -1189,7 +1269,8 @@ static int pick_formats(AVFilterGraph *graph) > } > if (filter->nb_outputs){ > for (j = 0; j < filter->nb_outputs; j++){ > - if (filter->outputs[j]->incfg.formats && > filter->outputs[j]->incfg.formats->nb_formats == 1) { > + if (filter->outputs[j]->format < 0 && > + filter->outputs[j]->incfg.formats && > filter->outputs[j]->incfg.formats->nb_formats == 1) { > if ((ret = pick_format(filter->outputs[j], NULL)) < 0) > return ret; > change = 1; > @@ -1198,7 +1279,7 @@ static int pick_formats(AVFilterGraph *graph) > } > if (filter->nb_inputs && filter->nb_outputs && > filter->inputs[0]->format>=0) { > for (j = 0; j < filter->nb_outputs; j++) { > - if (filter->outputs[j]->format<0) { > + if (filter->outputs[j]->format < 0 && > filter->outputs[j]->incfg.formats) { > if ((ret = pick_format(filter->outputs[j], > filter->inputs[0])) < 0) > return ret; > change = 1; > @@ -1292,6 +1373,82 @@ static int graph_config_pointers(AVFilterGraph *graph, > void *log_ctx) > return 0; > } > > +static void graph_clear_formats(AVFilterGraph *graph, void *log_ctx) > +{ > + int status_in, status_out; > + AVRational time_base; > + AVFilterContext *f; > + FilterLinkInternal *ilinki; > + FilterLinkInternal *olinki; > + int i, j; > + > + for (i = graph->nb_filters - 1; i >= 0; i--) { > + f = graph->filters[i]; > + > + if (!strncmp(f->name, "auto_", strlen("auto_"))) { > + ilinki = ff_link_internal(f->inputs[0]); > + olinki = ff_link_internal(f->outputs[0]); > + if (ilinki->status_out || ilinki->status_in || > + olinki->status_out || olinki->status_in) { > + AVFilterContext *src = f->inputs[0]->src; > + AVFilterContext *dst = f->outputs[0]->dst; > + unsigned srcpad = FF_OUTLINK_IDX(f->inputs[0]); > + unsigned dstpad = FF_INLINK_IDX(f->outputs[0]); > + > + status_in = ilinki->status_in; > + status_out = ilinki->status_out; > + time_base = f->inputs[0]->time_base; > + > + avfilter_free(f); > + avfilter_link(src, srcpad, dst, dstpad); > + > + olinki->status_in = status_in; > + olinki->status_out = status_out; > + dst->inputs[0]->time_base = time_base; > + > + ff_filter_set_ready(dst, 200); > + continue; > + } > + } > + > + for (j = 0; j < f->nb_inputs; j++) { > + ilinki = ff_link_internal(f->inputs[j]); > + if (ilinki->status_out) { > + ff_formats_unref(&f->inputs[j]->outcfg.formats); > + ff_formats_unref(&f->inputs[j]->outcfg.samplerates); > + > ff_channel_layouts_unref(&f->inputs[j]->outcfg.channel_layouts); > + > + if (ilinki->status_in) { > + f->inputs[j]->format = -1; > + f->inputs[j]->sample_rate = 0; > + ilinki->init_state = AVLINK_UNINIT; > + f->inputs[j]->time_base.num = 0; > + f->inputs[j]->time_base.den = 0; > + av_channel_layout_uninit(&f->inputs[j]->ch_layout); > + } > + } > + } > + > + for (j = 0; j < f->nb_outputs; j++) { > + olinki = ff_link_internal(f->outputs[j]); > + if (olinki->status_in) { > + ff_formats_unref(&f->outputs[j]->incfg.formats); > + ff_formats_unref(&f->outputs[j]->incfg.samplerates); > + > ff_channel_layouts_unref(&f->outputs[j]->incfg.channel_layouts); > + > + if (olinki->status_out) { > + f->outputs[j]->format = -1; > + f->outputs[j]->sample_rate = 0; > + olinki->init_state = AVLINK_UNINIT; > + f->outputs[j]->time_base.num = 0; > + f->outputs[j]->time_base.den = 0; > + av_channel_layout_uninit(&f->outputs[j]->ch_layout); > + } > + } > + } > + } > +} > + > int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx) > { > int ret; > @@ -1310,6 +1467,30 @@ int avfilter_graph_config(AVFilterGraph *graphctx, > void *log_ctx) > return 0; > } > > +int ff_avfilter_graph_reconfig(AVFilterGraph *graphctx, void *log_ctx) > +{ > + AVClass *avc = log_ctx ? *(AVClass **)log_ctx : NULL; > + int ret, i; > + > + if (avc) > + av_log(log_ctx, AV_LOG_INFO, "%s set graph reconfig\n", > avc->item_name(log_ctx)); > + > + for (i = 0; i < graphctx->nb_filters; i++) > + av_log(log_ctx, AV_LOG_INFO, "\t%s", graphctx->filters[i]->name); > + > + graph_clear_formats(graphctx, log_ctx); > + if ((ret = graph_config_formats(graphctx, log_ctx))) > + return ret; > + if ((ret = graph_config_links(graphctx, log_ctx))) > + return ret; > + if ((ret = graph_check_links(graphctx, log_ctx))) > + return ret; > + if ((ret = graph_config_pointers(graphctx, log_ctx))) > + return ret; > + > + return 0; > +}
This isn’t API of libavfilter. Please note ff_ is for libavfilter internal usage. I don’t think the patch works. > + > int avfilter_graph_send_command(AVFilterGraph *graph, const char *target, > const char *cmd, const char *arg, char *res, int res_len, int flags) > { > int i, r = AVERROR(ENOSYS); > -- > 2.47.0 > > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe". _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".