lavfi: add query_func2()

It differs from query_func() in accepting arrays of input/output format
configurations to be filled as callback parameters. This allows to mark
the filter context as const, ensuring it is not modified by this
function, as it is not supposed to have any side effects beyond
returning the supported formats.
This commit is contained in:
Anton Khirnov 2024-08-29 08:23:19 +02:00
parent 01f2d95fbf
commit eddffbedb3
6 changed files with 347 additions and 36 deletions

View File

@ -98,6 +98,41 @@ const char *avfilter_pad_get_name(const AVFilterPad *pads, int pad_idx);
*/
enum AVMediaType avfilter_pad_get_type(const AVFilterPad *pads, int pad_idx);
/**
* Lists of formats / etc. supported by an end of a link.
*
* This structure is directly part of AVFilterLink, in two copies:
* one for the source filter, one for the destination filter.
* These lists are used for negotiating the format to actually be used,
* which will be loaded into the format and channel_layout members of
* AVFilterLink, when chosen.
*/
typedef struct AVFilterFormatsConfig {
/**
* List of supported formats (pixel or sample).
*/
AVFilterFormats *formats;
/**
* Lists of supported sample rates, only for audio.
*/
AVFilterFormats *samplerates;
/**
* Lists of supported channel layouts, only for audio.
*/
AVFilterChannelLayouts *channel_layouts;
/**
* Lists of supported YUV color metadata, only for YUV video.
*/
AVFilterFormats *color_spaces; ///< AVColorSpace
AVFilterFormats *color_ranges; ///< AVColorRange
} AVFilterFormatsConfig;
/**
* The number of the filter inputs is not determined just by AVFilter.inputs.
* The filter might add additional inputs during initialization depending on the
@ -324,6 +359,21 @@ typedef struct AVFilter {
* AVERROR code otherwise
*/
int (*query_func)(AVFilterContext *);
/**
* Same as query_func(), except this function writes the results into
* provided arrays.
*
* @param cfg_in array of input format configurations with as many
* members as the filters has inputs (NULL when there are
* no inputs);
* @param cfg_out array of output format configurations with as many
* members as the filters has outputs (NULL when there
* are no outputs);
*/
int (*query_func2)(const AVFilterContext *,
struct AVFilterFormatsConfig **cfg_in,
struct AVFilterFormatsConfig **cfg_out);
/**
* A pointer to an array of admissible pixel formats delimited
* by AV_PIX_FMT_NONE. The generic code will use this list
@ -492,41 +542,6 @@ struct AVFilterContext {
int extra_hw_frames;
};
/**
* Lists of formats / etc. supported by an end of a link.
*
* This structure is directly part of AVFilterLink, in two copies:
* one for the source filter, one for the destination filter.
* These lists are used for negotiating the format to actually be used,
* which will be loaded into the format and channel_layout members of
* AVFilterLink, when chosen.
*/
typedef struct AVFilterFormatsConfig {
/**
* List of supported formats (pixel or sample).
*/
AVFilterFormats *formats;
/**
* Lists of supported sample rates, only for audio.
*/
AVFilterFormats *samplerates;
/**
* Lists of supported channel layouts, only for audio.
*/
AVFilterChannelLayouts *channel_layouts;
/**
* Lists of supported YUV color metadata, only for YUV video.
*/
AVFilterFormats *color_spaces; ///< AVColorSpace
AVFilterFormats *color_ranges; ///< AVColorRange
} AVFilterFormatsConfig;
/**
* A link between two filters. This contains pointers to the source and
* destination filters between which this link exists, and the indexes of

View File

@ -352,7 +352,50 @@ static int filter_query_formats(AVFilterContext *ctx)
ctx->name, av_err2str(ret));
return ret;
}
} else if (ctx->filter->formats_state == FF_FILTER_FORMATS_QUERY_FUNC2) {
AVFilterFormatsConfig *cfg_in_stack[64], *cfg_out_stack[64];
AVFilterFormatsConfig **cfg_in_dyn = NULL, **cfg_out_dyn = NULL;
AVFilterFormatsConfig **cfg_in, **cfg_out;
if (ctx->nb_inputs > FF_ARRAY_ELEMS(cfg_in_stack)) {
cfg_in_dyn = av_malloc_array(ctx->nb_inputs, sizeof(*cfg_in_dyn));
if (!cfg_in_dyn)
return AVERROR(ENOMEM);
cfg_in = cfg_in_dyn;
} else
cfg_in = ctx->nb_inputs ? cfg_in_stack : NULL;
for (unsigned i = 0; i < ctx->nb_inputs; i++) {
AVFilterLink *l = ctx->inputs[i];
cfg_in[i] = &l->outcfg;
}
if (ctx->nb_outputs > FF_ARRAY_ELEMS(cfg_out_stack)) {
cfg_out_dyn = av_malloc_array(ctx->nb_outputs, sizeof(*cfg_out_dyn));
if (!cfg_out_dyn)
return AVERROR(ENOMEM);
cfg_out = cfg_out_dyn;
} else
cfg_out = ctx->nb_outputs ? cfg_out_stack : NULL;
for (unsigned i = 0; i < ctx->nb_outputs; i++) {
AVFilterLink *l = ctx->outputs[i];
cfg_out[i] = &l->incfg;
}
ret = ctx->filter->formats.query_func2(ctx, cfg_in, cfg_out);
av_freep(&cfg_in_dyn);
av_freep(&cfg_out_dyn);
if (ret < 0) {
if (ret != AVERROR(EAGAIN))
av_log(ctx, AV_LOG_ERROR, "Query format failed for '%s': %s\n",
ctx->name, av_err2str(ret));
return ret;
}
}
if (ctx->filter->formats_state == FF_FILTER_FORMATS_QUERY_FUNC ||
ctx->filter->formats_state == FF_FILTER_FORMATS_QUERY_FUNC2) {
ret = filter_check_formats(ctx);
if (ret < 0)
return ret;

View File

@ -226,6 +226,7 @@ enum FilterFormatsState {
*/
FF_FILTER_FORMATS_PASSTHROUGH = 0,
FF_FILTER_FORMATS_QUERY_FUNC, ///< formats.query active.
FF_FILTER_FORMATS_QUERY_FUNC2, ///< formats.query_func2 active.
FF_FILTER_FORMATS_PIXFMT_LIST, ///< formats.pixels_list active.
FF_FILTER_FORMATS_SAMPLEFMTS_LIST, ///< formats.samples_list active.
FF_FILTER_FORMATS_SINGLE_PIXFMT, ///< formats.pix_fmt active
@ -235,6 +236,9 @@ enum FilterFormatsState {
#define FILTER_QUERY_FUNC(func) \
.formats.query_func = func, \
.formats_state = FF_FILTER_FORMATS_QUERY_FUNC
#define FILTER_QUERY_FUNC2(func) \
.formats.query_func2 = func, \
.formats_state = FF_FILTER_FORMATS_QUERY_FUNC2
#define FILTER_PIXFMTS_ARRAY(array) \
.formats.pixels_list = array, \
.formats_state = FF_FILTER_FORMATS_PIXFMT_LIST

View File

@ -876,6 +876,153 @@ int ff_set_common_formats_from_list(AVFilterContext *ctx, const int *fmts)
return ff_set_common_formats(ctx, ff_make_format_list(fmts));
}
#define SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, fmts, media_type, \
ref_fn, unref_fn) \
if (!fmts) \
return AVERROR(ENOMEM); \
\
for (unsigned i = 0; i < ctx->nb_inputs; i++) { \
const AVFilterLink *const link = ctx->inputs[i]; \
if (!cfg_in[i]->fmts && \
(media_type == AVMEDIA_TYPE_UNKNOWN || \
link->type == media_type)) { \
int ret = ref_fn(fmts, &cfg_in[i]->fmts); \
if (ret < 0) { \
return ret; \
} \
} \
} \
for (unsigned i = 0; i < ctx->nb_outputs; i++) { \
const AVFilterLink *const link = ctx->outputs[i]; \
if (!cfg_out[i]->fmts && \
(media_type == AVMEDIA_TYPE_UNKNOWN || \
link->type == media_type)) { \
int ret = ref_fn(fmts, &cfg_out[i]->fmts); \
if (ret < 0) { \
return ret; \
} \
} \
} \
\
if (!fmts->refcount) \
unref_fn(&fmts); \
\
return 0;
int ff_set_common_channel_layouts2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
AVFilterChannelLayouts *channel_layouts)
{
SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, channel_layouts, AVMEDIA_TYPE_AUDIO,
ff_channel_layouts_ref, ff_channel_layouts_unref);
}
int ff_set_common_channel_layouts_from_list2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
const AVChannelLayout *fmts)
{
return ff_set_common_channel_layouts2(ctx, cfg_in, cfg_out, ff_make_channel_layout_list(fmts));
}
int ff_set_common_all_channel_counts2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out)
{
return ff_set_common_channel_layouts2(ctx, cfg_in, cfg_out, ff_all_channel_counts());
}
int ff_set_common_samplerates2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
AVFilterFormats *samplerates)
{
SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, samplerates, AVMEDIA_TYPE_AUDIO,
ff_formats_ref, ff_formats_unref);
}
int ff_set_common_samplerates_from_list2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
const int *samplerates)
{
return ff_set_common_samplerates2(ctx, cfg_in, cfg_out, ff_make_format_list(samplerates));
}
int ff_set_common_all_samplerates2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out)
{
return ff_set_common_samplerates2(ctx, cfg_in, cfg_out, ff_all_samplerates());
}
int ff_set_common_color_spaces2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
AVFilterFormats *color_spaces)
{
SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, color_spaces, AVMEDIA_TYPE_VIDEO,
ff_formats_ref, ff_formats_unref);
}
int ff_set_common_color_spaces_from_list2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
const int *color_ranges)
{
return ff_set_common_color_spaces2(ctx, cfg_in, cfg_out, ff_make_format_list(color_ranges));
}
int ff_set_common_all_color_spaces2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out)
{
return ff_set_common_color_spaces2(ctx, cfg_in, cfg_out, ff_all_color_spaces());
}
int ff_set_common_color_ranges2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
AVFilterFormats *color_ranges)
{
SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, color_ranges, AVMEDIA_TYPE_VIDEO,
ff_formats_ref, ff_formats_unref);
}
int ff_set_common_color_ranges_from_list2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
const int *color_ranges)
{
return ff_set_common_color_ranges2(ctx, cfg_in, cfg_out, ff_make_format_list(color_ranges));
}
int ff_set_common_all_color_ranges2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out)
{
return ff_set_common_color_ranges2(ctx, cfg_in, cfg_out, ff_all_color_ranges());
}
int ff_set_common_formats2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
AVFilterFormats *formats)
{
SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, formats, AVMEDIA_TYPE_UNKNOWN,
ff_formats_ref, ff_formats_unref);
}
int ff_set_common_formats_from_list2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
const int *fmts)
{
return ff_set_common_formats2(ctx, cfg_in, cfg_out, ff_make_format_list(fmts));
}
int ff_default_query_formats(AVFilterContext *ctx)
{
const AVFilter *const f = ctx->filter;
@ -905,6 +1052,7 @@ int ff_default_query_formats(AVFilterContext *ctx)
/* Intended fallthrough */
case FF_FILTER_FORMATS_PASSTHROUGH:
case FF_FILTER_FORMATS_QUERY_FUNC:
case FF_FILTER_FORMATS_QUERY_FUNC2:
type = AVMEDIA_TYPE_UNKNOWN;
formats = ff_all_formats(ctx->nb_inputs ? ctx->inputs [0]->type :
ctx->nb_outputs ? ctx->outputs[0]->type :

View File

@ -225,6 +225,90 @@ int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats);
av_warn_unused_result
int ff_set_common_formats_from_list(AVFilterContext *ctx, const int *fmts);
/**
* Helpers for query_formats2() which set all free audio links to the same list
* of channel layouts/sample rates. If there are no links hooked to this list,
* the list is freed.
*/
av_warn_unused_result
int ff_set_common_channel_layouts2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
AVFilterChannelLayouts *channel_layouts);
av_warn_unused_result
int ff_set_common_channel_layouts_from_list2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
const AVChannelLayout *fmts);
av_warn_unused_result
int ff_set_common_all_channel_counts2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out);
av_warn_unused_result
int ff_set_common_samplerates2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
AVFilterFormats *samplerates);
av_warn_unused_result
int ff_set_common_samplerates_from_list2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
const int *samplerates);
av_warn_unused_result
int ff_set_common_all_samplerates2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out);
av_warn_unused_result
int ff_set_common_color_spaces2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
AVFilterFormats *color_spaces);
av_warn_unused_result
int ff_set_common_color_spaces_from_list2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
const int *color_ranges);
av_warn_unused_result
int ff_set_common_all_color_spaces2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out);
av_warn_unused_result
int ff_set_common_color_ranges2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
AVFilterFormats *color_ranges);
av_warn_unused_result
int ff_set_common_color_ranges_from_list2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
const int *color_ranges);
av_warn_unused_result
int ff_set_common_all_color_ranges2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out);
av_warn_unused_result
int ff_set_common_formats2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
AVFilterFormats *formats);
av_warn_unused_result
int ff_set_common_formats_from_list2(const AVFilterContext *ctx,
AVFilterFormatsConfig **cfg_in,
AVFilterFormatsConfig **cfg_out,
const int *fmts);
av_warn_unused_result
int ff_add_channel_layout(AVFilterChannelLayouts **l,
const AVChannelLayout *channel_layout);

View File

@ -143,7 +143,24 @@ int main(int argc, char **argv)
if (filter->formats_state == FF_FILTER_FORMATS_QUERY_FUNC)
ret = filter->formats.query_func(filter_ctx);
else
else if (filter->formats_state == FF_FILTER_FORMATS_QUERY_FUNC2) {
AVFilterFormatsConfig **cfg_in = NULL, **cfg_out = NULL;
if (filter_ctx->nb_inputs) {
cfg_in = av_malloc_array(filter_ctx->nb_inputs, sizeof(*cfg_in));
for (unsigned i = 0; i < filter_ctx->nb_inputs; i++)
cfg_in[i] = &filter_ctx->inputs[i]->outcfg;
}
if (filter_ctx->nb_outputs) {
cfg_out = av_malloc_array(filter_ctx->nb_outputs, sizeof(*cfg_out));
for (unsigned i = 0; i < filter_ctx->nb_outputs; i++)
cfg_out[i] = &filter_ctx->outputs[i]->incfg;
}
ret = filter->formats.query_func2(filter_ctx, cfg_in, cfg_out);
av_freep(&cfg_in);
av_freep(&cfg_out);
} else
ret = ff_default_query_formats(filter_ctx);
print_formats(filter_ctx);