diff mbox

[FFmpeg-devel] libavfilter: constify filter list

Message ID 20180130072412.22887-1-mfcc64@gmail.com
State New
Headers show

Commit Message

Muhammad Faiz Jan. 30, 2018, 7:24 a.m. UTC
Move REGISTER_FILTER to FILTER_TABLE in configure.
Auto generate filter extern and filter table.
Sort filter table, use bsearch on avfilter_get_by_name.
Define next pointer at filter extern, no need to initialize
next pointer at run time, so AVFilter can be set to const.
Make avfilter_register always return error.
Target checkasm now depends on EXTRALIBS-avformat.

Signed-off-by: Muhammad Faiz <mfcc64@gmail.com>
---
 Makefile                           |   4 +-
 configure                          | 440 ++++++++++++++++++++++++++++++++++++-
 libavfilter/aeval.c                |   6 +-
 libavfilter/af_acontrast.c         |   3 +-
 libavfilter/af_acopy.c             |   3 +-
 libavfilter/af_acrusher.c          |   3 +-
 libavfilter/af_adelay.c            |   3 +-
 libavfilter/af_aecho.c             |   3 +-
 libavfilter/af_aemphasis.c         |   3 +-
 libavfilter/af_afade.c             |   6 +-
 libavfilter/af_afftfilt.c          |   3 +-
 libavfilter/af_afir.c              |   3 +-
 libavfilter/af_aformat.c           |   3 +-
 libavfilter/af_agate.c             |   6 +-
 libavfilter/af_aiir.c              |   3 +-
 libavfilter/af_alimiter.c          |   3 +-
 libavfilter/af_amerge.c            |   3 +-
 libavfilter/af_amix.c              |   3 +-
 libavfilter/af_anequalizer.c       |   3 +-
 libavfilter/af_anull.c             |   3 +-
 libavfilter/af_apad.c              |   3 +-
 libavfilter/af_aphaser.c           |   3 +-
 libavfilter/af_apulsator.c         |   3 +-
 libavfilter/af_aresample.c         |   3 +-
 libavfilter/af_asetnsamples.c      |   3 +-
 libavfilter/af_asetrate.c          |   3 +-
 libavfilter/af_ashowinfo.c         |   3 +-
 libavfilter/af_astats.c            |   3 +-
 libavfilter/af_atempo.c            |   3 +-
 libavfilter/af_biquads.c           |   3 +-
 libavfilter/af_bs2b.c              |   3 +-
 libavfilter/af_channelmap.c        |   3 +-
 libavfilter/af_channelsplit.c      |   3 +-
 libavfilter/af_chorus.c            |   3 +-
 libavfilter/af_compand.c           |   3 +-
 libavfilter/af_compensationdelay.c |   3 +-
 libavfilter/af_crossfeed.c         |   3 +-
 libavfilter/af_crystalizer.c       |   3 +-
 libavfilter/af_dcshift.c           |   3 +-
 libavfilter/af_dynaudnorm.c        |   3 +-
 libavfilter/af_earwax.c            |   3 +-
 libavfilter/af_extrastereo.c       |   3 +-
 libavfilter/af_firequalizer.c      |   3 +-
 libavfilter/af_flanger.c           |   3 +-
 libavfilter/af_haas.c              |   3 +-
 libavfilter/af_hdcd.c              |   3 +-
 libavfilter/af_headphone.c         |   3 +-
 libavfilter/af_join.c              |   3 +-
 libavfilter/af_ladspa.c            |   3 +-
 libavfilter/af_loudnorm.c          |   3 +-
 libavfilter/af_lv2.c               |   3 +-
 libavfilter/af_mcompand.c          |   3 +-
 libavfilter/af_pan.c               |   3 +-
 libavfilter/af_replaygain.c        |   3 +-
 libavfilter/af_resample.c          |   3 +-
 libavfilter/af_rubberband.c        |   3 +-
 libavfilter/af_sidechaincompress.c |   6 +-
 libavfilter/af_silencedetect.c     |   3 +-
 libavfilter/af_silenceremove.c     |   3 +-
 libavfilter/af_sofalizer.c         |   3 +-
 libavfilter/af_stereotools.c       |   3 +-
 libavfilter/af_stereowiden.c       |   3 +-
 libavfilter/af_superequalizer.c    |   3 +-
 libavfilter/af_surround.c          |   3 +-
 libavfilter/af_tremolo.c           |   3 +-
 libavfilter/af_vibrato.c           |   3 +-
 libavfilter/af_volume.c            |   3 +-
 libavfilter/af_volumedetect.c      |   3 +-
 libavfilter/allfilters.c           | 433 ++++--------------------------------
 libavfilter/asink_anullsink.c      |   3 +-
 libavfilter/asrc_anoisesrc.c       |   3 +-
 libavfilter/asrc_anullsrc.c        |   3 +-
 libavfilter/asrc_flite.c           |   3 +-
 libavfilter/asrc_hilbert.c         |   3 +-
 libavfilter/asrc_sine.c            |   3 +-
 libavfilter/avf_abitscope.c        |   3 +-
 libavfilter/avf_ahistogram.c       |   3 +-
 libavfilter/avf_aphasemeter.c      |   3 +-
 libavfilter/avf_avectorscope.c     |   3 +-
 libavfilter/avf_concat.c           |   3 +-
 libavfilter/avf_showcqt.c          |   3 +-
 libavfilter/avf_showfreqs.c        |   3 +-
 libavfilter/avf_showspectrum.c     |   6 +-
 libavfilter/avf_showvolume.c       |   3 +-
 libavfilter/avf_showwaves.c        |   6 +-
 libavfilter/avfilter.c             |  43 +---
 libavfilter/avfilter.h             |   2 +-
 libavfilter/buffersink.c           |   6 +-
 libavfilter/buffersrc.c            |   6 +-
 libavfilter/f_bench.c              |   6 +-
 libavfilter/f_drawgraph.c          |   6 +-
 libavfilter/f_ebur128.c            |   3 +-
 libavfilter/f_interleave.c         |   6 +-
 libavfilter/f_loop.c               |   6 +-
 libavfilter/f_metadata.c           |   6 +-
 libavfilter/f_perms.c              |   6 +-
 libavfilter/f_realtime.c           |   6 +-
 libavfilter/f_reverse.c            |   6 +-
 libavfilter/f_select.c             |   6 +-
 libavfilter/f_sendcmd.c            |   6 +-
 libavfilter/f_sidedata.c           |   6 +-
 libavfilter/f_streamselect.c       |  10 +-
 libavfilter/f_zmq.c                |   6 +-
 libavfilter/fifo.c                 |   6 +-
 libavfilter/internal.h             |   2 +
 libavfilter/setpts.c               |   6 +-
 libavfilter/settb.c                |   6 +-
 libavfilter/split.c                |  10 +-
 libavfilter/src_movie.c            |   8 +-
 libavfilter/trim.c                 |   6 +-
 libavfilter/vaf_spectrumsynth.c    |   3 +-
 libavfilter/vf_alphamerge.c        |   3 +-
 libavfilter/vf_aspect.c            |   6 +-
 libavfilter/vf_atadenoise.c        |   3 +-
 libavfilter/vf_avgblur.c           |   3 +-
 libavfilter/vf_bbox.c              |   3 +-
 libavfilter/vf_bitplanenoise.c     |   3 +-
 libavfilter/vf_blackdetect.c       |   3 +-
 libavfilter/vf_blackframe.c        |   3 +-
 libavfilter/vf_blend.c             |   6 +-
 libavfilter/vf_boxblur.c           |   3 +-
 libavfilter/vf_bwdif.c             |   3 +-
 libavfilter/vf_chromakey.c         |   3 +-
 libavfilter/vf_ciescope.c          |   3 +-
 libavfilter/vf_codecview.c         |   3 +-
 libavfilter/vf_colorbalance.c      |   3 +-
 libavfilter/vf_colorchannelmixer.c |   3 +-
 libavfilter/vf_colorkey.c          |   3 +-
 libavfilter/vf_colorlevels.c       |   3 +-
 libavfilter/vf_colormatrix.c       |   3 +-
 libavfilter/vf_colorspace.c        |   3 +-
 libavfilter/vf_convolution.c       |  12 +-
 libavfilter/vf_convolve.c          |   6 +-
 libavfilter/vf_copy.c              |   3 +-
 libavfilter/vf_coreimage.m         |   6 +-
 libavfilter/vf_cover_rect.c        |   3 +-
 libavfilter/vf_crop.c              |   3 +-
 libavfilter/vf_cropdetect.c        |   3 +-
 libavfilter/vf_curves.c            |   3 +-
 libavfilter/vf_datascope.c         |  15 +-
 libavfilter/vf_dctdnoiz.c          |   3 +-
 libavfilter/vf_deband.c            |   3 +-
 libavfilter/vf_decimate.c          |   3 +-
 libavfilter/vf_deflicker.c         |   3 +-
 libavfilter/vf_deinterlace_qsv.c   |   3 +-
 libavfilter/vf_deinterlace_vaapi.c |   3 +-
 libavfilter/vf_dejudder.c          |   3 +-
 libavfilter/vf_delogo.c            |   3 +-
 libavfilter/vf_deshake.c           |   3 +-
 libavfilter/vf_despill.c           |   3 +-
 libavfilter/vf_detelecine.c        |   3 +-
 libavfilter/vf_displace.c          |   3 +-
 libavfilter/vf_drawbox.c           |   6 +-
 libavfilter/vf_drawtext.c          |   3 +-
 libavfilter/vf_edgedetect.c        |   3 +-
 libavfilter/vf_elbg.c              |   3 +-
 libavfilter/vf_entropy.c           |   3 +-
 libavfilter/vf_eq.c                |   3 +-
 libavfilter/vf_extractplanes.c     |   6 +-
 libavfilter/vf_fade.c              |   3 +-
 libavfilter/vf_fftfilt.c           |   3 +-
 libavfilter/vf_field.c             |   3 +-
 libavfilter/vf_fieldhint.c         |   3 +-
 libavfilter/vf_fieldmatch.c        |   3 +-
 libavfilter/vf_fieldorder.c        |   3 +-
 libavfilter/vf_fillborders.c       |   3 +-
 libavfilter/vf_find_rect.c         |   3 +-
 libavfilter/vf_floodfill.c         |   3 +-
 libavfilter/vf_format.c            |   6 +-
 libavfilter/vf_fps.c               |   3 +-
 libavfilter/vf_framepack.c         |   3 +-
 libavfilter/vf_framerate.c         |   3 +-
 libavfilter/vf_framestep.c         |   3 +-
 libavfilter/vf_frei0r.c            |  10 +-
 libavfilter/vf_fspp.c              |   3 +-
 libavfilter/vf_gblur.c             |   3 +-
 libavfilter/vf_geq.c               |   3 +-
 libavfilter/vf_gradfun.c           |   3 +-
 libavfilter/vf_hflip.c             |   3 +-
 libavfilter/vf_histeq.c            |   3 +-
 libavfilter/vf_histogram.c         |   3 +-
 libavfilter/vf_hqdn3d.c            |   3 +-
 libavfilter/vf_hqx.c               |   3 +-
 libavfilter/vf_hue.c               |   3 +-
 libavfilter/vf_hwdownload.c        |   3 +-
 libavfilter/vf_hwmap.c             |   3 +-
 libavfilter/vf_hwupload.c          |   3 +-
 libavfilter/vf_hwupload_cuda.c     |   3 +-
 libavfilter/vf_hysteresis.c        |   3 +-
 libavfilter/vf_idet.c              |   3 +-
 libavfilter/vf_il.c                |   3 +-
 libavfilter/vf_interlace.c         |   3 +-
 libavfilter/vf_kerndeint.c         |   3 +-
 libavfilter/vf_lenscorrection.c    |   3 +-
 libavfilter/vf_libopencv.c         |   3 +-
 libavfilter/vf_libvmaf.c           |   3 +-
 libavfilter/vf_limiter.c           |   3 +-
 libavfilter/vf_lumakey.c           |   3 +-
 libavfilter/vf_lut.c               |   3 +-
 libavfilter/vf_lut2.c              |   6 +-
 libavfilter/vf_lut3d.c             |   6 +-
 libavfilter/vf_maskedclamp.c       |   3 +-
 libavfilter/vf_maskedmerge.c       |   3 +-
 libavfilter/vf_mcdeint.c           |   3 +-
 libavfilter/vf_mergeplanes.c       |   3 +-
 libavfilter/vf_mestimate.c         |   3 +-
 libavfilter/vf_midequalizer.c      |   3 +-
 libavfilter/vf_minterpolate.c      |   3 +-
 libavfilter/vf_misc_vaapi.c        |  10 +-
 libavfilter/vf_mix.c               |   3 +-
 libavfilter/vf_mpdecimate.c        |   3 +-
 libavfilter/vf_neighbor.c          |   3 +-
 libavfilter/vf_nlmeans.c           |   3 +-
 libavfilter/vf_nnedi.c             |   3 +-
 libavfilter/vf_noise.c             |   3 +-
 libavfilter/vf_normalize.c         |   3 +-
 libavfilter/vf_null.c              |   3 +-
 libavfilter/vf_ocr.c               |   3 +-
 libavfilter/vf_overlay.c           |   3 +-
 libavfilter/vf_overlay_opencl.c    |   3 +-
 libavfilter/vf_overlay_qsv.c       |   3 +-
 libavfilter/vf_owdenoise.c         |   3 +-
 libavfilter/vf_pad.c               |   3 +-
 libavfilter/vf_palettegen.c        |   3 +-
 libavfilter/vf_paletteuse.c        |   3 +-
 libavfilter/vf_perspective.c       |   3 +-
 libavfilter/vf_phase.c             |   3 +-
 libavfilter/vf_pixdesctest.c       |   3 +-
 libavfilter/vf_pp.c                |   3 +-
 libavfilter/vf_pp7.c               |   3 +-
 libavfilter/vf_premultiply.c       |   6 +-
 libavfilter/vf_procamp_vaapi.c     |   3 +-
 libavfilter/vf_program_opencl.c    |   6 +-
 libavfilter/vf_pseudocolor.c       |   3 +-
 libavfilter/vf_psnr.c              |   3 +-
 libavfilter/vf_pullup.c            |   3 +-
 libavfilter/vf_qp.c                |   3 +-
 libavfilter/vf_random.c            |   3 +-
 libavfilter/vf_readeia608.c        |   3 +-
 libavfilter/vf_readvitc.c          |   3 +-
 libavfilter/vf_remap.c             |   3 +-
 libavfilter/vf_removegrain.c       |   3 +-
 libavfilter/vf_removelogo.c        |   3 +-
 libavfilter/vf_repeatfields.c      |   3 +-
 libavfilter/vf_rotate.c            |   3 +-
 libavfilter/vf_sab.c               |   3 +-
 libavfilter/vf_scale.c             |  12 +-
 libavfilter/vf_scale_cuda.c        |   3 +-
 libavfilter/vf_scale_npp.c         |   3 +-
 libavfilter/vf_scale_qsv.c         |   3 +-
 libavfilter/vf_scale_vaapi.c       |   3 +-
 libavfilter/vf_selectivecolor.c    |   3 +-
 libavfilter/vf_separatefields.c    |   3 +-
 libavfilter/vf_setfield.c          |   3 +-
 libavfilter/vf_setparams.c         |   3 +-
 libavfilter/vf_showinfo.c          |   3 +-
 libavfilter/vf_showpalette.c       |   3 +-
 libavfilter/vf_shuffleframes.c     |   3 +-
 libavfilter/vf_shuffleplanes.c     |   3 +-
 libavfilter/vf_signalstats.c       |   3 +-
 libavfilter/vf_signature.c         |   3 +-
 libavfilter/vf_smartblur.c         |   3 +-
 libavfilter/vf_spp.c               |   3 +-
 libavfilter/vf_ssim.c              |   3 +-
 libavfilter/vf_stack.c             |   6 +-
 libavfilter/vf_stereo3d.c          |   3 +-
 libavfilter/vf_subtitles.c         |   6 +-
 libavfilter/vf_super2xsai.c        |   3 +-
 libavfilter/vf_swaprect.c          |   3 +-
 libavfilter/vf_swapuv.c            |   3 +-
 libavfilter/vf_telecine.c          |   3 +-
 libavfilter/vf_threshold.c         |   3 +-
 libavfilter/vf_thumbnail.c         |   3 +-
 libavfilter/vf_thumbnail_cuda.c    |   3 +-
 libavfilter/vf_tile.c              |   3 +-
 libavfilter/vf_tinterlace.c        |   3 +-
 libavfilter/vf_tonemap.c           |   3 +-
 libavfilter/vf_transpose.c         |   3 +-
 libavfilter/vf_unsharp.c           |   3 +-
 libavfilter/vf_unsharp_opencl.c    |   3 +-
 libavfilter/vf_uspp.c              |   3 +-
 libavfilter/vf_vaguedenoiser.c     |   3 +-
 libavfilter/vf_vectorscope.c       |   3 +-
 libavfilter/vf_vflip.c             |   3 +-
 libavfilter/vf_vidstabdetect.c     |   3 +-
 libavfilter/vf_vidstabtransform.c  |   3 +-
 libavfilter/vf_vignette.c          |   3 +-
 libavfilter/vf_vmafmotion.c        |   3 +-
 libavfilter/vf_vpp_qsv.c           |   3 +-
 libavfilter/vf_w3fdif.c            |   3 +-
 libavfilter/vf_waveform.c          |   3 +-
 libavfilter/vf_weave.c             |  10 +-
 libavfilter/vf_xbr.c               |   3 +-
 libavfilter/vf_yadif.c             |   3 +-
 libavfilter/vf_zoompan.c           |   3 +-
 libavfilter/vf_zscale.c            |   3 +-
 libavfilter/vsink_nullsink.c       |   3 +-
 libavfilter/vsrc_cellauto.c        |   3 +-
 libavfilter/vsrc_life.c            |   3 +-
 libavfilter/vsrc_mandelbrot.c      |   3 +-
 libavfilter/vsrc_mptestsrc.c       |   3 +-
 libavfilter/vsrc_testsrc.c         |  33 ++-
 tests/checkasm/Makefile            |   2 +-
 303 files changed, 1229 insertions(+), 796 deletions(-)

Comments

wm4 Jan. 30, 2018, 7:58 a.m. UTC | #1
On Tue, 30 Jan 2018 14:24:12 +0700
Muhammad Faiz <mfcc64@gmail.com> wrote:

> Move REGISTER_FILTER to FILTER_TABLE in configure.
> Auto generate filter extern and filter table.
> Sort filter table, use bsearch on avfilter_get_by_name.
> Define next pointer at filter extern, no need to initialize
> next pointer at run time, so AVFilter can be set to const.
> Make avfilter_register always return error.
> Target checkasm now depends on EXTRALIBS-avformat.
> 
> Signed-off-by: Muhammad Faiz <mfcc64@gmail.com>
> ---

Pretty nice. Should we still add a new filter listing API that uses the
same idiom as the BSF API? (Although you generate the .next links at
configure time it might still be nice to eventually get rid of that.)
Carl Eugen Hoyos Jan. 30, 2018, 9:47 a.m. UTC | #2
2018-01-30 8:24 GMT+01:00 Muhammad Faiz <mfcc64@gmail.com>:

> +UNCONDITIONAL_FILTER_TABLE="
> +abuffer         asrc
> +buffer          vsrc
> +abuffersink     asink
> +buffersink      vsink
> +afifo           af
> +fifo            vf

Why are these unconditional now?

Carl Eugen
Michael Niedermayer Jan. 30, 2018, 12:50 p.m. UTC | #3
On Tue, Jan 30, 2018 at 02:24:12PM +0700, Muhammad Faiz wrote:
> Move REGISTER_FILTER to FILTER_TABLE in configure.
> Auto generate filter extern and filter table.
> Sort filter table, use bsearch on avfilter_get_by_name.
> Define next pointer at filter extern, no need to initialize
> next pointer at run time, so AVFilter can be set to const.

> Make avfilter_register always return error.

That breaks API
Its also a step away from supporting plugins.
Why plugins matter ? Because having plugin
support is a big advantage, it allows a much wider
community to work on, write and maintain filters.

With plugins, people can write filters that are
written in languages other than C. Or filters which
some developer in FFmpeg doesnt want. Or they can be
maintained externally by people who just do not like us.
Or by people who perfer a FOSS license different from
LGPL/GPL/BSD. Iam sure others can come up with more
reasons ...

Of course avfilter_register() isnt enough for plugins
but it or something equivalent is needed for plugins.

So i would prefer if avfilter_register() stays supported
indefinitly or in case a different system is written for
plugins then until that system is in place.

That is just a preferrance, not an objection to the patch.


[...]
wm4 Jan. 30, 2018, 1:03 p.m. UTC | #4
On Tue, 30 Jan 2018 13:50:37 +0100
Michael Niedermayer <michael@niedermayer.cc> wrote:

> On Tue, Jan 30, 2018 at 02:24:12PM +0700, Muhammad Faiz wrote:
> > Move REGISTER_FILTER to FILTER_TABLE in configure.
> > Auto generate filter extern and filter table.
> > Sort filter table, use bsearch on avfilter_get_by_name.
> > Define next pointer at filter extern, no need to initialize
> > next pointer at run time, so AVFilter can be set to const.  
> 
> > Make avfilter_register always return error.  
> 
> That breaks API
> Its also a step away from supporting plugins.
> Why plugins matter ? Because having plugin
> support is a big advantage, it allows a much wider
> community to work on, write and maintain filters.
> 
> With plugins, people can write filters that are
> written in languages other than C. Or filters which
> some developer in FFmpeg doesnt want. Or they can be
> maintained externally by people who just do not like us.
> Or by people who perfer a FOSS license different from
> LGPL/GPL/BSD. Iam sure others can come up with more
> reasons ...
> 
> Of course avfilter_register() isnt enough for plugins
> but it or something equivalent is needed for plugins.
> 
> So i would prefer if avfilter_register() stays supported
> indefinitly or in case a different system is written for
> plugins then until that system is in place.

<insert contents of dozens of mails I've written before about this as
reply to exactly the same posts by you>

> That is just a preferrance, not an objection to the patch.

It certainly sounds like one.
Muhammad Faiz Jan. 30, 2018, 1:16 p.m. UTC | #5
On Tue, Jan 30, 2018 at 2:58 PM, wm4 <nfxjfg@googlemail.com> wrote:
> On Tue, 30 Jan 2018 14:24:12 +0700
> Muhammad Faiz <mfcc64@gmail.com> wrote:
>
>> Move REGISTER_FILTER to FILTER_TABLE in configure.
>> Auto generate filter extern and filter table.
>> Sort filter table, use bsearch on avfilter_get_by_name.
>> Define next pointer at filter extern, no need to initialize
>> next pointer at run time, so AVFilter can be set to const.
>> Make avfilter_register always return error.
>> Target checkasm now depends on EXTRALIBS-avformat.
>>
>> Signed-off-by: Muhammad Faiz <mfcc64@gmail.com>
>> ---
>
> Pretty nice. Should we still add a new filter listing API that uses the
> same idiom as the BSF API? (Although you generate the .next links at
> configure time it might still be nice to eventually get rid of that.)

IMHO, it is uglier (i.e. by requiring opaque pointer). I don't know
other people's opinion. Anyway, it can be added later.
Muhammad Faiz Jan. 30, 2018, 1:17 p.m. UTC | #6
On Tue, Jan 30, 2018 at 4:47 PM, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
> 2018-01-30 8:24 GMT+01:00 Muhammad Faiz <mfcc64@gmail.com>:
>
>> +UNCONDITIONAL_FILTER_TABLE="
>> +abuffer         asrc
>> +buffer          vsrc
>> +abuffersink     asink
>> +buffersink      vsink
>> +afifo           af
>> +fifo            vf
>
> Why are these unconditional now?

They were previously unconditional by REGISTER_FILTER_UNCONDITIONAL().
Muhammad Faiz Jan. 30, 2018, 1:27 p.m. UTC | #7
On Tue, Jan 30, 2018 at 7:50 PM, Michael Niedermayer
<michael@niedermayer.cc> wrote:
> On Tue, Jan 30, 2018 at 02:24:12PM +0700, Muhammad Faiz wrote:
>> Move REGISTER_FILTER to FILTER_TABLE in configure.
>> Auto generate filter extern and filter table.
>> Sort filter table, use bsearch on avfilter_get_by_name.
>> Define next pointer at filter extern, no need to initialize
>> next pointer at run time, so AVFilter can be set to const.
>
>> Make avfilter_register always return error.
>
> That breaks API

No. Because with current API, it is impossible to implement external filter.


> Its also a step away from supporting plugins.
> Why plugins matter ? Because having plugin
> support is a big advantage, it allows a much wider
> community to work on, write and maintain filters.
>
> With plugins, people can write filters that are
> written in languages other than C. Or filters which
> some developer in FFmpeg doesnt want. Or they can be
> maintained externally by people who just do not like us.
> Or by people who perfer a FOSS license different from
> LGPL/GPL/BSD. Iam sure others can come up with more
> reasons ...
>
> Of course avfilter_register() isnt enough for plugins
> but it or something equivalent is needed for plugins.
>
> So i would prefer if avfilter_register() stays supported
> indefinitly or in case a different system is written for
> plugins then until that system is in place.

It just returns error and logs error message that currently external
filter is unsupported. If someone wants to implement support for
external filter, it can be easily added later using separate
list/table.
wm4 Jan. 30, 2018, 1:37 p.m. UTC | #8
On Tue, 30 Jan 2018 20:16:50 +0700
Muhammad Faiz <mfcc64@gmail.com> wrote:

> On Tue, Jan 30, 2018 at 2:58 PM, wm4 <nfxjfg@googlemail.com> wrote:
> > On Tue, 30 Jan 2018 14:24:12 +0700
> > Muhammad Faiz <mfcc64@gmail.com> wrote:
> >  
> >> Move REGISTER_FILTER to FILTER_TABLE in configure.
> >> Auto generate filter extern and filter table.
> >> Sort filter table, use bsearch on avfilter_get_by_name.
> >> Define next pointer at filter extern, no need to initialize
> >> next pointer at run time, so AVFilter can be set to const.
> >> Make avfilter_register always return error.
> >> Target checkasm now depends on EXTRALIBS-avformat.
> >>
> >> Signed-off-by: Muhammad Faiz <mfcc64@gmail.com>
> >> ---  
> >
> > Pretty nice. Should we still add a new filter listing API that uses the
> > same idiom as the BSF API? (Although you generate the .next links at
> > configure time it might still be nice to eventually get rid of that.)  
> 
> IMHO, it is uglier (i.e. by requiring opaque pointer). I don't know
> other people's opinion. Anyway, it can be added later.

Yes, because it uses an opaque pointer, it could use the linked list,
without changing any of the existing code. I think a simple filter array
would be preferable in the end, which would require an iteration
function which can pass an index to the user.

(Although I agree that this opaque thing is uglier than the current API
or an API which just takes an index.)
Mark Thompson Jan. 30, 2018, 2:09 p.m. UTC | #9
On 30/01/18 07:24, Muhammad Faiz wrote:
> Move REGISTER_FILTER to FILTER_TABLE in configure.
> Auto generate filter extern and filter table.
> Sort filter table, use bsearch on avfilter_get_by_name.
> Define next pointer at filter extern, no need to initialize
> next pointer at run time, so AVFilter can be set to const.
> Make avfilter_register always return error.
> Target checkasm now depends on EXTRALIBS-avformat.
> 
> Signed-off-by: Muhammad Faiz <mfcc64@gmail.com>
> ---

I like the idea of this, but I'm not sure about some of the implementation details.

Have you considered dropping the "next" links entirely and having just the array of pointers instead?  I feel like they don't really add anything useful once we are in this form, and result in more boilerplate on every filter and some tricky generation code.

avfilter_next() would be a bit slower (since it would have to search the array, and therefore have runtime linear in the number of filters), but avfilter_get_by_name() is a lot faster because of the binary search (and is the only common use of it) so I don't think that complaint would be a strong one.

>  Makefile                           |   4 +-
>  configure                          | 440 ++++++++++++++++++++++++++++++++++++-
> ...
>  tests/checkasm/Makefile            |   2 +-
>  303 files changed, 1229 insertions(+), 796 deletions(-)
> 
> diff --git a/Makefile b/Makefile
> index 9defddebfd..f607579369 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -56,6 +56,7 @@ tools/uncoded_frame$(EXESUF): ELIBS = $(FF_EXTRALIBS)
>  tools/target_dec_%_fuzzer$(EXESUF): $(FF_DEP_LIBS)
>  
>  CONFIGURABLE_COMPONENTS =                                           \
> +    $(SRC_PATH)/configure                                           \
>      $(wildcard $(FFLIBS:%=$(SRC_PATH)/lib%/all*.c))                 \
>      $(SRC_PATH)/libavcodec/bitstream_filters.c                      \
>      $(SRC_PATH)/libavformat/protocols.c                             \
> @@ -142,7 +143,8 @@ distclean:: clean
>  	$(RM) .version avversion.h config.asm config.h mapfile  \
>  		ffbuild/.config ffbuild/config.* libavutil/avconfig.h \
>  		version.h libavutil/ffversion.h libavcodec/codec_names.h \
> -		libavcodec/bsf_list.c libavformat/protocol_list.c
> +		libavcodec/bsf_list.c libavformat/protocol_list.c \
> +		libavfilter/filter_list.h libavfilter/filter_list.c
>  ifeq ($(SRC_LINK),src)
>  	$(RM) src
>  endif
> diff --git a/configure b/configure
> index fcfa7aa442..3261f5fd1a 100755
> --- a/configure
> +++ b/configure
> @@ -3177,6 +3177,381 @@ unix_protocol_deps="sys_un_h"
>  unix_protocol_select="network"
>  
>  # filters
> +FILTER_TABLE="
> +abench                  af
> +acompressor             af
> +acontrast               af
> ...
> +spectrumsynth           vaf
> +amovie                  avsrc
> +movie                   avsrc
> +"
> +
> +UNCONDITIONAL_FILTER_TABLE="
> +abuffer         asrc
> +buffer          vsrc
> +abuffersink     asink
> +buffersink      vsink
> +afifo           af
> +fifo            vf
> +"
> +

I don't really like having this table in configure.  Since you're generating the filter_list.h header with the external definitions from it anyway, why not write that and use it as the source rather than having the table here?

>  afftfilt_filter_deps="avcodec"
>  afftfilt_filter_select="fft"
>  afir_filter_deps="avcodec"
> @@ -3530,7 +3905,18 @@ MUXER_LIST=$(find_things    muxer    _MUX     libavformat/allformats.c)
>  DEMUXER_LIST=$(find_things  demuxer  DEMUX    libavformat/allformats.c)
>  OUTDEV_LIST=$(find_things   outdev   OUTDEV   libavdevice/alldevices.c)
>  INDEV_LIST=$(find_things    indev    _IN      libavdevice/alldevices.c)
> -FILTER_LIST=$(find_things   filter   FILTER   libavfilter/allfilters.c)
> +
> +extract_list_from_table(){
> +    cols=$1
> +    suffix=$2
> +    shift 2
> +    while test -n "$1"; do
> +        echo "${1}${suffix}"
> +        shift $cols
> +    done
> +}
> +
> +FILTER_LIST=$(extract_list_from_table 2 _filter $FILTER_TABLE)
>  
>  find_things_extern(){
>      thing=$1
> @@ -7030,6 +7416,58 @@ print_enabled_components(){
>  print_enabled_components libavcodec/bsf_list.c AVBitStreamFilter bitstream_filters $BSF_LIST
>  print_enabled_components libavformat/protocol_list.c URLProtocol url_protocols $PROTOCOL_LIST
>  
> +# filters
> +extract_enabled_filter(){
> +    while test -n "$1"; do
> +        if enabled "${1}_filter"; then
> +            echo "$1 $2"
> +        fi
> +        shift 2
> +    done
> +}
> +
> +extract_sorted_filter(){
> +    while test -n "$1"; do
> +        echo "$1 $2"
> +        shift 2
> +    done | sort
> +}
> +
> +print_filter_extern(){
> +    while test -n "$1"; do
> +        echo "extern const AVFilter ff_${2}_${1};"
> +        if test -n "$3"; then
> +            echo "#define ff_next_${2}_${1} &ff_${4}_${3}"
> +        else
> +            echo "#define ff_next_${2}_${1} NULL"
> +        fi
> +        shift 2
> +    done
> +}
> +
> +print_filter_array(){
> +    echo "static const AVFilter *const filter_list[] = {"
> +    while test -n "$1"; do
> +        echo "    &ff_${2}_${1},"
> +        shift 2
> +    done
> +    echo "    NULL"
> +    echo "};"
> +}
> +
> +sorted_filter_table=$(extract_sorted_filter $(extract_enabled_filter $FILTER_TABLE) $UNCONDITIONAL_FILTER_TABLE)
> +
> +echo "/* Automatically generated by configure - do not modify! */" > $TMPH
> +echo "#ifndef AVFILTER_FILTER_LIST_H" >> $TMPH
> +echo "#define AVFILTER_FILTER_LIST_H" >> $TMPH
> +print_filter_extern $sorted_filter_table >> $TMPH
> +echo "#endif" >> $TMPH
> +cp_if_changed $TMPH libavfilter/filter_list.h
> +
> +echo "/* Automatically generated by configure - do not modify! */" > $TMPH
> +print_filter_array $sorted_filter_table >> $TMPH
> +cp_if_changed $TMPH libavfilter/filter_list.c
> +
>  # Settings for pkg-config files
>  
>  cat > $TMPH <<EOF
> diff --git a/libavfilter/aeval.c b/libavfilter/aeval.c
> index cdddbaf31d..d5963367a1 100644
> --- a/libavfilter/aeval.c
> +++ b/libavfilter/aeval.c
> @@ -322,7 +322,7 @@ static const AVFilterPad aevalsrc_outputs[] = {
>      { NULL }
>  };
>  
> -AVFilter ff_asrc_aevalsrc = {
> +const AVFilter ff_asrc_aevalsrc = {
>      .name          = "aevalsrc",
>      .description   = NULL_IF_CONFIG_SMALL("Generate an audio signal generated by an expression."),
>      .query_formats = query_formats,
> @@ -332,6 +332,7 @@ AVFilter ff_asrc_aevalsrc = {
>      .inputs        = NULL,
>      .outputs       = aevalsrc_outputs,
>      .priv_class    = &aevalsrc_class,
> +    .next          = ff_next_asrc_aevalsrc,

If we're going to go with this approach, I think this field should be macroed somehow because it is entirely boilerplate.

Maybe an AVFILTER_NAME() macro which sets the "name" and "next" fields?

>  };
>  
>  #endif /* CONFIG_AEVALSRC_FILTER */
> @@ -475,7 +476,7 @@ static const AVFilterPad aeval_outputs[] = {
>      { NULL }
>  };
>  
> -AVFilter ff_af_aeval = {
> +const AVFilter ff_af_aeval = {
>      .name          = "aeval",
>      .description   = NULL_IF_CONFIG_SMALL("Filter audio signal according to a specified expression."),
>      .query_formats = aeval_query_formats,
> @@ -485,6 +486,7 @@ AVFilter ff_af_aeval = {
>      .inputs        = aeval_inputs,
>      .outputs       = aeval_outputs,
>      .priv_class    = &aeval_class,
> +    .next          = ff_next_af_aeval,
>  };
>  
>  #endif /* CONFIG_AEVAL_FILTER */
> 
> ...
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 9adb1090b7..8bab79ff96 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -19,412 +19,59 @@
>   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>   */
>  
> +#include "libavutil/avassert.h"
>  #include "libavutil/thread.h"
>  #include "avfilter.h"
>  #include "config.h"
>  
> +#include "libavfilter/filter_list.h"
> +#include "libavfilter/filter_list.c"
>  
> -#define REGISTER_FILTER(X, x, y)                                        \
> -    {                                                                   \
> -        extern AVFilter ff_##y##_##x;                                   \
> -        if (CONFIG_##X##_FILTER)                                        \
> -            avfilter_register(&ff_##y##_##x);                           \
> -    }
> -
> -#define REGISTER_FILTER_UNCONDITIONAL(x)                                \
> -    {                                                                   \
> -        extern AVFilter ff_##x;                                         \
> -        avfilter_register(&ff_##x);                                     \
> -    }
>  
> -static void register_all(void)
> +#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1
> +static void check_validity(void)
>  {
> -    REGISTER_FILTER(ABENCH,         abench,         af);
> -    REGISTER_FILTER(ACOMPRESSOR,    acompressor,    af);
> -    REGISTER_FILTER(ACONTRAST,      acontrast,      af);
> ...
> -    REGISTER_FILTER(TESTSRC,        testsrc,        vsrc);
> -    REGISTER_FILTER(TESTSRC2,       testsrc2,       vsrc);
> -    REGISTER_FILTER(YUVTESTSRC,     yuvtestsrc,     vsrc);
> +    int k;
> +    for (k = 0; k < FF_ARRAY_ELEMS(filter_list) - 2; k++) {
> +        av_assert2(filter_list[k]->next == filter_list[k+1] ||
> +                   (av_log(NULL, AV_LOG_FATAL, "%s filter: invalid next pointer.\n", filter_list[k]->name),0));
> +        av_assert2(strcmp(filter_list[k]->name, filter_list[k+1]->name) < 0 ||
> +                   (av_log(NULL, AV_LOG_FATAL, "%s filter: unsorted with %s.\n", filter_list[k]->name, filter_list[k+1]->name),0));
> +    }
> +    av_assert2(!filter_list[k]->next);
> +    av_assert2(!filter_list[k+1]);
> +}

Cute :)  I think it should be assert0() if we go with the links, though - almost noone builds with --assert-level=2, and the overhead of this check is not very high.

>  
> -    REGISTER_FILTER(NULLSINK,       nullsink,       vsink);
> +static AVOnce check_validity_once = AV_ONCE_INIT;
> +#define CHECK_VALIDITY() ff_thread_once(&check_validity_once, check_validity)
> +#else
> +#define CHECK_VALIDITY() ((void)0)
> +#endif
>  
> -    /* multimedia filters */
> -    REGISTER_FILTER(ABITSCOPE,      abitscope,      avf);
> -    REGISTER_FILTER(ADRAWGRAPH,     adrawgraph,     avf);
> -    REGISTER_FILTER(AHISTOGRAM,     ahistogram,     avf);
> -    REGISTER_FILTER(APHASEMETER,    aphasemeter,    avf);
> -    REGISTER_FILTER(AVECTORSCOPE,   avectorscope,   avf);
> -    REGISTER_FILTER(CONCAT,         concat,         avf);
> -    REGISTER_FILTER(SHOWCQT,        showcqt,        avf);
> -    REGISTER_FILTER(SHOWFREQS,      showfreqs,      avf);
> -    REGISTER_FILTER(SHOWSPECTRUM,   showspectrum,   avf);
> -    REGISTER_FILTER(SHOWSPECTRUMPIC, showspectrumpic, avf);
> -    REGISTER_FILTER(SHOWVOLUME,     showvolume,     avf);
> -    REGISTER_FILTER(SHOWWAVES,      showwaves,      avf);
> -    REGISTER_FILTER(SHOWWAVESPIC,   showwavespic,   avf);
> -    REGISTER_FILTER(SPECTRUMSYNTH,  spectrumsynth,  vaf);
> +void avfilter_register_all(void)
> +{
> +    CHECK_VALIDITY();
> +}
>  
> -    /* multimedia sources */
> -    REGISTER_FILTER(AMOVIE,         amovie,         avsrc);
> -    REGISTER_FILTER(MOVIE,          movie,          avsrc);
> +const AVFilter *avfilter_next(const AVFilter *prev)
> +{
> +    CHECK_VALIDITY();

Calling avfilter_next() without having called avfilter_register_all() violates the API, though?

(Or is there an intent to deprecate avfilter_register_all() immediately after this?)

> +    return prev ? prev->next : filter_list[0];
> +}
>  
> -    /* those filters are part of public or internal API => registered
> -     * unconditionally */
> -    REGISTER_FILTER_UNCONDITIONAL(asrc_abuffer);
> -    REGISTER_FILTER_UNCONDITIONAL(vsrc_buffer);
> -    REGISTER_FILTER_UNCONDITIONAL(asink_abuffer);
> -    REGISTER_FILTER_UNCONDITIONAL(vsink_buffer);
> -    REGISTER_FILTER_UNCONDITIONAL(af_afifo);
> -    REGISTER_FILTER_UNCONDITIONAL(vf_fifo);
> +static int compare_name(const void *key, const void *elem)
> +{
> +    const char *name = key;
> +    const AVFilter *const *filter = elem;
> +    return strcmp(name, (*filter)->name);
>  }
>  
> -void avfilter_register_all(void)
> +const AVFilter *avfilter_get_by_name(const char *name)
>  {
> -    static AVOnce control = AV_ONCE_INIT;
> +    const AVFilter **filter;
>  
> -    ff_thread_once(&control, register_all);
> +    CHECK_VALIDITY();
> +    filter = bsearch(name, filter_list, FF_ARRAY_ELEMS(filter_list) - 1,
> +                     sizeof(filter_list[0]), compare_name);
> +    return filter ? *filter : NULL;
>  }
> ...
> diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
> index ea75467a75..b89c28d57e 100644
> --- a/libavfilter/avfilter.c
> +++ b/libavfilter/avfilter.c
> @@ -575,49 +575,10 @@ int avfilter_process_command(AVFilterContext *filter, const char *cmd, const cha
>      return AVERROR(ENOSYS);
>  }
>  
> -static AVFilter *first_filter;
> -static AVFilter **last_filter = &first_filter;
> -
> -const AVFilter *avfilter_get_by_name(const char *name)
> -{
> -    const AVFilter *f = NULL;
> -
> -    if (!name)
> -        return NULL;
> -
> -    while ((f = avfilter_next(f)))
> -        if (!strcmp(f->name, name))
> -            return (AVFilter *)f;
> -
> -    return NULL;
> -}
> -
> -static AVMutex filter_register_mutex = AV_MUTEX_INITIALIZER;
> -
>  int avfilter_register(AVFilter *filter)
>  {
> -    AVFilter **f;
> -
> -    /* the filter must select generic or internal exclusively */
> -    av_assert0((filter->flags & AVFILTER_FLAG_SUPPORT_TIMELINE) != AVFILTER_FLAG_SUPPORT_TIMELINE);
> -
> -    ff_mutex_lock(&filter_register_mutex);
> -    f = last_filter;
> -
> -    while (*f)
> -        f = &(*f)->next;
> -    *f = filter;
> -    filter->next = NULL;
> -    last_filter = &filter->next;
> -
> -    ff_mutex_unlock(&filter_register_mutex);
> -
> -    return 0;
> -}
> -
> -const AVFilter *avfilter_next(const AVFilter *prev)
> -{
> -    return prev ? prev->next : first_filter;
> +    av_log(NULL, AV_LOG_ERROR, "External filter registration is currently unsupported.\n");
> +    return AVERROR(EINVAL);

+1 to explicitly blocking external filter registration.

>  }
>  
>  int avfilter_pad_count(const AVFilterPad *pads)
> diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
> index 62eed2168f..24e46a9b40 100644
> --- a/libavfilter/avfilter.h
> +++ b/libavfilter/avfilter.h
> @@ -289,7 +289,7 @@ typedef struct AVFilter {
>       * Used by the filter registration system. Must not be touched by any other
>       * code.
>       */
> -    struct AVFilter *next;
> +    const struct AVFilter *next;
>  
>      /**
>       * Make the filter instance process a command.
> 
> ...
> diff --git a/libavfilter/vf_datascope.c b/libavfilter/vf_datascope.c
> index 467663556e..bb9e2befe2 100644
> --- a/libavfilter/vf_datascope.c
> +++ b/libavfilter/vf_datascope.c
> @@ -390,6 +390,7 @@ static int config_output(AVFilterLink *outlink)
>      return 0;
>  }
>  
> +#if CONFIG_DATASCOPE_FILTER

This sort of fixup should be an independent change.

>  static const AVFilterPad inputs[] = {
>      {
>          .name         = "default",
> @@ -409,7 +410,7 @@ static const AVFilterPad outputs[] = {
>      { NULL }
>  };
>  
> -AVFilter ff_vf_datascope = {
> +const AVFilter ff_vf_datascope = {
>      .name          = "datascope",
>      .description   = NULL_IF_CONFIG_SMALL("Video data analysis."),
>      .priv_size     = sizeof(DatascopeContext),
> @@ -418,7 +419,9 @@ AVFilter ff_vf_datascope = {
>      .inputs        = inputs,
>      .outputs       = outputs,
>      .flags         = AVFILTER_FLAG_SLICE_THREADS,
> +    .next          = ff_next_vf_datascope,
>  };
> +#endif /* CONFIG_DATASCOPE_FILTER */
>  
>  typedef struct PixscopeContext {
>      const AVClass *class;
> @@ -642,6 +645,7 @@ static int pixscope_filter_frame(AVFilterLink *inlink, AVFrame *in)
>      return ff_filter_frame(outlink, out);
>  }
>  
> +#if CONFIG_PIXSCOPE_FILTER
>  static const AVFilterPad pixscope_inputs[] = {
>      {
>          .name           = "default",
> @@ -660,7 +664,7 @@ static const AVFilterPad pixscope_outputs[] = {
>      { NULL }
>  };
>  
> -AVFilter ff_vf_pixscope = {
> +const AVFilter ff_vf_pixscope = {
>      .name          = "pixscope",
>      .description   = NULL_IF_CONFIG_SMALL("Pixel data analysis."),
>      .priv_size     = sizeof(PixscopeContext),
> @@ -669,7 +673,9 @@ AVFilter ff_vf_pixscope = {
>      .inputs        = pixscope_inputs,
>      .outputs       = pixscope_outputs,
>      .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
> +    .next          = ff_next_vf_pixscope,
>  };
> +#endif /* CONFIG_PIXSCOPE_FILTER */
>  
>  typedef struct PixelValues {
>      uint16_t p[4];
> @@ -1022,6 +1028,7 @@ static int oscilloscope_filter_frame(AVFilterLink *inlink, AVFrame *frame)
>      return ff_filter_frame(outlink, frame);
>  }
>  
> +#if CONFIG_OSCILLOSCOPE_FILTER
>  static const AVFilterPad oscilloscope_inputs[] = {
>      {
>          .name           = "default",
> @@ -1041,7 +1048,7 @@ static const AVFilterPad oscilloscope_outputs[] = {
>      { NULL }
>  };
>  
> -AVFilter ff_vf_oscilloscope = {
> +const AVFilter ff_vf_oscilloscope = {
>      .name          = "oscilloscope",
>      .description   = NULL_IF_CONFIG_SMALL("2D Video Oscilloscope."),
>      .priv_size     = sizeof(OscilloscopeContext),
> @@ -1051,4 +1058,6 @@ AVFilter ff_vf_oscilloscope = {
>      .inputs        = oscilloscope_inputs,
>      .outputs       = oscilloscope_outputs,
>      .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
> +    .next          = ff_next_vf_oscilloscope,
>  };
> +#endif /* CONFIG_OSCILLOSCOPE_FILTER */
> ...
> diff --git a/tests/checkasm/Makefile b/tests/checkasm/Makefile
> index afbd09b940..4a96159c1a 100644
> --- a/tests/checkasm/Makefile
> +++ b/tests/checkasm/Makefile
> @@ -61,7 +61,7 @@ tests/checkasm/checkasm.o: CFLAGS += -Umain
>  CHECKASM := tests/checkasm/checkasm$(EXESUF)
>  
>  $(CHECKASM): $(CHECKASMOBJS) $(FF_STATIC_DEP_LIBS)
> -	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(CHECKASMOBJS) $(FF_STATIC_DEP_LIBS) $(EXTRALIBS-avcodec) $(EXTRALIBS-avfilter) $(EXTRALIBS-avutil) $(EXTRALIBS)
> +	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(CHECKASMOBJS) $(FF_STATIC_DEP_LIBS) $(EXTRALIBS-avcodec) $(EXTRALIBS-avformat) $(EXTRALIBS-avfilter) $(EXTRALIBS-avutil) $(EXTRALIBS)
>  
>  checkasm: $(CHECKASM)
>  
> 

Thanks,

- Mark
wm4 Jan. 30, 2018, 2:20 p.m. UTC | #10
On Tue, 30 Jan 2018 14:09:43 +0000
Mark Thompson <sw@jkqxz.net> wrote:

> On 30/01/18 07:24, Muhammad Faiz wrote:
> > Move REGISTER_FILTER to FILTER_TABLE in configure.
> > Auto generate filter extern and filter table.
> > Sort filter table, use bsearch on avfilter_get_by_name.
> > Define next pointer at filter extern, no need to initialize
> > next pointer at run time, so AVFilter can be set to const.
> > Make avfilter_register always return error.
> > Target checkasm now depends on EXTRALIBS-avformat.
> > 
> > Signed-off-by: Muhammad Faiz <mfcc64@gmail.com>
> > ---  
> 
> I like the idea of this, but I'm not sure about some of the implementation details.
> 
> Have you considered dropping the "next" links entirely and having just the array of pointers instead?  I feel like they don't really add anything useful once we are in this form, and result in more boilerplate on every filter and some tricky generation code.
> 
> avfilter_next() would be a bit slower (since it would have to search the array, and therefore have runtime linear in the number of filters), but avfilter_get_by_name() is a lot faster because of the binary search (and is the only common use of it) so I don't think that complaint would be a strong one.

I think we considered that for libavcodec, but discarded the idea
because ffmpeg.c's use was causing noticable performance problems? (I
don't remember the details.) Also wouldn't the runtime be quadratic
rather than linear when iterating filters?

> > +const AVFilter *avfilter_next(const AVFilter *prev)
> > +{
> > +    CHECK_VALIDITY();  
> 
> Calling avfilter_next() without having called avfilter_register_all() violates the API, though?

Before this change it just returned NULL, now it returns the full list.
I think that was the intent and I don't think it's a problem, besides
keeping the old behavior would require mutable state.


> (Or is there an intent to deprecate avfilter_register_all() immediately after this?)

Hopefully.
Mark Thompson Jan. 30, 2018, 2:36 p.m. UTC | #11
On 30/01/18 14:20, wm4 wrote:
> On Tue, 30 Jan 2018 14:09:43 +0000
> Mark Thompson <sw@jkqxz.net> wrote:
> 
>> On 30/01/18 07:24, Muhammad Faiz wrote:
>>> Move REGISTER_FILTER to FILTER_TABLE in configure.
>>> Auto generate filter extern and filter table.
>>> Sort filter table, use bsearch on avfilter_get_by_name.
>>> Define next pointer at filter extern, no need to initialize
>>> next pointer at run time, so AVFilter can be set to const.
>>> Make avfilter_register always return error.
>>> Target checkasm now depends on EXTRALIBS-avformat.
>>>
>>> Signed-off-by: Muhammad Faiz <mfcc64@gmail.com>
>>> ---  
>>
>> I like the idea of this, but I'm not sure about some of the implementation details.
>>
>> Have you considered dropping the "next" links entirely and having just the array of pointers instead?  I feel like they don't really add anything useful once we are in this form, and result in more boilerplate on every filter and some tricky generation code.
>>
>> avfilter_next() would be a bit slower (since it would have to search the array, and therefore have runtime linear in the number of filters), but avfilter_get_by_name() is a lot faster because of the binary search (and is the only common use of it) so I don't think that complaint would be a strong one.
> 
> I think we considered that for libavcodec, but discarded the idea
> because ffmpeg.c's use was causing noticable performance problems? (I
> don't remember the details.) Also wouldn't the runtime be quadratic
> rather than linear when iterating filters?

Linear per-call, quadratic to iterate over all.

Do you have any reference for that discussion so we can compare?  The only call to avfilter_next() in ffmpeg.c is for showing the filter list (-filters), which I don't think is a performance-sensitive option.

Also, avfilter_next() can be made faster (possibly, constants will be larger) by using the binary search in the list since you know the name of the current filter (it's in the pointer you are given).

>>> +const AVFilter *avfilter_next(const AVFilter *prev)
>>> +{
>>> +    CHECK_VALIDITY();  
>>
>> Calling avfilter_next() without having called avfilter_register_all() violates the API, though?
> 
> Before this change it just returned NULL, now it returns the full list.
> I think that was the intent and I don't think it's a problem, besides
> keeping the old behavior would require mutable state.

I was thinking of just not having the validity check there.  Maybe it doesn't matter.

>> (Or is there an intent to deprecate avfilter_register_all() immediately after this?)
> 
> Hopefully.

Sounds good :)

Thanks,

- Mark
Muhammad Faiz Jan. 30, 2018, 6:06 p.m. UTC | #12
On Tue, Jan 30, 2018 at 9:09 PM, Mark Thompson <sw@jkqxz.net> wrote:
> On 30/01/18 07:24, Muhammad Faiz wrote:
>> Move REGISTER_FILTER to FILTER_TABLE in configure.
>> Auto generate filter extern and filter table.
>> Sort filter table, use bsearch on avfilter_get_by_name.
>> Define next pointer at filter extern, no need to initialize
>> next pointer at run time, so AVFilter can be set to const.
>> Make avfilter_register always return error.
>> Target checkasm now depends on EXTRALIBS-avformat.
>>
>> Signed-off-by: Muhammad Faiz <mfcc64@gmail.com>
>> ---
>
> I like the idea of this, but I'm not sure about some of the implementation details.
>
> Have you considered dropping the "next" links entirely and having just the array of pointers instead?  I feel like they don't really add anything useful once we are in this form, and result in more boilerplate on every filter and some tricky generation code.
>
> avfilter_next() would be a bit slower (since it would have to search the array, and therefore have runtime linear in the number of filters), but avfilter_get_by_name() is a lot faster because of the binary search (and is the only common use of it) so I don't think that complaint would be a strong one.

Making avfilter_next() slower (even if it is rarely used) isn't good, I think.


>
>>  Makefile                           |   4 +-
>>  configure                          | 440 ++++++++++++++++++++++++++++++++++++-
>> ...
>>  tests/checkasm/Makefile            |   2 +-
>>  303 files changed, 1229 insertions(+), 796 deletions(-)
>>
>> diff --git a/Makefile b/Makefile
>> index 9defddebfd..f607579369 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -56,6 +56,7 @@ tools/uncoded_frame$(EXESUF): ELIBS = $(FF_EXTRALIBS)
>>  tools/target_dec_%_fuzzer$(EXESUF): $(FF_DEP_LIBS)
>>
>>  CONFIGURABLE_COMPONENTS =                                           \
>> +    $(SRC_PATH)/configure                                           \
>>      $(wildcard $(FFLIBS:%=$(SRC_PATH)/lib%/all*.c))                 \
>>      $(SRC_PATH)/libavcodec/bitstream_filters.c                      \
>>      $(SRC_PATH)/libavformat/protocols.c                             \
>> @@ -142,7 +143,8 @@ distclean:: clean
>>       $(RM) .version avversion.h config.asm config.h mapfile  \
>>               ffbuild/.config ffbuild/config.* libavutil/avconfig.h \
>>               version.h libavutil/ffversion.h libavcodec/codec_names.h \
>> -             libavcodec/bsf_list.c libavformat/protocol_list.c
>> +             libavcodec/bsf_list.c libavformat/protocol_list.c \
>> +             libavfilter/filter_list.h libavfilter/filter_list.c
>>  ifeq ($(SRC_LINK),src)
>>       $(RM) src
>>  endif
>> diff --git a/configure b/configure
>> index fcfa7aa442..3261f5fd1a 100755
>> --- a/configure
>> +++ b/configure
>> @@ -3177,6 +3177,381 @@ unix_protocol_deps="sys_un_h"
>>  unix_protocol_select="network"
>>
>>  # filters
>> +FILTER_TABLE="
>> +abench                  af
>> +acompressor             af
>> +acontrast               af
>> ...
>> +spectrumsynth           vaf
>> +amovie                  avsrc
>> +movie                   avsrc
>> +"
>> +
>> +UNCONDITIONAL_FILTER_TABLE="
>> +abuffer         asrc
>> +buffer          vsrc
>> +abuffersink     asink
>> +buffersink      vsink
>> +afifo           af
>> +fifo            vf
>> +"
>> +
>
> I don't really like having this table in configure.  Since you're generating the filter_list.h header with the external definitions from it anyway, why not write that and use it as the source rather than having the table here?

Imho, parsing source code and then generating source code is
pointless, it is redundant. Previously, it was just parsing source
code without generating source code. And now it is just generating
source code without parsing source code. There are no duplicates here.

Also storing filter table in configure is more consistent, I think.
Because filter dependencies are in configure.


>
>>  afftfilt_filter_deps="avcodec"
>>  afftfilt_filter_select="fft"
>>  afir_filter_deps="avcodec"
>> @@ -3530,7 +3905,18 @@ MUXER_LIST=$(find_things    muxer    _MUX     libavformat/allformats.c)
>>  DEMUXER_LIST=$(find_things  demuxer  DEMUX    libavformat/allformats.c)
>>  OUTDEV_LIST=$(find_things   outdev   OUTDEV   libavdevice/alldevices.c)
>>  INDEV_LIST=$(find_things    indev    _IN      libavdevice/alldevices.c)
>> -FILTER_LIST=$(find_things   filter   FILTER   libavfilter/allfilters.c)
>> +
>> +extract_list_from_table(){
>> +    cols=$1
>> +    suffix=$2
>> +    shift 2
>> +    while test -n "$1"; do
>> +        echo "${1}${suffix}"
>> +        shift $cols
>> +    done
>> +}
>> +
>> +FILTER_LIST=$(extract_list_from_table 2 _filter $FILTER_TABLE)
>>
>>  find_things_extern(){
>>      thing=$1
>> @@ -7030,6 +7416,58 @@ print_enabled_components(){
>>  print_enabled_components libavcodec/bsf_list.c AVBitStreamFilter bitstream_filters $BSF_LIST
>>  print_enabled_components libavformat/protocol_list.c URLProtocol url_protocols $PROTOCOL_LIST
>>
>> +# filters
>> +extract_enabled_filter(){
>> +    while test -n "$1"; do
>> +        if enabled "${1}_filter"; then
>> +            echo "$1 $2"
>> +        fi
>> +        shift 2
>> +    done
>> +}
>> +
>> +extract_sorted_filter(){
>> +    while test -n "$1"; do
>> +        echo "$1 $2"
>> +        shift 2
>> +    done | sort
>> +}
>> +
>> +print_filter_extern(){
>> +    while test -n "$1"; do
>> +        echo "extern const AVFilter ff_${2}_${1};"
>> +        if test -n "$3"; then
>> +            echo "#define ff_next_${2}_${1} &ff_${4}_${3}"
>> +        else
>> +            echo "#define ff_next_${2}_${1} NULL"
>> +        fi
>> +        shift 2
>> +    done
>> +}
>> +
>> +print_filter_array(){
>> +    echo "static const AVFilter *const filter_list[] = {"
>> +    while test -n "$1"; do
>> +        echo "    &ff_${2}_${1},"
>> +        shift 2
>> +    done
>> +    echo "    NULL"
>> +    echo "};"
>> +}
>> +
>> +sorted_filter_table=$(extract_sorted_filter $(extract_enabled_filter $FILTER_TABLE) $UNCONDITIONAL_FILTER_TABLE)
>> +
>> +echo "/* Automatically generated by configure - do not modify! */" > $TMPH
>> +echo "#ifndef AVFILTER_FILTER_LIST_H" >> $TMPH
>> +echo "#define AVFILTER_FILTER_LIST_H" >> $TMPH
>> +print_filter_extern $sorted_filter_table >> $TMPH
>> +echo "#endif" >> $TMPH
>> +cp_if_changed $TMPH libavfilter/filter_list.h
>> +
>> +echo "/* Automatically generated by configure - do not modify! */" > $TMPH
>> +print_filter_array $sorted_filter_table >> $TMPH
>> +cp_if_changed $TMPH libavfilter/filter_list.c
>> +
>>  # Settings for pkg-config files
>>
>>  cat > $TMPH <<EOF
>> diff --git a/libavfilter/aeval.c b/libavfilter/aeval.c
>> index cdddbaf31d..d5963367a1 100644
>> --- a/libavfilter/aeval.c
>> +++ b/libavfilter/aeval.c
>> @@ -322,7 +322,7 @@ static const AVFilterPad aevalsrc_outputs[] = {
>>      { NULL }
>>  };
>>
>> -AVFilter ff_asrc_aevalsrc = {
>> +const AVFilter ff_asrc_aevalsrc = {
>>      .name          = "aevalsrc",
>>      .description   = NULL_IF_CONFIG_SMALL("Generate an audio signal generated by an expression."),
>>      .query_formats = query_formats,
>> @@ -332,6 +332,7 @@ AVFilter ff_asrc_aevalsrc = {
>>      .inputs        = NULL,
>>      .outputs       = aevalsrc_outputs,
>>      .priv_class    = &aevalsrc_class,
>> +    .next          = ff_next_asrc_aevalsrc,
>
> If we're going to go with this approach, I think this field should be macroed somehow because it is entirely boilerplate.
>
> Maybe an AVFILTER_NAME() macro which sets the "name" and "next" fields?

I guess people will like something start with FF_, so I will try with
FF_DEFINE_AVFILTER_NAME().


>
>>  };
>>
>>  #endif /* CONFIG_AEVALSRC_FILTER */
>> @@ -475,7 +476,7 @@ static const AVFilterPad aeval_outputs[] = {
>>      { NULL }
>>  };
>>
>> -AVFilter ff_af_aeval = {
>> +const AVFilter ff_af_aeval = {
>>      .name          = "aeval",
>>      .description   = NULL_IF_CONFIG_SMALL("Filter audio signal according to a specified expression."),
>>      .query_formats = aeval_query_formats,
>> @@ -485,6 +486,7 @@ AVFilter ff_af_aeval = {
>>      .inputs        = aeval_inputs,
>>      .outputs       = aeval_outputs,
>>      .priv_class    = &aeval_class,
>> +    .next          = ff_next_af_aeval,
>>  };
>>
>>  #endif /* CONFIG_AEVAL_FILTER */
>>
>> ...
>> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
>> index 9adb1090b7..8bab79ff96 100644
>> --- a/libavfilter/allfilters.c
>> +++ b/libavfilter/allfilters.c
>> @@ -19,412 +19,59 @@
>>   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>>   */
>>
>> +#include "libavutil/avassert.h"
>>  #include "libavutil/thread.h"
>>  #include "avfilter.h"
>>  #include "config.h"
>>
>> +#include "libavfilter/filter_list.h"
>> +#include "libavfilter/filter_list.c"
>>
>> -#define REGISTER_FILTER(X, x, y)                                        \
>> -    {                                                                   \
>> -        extern AVFilter ff_##y##_##x;                                   \
>> -        if (CONFIG_##X##_FILTER)                                        \
>> -            avfilter_register(&ff_##y##_##x);                           \
>> -    }
>> -
>> -#define REGISTER_FILTER_UNCONDITIONAL(x)                                \
>> -    {                                                                   \
>> -        extern AVFilter ff_##x;                                         \
>> -        avfilter_register(&ff_##x);                                     \
>> -    }
>>
>> -static void register_all(void)
>> +#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1
>> +static void check_validity(void)
>>  {
>> -    REGISTER_FILTER(ABENCH,         abench,         af);
>> -    REGISTER_FILTER(ACOMPRESSOR,    acompressor,    af);
>> -    REGISTER_FILTER(ACONTRAST,      acontrast,      af);
>> ...
>> -    REGISTER_FILTER(TESTSRC,        testsrc,        vsrc);
>> -    REGISTER_FILTER(TESTSRC2,       testsrc2,       vsrc);
>> -    REGISTER_FILTER(YUVTESTSRC,     yuvtestsrc,     vsrc);
>> +    int k;
>> +    for (k = 0; k < FF_ARRAY_ELEMS(filter_list) - 2; k++) {
>> +        av_assert2(filter_list[k]->next == filter_list[k+1] ||
>> +                   (av_log(NULL, AV_LOG_FATAL, "%s filter: invalid next pointer.\n", filter_list[k]->name),0));
>> +        av_assert2(strcmp(filter_list[k]->name, filter_list[k+1]->name) < 0 ||
>> +                   (av_log(NULL, AV_LOG_FATAL, "%s filter: unsorted with %s.\n", filter_list[k]->name, filter_list[k+1]->name),0));
>> +    }
>> +    av_assert2(!filter_list[k]->next);
>> +    av_assert2(!filter_list[k+1]);
>> +}
>
> Cute :)  I think it should be assert0() if we go with the links, though - almost noone builds with --assert-level=2, and the overhead of this check is not very high.

It is inside #if ASSERT_LEVEL > 1. I think we should not include this
check on production use.


>
>>
>> -    REGISTER_FILTER(NULLSINK,       nullsink,       vsink);
>> +static AVOnce check_validity_once = AV_ONCE_INIT;
>> +#define CHECK_VALIDITY() ff_thread_once(&check_validity_once, check_validity)
>> +#else
>> +#define CHECK_VALIDITY() ((void)0)
>> +#endif
>>
>> -    /* multimedia filters */
>> -    REGISTER_FILTER(ABITSCOPE,      abitscope,      avf);
>> -    REGISTER_FILTER(ADRAWGRAPH,     adrawgraph,     avf);
>> -    REGISTER_FILTER(AHISTOGRAM,     ahistogram,     avf);
>> -    REGISTER_FILTER(APHASEMETER,    aphasemeter,    avf);
>> -    REGISTER_FILTER(AVECTORSCOPE,   avectorscope,   avf);
>> -    REGISTER_FILTER(CONCAT,         concat,         avf);
>> -    REGISTER_FILTER(SHOWCQT,        showcqt,        avf);
>> -    REGISTER_FILTER(SHOWFREQS,      showfreqs,      avf);
>> -    REGISTER_FILTER(SHOWSPECTRUM,   showspectrum,   avf);
>> -    REGISTER_FILTER(SHOWSPECTRUMPIC, showspectrumpic, avf);
>> -    REGISTER_FILTER(SHOWVOLUME,     showvolume,     avf);
>> -    REGISTER_FILTER(SHOWWAVES,      showwaves,      avf);
>> -    REGISTER_FILTER(SHOWWAVESPIC,   showwavespic,   avf);
>> -    REGISTER_FILTER(SPECTRUMSYNTH,  spectrumsynth,  vaf);
>> +void avfilter_register_all(void)
>> +{
>> +    CHECK_VALIDITY();
>> +}
>>
>> -    /* multimedia sources */
>> -    REGISTER_FILTER(AMOVIE,         amovie,         avsrc);
>> -    REGISTER_FILTER(MOVIE,          movie,          avsrc);
>> +const AVFilter *avfilter_next(const AVFilter *prev)
>> +{
>> +    CHECK_VALIDITY();
>
> Calling avfilter_next() without having called avfilter_register_all() violates the API, though?
>
> (Or is there an intent to deprecate avfilter_register_all() immediately after this?)

Of course.


>
>> +    return prev ? prev->next : filter_list[0];
>> +}
>>
>> -    /* those filters are part of public or internal API => registered
>> -     * unconditionally */
>> -    REGISTER_FILTER_UNCONDITIONAL(asrc_abuffer);
>> -    REGISTER_FILTER_UNCONDITIONAL(vsrc_buffer);
>> -    REGISTER_FILTER_UNCONDITIONAL(asink_abuffer);
>> -    REGISTER_FILTER_UNCONDITIONAL(vsink_buffer);
>> -    REGISTER_FILTER_UNCONDITIONAL(af_afifo);
>> -    REGISTER_FILTER_UNCONDITIONAL(vf_fifo);
>> +static int compare_name(const void *key, const void *elem)
>> +{
>> +    const char *name = key;
>> +    const AVFilter *const *filter = elem;
>> +    return strcmp(name, (*filter)->name);
>>  }
>>
>> -void avfilter_register_all(void)
>> +const AVFilter *avfilter_get_by_name(const char *name)
>>  {
>> -    static AVOnce control = AV_ONCE_INIT;
>> +    const AVFilter **filter;
>>
>> -    ff_thread_once(&control, register_all);
>> +    CHECK_VALIDITY();
>> +    filter = bsearch(name, filter_list, FF_ARRAY_ELEMS(filter_list) - 1,
>> +                     sizeof(filter_list[0]), compare_name);
>> +    return filter ? *filter : NULL;
>>  }
>> ...
>> diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
>> index ea75467a75..b89c28d57e 100644
>> --- a/libavfilter/avfilter.c
>> +++ b/libavfilter/avfilter.c
>> @@ -575,49 +575,10 @@ int avfilter_process_command(AVFilterContext *filter, const char *cmd, const cha
>>      return AVERROR(ENOSYS);
>>  }
>>
>> -static AVFilter *first_filter;
>> -static AVFilter **last_filter = &first_filter;
>> -
>> -const AVFilter *avfilter_get_by_name(const char *name)
>> -{
>> -    const AVFilter *f = NULL;
>> -
>> -    if (!name)
>> -        return NULL;
>> -
>> -    while ((f = avfilter_next(f)))
>> -        if (!strcmp(f->name, name))
>> -            return (AVFilter *)f;
>> -
>> -    return NULL;
>> -}
>> -
>> -static AVMutex filter_register_mutex = AV_MUTEX_INITIALIZER;
>> -
>>  int avfilter_register(AVFilter *filter)
>>  {
>> -    AVFilter **f;
>> -
>> -    /* the filter must select generic or internal exclusively */
>> -    av_assert0((filter->flags & AVFILTER_FLAG_SUPPORT_TIMELINE) != AVFILTER_FLAG_SUPPORT_TIMELINE);
>> -
>> -    ff_mutex_lock(&filter_register_mutex);
>> -    f = last_filter;
>> -
>> -    while (*f)
>> -        f = &(*f)->next;
>> -    *f = filter;
>> -    filter->next = NULL;
>> -    last_filter = &filter->next;
>> -
>> -    ff_mutex_unlock(&filter_register_mutex);
>> -
>> -    return 0;
>> -}
>> -
>> -const AVFilter *avfilter_next(const AVFilter *prev)
>> -{
>> -    return prev ? prev->next : first_filter;
>> +    av_log(NULL, AV_LOG_ERROR, "External filter registration is currently unsupported.\n");
>> +    return AVERROR(EINVAL);
>
> +1 to explicitly blocking external filter registration.
>
>>  }
>>
>>  int avfilter_pad_count(const AVFilterPad *pads)
>> diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
>> index 62eed2168f..24e46a9b40 100644
>> --- a/libavfilter/avfilter.h
>> +++ b/libavfilter/avfilter.h
>> @@ -289,7 +289,7 @@ typedef struct AVFilter {
>>       * Used by the filter registration system. Must not be touched by any other
>>       * code.
>>       */
>> -    struct AVFilter *next;
>> +    const struct AVFilter *next;
>>
>>      /**
>>       * Make the filter instance process a command.
>>
>> ...
>> diff --git a/libavfilter/vf_datascope.c b/libavfilter/vf_datascope.c
>> index 467663556e..bb9e2befe2 100644
>> --- a/libavfilter/vf_datascope.c
>> +++ b/libavfilter/vf_datascope.c
>> @@ -390,6 +390,7 @@ static int config_output(AVFilterLink *outlink)
>>      return 0;
>>  }
>>
>> +#if CONFIG_DATASCOPE_FILTER
>
> This sort of fixup should be an independent change.

Yeah, some people forget to guard multiple filter definition on one
file with #if CONFIG_


>
>>  static const AVFilterPad inputs[] = {
>>      {
>>          .name         = "default",
>> @@ -409,7 +410,7 @@ static const AVFilterPad outputs[] = {
>>      { NULL }
>>  };
>>
>> -AVFilter ff_vf_datascope = {
>> +const AVFilter ff_vf_datascope = {
>>      .name          = "datascope",
>>      .description   = NULL_IF_CONFIG_SMALL("Video data analysis."),
>>      .priv_size     = sizeof(DatascopeContext),
>> @@ -418,7 +419,9 @@ AVFilter ff_vf_datascope = {
>>      .inputs        = inputs,
>>      .outputs       = outputs,
>>      .flags         = AVFILTER_FLAG_SLICE_THREADS,
>> +    .next          = ff_next_vf_datascope,
>>  };
>> +#endif /* CONFIG_DATASCOPE_FILTER */
>>
>>  typedef struct PixscopeContext {
>>      const AVClass *class;
>> @@ -642,6 +645,7 @@ static int pixscope_filter_frame(AVFilterLink *inlink, AVFrame *in)
>>      return ff_filter_frame(outlink, out);
>>  }
>>
>> +#if CONFIG_PIXSCOPE_FILTER
>>  static const AVFilterPad pixscope_inputs[] = {
>>      {
>>          .name           = "default",
>> @@ -660,7 +664,7 @@ static const AVFilterPad pixscope_outputs[] = {
>>      { NULL }
>>  };
>>
>> -AVFilter ff_vf_pixscope = {
>> +const AVFilter ff_vf_pixscope = {
>>      .name          = "pixscope",
>>      .description   = NULL_IF_CONFIG_SMALL("Pixel data analysis."),
>>      .priv_size     = sizeof(PixscopeContext),
>> @@ -669,7 +673,9 @@ AVFilter ff_vf_pixscope = {
>>      .inputs        = pixscope_inputs,
>>      .outputs       = pixscope_outputs,
>>      .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
>> +    .next          = ff_next_vf_pixscope,
>>  };
>> +#endif /* CONFIG_PIXSCOPE_FILTER */
>>
>>  typedef struct PixelValues {
>>      uint16_t p[4];
>> @@ -1022,6 +1028,7 @@ static int oscilloscope_filter_frame(AVFilterLink *inlink, AVFrame *frame)
>>      return ff_filter_frame(outlink, frame);
>>  }
>>
>> +#if CONFIG_OSCILLOSCOPE_FILTER
>>  static const AVFilterPad oscilloscope_inputs[] = {
>>      {
>>          .name           = "default",
>> @@ -1041,7 +1048,7 @@ static const AVFilterPad oscilloscope_outputs[] = {
>>      { NULL }
>>  };
>>
>> -AVFilter ff_vf_oscilloscope = {
>> +const AVFilter ff_vf_oscilloscope = {
>>      .name          = "oscilloscope",
>>      .description   = NULL_IF_CONFIG_SMALL("2D Video Oscilloscope."),
>>      .priv_size     = sizeof(OscilloscopeContext),
>> @@ -1051,4 +1058,6 @@ AVFilter ff_vf_oscilloscope = {
>>      .inputs        = oscilloscope_inputs,
>>      .outputs       = oscilloscope_outputs,
>>      .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
>> +    .next          = ff_next_vf_oscilloscope,
>>  };
>> +#endif /* CONFIG_OSCILLOSCOPE_FILTER */
>> ...
>> diff --git a/tests/checkasm/Makefile b/tests/checkasm/Makefile
>> index afbd09b940..4a96159c1a 100644
>> --- a/tests/checkasm/Makefile
>> +++ b/tests/checkasm/Makefile
>> @@ -61,7 +61,7 @@ tests/checkasm/checkasm.o: CFLAGS += -Umain
>>  CHECKASM := tests/checkasm/checkasm$(EXESUF)
>>
>>  $(CHECKASM): $(CHECKASMOBJS) $(FF_STATIC_DEP_LIBS)
>> -     $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(CHECKASMOBJS) $(FF_STATIC_DEP_LIBS) $(EXTRALIBS-avcodec) $(EXTRALIBS-avfilter) $(EXTRALIBS-avutil) $(EXTRALIBS)
>> +     $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(CHECKASMOBJS) $(FF_STATIC_DEP_LIBS) $(EXTRALIBS-avcodec) $(EXTRALIBS-avformat) $(EXTRALIBS-avfilter) $(EXTRALIBS-avutil) $(EXTRALIBS)
>>
>>  checkasm: $(CHECKASM)
>>
>>
>
> Thanks,
>
> - Mark

Thanks.
Michael Niedermayer Jan. 30, 2018, 7:37 p.m. UTC | #13
On Tue, Jan 30, 2018 at 08:27:27PM +0700, Muhammad Faiz wrote:
> On Tue, Jan 30, 2018 at 7:50 PM, Michael Niedermayer
> <michael@niedermayer.cc> wrote:
> > On Tue, Jan 30, 2018 at 02:24:12PM +0700, Muhammad Faiz wrote:
> >> Move REGISTER_FILTER to FILTER_TABLE in configure.
> >> Auto generate filter extern and filter table.
> >> Sort filter table, use bsearch on avfilter_get_by_name.
> >> Define next pointer at filter extern, no need to initialize
> >> next pointer at run time, so AVFilter can be set to const.
> >
> >> Make avfilter_register always return error.
> >
> > That breaks API
> 
> No. Because with current API, it is impossible to implement external filter.
> 
> 
> > Its also a step away from supporting plugins.
> > Why plugins matter ? Because having plugin
> > support is a big advantage, it allows a much wider
> > community to work on, write and maintain filters.
> >
> > With plugins, people can write filters that are
> > written in languages other than C. Or filters which
> > some developer in FFmpeg doesnt want. Or they can be
> > maintained externally by people who just do not like us.
> > Or by people who perfer a FOSS license different from
> > LGPL/GPL/BSD. Iam sure others can come up with more
> > reasons ...
> >
> > Of course avfilter_register() isnt enough for plugins
> > but it or something equivalent is needed for plugins.
> >
> > So i would prefer if avfilter_register() stays supported
> > indefinitly or in case a different system is written for
> > plugins then until that system is in place.
> 
> It just returns error and logs error message that currently external
> filter is unsupported. If someone wants to implement support for
> external filter, it can be easily added later using separate
> list/table.

Iam not sure "easily" is true

We started out with a fully public API that allowed external filters.
and little by little its removed or made private.
each individual change may be easy to undo but as a whole moving
libavfilter back to having a public API is not that easy anymore

IIRC the arguments to make things private where that people wanted to
improve the API. But from the idea and impression of a plan like:
1. make it private
2. design and implement better API
3. make it public again

Somehow now people lost sight and interrest in 3. and even 2. is becoming
less interresting. But the problem is really without 2 + 3 actually happening
doing 1 seems like not a good idea at all.

To me it seems even mentioning external filters and public API makes some
people angry.
But really that has to be the goal at some point. To again have a public API

Is the plan to have 2 seperrate lists at that point ?
one static for internal filters and one dynamic for externally registered
ones ?

Iam not objecting to the patch, theres nothing i have that uses the call
but iam a bit concerned about interrest_to_remove > interrest_in_public_api

thanks

[...]
Mark Thompson Jan. 30, 2018, 10:26 p.m. UTC | #14
On 30/01/18 18:06, Muhammad Faiz wrote:
> On Tue, Jan 30, 2018 at 9:09 PM, Mark Thompson <sw@jkqxz.net> wrote:
>> On 30/01/18 07:24, Muhammad Faiz wrote:
>>> Move REGISTER_FILTER to FILTER_TABLE in configure.
>>> Auto generate filter extern and filter table.
>>> Sort filter table, use bsearch on avfilter_get_by_name.
>>> Define next pointer at filter extern, no need to initialize
>>> next pointer at run time, so AVFilter can be set to const.
>>> Make avfilter_register always return error.
>>> Target checkasm now depends on EXTRALIBS-avformat.
>>>
>>> Signed-off-by: Muhammad Faiz <mfcc64@gmail.com>
>>> ---
>>
>> I like the idea of this, but I'm not sure about some of the implementation details.
>>
>> Have you considered dropping the "next" links entirely and having just the array of pointers instead?  I feel like they don't really add anything useful once we are in this form, and result in more boilerplate on every filter and some tricky generation code.
>>
>> avfilter_next() would be a bit slower (since it would have to search the array, and therefore have runtime linear in the number of filters), but avfilter_get_by_name() is a lot faster because of the binary search (and is the only common use of it) so I don't think that complaint would be a strong one.
> 
> Making avfilter_next() slower (even if it is rarely used) isn't good, I think.

I think the slowdown is irrelevant if it is rarely used.

On the other side, you get rid of a field in AVFilter and avoid having to put some pointless boilerplate in a lot of places.

Related: the one remaining use of avfilter_next() inside lavfi, in filter_child_class_next(), should also be replaced with array operations.  (I can easily do that if you don't want to bother as part of this patch.)

>>
>>>  Makefile                           |   4 +-
>>>  configure                          | 440 ++++++++++++++++++++++++++++++++++++-
>>> ...
>>>  tests/checkasm/Makefile            |   2 +-
>>>  303 files changed, 1229 insertions(+), 796 deletions(-)
>>>
>>> diff --git a/Makefile b/Makefile
>>> index 9defddebfd..f607579369 100644
>>> --- a/Makefile
>>> +++ b/Makefile
>>> @@ -56,6 +56,7 @@ tools/uncoded_frame$(EXESUF): ELIBS = $(FF_EXTRALIBS)
>>>  tools/target_dec_%_fuzzer$(EXESUF): $(FF_DEP_LIBS)
>>>
>>>  CONFIGURABLE_COMPONENTS =                                           \
>>> +    $(SRC_PATH)/configure                                           \
>>>      $(wildcard $(FFLIBS:%=$(SRC_PATH)/lib%/all*.c))                 \
>>>      $(SRC_PATH)/libavcodec/bitstream_filters.c                      \
>>>      $(SRC_PATH)/libavformat/protocols.c                             \
>>> @@ -142,7 +143,8 @@ distclean:: clean
>>>       $(RM) .version avversion.h config.asm config.h mapfile  \
>>>               ffbuild/.config ffbuild/config.* libavutil/avconfig.h \
>>>               version.h libavutil/ffversion.h libavcodec/codec_names.h \
>>> -             libavcodec/bsf_list.c libavformat/protocol_list.c
>>> +             libavcodec/bsf_list.c libavformat/protocol_list.c \
>>> +             libavfilter/filter_list.h libavfilter/filter_list.c
>>>  ifeq ($(SRC_LINK),src)
>>>       $(RM) src
>>>  endif
>>> diff --git a/configure b/configure
>>> index fcfa7aa442..3261f5fd1a 100755
>>> --- a/configure
>>> +++ b/configure
>>> @@ -3177,6 +3177,381 @@ unix_protocol_deps="sys_un_h"
>>>  unix_protocol_select="network"
>>>
>>>  # filters
>>> +FILTER_TABLE="
>>> +abench                  af
>>> +acompressor             af
>>> +acontrast               af
>>> ...
>>> +spectrumsynth           vaf
>>> +amovie                  avsrc
>>> +movie                   avsrc
>>> +"
>>> +
>>> +UNCONDITIONAL_FILTER_TABLE="
>>> +abuffer         asrc
>>> +buffer          vsrc
>>> +abuffersink     asink
>>> +buffersink      vsink
>>> +afifo           af
>>> +fifo            vf
>>> +"
>>> +
>>
>> I don't really like having this table in configure.  Since you're generating the filter_list.h header with the external definitions from it anyway, why not write that and use it as the source rather than having the table here?
> 
> Imho, parsing source code and then generating source code is
> pointless, it is redundant. Previously, it was just parsing source
> code without generating source code. And now it is just generating
> source code without parsing source code. There are no duplicates here.

This is parsing a huge variable in configure and generating two different files from it.  Parsing one file and generating one file seems clearer, though I guess that's mostly subjective.

> Also storing filter table in configure is more consistent, I think.
> Because filter dependencies are in configure.

Storing it in configure feels less consistent to me - no other components lists like this are in configure, they are all in their own files (either as macros the allfoo.c files like the one you are removing or as external declarations).  Also configure is already inconveniently huge, and this is adding many more lines to it.

As a middle ground between the two options, perhaps a non-C file outside configure ("libavfilter/filter_list", I guess) which can just be loaded directly into a shell variable by configure?

>>
>>>  afftfilt_filter_deps="avcodec"
>>>  afftfilt_filter_select="fft"
>>>  afir_filter_deps="avcodec"
>>> @@ -3530,7 +3905,18 @@ MUXER_LIST=$(find_things    muxer    _MUX     libavformat/allformats.c)
>>>  DEMUXER_LIST=$(find_things  demuxer  DEMUX    libavformat/allformats.c)
>>>  OUTDEV_LIST=$(find_things   outdev   OUTDEV   libavdevice/alldevices.c)
>>>  INDEV_LIST=$(find_things    indev    _IN      libavdevice/alldevices.c)
>>> -FILTER_LIST=$(find_things   filter   FILTER   libavfilter/allfilters.c)
>>> +
>>> +extract_list_from_table(){
>>> +    cols=$1
>>> +    suffix=$2
>>> +    shift 2
>>> +    while test -n "$1"; do
>>> +        echo "${1}${suffix}"
>>> +        shift $cols
>>> +    done
>>> +}
>>> +
>>> +FILTER_LIST=$(extract_list_from_table 2 _filter $FILTER_TABLE)
>>>
>>>  find_things_extern(){
>>>      thing=$1
>>> @@ -7030,6 +7416,58 @@ print_enabled_components(){
>>>  print_enabled_components libavcodec/bsf_list.c AVBitStreamFilter bitstream_filters $BSF_LIST
>>>  print_enabled_components libavformat/protocol_list.c URLProtocol url_protocols $PROTOCOL_LIST
>>>
>>> +# filters
>>> +extract_enabled_filter(){
>>> +    while test -n "$1"; do
>>> +        if enabled "${1}_filter"; then
>>> +            echo "$1 $2"
>>> +        fi
>>> +        shift 2
>>> +    done
>>> +}
>>> +
>>> +extract_sorted_filter(){
>>> +    while test -n "$1"; do
>>> +        echo "$1 $2"
>>> +        shift 2
>>> +    done | sort
>>> +}
>>> +
>>> +print_filter_extern(){
>>> +    while test -n "$1"; do
>>> +        echo "extern const AVFilter ff_${2}_${1};"
>>> +        if test -n "$3"; then
>>> +            echo "#define ff_next_${2}_${1} &ff_${4}_${3}"
>>> +        else
>>> +            echo "#define ff_next_${2}_${1} NULL"
>>> +        fi
>>> +        shift 2
>>> +    done
>>> +}
>>> +
>>> +print_filter_array(){
>>> +    echo "static const AVFilter *const filter_list[] = {"
>>> +    while test -n "$1"; do
>>> +        echo "    &ff_${2}_${1},"
>>> +        shift 2
>>> +    done
>>> +    echo "    NULL"
>>> +    echo "};"
>>> +}
>>> +
>>> +sorted_filter_table=$(extract_sorted_filter $(extract_enabled_filter $FILTER_TABLE) $UNCONDITIONAL_FILTER_TABLE)
>>> +
>>> +echo "/* Automatically generated by configure - do not modify! */" > $TMPH
>>> +echo "#ifndef AVFILTER_FILTER_LIST_H" >> $TMPH
>>> +echo "#define AVFILTER_FILTER_LIST_H" >> $TMPH
>>> +print_filter_extern $sorted_filter_table >> $TMPH
>>> +echo "#endif" >> $TMPH
>>> +cp_if_changed $TMPH libavfilter/filter_list.h
>>> +
>>> +echo "/* Automatically generated by configure - do not modify! */" > $TMPH
>>> +print_filter_array $sorted_filter_table >> $TMPH
>>> +cp_if_changed $TMPH libavfilter/filter_list.c
>>> +
>>>  # Settings for pkg-config files
>>>
>>>  cat > $TMPH <<EOF
>>> diff --git a/libavfilter/aeval.c b/libavfilter/aeval.c
>>> index cdddbaf31d..d5963367a1 100644
>>> --- a/libavfilter/aeval.c
>>> +++ b/libavfilter/aeval.c
>>> @@ -322,7 +322,7 @@ static const AVFilterPad aevalsrc_outputs[] = {
>>>      { NULL }
>>>  };
>>>
>>> -AVFilter ff_asrc_aevalsrc = {
>>> +const AVFilter ff_asrc_aevalsrc = {
>>>      .name          = "aevalsrc",
>>>      .description   = NULL_IF_CONFIG_SMALL("Generate an audio signal generated by an expression."),
>>>      .query_formats = query_formats,
>>> @@ -332,6 +332,7 @@ AVFilter ff_asrc_aevalsrc = {
>>>      .inputs        = NULL,
>>>      .outputs       = aevalsrc_outputs,
>>>      .priv_class    = &aevalsrc_class,
>>> +    .next          = ff_next_asrc_aevalsrc,
>>
>> If we're going to go with this approach, I think this field should be macroed somehow because it is entirely boilerplate.
>>
>> Maybe an AVFILTER_NAME() macro which sets the "name" and "next" fields?
> 
> I guess people will like something start with FF_, so I will try with
> FF_DEFINE_AVFILTER_NAME().

It shouldn't matter - it's not a symbol and won't be user-visible.

>>>  };
>>>
>>>  #endif /* CONFIG_AEVALSRC_FILTER */
>>> @@ -475,7 +476,7 @@ static const AVFilterPad aeval_outputs[] = {
>>>      { NULL }
>>>  };
>>>
>>> -AVFilter ff_af_aeval = {
>>> +const AVFilter ff_af_aeval = {
>>>      .name          = "aeval",
>>>      .description   = NULL_IF_CONFIG_SMALL("Filter audio signal according to a specified expression."),
>>>      .query_formats = aeval_query_formats,
>>> @@ -485,6 +486,7 @@ AVFilter ff_af_aeval = {
>>>      .inputs        = aeval_inputs,
>>>      .outputs       = aeval_outputs,
>>>      .priv_class    = &aeval_class,
>>> +    .next          = ff_next_af_aeval,
>>>  };
>>>
>>>  #endif /* CONFIG_AEVAL_FILTER */
>>>
>>> ...
>>> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
>>> index 9adb1090b7..8bab79ff96 100644
>>> --- a/libavfilter/allfilters.c
>>> +++ b/libavfilter/allfilters.c
>>> @@ -19,412 +19,59 @@
>>>   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>>>   */
>>>
>>> +#include "libavutil/avassert.h"
>>>  #include "libavutil/thread.h"
>>>  #include "avfilter.h"
>>>  #include "config.h"
>>>
>>> +#include "libavfilter/filter_list.h"
>>> +#include "libavfilter/filter_list.c"
>>>
>>> -#define REGISTER_FILTER(X, x, y)                                        \
>>> -    {                                                                   \
>>> -        extern AVFilter ff_##y##_##x;                                   \
>>> -        if (CONFIG_##X##_FILTER)                                        \
>>> -            avfilter_register(&ff_##y##_##x);                           \
>>> -    }
>>> -
>>> -#define REGISTER_FILTER_UNCONDITIONAL(x)                                \
>>> -    {                                                                   \
>>> -        extern AVFilter ff_##x;                                         \
>>> -        avfilter_register(&ff_##x);                                     \
>>> -    }
>>>
>>> -static void register_all(void)
>>> +#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1
>>> +static void check_validity(void)
>>>  {
>>> -    REGISTER_FILTER(ABENCH,         abench,         af);
>>> -    REGISTER_FILTER(ACOMPRESSOR,    acompressor,    af);
>>> -    REGISTER_FILTER(ACONTRAST,      acontrast,      af);
>>> ...
>>> -    REGISTER_FILTER(TESTSRC,        testsrc,        vsrc);
>>> -    REGISTER_FILTER(TESTSRC2,       testsrc2,       vsrc);
>>> -    REGISTER_FILTER(YUVTESTSRC,     yuvtestsrc,     vsrc);
>>> +    int k;
>>> +    for (k = 0; k < FF_ARRAY_ELEMS(filter_list) - 2; k++) {
>>> +        av_assert2(filter_list[k]->next == filter_list[k+1] ||
>>> +                   (av_log(NULL, AV_LOG_FATAL, "%s filter: invalid next pointer.\n", filter_list[k]->name),0));
>>> +        av_assert2(strcmp(filter_list[k]->name, filter_list[k+1]->name) < 0 ||
>>> +                   (av_log(NULL, AV_LOG_FATAL, "%s filter: unsorted with %s.\n", filter_list[k]->name, filter_list[k+1]->name),0));
>>> +    }
>>> +    av_assert2(!filter_list[k]->next);
>>> +    av_assert2(!filter_list[k+1]);
>>> +}
>>
>> Cute :)  I think it should be assert0() if we go with the links, though - almost noone builds with --assert-level=2, and the overhead of this check is not very high.
> 
> It is inside #if ASSERT_LEVEL > 1. I think we should not include this
> check on production use.

I was thinking that it's a problem because it would waste developer time on weird problems when making new filters by copying existing ones (and forgetting to change the "next" link), but if the explicit value is macroed away then it's not going to happen.

Thanks,

- Mark
Tobias Rapp Jan. 31, 2018, 8:08 a.m. UTC | #15
On 30.01.2018 20:37, Michael Niedermayer wrote:
> On Tue, Jan 30, 2018 at 08:27:27PM +0700, Muhammad Faiz wrote:
>> On Tue, Jan 30, 2018 at 7:50 PM, Michael Niedermayer
>> <michael@niedermayer.cc> wrote:
>>> Its also a step away from supporting plugins.
>>> Why plugins matter ? Because having plugin
>>> support is a big advantage, it allows a much wider
>>> community to work on, write and maintain filters.
>>>
>>> With plugins, people can write filters that are
>>> written in languages other than C. Or filters which
>>> some developer in FFmpeg doesnt want. Or they can be
>>> maintained externally by people who just do not like us.
>>> Or by people who perfer a FOSS license different from
>>> LGPL/GPL/BSD. Iam sure others can come up with more
>>> reasons ...
>>>
>>> Of course avfilter_register() isnt enough for plugins
>>> but it or something equivalent is needed for plugins.
>>>
>>> So i would prefer if avfilter_register() stays supported
>>> indefinitly or in case a different system is written for
>>> plugins then until that system is in place.
>>
>> It just returns error and logs error message that currently external
>> filter is unsupported. If someone wants to implement support for
>> external filter, it can be easily added later using separate
>> list/table.
> 
> Iam not sure "easily" is true
> 
> We started out with a fully public API that allowed external filters.
> and little by little its removed or made private.
> each individual change may be easy to undo but as a whole moving
> libavfilter back to having a public API is not that easy anymore
> 
> IIRC the arguments to make things private where that people wanted to
> improve the API. But from the idea and impression of a plan like:
> 1. make it private
> 2. design and implement better API
> 3. make it public again
> 
> Somehow now people lost sight and interrest in 3. and even 2. is becoming
> less interresting. But the problem is really without 2 + 3 actually happening
> doing 1 seems like not a good idea at all.
> 
> To me it seems even mentioning external filters and public API makes some
> people angry.
> But really that has to be the goal at some point. To again have a public API

I agree that having (again) a public filter API would be good. This 
would give users the freedom to implement their own special-purpose 
filters (see the "dumpwave" discussion).

> Is the plan to have 2 seperrate lists at that point ?
> one static for internal filters and one dynamic for externally registered
> ones ?
> 
> Iam not objecting to the patch, theres nothing i have that uses the call
> but iam a bit concerned about interrest_to_remove > interrest_in_public_api
> 
> thanks

Regards,
Tobias
wm4 Jan. 31, 2018, 9:03 a.m. UTC | #16
On Wed, 31 Jan 2018 09:08:23 +0100
Tobias Rapp <t.rapp@noa-archive.com> wrote:

> On 30.01.2018 20:37, Michael Niedermayer wrote:
> > On Tue, Jan 30, 2018 at 08:27:27PM +0700, Muhammad Faiz wrote:  
> >> On Tue, Jan 30, 2018 at 7:50 PM, Michael Niedermayer
> >> <michael@niedermayer.cc> wrote:  
> >>> Its also a step away from supporting plugins.
> >>> Why plugins matter ? Because having plugin
> >>> support is a big advantage, it allows a much wider
> >>> community to work on, write and maintain filters.
> >>>
> >>> With plugins, people can write filters that are
> >>> written in languages other than C. Or filters which
> >>> some developer in FFmpeg doesnt want. Or they can be
> >>> maintained externally by people who just do not like us.
> >>> Or by people who perfer a FOSS license different from
> >>> LGPL/GPL/BSD. Iam sure others can come up with more
> >>> reasons ...
> >>>
> >>> Of course avfilter_register() isnt enough for plugins
> >>> but it or something equivalent is needed for plugins.
> >>>
> >>> So i would prefer if avfilter_register() stays supported
> >>> indefinitly or in case a different system is written for
> >>> plugins then until that system is in place.  
> >>
> >> It just returns error and logs error message that currently external
> >> filter is unsupported. If someone wants to implement support for
> >> external filter, it can be easily added later using separate
> >> list/table.  
> > 
> > Iam not sure "easily" is true
> > 
> > We started out with a fully public API that allowed external filters.
> > and little by little its removed or made private.
> > each individual change may be easy to undo but as a whole moving
> > libavfilter back to having a public API is not that easy anymore
> > 
> > IIRC the arguments to make things private where that people wanted to
> > improve the API. But from the idea and impression of a plan like:
> > 1. make it private
> > 2. design and implement better API
> > 3. make it public again
> > 
> > Somehow now people lost sight and interrest in 3. and even 2. is becoming
> > less interresting. But the problem is really without 2 + 3 actually happening
> > doing 1 seems like not a good idea at all.
> > 
> > To me it seems even mentioning external filters and public API makes some
> > people angry.
> > But really that has to be the goal at some point. To again have a public API  
> 
> I agree that having (again) a public filter API would be good. This 
> would give users the freedom to implement their own special-purpose 
> filters (see the "dumpwave" discussion).

Someone needs to design and write it.

Whether we have external filters is completely orthogonal to this
change. They were not possible before, because not enough API for
implementing them is public. They can be possible in the future, but
then registering them would need a different API anyway (one that
doesn't use global mutable state, but uses some sort of context
instead).
Tobias Rapp Jan. 31, 2018, 9:58 a.m. UTC | #17
On 31.01.2018 10:03, wm4 wrote:
> On Wed, 31 Jan 2018 09:08:23 +0100
> Tobias Rapp <t.rapp@noa-archive.com> wrote:
> 
>> On 30.01.2018 20:37, Michael Niedermayer wrote:
>>> On Tue, Jan 30, 2018 at 08:27:27PM +0700, Muhammad Faiz wrote:
>>>> On Tue, Jan 30, 2018 at 7:50 PM, Michael Niedermayer
>>>> <michael@niedermayer.cc> wrote:
>>>>> Its also a step away from supporting plugins.
>>>>> Why plugins matter ? Because having plugin
>>>>> support is a big advantage, it allows a much wider
>>>>> community to work on, write and maintain filters.
>>>>>
>>>>> With plugins, people can write filters that are
>>>>> written in languages other than C. Or filters which
>>>>> some developer in FFmpeg doesnt want. Or they can be
>>>>> maintained externally by people who just do not like us.
>>>>> Or by people who perfer a FOSS license different from
>>>>> LGPL/GPL/BSD. Iam sure others can come up with more
>>>>> reasons ...
>>>>>
>>>>> Of course avfilter_register() isnt enough for plugins
>>>>> but it or something equivalent is needed for plugins.
>>>>>
>>>>> So i would prefer if avfilter_register() stays supported
>>>>> indefinitly or in case a different system is written for
>>>>> plugins then until that system is in place.
>>>>
>>>> It just returns error and logs error message that currently external
>>>> filter is unsupported. If someone wants to implement support for
>>>> external filter, it can be easily added later using separate
>>>> list/table.
>>>
>>> Iam not sure "easily" is true
>>>
>>> We started out with a fully public API that allowed external filters.
>>> and little by little its removed or made private.
>>> each individual change may be easy to undo but as a whole moving
>>> libavfilter back to having a public API is not that easy anymore
>>>
>>> IIRC the arguments to make things private where that people wanted to
>>> improve the API. But from the idea and impression of a plan like:
>>> 1. make it private
>>> 2. design and implement better API
>>> 3. make it public again
>>>
>>> Somehow now people lost sight and interrest in 3. and even 2. is becoming
>>> less interresting. But the problem is really without 2 + 3 actually happening
>>> doing 1 seems like not a good idea at all.
>>>
>>> To me it seems even mentioning external filters and public API makes some
>>> people angry.
>>> But really that has to be the goal at some point. To again have a public API
>>
>> I agree that having (again) a public filter API would be good. This
>> would give users the freedom to implement their own special-purpose
>> filters (see the "dumpwave" discussion).
> 
> Someone needs to design and write it.
> 
> Whether we have external filters is completely orthogonal to this
> change. They were not possible before, because not enough API for
> implementing them is public. They can be possible in the future, but
> then registering them would need a different API anyway (one that
> doesn't use global mutable state, but uses some sort of context
> instead).

Thanks for clarifying, so don't count my comment as an disagreement on 
the patch.

Regards,
Tobias
Muhammad Faiz Jan. 31, 2018, 10:37 a.m. UTC | #18
On Wed, Jan 31, 2018 at 5:26 AM, Mark Thompson <sw@jkqxz.net> wrote:
> On 30/01/18 18:06, Muhammad Faiz wrote:
>> On Tue, Jan 30, 2018 at 9:09 PM, Mark Thompson <sw@jkqxz.net> wrote:
>>> On 30/01/18 07:24, Muhammad Faiz wrote:
>>>> Move REGISTER_FILTER to FILTER_TABLE in configure.
>>>> Auto generate filter extern and filter table.
>>>> Sort filter table, use bsearch on avfilter_get_by_name.
>>>> Define next pointer at filter extern, no need to initialize
>>>> next pointer at run time, so AVFilter can be set to const.
>>>> Make avfilter_register always return error.
>>>> Target checkasm now depends on EXTRALIBS-avformat.
>>>>
>>>> Signed-off-by: Muhammad Faiz <mfcc64@gmail.com>
>>>> ---
>>>
>>> I like the idea of this, but I'm not sure about some of the implementation details.
>>>
>>> Have you considered dropping the "next" links entirely and having just the array of pointers instead?  I feel like they don't really add anything useful once we are in this form, and result in more boilerplate on every filter and some tricky generation code.
>>>
>>> avfilter_next() would be a bit slower (since it would have to search the array, and therefore have runtime linear in the number of filters), but avfilter_get_by_name() is a lot faster because of the binary search (and is the only common use of it) so I don't think that complaint would be a strong one.
>>
>> Making avfilter_next() slower (even if it is rarely used) isn't good, I think.
>
> I think the slowdown is irrelevant if it is rarely used.
>
> On the other side, you get rid of a field in AVFilter and avoid having to put some pointless boilerplate in a lot of places.

The other solution is to initialize next pointer on
avfilter_register_all() as before,  add new iterate API, and deprecate
both avfilter_register_all() and avfilter_next().


>
> Related: the one remaining use of avfilter_next() inside lavfi, in filter_child_class_next(), should also be replaced with array operations.  (I can easily do that if you don't want to bother as part of this patch.)

Oh, I forgot it.


>
>>>
>>>>  Makefile                           |   4 +-
>>>>  configure                          | 440 ++++++++++++++++++++++++++++++++++++-
>>>> ...
>>>>  tests/checkasm/Makefile            |   2 +-
>>>>  303 files changed, 1229 insertions(+), 796 deletions(-)
>>>>
>>>> diff --git a/Makefile b/Makefile
>>>> index 9defddebfd..f607579369 100644
>>>> --- a/Makefile
>>>> +++ b/Makefile
>>>> @@ -56,6 +56,7 @@ tools/uncoded_frame$(EXESUF): ELIBS = $(FF_EXTRALIBS)
>>>>  tools/target_dec_%_fuzzer$(EXESUF): $(FF_DEP_LIBS)
>>>>
>>>>  CONFIGURABLE_COMPONENTS =                                           \
>>>> +    $(SRC_PATH)/configure                                           \
>>>>      $(wildcard $(FFLIBS:%=$(SRC_PATH)/lib%/all*.c))                 \
>>>>      $(SRC_PATH)/libavcodec/bitstream_filters.c                      \
>>>>      $(SRC_PATH)/libavformat/protocols.c                             \
>>>> @@ -142,7 +143,8 @@ distclean:: clean
>>>>       $(RM) .version avversion.h config.asm config.h mapfile  \
>>>>               ffbuild/.config ffbuild/config.* libavutil/avconfig.h \
>>>>               version.h libavutil/ffversion.h libavcodec/codec_names.h \
>>>> -             libavcodec/bsf_list.c libavformat/protocol_list.c
>>>> +             libavcodec/bsf_list.c libavformat/protocol_list.c \
>>>> +             libavfilter/filter_list.h libavfilter/filter_list.c
>>>>  ifeq ($(SRC_LINK),src)
>>>>       $(RM) src
>>>>  endif
>>>> diff --git a/configure b/configure
>>>> index fcfa7aa442..3261f5fd1a 100755
>>>> --- a/configure
>>>> +++ b/configure
>>>> @@ -3177,6 +3177,381 @@ unix_protocol_deps="sys_un_h"
>>>>  unix_protocol_select="network"
>>>>
>>>>  # filters
>>>> +FILTER_TABLE="
>>>> +abench                  af
>>>> +acompressor             af
>>>> +acontrast               af
>>>> ...
>>>> +spectrumsynth           vaf
>>>> +amovie                  avsrc
>>>> +movie                   avsrc
>>>> +"
>>>> +
>>>> +UNCONDITIONAL_FILTER_TABLE="
>>>> +abuffer         asrc
>>>> +buffer          vsrc
>>>> +abuffersink     asink
>>>> +buffersink      vsink
>>>> +afifo           af
>>>> +fifo            vf
>>>> +"
>>>> +
>>>
>>> I don't really like having this table in configure.  Since you're generating the filter_list.h header with the external definitions from it anyway, why not write that and use it as the source rather than having the table here?
>>
>> Imho, parsing source code and then generating source code is
>> pointless, it is redundant. Previously, it was just parsing source
>> code without generating source code. And now it is just generating
>> source code without parsing source code. There are no duplicates here.
>
> This is parsing a huge variable in configure and generating two different files from it.  Parsing one file and generating one file seems clearer, though I guess that's mostly subjective.

Without ff_next_* stuff, it will generate only one file. With
ff_next_* stuff, it will generate two files in both cases.


>
>> Also storing filter table in configure is more consistent, I think.
>> Because filter dependencies are in configure.
>
> Storing it in configure feels less consistent to me - no other components lists like this are in configure, they are all in their own files (either as macros the allfoo.c files like the one you are removing or as external declarations).  Also configure is already inconveniently huge, and this is adding many more lines to it.

So, this is the first which is moved from all*.c to configure. The
others will follow. Also this will remove 'all*.c is newer than
config.h' message. The only remaining will be 'configure is newer than
config.h' message, which is natural (when we modify configure, we
should rerun it).

I think, the purpose of storing REGISTER_*() in all*.c is to avoid
generating C-code from configure. Because now we generate C-code, it
is irrelevant. The size of configure isn't really a problem.


>
> As a middle ground between the two options, perhaps a non-C file outside configure ("libavfilter/filter_list", I guess) which can just be loaded directly into a shell variable by configure?

I think, it will just add more complexity.

>
>>>
>>>>  afftfilt_filter_deps="avcodec"
>>>>  afftfilt_filter_select="fft"
>>>>  afir_filter_deps="avcodec"
>>>> @@ -3530,7 +3905,18 @@ MUXER_LIST=$(find_things    muxer    _MUX     libavformat/allformats.c)
>>>>  DEMUXER_LIST=$(find_things  demuxer  DEMUX    libavformat/allformats.c)
>>>>  OUTDEV_LIST=$(find_things   outdev   OUTDEV   libavdevice/alldevices.c)
>>>>  INDEV_LIST=$(find_things    indev    _IN      libavdevice/alldevices.c)
>>>> -FILTER_LIST=$(find_things   filter   FILTER   libavfilter/allfilters.c)
>>>> +
>>>> +extract_list_from_table(){
>>>> +    cols=$1
>>>> +    suffix=$2
>>>> +    shift 2
>>>> +    while test -n "$1"; do
>>>> +        echo "${1}${suffix}"
>>>> +        shift $cols
>>>> +    done
>>>> +}
>>>> +
>>>> +FILTER_LIST=$(extract_list_from_table 2 _filter $FILTER_TABLE)
>>>>
>>>>  find_things_extern(){
>>>>      thing=$1
>>>> @@ -7030,6 +7416,58 @@ print_enabled_components(){
>>>>  print_enabled_components libavcodec/bsf_list.c AVBitStreamFilter bitstream_filters $BSF_LIST
>>>>  print_enabled_components libavformat/protocol_list.c URLProtocol url_protocols $PROTOCOL_LIST
>>>>
>>>> +# filters
>>>> +extract_enabled_filter(){
>>>> +    while test -n "$1"; do
>>>> +        if enabled "${1}_filter"; then
>>>> +            echo "$1 $2"
>>>> +        fi
>>>> +        shift 2
>>>> +    done
>>>> +}
>>>> +
>>>> +extract_sorted_filter(){
>>>> +    while test -n "$1"; do
>>>> +        echo "$1 $2"
>>>> +        shift 2
>>>> +    done | sort
>>>> +}
>>>> +
>>>> +print_filter_extern(){
>>>> +    while test -n "$1"; do
>>>> +        echo "extern const AVFilter ff_${2}_${1};"
>>>> +        if test -n "$3"; then
>>>> +            echo "#define ff_next_${2}_${1} &ff_${4}_${3}"
>>>> +        else
>>>> +            echo "#define ff_next_${2}_${1} NULL"
>>>> +        fi
>>>> +        shift 2
>>>> +    done
>>>> +}
>>>> +
>>>> +print_filter_array(){
>>>> +    echo "static const AVFilter *const filter_list[] = {"
>>>> +    while test -n "$1"; do
>>>> +        echo "    &ff_${2}_${1},"
>>>> +        shift 2
>>>> +    done
>>>> +    echo "    NULL"
>>>> +    echo "};"
>>>> +}
>>>> +
>>>> +sorted_filter_table=$(extract_sorted_filter $(extract_enabled_filter $FILTER_TABLE) $UNCONDITIONAL_FILTER_TABLE)
>>>> +
>>>> +echo "/* Automatically generated by configure - do not modify! */" > $TMPH
>>>> +echo "#ifndef AVFILTER_FILTER_LIST_H" >> $TMPH
>>>> +echo "#define AVFILTER_FILTER_LIST_H" >> $TMPH
>>>> +print_filter_extern $sorted_filter_table >> $TMPH
>>>> +echo "#endif" >> $TMPH
>>>> +cp_if_changed $TMPH libavfilter/filter_list.h
>>>> +
>>>> +echo "/* Automatically generated by configure - do not modify! */" > $TMPH
>>>> +print_filter_array $sorted_filter_table >> $TMPH
>>>> +cp_if_changed $TMPH libavfilter/filter_list.c
>>>> +
>>>>  # Settings for pkg-config files
>>>>
>>>>  cat > $TMPH <<EOF
>>>> diff --git a/libavfilter/aeval.c b/libavfilter/aeval.c
>>>> index cdddbaf31d..d5963367a1 100644
>>>> --- a/libavfilter/aeval.c
>>>> +++ b/libavfilter/aeval.c
>>>> @@ -322,7 +322,7 @@ static const AVFilterPad aevalsrc_outputs[] = {
>>>>      { NULL }
>>>>  };
>>>>
>>>> -AVFilter ff_asrc_aevalsrc = {
>>>> +const AVFilter ff_asrc_aevalsrc = {
>>>>      .name          = "aevalsrc",
>>>>      .description   = NULL_IF_CONFIG_SMALL("Generate an audio signal generated by an expression."),
>>>>      .query_formats = query_formats,
>>>> @@ -332,6 +332,7 @@ AVFilter ff_asrc_aevalsrc = {
>>>>      .inputs        = NULL,
>>>>      .outputs       = aevalsrc_outputs,
>>>>      .priv_class    = &aevalsrc_class,
>>>> +    .next          = ff_next_asrc_aevalsrc,
>>>
>>> If we're going to go with this approach, I think this field should be macroed somehow because it is entirely boilerplate.
>>>
>>> Maybe an AVFILTER_NAME() macro which sets the "name" and "next" fields?
>>
>> I guess people will like something start with FF_, so I will try with
>> FF_DEFINE_AVFILTER_NAME().
>
> It shouldn't matter - it's not a symbol and won't be user-visible.
>
>>>>  };
>>>>
>>>>  #endif /* CONFIG_AEVALSRC_FILTER */
>>>> @@ -475,7 +476,7 @@ static const AVFilterPad aeval_outputs[] = {
>>>>      { NULL }
>>>>  };
>>>>
>>>> -AVFilter ff_af_aeval = {
>>>> +const AVFilter ff_af_aeval = {
>>>>      .name          = "aeval",
>>>>      .description   = NULL_IF_CONFIG_SMALL("Filter audio signal according to a specified expression."),
>>>>      .query_formats = aeval_query_formats,
>>>> @@ -485,6 +486,7 @@ AVFilter ff_af_aeval = {
>>>>      .inputs        = aeval_inputs,
>>>>      .outputs       = aeval_outputs,
>>>>      .priv_class    = &aeval_class,
>>>> +    .next          = ff_next_af_aeval,
>>>>  };
>>>>
>>>>  #endif /* CONFIG_AEVAL_FILTER */
>>>>
>>>> ...
>>>> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
>>>> index 9adb1090b7..8bab79ff96 100644
>>>> --- a/libavfilter/allfilters.c
>>>> +++ b/libavfilter/allfilters.c
>>>> @@ -19,412 +19,59 @@
>>>>   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>>>>   */
>>>>
>>>> +#include "libavutil/avassert.h"
>>>>  #include "libavutil/thread.h"
>>>>  #include "avfilter.h"
>>>>  #include "config.h"
>>>>
>>>> +#include "libavfilter/filter_list.h"
>>>> +#include "libavfilter/filter_list.c"
>>>>
>>>> -#define REGISTER_FILTER(X, x, y)                                        \
>>>> -    {                                                                   \
>>>> -        extern AVFilter ff_##y##_##x;                                   \
>>>> -        if (CONFIG_##X##_FILTER)                                        \
>>>> -            avfilter_register(&ff_##y##_##x);                           \
>>>> -    }
>>>> -
>>>> -#define REGISTER_FILTER_UNCONDITIONAL(x)                                \
>>>> -    {                                                                   \
>>>> -        extern AVFilter ff_##x;                                         \
>>>> -        avfilter_register(&ff_##x);                                     \
>>>> -    }
>>>>
>>>> -static void register_all(void)
>>>> +#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1
>>>> +static void check_validity(void)
>>>>  {
>>>> -    REGISTER_FILTER(ABENCH,         abench,         af);
>>>> -    REGISTER_FILTER(ACOMPRESSOR,    acompressor,    af);
>>>> -    REGISTER_FILTER(ACONTRAST,      acontrast,      af);
>>>> ...
>>>> -    REGISTER_FILTER(TESTSRC,        testsrc,        vsrc);
>>>> -    REGISTER_FILTER(TESTSRC2,       testsrc2,       vsrc);
>>>> -    REGISTER_FILTER(YUVTESTSRC,     yuvtestsrc,     vsrc);
>>>> +    int k;
>>>> +    for (k = 0; k < FF_ARRAY_ELEMS(filter_list) - 2; k++) {
>>>> +        av_assert2(filter_list[k]->next == filter_list[k+1] ||
>>>> +                   (av_log(NULL, AV_LOG_FATAL, "%s filter: invalid next pointer.\n", filter_list[k]->name),0));
>>>> +        av_assert2(strcmp(filter_list[k]->name, filter_list[k+1]->name) < 0 ||
>>>> +                   (av_log(NULL, AV_LOG_FATAL, "%s filter: unsorted with %s.\n", filter_list[k]->name, filter_list[k+1]->name),0));
>>>> +    }
>>>> +    av_assert2(!filter_list[k]->next);
>>>> +    av_assert2(!filter_list[k+1]);
>>>> +}
>>>
>>> Cute :)  I think it should be assert0() if we go with the links, though - almost noone builds with --assert-level=2, and the overhead of this check is not very high.
>>
>> It is inside #if ASSERT_LEVEL > 1. I think we should not include this
>> check on production use.
>
> I was thinking that it's a problem because it would waste developer time on weird problems when making new filters by copying existing ones (and forgetting to change the "next" link), but if the explicit value is macroed away then it's not going to happen.
>
> Thanks,
>
> - Mark

Thank's.
Michael Niedermayer Jan. 31, 2018, 11:11 a.m. UTC | #19
On Wed, Jan 31, 2018 at 10:03:58AM +0100, wm4 wrote:
> On Wed, 31 Jan 2018 09:08:23 +0100
> Tobias Rapp <t.rapp@noa-archive.com> wrote:
> 
> > On 30.01.2018 20:37, Michael Niedermayer wrote:
> > > On Tue, Jan 30, 2018 at 08:27:27PM +0700, Muhammad Faiz wrote:  
> > >> On Tue, Jan 30, 2018 at 7:50 PM, Michael Niedermayer
> > >> <michael@niedermayer.cc> wrote:  
> > >>> Its also a step away from supporting plugins.
> > >>> Why plugins matter ? Because having plugin
> > >>> support is a big advantage, it allows a much wider
> > >>> community to work on, write and maintain filters.
> > >>>
> > >>> With plugins, people can write filters that are
> > >>> written in languages other than C. Or filters which
> > >>> some developer in FFmpeg doesnt want. Or they can be
> > >>> maintained externally by people who just do not like us.
> > >>> Or by people who perfer a FOSS license different from
> > >>> LGPL/GPL/BSD. Iam sure others can come up with more
> > >>> reasons ...
> > >>>
> > >>> Of course avfilter_register() isnt enough for plugins
> > >>> but it or something equivalent is needed for plugins.
> > >>>
> > >>> So i would prefer if avfilter_register() stays supported
> > >>> indefinitly or in case a different system is written for
> > >>> plugins then until that system is in place.  
> > >>
> > >> It just returns error and logs error message that currently external
> > >> filter is unsupported. If someone wants to implement support for
> > >> external filter, it can be easily added later using separate
> > >> list/table.  
> > > 
> > > Iam not sure "easily" is true
> > > 
> > > We started out with a fully public API that allowed external filters.
> > > and little by little its removed or made private.
> > > each individual change may be easy to undo but as a whole moving
> > > libavfilter back to having a public API is not that easy anymore
> > > 
> > > IIRC the arguments to make things private where that people wanted to
> > > improve the API. But from the idea and impression of a plan like:
> > > 1. make it private
> > > 2. design and implement better API
> > > 3. make it public again
> > > 
> > > Somehow now people lost sight and interrest in 3. and even 2. is becoming
> > > less interresting. But the problem is really without 2 + 3 actually happening
> > > doing 1 seems like not a good idea at all.
> > > 
> > > To me it seems even mentioning external filters and public API makes some
> > > people angry.
> > > But really that has to be the goal at some point. To again have a public API  
> > 
> > I agree that having (again) a public filter API would be good. This 
> > would give users the freedom to implement their own special-purpose 
> > filters (see the "dumpwave" discussion).
> 
> Someone needs to design and write it.
> 
> Whether we have external filters is completely orthogonal to this
> change. 

> They were not possible before, because not enough API for
> implementing them is public. 

This is correct if by "before" you mean today before this is applied.
But longer ago, as in years, it was possible


> They can be possible in the future, but
> then registering them would need a different API anyway (one that
> doesn't use global mutable state, but uses some sort of context
> instead).

i understand that you arent a native english speaker nor am i but
what you write here is not true.

Several people desire to eliminate all "global mutable state"
and thats fine, iam happy if that is achieved. But its not
that its neccessary for a fully functional API register or otherwise.
The registering code with "global mutable state" and some form
of thread synchronization would work perfectly fine.

What exactly is in relation to registering the problem with 
"global mutable state" ?

One application registering a filter with a common name and another 
application unintentionally using that ?

This is not even possible because each application is in its own process and
will have its own independant copy of libavfilter in its address space only
with "read only" pages shared or with pages with "copy on write".

The "one registers the other uses it by mistake" issue is as far as i
understand only possible if an application uses libavfilter and that
application uses also a lib that itself uses libavfilter too or
similar cases. 
And then both mess with registering custom filters and using custom
filters.
That looks like a very rare situation. If thats the only issue, i would
really not say theres a "need" to avoid "global mutable state"



[...]
wm4 Jan. 31, 2018, 11:24 a.m. UTC | #20
On Wed, 31 Jan 2018 12:11:25 +0100
Michael Niedermayer <michael@niedermayer.cc> wrote:

> On Wed, Jan 31, 2018 at 10:03:58AM +0100, wm4 wrote:
> > On Wed, 31 Jan 2018 09:08:23 +0100
> > Tobias Rapp <t.rapp@noa-archive.com> wrote:
> >   
> > > On 30.01.2018 20:37, Michael Niedermayer wrote:  
> > > > On Tue, Jan 30, 2018 at 08:27:27PM +0700, Muhammad Faiz wrote:    
> > > >> On Tue, Jan 30, 2018 at 7:50 PM, Michael Niedermayer
> > > >> <michael@niedermayer.cc> wrote:    
> > > >>> Its also a step away from supporting plugins.
> > > >>> Why plugins matter ? Because having plugin
> > > >>> support is a big advantage, it allows a much wider
> > > >>> community to work on, write and maintain filters.
> > > >>>
> > > >>> With plugins, people can write filters that are
> > > >>> written in languages other than C. Or filters which
> > > >>> some developer in FFmpeg doesnt want. Or they can be
> > > >>> maintained externally by people who just do not like us.
> > > >>> Or by people who perfer a FOSS license different from
> > > >>> LGPL/GPL/BSD. Iam sure others can come up with more
> > > >>> reasons ...
> > > >>>
> > > >>> Of course avfilter_register() isnt enough for plugins
> > > >>> but it or something equivalent is needed for plugins.
> > > >>>
> > > >>> So i would prefer if avfilter_register() stays supported
> > > >>> indefinitly or in case a different system is written for
> > > >>> plugins then until that system is in place.    
> > > >>
> > > >> It just returns error and logs error message that currently external
> > > >> filter is unsupported. If someone wants to implement support for
> > > >> external filter, it can be easily added later using separate
> > > >> list/table.    
> > > > 
> > > > Iam not sure "easily" is true
> > > > 
> > > > We started out with a fully public API that allowed external filters.
> > > > and little by little its removed or made private.
> > > > each individual change may be easy to undo but as a whole moving
> > > > libavfilter back to having a public API is not that easy anymore
> > > > 
> > > > IIRC the arguments to make things private where that people wanted to
> > > > improve the API. But from the idea and impression of a plan like:
> > > > 1. make it private
> > > > 2. design and implement better API
> > > > 3. make it public again
> > > > 
> > > > Somehow now people lost sight and interrest in 3. and even 2. is becoming
> > > > less interresting. But the problem is really without 2 + 3 actually happening
> > > > doing 1 seems like not a good idea at all.
> > > > 
> > > > To me it seems even mentioning external filters and public API makes some
> > > > people angry.
> > > > But really that has to be the goal at some point. To again have a public API    
> > > 
> > > I agree that having (again) a public filter API would be good. This 
> > > would give users the freedom to implement their own special-purpose 
> > > filters (see the "dumpwave" discussion).  
> > 
> > Someone needs to design and write it.
> > 
> > Whether we have external filters is completely orthogonal to this
> > change.   
> 
> > They were not possible before, because not enough API for
> > implementing them is public.   
> 
> This is correct if by "before" you mean today before this is applied.
> But longer ago, as in years, it was possible
> 
> 
> > They can be possible in the future, but
> > then registering them would need a different API anyway (one that
> > doesn't use global mutable state, but uses some sort of context
> > instead).  
> 
> i understand that you arent a native english speaker nor am i but
> what you write here is not true.
> 
> Several people desire to eliminate all "global mutable state"
> and thats fine, iam happy if that is achieved. But its not
> that its neccessary for a fully functional API register or otherwise.
> The registering code with "global mutable state" and some form
> of thread synchronization would work perfectly fine.
> 
> What exactly is in relation to registering the problem with 
> "global mutable state" ?
> 
> One application registering a filter with a common name and another 
> application unintentionally using that ?

Yes. It's not library-safe.

> This is not even possible because each application is in its own process and
> will have its own independant copy of libavfilter in its address space only
> with "read only" pages shared or with pages with "copy on write".

This is a library. There can be multiple users of a library in the same
process.

> The "one registers the other uses it by mistake" issue is as far as i
> understand only possible if an application uses libavfilter and that
> application uses also a lib that itself uses libavfilter too or
> similar cases. 
> And then both mess with registering custom filters and using custom
> filters.
> That looks like a very rare situation. If thats the only issue, i would
> really not say theres a "need" to avoid "global mutable state"

I don't agree. It will cause problems you're not even thinking about
now. Look at any library that thought global mutable state is fine for
some thing, and what problems it caused. Besides someone will have the
idea to create some sort of plugin loader, and then all bets are off.

Why are you even stirring up an offtopic discussion again. I've also
mentioned multiple times how the main problem is creating public API
so filters can be implemented. This is not a trivial task, but adding
some sort of filter register API is absolutely trivial (even if it
readds the current API, which I hope it won't). So why bring it up
again and cause tons of fruitless discussions? Just why? Please explain
this.
Mark Thompson Jan. 31, 2018, 11:52 a.m. UTC | #21
On 31/01/18 10:37, Muhammad Faiz wrote:
> On Wed, Jan 31, 2018 at 5:26 AM, Mark Thompson <sw@jkqxz.net> wrote:
>> On 30/01/18 18:06, Muhammad Faiz wrote:
>>> On Tue, Jan 30, 2018 at 9:09 PM, Mark Thompson <sw@jkqxz.net> wrote:
>>>> On 30/01/18 07:24, Muhammad Faiz wrote:
>>>>> Move REGISTER_FILTER to FILTER_TABLE in configure.
>>>>> Auto generate filter extern and filter table.
>>>>> Sort filter table, use bsearch on avfilter_get_by_name.
>>>>> Define next pointer at filter extern, no need to initialize
>>>>> next pointer at run time, so AVFilter can be set to const.
>>>>> Make avfilter_register always return error.
>>>>> Target checkasm now depends on EXTRALIBS-avformat.
>>>>>
>>>>> Signed-off-by: Muhammad Faiz <mfcc64@gmail.com>
>>>>> ---
>>>>
>>>> I like the idea of this, but I'm not sure about some of the implementation details.
>>>>
>>>> Have you considered dropping the "next" links entirely and having just the array of pointers instead?  I feel like they don't really add anything useful once we are in this form, and result in more boilerplate on every filter and some tricky generation code.
>>>>
>>>> avfilter_next() would be a bit slower (since it would have to search the array, and therefore have runtime linear in the number of filters), but avfilter_get_by_name() is a lot faster because of the binary search (and is the only common use of it) so I don't think that complaint would be a strong one.
>>>
>>> Making avfilter_next() slower (even if it is rarely used) isn't good, I think.
>>
>> I think the slowdown is irrelevant if it is rarely used.
>>
>> On the other side, you get rid of a field in AVFilter and avoid having to put some pointless boilerplate in a lot of places.
> 
> The other solution is to initialize next pointer on
> avfilter_register_all() as before,  add new iterate API, and deprecate
> both avfilter_register_all() and avfilter_next().

I guess having thought about this further my problem is that you appear to be writing a lot of new infrastructure for creating and updating the next links (with special generation code in configure and extra boilerplate on every filter) when the feature does not have any clear value.  Once other functions are properly updated the only place where the next link is used is in avfilter_next(), which is only slowed down a little bit and would never be called in performance-sensitive code anyway.  A new iterate API would be welcome but is mostly orthogonal - you aren't going to call that in performance-sensitive code either.

So, can we just drop the next links completely?

- Mark


(Everything else trimmed.  I'm ok with the huge lists in configure if other people agree to it, I just found it rather inelegant compared to the current per-library information in separate files.)
wm4 Jan. 31, 2018, 12:03 p.m. UTC | #22
On Wed, 31 Jan 2018 11:52:14 +0000
Mark Thompson <sw@jkqxz.net> wrote:

> >> On the other side, you get rid of a field in AVFilter and avoid having to put some pointless boilerplate in a lot of places.  
> > 
> > The other solution is to initialize next pointer on
> > avfilter_register_all() as before,  add new iterate API, and deprecate
> > both avfilter_register_all() and avfilter_next().  
> 
> I guess having thought about this further my problem is that you appear to be writing a lot of new infrastructure for creating and updating the next links (with special generation code in configure and extra boilerplate on every filter) when the feature does not have any clear value.  Once other functions are properly updated the only place where the next link is used is in avfilter_next(), which is only slowed down a little bit and would never be called in performance-sensitive code anyway.  A new iterate API would be welcome but is mostly orthogonal - you aren't going to call that in performance-sensitive code either.
> 
> So, can we just drop the next links completely?

Just as a comment on the side: if he changes that, I'd prefer of the
next links are lazily initialized and only when using the old API. That
would waste less memory (since writing the next link will trigger copy
on write and in the worst case waste 4K (a page) per filter.

(Personally I found the static next links rather neat, but yeah, maybe
it's too much configure magic.)
Michael Niedermayer Feb. 1, 2018, 1:06 a.m. UTC | #23
On Wed, Jan 31, 2018 at 12:24:43PM +0100, wm4 wrote:
> On Wed, 31 Jan 2018 12:11:25 +0100
> Michael Niedermayer <michael@niedermayer.cc> wrote:
> 
> > On Wed, Jan 31, 2018 at 10:03:58AM +0100, wm4 wrote:
> > > On Wed, 31 Jan 2018 09:08:23 +0100
> > > Tobias Rapp <t.rapp@noa-archive.com> wrote:
> > >   
> > > > On 30.01.2018 20:37, Michael Niedermayer wrote:  
> > > > > On Tue, Jan 30, 2018 at 08:27:27PM +0700, Muhammad Faiz wrote:    
> > > > >> On Tue, Jan 30, 2018 at 7:50 PM, Michael Niedermayer
> > > > >> <michael@niedermayer.cc> wrote:    
> > > > >>> Its also a step away from supporting plugins.
> > > > >>> Why plugins matter ? Because having plugin
> > > > >>> support is a big advantage, it allows a much wider
> > > > >>> community to work on, write and maintain filters.
> > > > >>>
> > > > >>> With plugins, people can write filters that are
> > > > >>> written in languages other than C. Or filters which
> > > > >>> some developer in FFmpeg doesnt want. Or they can be
> > > > >>> maintained externally by people who just do not like us.
> > > > >>> Or by people who perfer a FOSS license different from
> > > > >>> LGPL/GPL/BSD. Iam sure others can come up with more
> > > > >>> reasons ...
> > > > >>>
> > > > >>> Of course avfilter_register() isnt enough for plugins
> > > > >>> but it or something equivalent is needed for plugins.
> > > > >>>
> > > > >>> So i would prefer if avfilter_register() stays supported
> > > > >>> indefinitly or in case a different system is written for
> > > > >>> plugins then until that system is in place.    
> > > > >>
> > > > >> It just returns error and logs error message that currently external
> > > > >> filter is unsupported. If someone wants to implement support for
> > > > >> external filter, it can be easily added later using separate
> > > > >> list/table.    
> > > > > 
> > > > > Iam not sure "easily" is true
> > > > > 
> > > > > We started out with a fully public API that allowed external filters.
> > > > > and little by little its removed or made private.
> > > > > each individual change may be easy to undo but as a whole moving
> > > > > libavfilter back to having a public API is not that easy anymore
> > > > > 
> > > > > IIRC the arguments to make things private where that people wanted to
> > > > > improve the API. But from the idea and impression of a plan like:
> > > > > 1. make it private
> > > > > 2. design and implement better API
> > > > > 3. make it public again
> > > > > 
> > > > > Somehow now people lost sight and interrest in 3. and even 2. is becoming
> > > > > less interresting. But the problem is really without 2 + 3 actually happening
> > > > > doing 1 seems like not a good idea at all.
> > > > > 
> > > > > To me it seems even mentioning external filters and public API makes some
> > > > > people angry.
> > > > > But really that has to be the goal at some point. To again have a public API    
> > > > 
> > > > I agree that having (again) a public filter API would be good. This 
> > > > would give users the freedom to implement their own special-purpose 
> > > > filters (see the "dumpwave" discussion).  
> > > 
> > > Someone needs to design and write it.
> > > 
> > > Whether we have external filters is completely orthogonal to this
> > > change.   
> > 
> > > They were not possible before, because not enough API for
> > > implementing them is public.   
> > 
> > This is correct if by "before" you mean today before this is applied.
> > But longer ago, as in years, it was possible
> > 
> > 
> > > They can be possible in the future, but
> > > then registering them would need a different API anyway (one that
> > > doesn't use global mutable state, but uses some sort of context
> > > instead).  
> > 
> > i understand that you arent a native english speaker nor am i but
> > what you write here is not true.
> > 
> > Several people desire to eliminate all "global mutable state"
> > and thats fine, iam happy if that is achieved. But its not
> > that its neccessary for a fully functional API register or otherwise.
> > The registering code with "global mutable state" and some form
> > of thread synchronization would work perfectly fine.
> > 
> > What exactly is in relation to registering the problem with 
> > "global mutable state" ?
> > 
> > One application registering a filter with a common name and another 
> > application unintentionally using that ?
> 
> Yes. It's not library-safe.
> 
> > This is not even possible because each application is in its own process and
> > will have its own independant copy of libavfilter in its address space only
> > with "read only" pages shared or with pages with "copy on write".
> 
> This is a library. There can be multiple users of a library in the same
> process.
> 
> > The "one registers the other uses it by mistake" issue is as far as i
> > understand only possible if an application uses libavfilter and that
> > application uses also a lib that itself uses libavfilter too or
> > similar cases. 
> > And then both mess with registering custom filters and using custom
> > filters.
> > That looks like a very rare situation. If thats the only issue, i would
> > really not say theres a "need" to avoid "global mutable state"
> 
> I don't agree. It will cause problems you're not even thinking about
> now. Look at any library that thought global mutable state is fine for
> some thing, and what problems it caused. Besides someone will have the
> idea to create some sort of plugin loader, and then all bets are off.

to awnser just
 "look at any library that thought global mutable state is fine for some thing,"
Well we can look at libavcodec and libavformat and libavfilters register
code. That existed since a long time and used global mutable state.
searching trac for 
"_register(" -> 1 match thats about the lock manager not the codec/format register
"av_register_output_format" -> no matches
"av_register_input_format" -> no matches

So as you requested, i looked at not just any library but at multiple
and at exactly the very identical case of registering.
Zero issues have occured there.

People hate "global mutable state" and i do not like it either but the
thing that its causing problems in this case is exagerated.


> 
> Why are you even stirring up an offtopic discussion again. 

How can discussing the removal and its reasons of a public API
be off topic in a thread of a patch removing that API !?


> I've also
> mentioned multiple times how the main problem is creating public API
> so filters can be implemented. This is not a trivial task, but adding
> some sort of filter register API is absolutely trivial (even if it
> readds the current API, which I hope it won't). So why bring it up
> again and cause tons of fruitless discussions? Just why? Please explain
> this.

Yes, iam concerned and upset about the lack of activity on what you
call the "main problem".
If there was someone working on creating a new public API, then i would
never complain about him removing existing API.
"remove that code so we can put a better one in place" vs. "remove this,
its trivial to put it or something back but that will never happen"
Its the lack of a plan, the lack of intend to walk forward which makes me
raise the issue of people taking a step backward.


[...]
Michael Niedermayer Feb. 1, 2018, 1:27 a.m. UTC | #24
On Thu, Feb 01, 2018 at 02:06:52AM +0100, Michael Niedermayer wrote:
[...]
> > I've also
> > mentioned multiple times how the main problem is creating public API
> > so filters can be implemented. This is not a trivial task, but adding
> > some sort of filter register API is absolutely trivial (even if it
> > readds the current API, which I hope it won't). So why bring it up
> > again and cause tons of fruitless discussions? Just why? Please explain
> > this.
> 
> Yes, iam concerned and upset about the lack of activity on what you
> call the "main problem".

> If there was someone working on creating a new public API, then i would
> never complain about him removing existing API.

re-reading what i wrote, i realize this can be very badly mis interpreted
Its of course meant just in the context of a removial not breaking any users
and following API deprecation and all when that is needed.

[...]
wm4 Feb. 1, 2018, 1:43 a.m. UTC | #25
On Thu, 1 Feb 2018 02:06:52 +0100
Michael Niedermayer <michael@niedermayer.cc> wrote:

> On Wed, Jan 31, 2018 at 12:24:43PM +0100, wm4 wrote:
> > On Wed, 31 Jan 2018 12:11:25 +0100
> > Michael Niedermayer <michael@niedermayer.cc> wrote:
> >   
> > > On Wed, Jan 31, 2018 at 10:03:58AM +0100, wm4 wrote:  
> > > > On Wed, 31 Jan 2018 09:08:23 +0100
> > > > Tobias Rapp <t.rapp@noa-archive.com> wrote:
> > > >     
> > > > > On 30.01.2018 20:37, Michael Niedermayer wrote:    
> > > > > > On Tue, Jan 30, 2018 at 08:27:27PM +0700, Muhammad Faiz wrote:      
> > > > > >> On Tue, Jan 30, 2018 at 7:50 PM, Michael Niedermayer
> > > > > >> <michael@niedermayer.cc> wrote:      
> > > > > >>> Its also a step away from supporting plugins.
> > > > > >>> Why plugins matter ? Because having plugin
> > > > > >>> support is a big advantage, it allows a much wider
> > > > > >>> community to work on, write and maintain filters.
> > > > > >>>
> > > > > >>> With plugins, people can write filters that are
> > > > > >>> written in languages other than C. Or filters which
> > > > > >>> some developer in FFmpeg doesnt want. Or they can be
> > > > > >>> maintained externally by people who just do not like us.
> > > > > >>> Or by people who perfer a FOSS license different from
> > > > > >>> LGPL/GPL/BSD. Iam sure others can come up with more
> > > > > >>> reasons ...
> > > > > >>>
> > > > > >>> Of course avfilter_register() isnt enough for plugins
> > > > > >>> but it or something equivalent is needed for plugins.
> > > > > >>>
> > > > > >>> So i would prefer if avfilter_register() stays supported
> > > > > >>> indefinitly or in case a different system is written for
> > > > > >>> plugins then until that system is in place.      
> > > > > >>
> > > > > >> It just returns error and logs error message that currently external
> > > > > >> filter is unsupported. If someone wants to implement support for
> > > > > >> external filter, it can be easily added later using separate
> > > > > >> list/table.      
> > > > > > 
> > > > > > Iam not sure "easily" is true
> > > > > > 
> > > > > > We started out with a fully public API that allowed external filters.
> > > > > > and little by little its removed or made private.
> > > > > > each individual change may be easy to undo but as a whole moving
> > > > > > libavfilter back to having a public API is not that easy anymore
> > > > > > 
> > > > > > IIRC the arguments to make things private where that people wanted to
> > > > > > improve the API. But from the idea and impression of a plan like:
> > > > > > 1. make it private
> > > > > > 2. design and implement better API
> > > > > > 3. make it public again
> > > > > > 
> > > > > > Somehow now people lost sight and interrest in 3. and even 2. is becoming
> > > > > > less interresting. But the problem is really without 2 + 3 actually happening
> > > > > > doing 1 seems like not a good idea at all.
> > > > > > 
> > > > > > To me it seems even mentioning external filters and public API makes some
> > > > > > people angry.
> > > > > > But really that has to be the goal at some point. To again have a public API      
> > > > > 
> > > > > I agree that having (again) a public filter API would be good. This 
> > > > > would give users the freedom to implement their own special-purpose 
> > > > > filters (see the "dumpwave" discussion).    
> > > > 
> > > > Someone needs to design and write it.
> > > > 
> > > > Whether we have external filters is completely orthogonal to this
> > > > change.     
> > >   
> > > > They were not possible before, because not enough API for
> > > > implementing them is public.     
> > > 
> > > This is correct if by "before" you mean today before this is applied.
> > > But longer ago, as in years, it was possible
> > > 
> > >   
> > > > They can be possible in the future, but
> > > > then registering them would need a different API anyway (one that
> > > > doesn't use global mutable state, but uses some sort of context
> > > > instead).    
> > > 
> > > i understand that you arent a native english speaker nor am i but
> > > what you write here is not true.
> > > 
> > > Several people desire to eliminate all "global mutable state"
> > > and thats fine, iam happy if that is achieved. But its not
> > > that its neccessary for a fully functional API register or otherwise.
> > > The registering code with "global mutable state" and some form
> > > of thread synchronization would work perfectly fine.
> > > 
> > > What exactly is in relation to registering the problem with 
> > > "global mutable state" ?
> > > 
> > > One application registering a filter with a common name and another 
> > > application unintentionally using that ?  
> > 
> > Yes. It's not library-safe.
> >   
> > > This is not even possible because each application is in its own process and
> > > will have its own independant copy of libavfilter in its address space only
> > > with "read only" pages shared or with pages with "copy on write".  
> > 
> > This is a library. There can be multiple users of a library in the same
> > process.
> >   
> > > The "one registers the other uses it by mistake" issue is as far as i
> > > understand only possible if an application uses libavfilter and that
> > > application uses also a lib that itself uses libavfilter too or
> > > similar cases. 
> > > And then both mess with registering custom filters and using custom
> > > filters.
> > > That looks like a very rare situation. If thats the only issue, i would
> > > really not say theres a "need" to avoid "global mutable state"  
> > 
> > I don't agree. It will cause problems you're not even thinking about
> > now. Look at any library that thought global mutable state is fine for
> > some thing, and what problems it caused. Besides someone will have the
> > idea to create some sort of plugin loader, and then all bets are off.  
> 
> to awnser just
>  "look at any library that thought global mutable state is fine for some thing,"
> Well we can look at libavcodec and libavformat and libavfilters register
> code. That existed since a long time and used global mutable state.
> searching trac for 
> "_register(" -> 1 match thats about the lock manager not the codec/format register
> "av_register_output_format" -> no matches
> "av_register_input_format" -> no matches
> 
> So as you requested, i looked at not just any library but at multiple
> and at exactly the very identical case of registering.
> Zero issues have occured there.
> 
> People hate "global mutable state" and i do not like it either but the
> thing that its causing problems in this case is exagerated.

The problem so far has been easier, because you could NOT register user
defined codecs/filters. So a whole class of problems falls away. You
added all the atomics code to hack it into somewhat working yourself,
so I'm not sure why you deny that it had problems.

It's true that with proper synchronization, the current state is mostly
just an inconvenience of having to call completely useless functions
(the register_all ones), and more memory usage due to the register
funtions writing to data pages (i.e. it will make the process use
more memory even if the libs are not actually used).

> 
> > 
> > Why are you even stirring up an offtopic discussion again.   
> 
> How can discussing the removal and its reasons of a public API
> be off topic in a thread of a patch removing that API !?

For the hundredth time, having user filters has nothing to do with
having a global register function, and especially has nothing to do
with having a register_all function.

> 
> > I've also
> > mentioned multiple times how the main problem is creating public API
> > so filters can be implemented. This is not a trivial task, but adding
> > some sort of filter register API is absolutely trivial (even if it
> > readds the current API, which I hope it won't). So why bring it up
> > again and cause tons of fruitless discussions? Just why? Please explain
> > this.  
> 
> Yes, iam concerned and upset about the lack of activity on what you
> call the "main problem".
> If there was someone working on creating a new public API, then i would
> never complain about him removing existing API.
> "remove that code so we can put a better one in place" vs. "remove this,
> its trivial to put it or something back but that will never happen"
> Its the lack of a plan, the lack of intend to walk forward which makes me
> raise the issue of people taking a step backward.

For the hundredth time, this patch does not remove any API that would
actually enable you to have public API.

In fact, you probably merged the patch that deprecated/removed the
public filter API _yourself_.

I'm still expecting a proper explanation from you about your
misleading posts.
Nicolas George Feb. 1, 2018, 10:03 a.m. UTC | #26
Hi.

Muhammad Faiz (2018-01-30):
> Move REGISTER_FILTER to FILTER_TABLE in configure.
> Auto generate filter extern and filter table.
> Sort filter table, use bsearch on avfilter_get_by_name.
> Define next pointer at filter extern, no need to initialize
> next pointer at run time, so AVFilter can be set to const.
> Make avfilter_register always return error.
> Target checkasm now depends on EXTRALIBS-avformat.

Regarding registering user filters, do not bother about that. That ship
has sailed a long time ago; it is already more than half-way across and
decelerating. If somebody wants to implement plugins, they will have a
lot of work. Designing a way of registering user filters so that they
can be parsed by name would be a spoonful in the pool.

Regarding the actual implementation:

I do not like that you moved the list into configure. Not per se, but
because it is completely different from what Josh de Kock is doing with
codecs, parsers and formats. Please coordinate with Josh to have a
consistent solution for all components.

I think the fact that you need to insert a line in each filter is rather
a sign that the design is far from perfect.

Regarding the efficiency of avfilter_next(), I think we should not care.
Almost nobody uses that function outside lavfi anyway. If it proves a
concern for some application, we can deal with it when it is found.

Regards,
Michael Niedermayer Feb. 1, 2018, 12:46 p.m. UTC | #27
On Thu, Feb 01, 2018 at 02:43:56AM +0100, wm4 wrote:
> On Thu, 1 Feb 2018 02:06:52 +0100
> Michael Niedermayer <michael@niedermayer.cc> wrote:
> 
> > On Wed, Jan 31, 2018 at 12:24:43PM +0100, wm4 wrote:
> > > On Wed, 31 Jan 2018 12:11:25 +0100
> > > Michael Niedermayer <michael@niedermayer.cc> wrote:
> > >   
> > > > On Wed, Jan 31, 2018 at 10:03:58AM +0100, wm4 wrote:  
> > > > > On Wed, 31 Jan 2018 09:08:23 +0100
> > > > > Tobias Rapp <t.rapp@noa-archive.com> wrote:
> > > > >     
> > > > > > On 30.01.2018 20:37, Michael Niedermayer wrote:    
> > > > > > > On Tue, Jan 30, 2018 at 08:27:27PM +0700, Muhammad Faiz wrote:      
> > > > > > >> On Tue, Jan 30, 2018 at 7:50 PM, Michael Niedermayer
> > > > > > >> <michael@niedermayer.cc> wrote:      
> > > > > > >>> Its also a step away from supporting plugins.
> > > > > > >>> Why plugins matter ? Because having plugin
> > > > > > >>> support is a big advantage, it allows a much wider
> > > > > > >>> community to work on, write and maintain filters.
> > > > > > >>>
> > > > > > >>> With plugins, people can write filters that are
> > > > > > >>> written in languages other than C. Or filters which
> > > > > > >>> some developer in FFmpeg doesnt want. Or they can be
> > > > > > >>> maintained externally by people who just do not like us.
> > > > > > >>> Or by people who perfer a FOSS license different from
> > > > > > >>> LGPL/GPL/BSD. Iam sure others can come up with more
> > > > > > >>> reasons ...
> > > > > > >>>
> > > > > > >>> Of course avfilter_register() isnt enough for plugins
> > > > > > >>> but it or something equivalent is needed for plugins.
> > > > > > >>>
> > > > > > >>> So i would prefer if avfilter_register() stays supported
> > > > > > >>> indefinitly or in case a different system is written for
> > > > > > >>> plugins then until that system is in place.      
> > > > > > >>
> > > > > > >> It just returns error and logs error message that currently external
> > > > > > >> filter is unsupported. If someone wants to implement support for
> > > > > > >> external filter, it can be easily added later using separate
> > > > > > >> list/table.      
> > > > > > > 
> > > > > > > Iam not sure "easily" is true
> > > > > > > 
> > > > > > > We started out with a fully public API that allowed external filters.
> > > > > > > and little by little its removed or made private.
> > > > > > > each individual change may be easy to undo but as a whole moving
> > > > > > > libavfilter back to having a public API is not that easy anymore
> > > > > > > 
> > > > > > > IIRC the arguments to make things private where that people wanted to
> > > > > > > improve the API. But from the idea and impression of a plan like:
> > > > > > > 1. make it private
> > > > > > > 2. design and implement better API
> > > > > > > 3. make it public again
> > > > > > > 
> > > > > > > Somehow now people lost sight and interrest in 3. and even 2. is becoming
> > > > > > > less interresting. But the problem is really without 2 + 3 actually happening
> > > > > > > doing 1 seems like not a good idea at all.
> > > > > > > 
> > > > > > > To me it seems even mentioning external filters and public API makes some
> > > > > > > people angry.
> > > > > > > But really that has to be the goal at some point. To again have a public API      
> > > > > > 
> > > > > > I agree that having (again) a public filter API would be good. This 
> > > > > > would give users the freedom to implement their own special-purpose 
> > > > > > filters (see the "dumpwave" discussion).    
> > > > > 
> > > > > Someone needs to design and write it.
> > > > > 
> > > > > Whether we have external filters is completely orthogonal to this
> > > > > change.     
> > > >   
> > > > > They were not possible before, because not enough API for
> > > > > implementing them is public.     
> > > > 
> > > > This is correct if by "before" you mean today before this is applied.
> > > > But longer ago, as in years, it was possible
> > > > 
> > > >   
> > > > > They can be possible in the future, but
> > > > > then registering them would need a different API anyway (one that
> > > > > doesn't use global mutable state, but uses some sort of context
> > > > > instead).    
> > > > 
> > > > i understand that you arent a native english speaker nor am i but
> > > > what you write here is not true.
> > > > 
> > > > Several people desire to eliminate all "global mutable state"
> > > > and thats fine, iam happy if that is achieved. But its not
> > > > that its neccessary for a fully functional API register or otherwise.
> > > > The registering code with "global mutable state" and some form
> > > > of thread synchronization would work perfectly fine.
> > > > 
> > > > What exactly is in relation to registering the problem with 
> > > > "global mutable state" ?
> > > > 
> > > > One application registering a filter with a common name and another 
> > > > application unintentionally using that ?  
> > > 
> > > Yes. It's not library-safe.
> > >   
> > > > This is not even possible because each application is in its own process and
> > > > will have its own independant copy of libavfilter in its address space only
> > > > with "read only" pages shared or with pages with "copy on write".  
> > > 
> > > This is a library. There can be multiple users of a library in the same
> > > process.
> > >   
> > > > The "one registers the other uses it by mistake" issue is as far as i
> > > > understand only possible if an application uses libavfilter and that
> > > > application uses also a lib that itself uses libavfilter too or
> > > > similar cases. 
> > > > And then both mess with registering custom filters and using custom
> > > > filters.
> > > > That looks like a very rare situation. If thats the only issue, i would
> > > > really not say theres a "need" to avoid "global mutable state"  
> > > 
> > > I don't agree. It will cause problems you're not even thinking about
> > > now. Look at any library that thought global mutable state is fine for
> > > some thing, and what problems it caused. Besides someone will have the
> > > idea to create some sort of plugin loader, and then all bets are off.  
> > 
> > to awnser just
> >  "look at any library that thought global mutable state is fine for some thing,"
> > Well we can look at libavcodec and libavformat and libavfilters register
> > code. That existed since a long time and used global mutable state.
> > searching trac for 
> > "_register(" -> 1 match thats about the lock manager not the codec/format register
> > "av_register_output_format" -> no matches
> > "av_register_input_format" -> no matches
> > 
> > So as you requested, i looked at not just any library but at multiple
> > and at exactly the very identical case of registering.
> > Zero issues have occured there.
> > 
> > People hate "global mutable state" and i do not like it either but the
> > thing that its causing problems in this case is exagerated.
> 
> The problem so far has been easier, because you could NOT register user
> defined codecs/filters. So a whole class of problems falls away. You
> added all the atomics code to hack it into somewhat working yourself,
> so I'm not sure why you deny that it had problems.

How exactly does what i said:
"The registering code with "global mutable state" and some form of thread synchronization ..."
and
"but the thing that its causing problems in this case is exagerated."

can lead to that i deny that it has/had problems !?
I even explicitly aknowledge the atomic issue in what i said.

Of course the atomic issue is theoretical its not a bug that to the best
of my knowledge anyone ever managed to hit in reality.


[...]

> In fact, you probably merged the patch that deprecated/removed the
> public filter API _yourself_.

This is quite possible, but that would have happened in the belive that
it was temporary and that there soon will be a public API again. Either
one designed by libav or ffmpeg or the old API could be re-enabled if
neither occurs.

[...]
Brian Matherly Feb. 1, 2018, 2:33 p.m. UTC | #28
On 2/1/2018 4:03 AM, Nicolas George wrote:
> Regarding the efficiency of avfilter_next(), I think we should not care.
> Almost nobody uses that function outside lavfi anyway. If it proves a
> concern for some application, we can deal with it when it is found.

For what it's worth, I use avfilter_next() on startup in MLT to generate 
a list of valid user options:

https://github.com/mltframework/mlt/blob/master/src/modules/avformat/factory.c#L415

I would prefer some way to loop through the filters in linear time.
Muhammad Faiz Feb. 1, 2018, 6:35 p.m. UTC | #29
On Wed, Jan 31, 2018 at 7:03 PM, wm4 <nfxjfg@googlemail.com> wrote:
> On Wed, 31 Jan 2018 11:52:14 +0000
> Mark Thompson <sw@jkqxz.net> wrote:
>
>> >> On the other side, you get rid of a field in AVFilter and avoid having to put some pointless boilerplate in a lot of places.
>> >
>> > The other solution is to initialize next pointer on
>> > avfilter_register_all() as before,  add new iterate API, and deprecate
>> > both avfilter_register_all() and avfilter_next().
>>
>> I guess having thought about this further my problem is that you appear to be writing a lot of new infrastructure for creating and updating the next links (with special generation code in configure and extra boilerplate on every filter) when the feature does not have any clear value.  Once other functions are properly updated the only place where the next link is used is in avfilter_next(), which is only slowed down a little bit and would never be called in performance-sensitive code anyway.  A new iterate API would be welcome but is mostly orthogonal - you aren't going to call that in performance-sensitive code either.
>>
>> So, can we just drop the next links completely?
>
> Just as a comment on the side: if he changes that, I'd prefer of the
> next links are lazily initialized and only when using the old API. That
> would waste less memory (since writing the next link will trigger copy
> on write and in the worst case waste 4K (a page) per filter.
>
> (Personally I found the static next links rather neat, but yeah, maybe
> it's too much configure magic.)

I've posted a new patch
https://ffmpeg.org/pipermail/ffmpeg-devel/2018-February/224787.html

Thank's.
Muhammad Faiz Feb. 1, 2018, 6:41 p.m. UTC | #30
On Thu, Feb 1, 2018 at 5:03 PM, Nicolas George <george@nsup.org> wrote:
> Hi.
>
> Muhammad Faiz (2018-01-30):
>> Move REGISTER_FILTER to FILTER_TABLE in configure.
>> Auto generate filter extern and filter table.
>> Sort filter table, use bsearch on avfilter_get_by_name.
>> Define next pointer at filter extern, no need to initialize
>> next pointer at run time, so AVFilter can be set to const.
>> Make avfilter_register always return error.
>> Target checkasm now depends on EXTRALIBS-avformat.
>
> Regarding registering user filters, do not bother about that. That ship
> has sailed a long time ago; it is already more than half-way across and
> decelerating. If somebody wants to implement plugins, they will have a
> lot of work. Designing a way of registering user filters so that they
> can be parsed by name would be a spoonful in the pool.
>
> Regarding the actual implementation:
>
> I do not like that you moved the list into configure. Not per se, but
> because it is completely different from what Josh de Kock is doing with
> codecs, parsers and formats. Please coordinate with Josh to have a
> consistent solution for all components.

Actually I have a plan to sort codecs based on name and codec_id. And
it is difficult to sort based on codec_id with only extern declaration
in allcodecs.


>
> I think the fact that you need to insert a line in each filter is rather
> a sign that the design is far from perfect.

I remove it in my new patch.

>
> Regarding the efficiency of avfilter_next(), I think we should not care.
> Almost nobody uses that function outside lavfi anyway. If it proves a
> concern for some application, we can deal with it when it is found.


Thank's.
Nicolas George Feb. 1, 2018, 6:47 p.m. UTC | #31
Muhammad Faiz (2018-02-02):
> Actually I have a plan to sort codecs based on name and codec_id. And
> it is difficult to sort based on codec_id with only extern declaration
> in allcodecs.

It seems like a good idea, although I have not given it much list. But
as it is, Josh and you are proposing patches that go in different
directions. Please coordinate with him. We do not want to go back and
forth between implementations, nor maintain subtly different
implementations. There is no rush to implement this, and therefore it
can be done properly.

Regards,
diff mbox

Patch

diff --git a/Makefile b/Makefile
index 9defddebfd..f607579369 100644
--- a/Makefile
+++ b/Makefile
@@ -56,6 +56,7 @@  tools/uncoded_frame$(EXESUF): ELIBS = $(FF_EXTRALIBS)
 tools/target_dec_%_fuzzer$(EXESUF): $(FF_DEP_LIBS)
 
 CONFIGURABLE_COMPONENTS =                                           \
+    $(SRC_PATH)/configure                                           \
     $(wildcard $(FFLIBS:%=$(SRC_PATH)/lib%/all*.c))                 \
     $(SRC_PATH)/libavcodec/bitstream_filters.c                      \
     $(SRC_PATH)/libavformat/protocols.c                             \
@@ -142,7 +143,8 @@  distclean:: clean
 	$(RM) .version avversion.h config.asm config.h mapfile  \
 		ffbuild/.config ffbuild/config.* libavutil/avconfig.h \
 		version.h libavutil/ffversion.h libavcodec/codec_names.h \
-		libavcodec/bsf_list.c libavformat/protocol_list.c
+		libavcodec/bsf_list.c libavformat/protocol_list.c \
+		libavfilter/filter_list.h libavfilter/filter_list.c
 ifeq ($(SRC_LINK),src)
 	$(RM) src
 endif
diff --git a/configure b/configure
index fcfa7aa442..3261f5fd1a 100755
--- a/configure
+++ b/configure
@@ -3177,6 +3177,381 @@  unix_protocol_deps="sys_un_h"
 unix_protocol_select="network"
 
 # filters
+FILTER_TABLE="
+abench                  af
+acompressor             af
+acontrast               af
+acopy                   af
+acrossfade              af
+acrusher                af
+adelay                  af
+aecho                   af
+aemphasis               af
+aeval                   af
+afade                   af
+afftfilt                af
+afir                    af
+aformat                 af
+agate                   af
+aiir                    af
+ainterleave             af
+alimiter                af
+allpass                 af
+aloop                   af
+amerge                  af
+ametadata               af
+amix                    af
+anequalizer             af
+anull                   af
+apad                    af
+aperms                  af
+aphaser                 af
+apulsator               af
+arealtime               af
+aresample               af
+areverse                af
+aselect                 af
+asendcmd                af
+asetnsamples            af
+asetpts                 af
+asetrate                af
+asettb                  af
+ashowinfo               af
+asidedata               af
+asplit                  af
+astats                  af
+astreamselect           af
+atempo                  af
+atrim                   af
+azmq                    af
+bandpass                af
+bandreject              af
+bass                    af
+biquad                  af
+bs2b                    af
+channelmap              af
+channelsplit            af
+chorus                  af
+compand                 af
+compensationdelay       af
+crossfeed               af
+crystalizer             af
+dcshift                 af
+dynaudnorm              af
+earwax                  af
+ebur128                 af
+equalizer               af
+extrastereo             af
+firequalizer            af
+flanger                 af
+haas                    af
+hdcd                    af
+headphone               af
+highpass                af
+join                    af
+ladspa                  af
+loudnorm                af
+lowpass                 af
+lv2                     af
+mcompand                af
+pan                     af
+replaygain              af
+resample                af
+rubberband              af
+sidechaincompress       af
+sidechaingate           af
+silencedetect           af
+silenceremove           af
+sofalizer               af
+stereotools             af
+stereowiden             af
+superequalizer          af
+surround                af
+treble                  af
+tremolo                 af
+vibrato                 af
+volume                  af
+volumedetect            af
+aevalsrc                asrc
+anoisesrc               asrc
+anullsrc                asrc
+flite                   asrc
+hilbert                 asrc
+sine                    asrc
+anullsink               asink
+alphaextract            vf
+alphamerge              vf
+ass                     vf
+atadenoise              vf
+avgblur                 vf
+bbox                    vf
+bench                   vf
+bitplanenoise           vf
+blackdetect             vf
+blackframe              vf
+blend                   vf
+boxblur                 vf
+bwdif                   vf
+chromakey               vf
+ciescope                vf
+codecview               vf
+colorbalance            vf
+colorchannelmixer       vf
+colorkey                vf
+colorlevels             vf
+colormatrix             vf
+colorspace              vf
+convolution             vf
+convolve                vf
+copy                    vf
+coreimage               vf
+cover_rect              vf
+crop                    vf
+cropdetect              vf
+curves                  vf
+datascope               vf
+dctdnoiz                vf
+deband                  vf
+decimate                vf
+deconvolve              vf
+deflate                 vf
+deflicker               vf
+deinterlace_qsv         vf
+deinterlace_vaapi       vf
+dejudder                vf
+delogo                  vf
+denoise_vaapi           vf
+deshake                 vf
+despill                 vf
+detelecine              vf
+dilation                vf
+displace                vf
+doubleweave             vf
+drawbox                 vf
+drawgraph               vf
+drawgrid                vf
+drawtext                vf
+edgedetect              vf
+elbg                    vf
+entropy                 vf
+eq                      vf
+erosion                 vf
+extractplanes           vf
+fade                    vf
+fftfilt                 vf
+field                   vf
+fieldhint               vf
+fieldmatch              vf
+fieldorder              vf
+fillborders             vf
+find_rect               vf
+floodfill               vf
+format                  vf
+fps                     vf
+framepack               vf
+framerate               vf
+framestep               vf
+frei0r                  vf
+fspp                    vf
+gblur                   vf
+geq                     vf
+gradfun                 vf
+haldclut                vf
+hflip                   vf
+histeq                  vf
+histogram               vf
+hqdn3d                  vf
+hqx                     vf
+hstack                  vf
+hue                     vf
+hwdownload              vf
+hwmap                   vf
+hwupload                vf
+hwupload_cuda           vf
+hysteresis              vf
+idet                    vf
+il                      vf
+inflate                 vf
+interlace               vf
+interleave              vf
+kerndeint               vf
+lenscorrection          vf
+libvmaf                 vf
+limiter                 vf
+loop                    vf
+lumakey                 vf
+lut                     vf
+lut2                    vf
+lut3d                   vf
+lutrgb                  vf
+lutyuv                  vf
+maskedclamp             vf
+maskedmerge             vf
+mcdeint                 vf
+mergeplanes             vf
+mestimate               vf
+metadata                vf
+midequalizer            vf
+minterpolate            vf
+mix                     vf
+mpdecimate              vf
+negate                  vf
+nlmeans                 vf
+nnedi                   vf
+noformat                vf
+noise                   vf
+normalize               vf
+null                    vf
+ocr                     vf
+ocv                     vf
+oscilloscope            vf
+overlay                 vf
+overlay_opencl          vf
+overlay_qsv             vf
+owdenoise               vf
+pad                     vf
+palettegen              vf
+paletteuse              vf
+perms                   vf
+perspective             vf
+phase                   vf
+pixdesctest             vf
+pixscope                vf
+pp                      vf
+pp7                     vf
+premultiply             vf
+prewitt                 vf
+procamp_vaapi           vf
+program_opencl          vf
+pseudocolor             vf
+psnr                    vf
+pullup                  vf
+qp                      vf
+random                  vf
+readeia608              vf
+readvitc                vf
+realtime                vf
+remap                   vf
+removegrain             vf
+removelogo              vf
+repeatfields            vf
+reverse                 vf
+roberts                 vf
+rotate                  vf
+sab                     vf
+scale                   vf
+scale_cuda              vf
+scale_npp               vf
+scale_qsv               vf
+scale_vaapi             vf
+scale2ref               vf
+select                  vf
+selectivecolor          vf
+sendcmd                 vf
+separatefields          vf
+setdar                  vf
+setfield                vf
+setpts                  vf
+setrange                vf
+setsar                  vf
+settb                   vf
+sharpness_vaapi         vf
+showinfo                vf
+showpalette             vf
+shuffleframes           vf
+shuffleplanes           vf
+sidedata                vf
+signalstats             vf
+signature               vf
+smartblur               vf
+sobel                   vf
+split                   vf
+spp                     vf
+ssim                    vf
+stereo3d                vf
+streamselect            vf
+subtitles               vf
+super2xsai              vf
+swaprect                vf
+swapuv                  vf
+tblend                  vf
+telecine                vf
+threshold               vf
+thumbnail               vf
+thumbnail_cuda          vf
+tile                    vf
+tinterlace              vf
+tlut2                   vf
+tonemap                 vf
+transpose               vf
+trim                    vf
+unpremultiply           vf
+unsharp                 vf
+unsharp_opencl          vf
+uspp                    vf
+vaguedenoiser           vf
+vectorscope             vf
+vflip                   vf
+vidstabdetect           vf
+vidstabtransform        vf
+vignette                vf
+vmafmotion              vf
+vpp_qsv                 vf
+vstack                  vf
+w3fdif                  vf
+waveform                vf
+weave                   vf
+xbr                     vf
+yadif                   vf
+zmq                     vf
+zoompan                 vf
+zscale                  vf
+allrgb                  vsrc
+allyuv                  vsrc
+cellauto                vsrc
+color                   vsrc
+coreimagesrc            vsrc
+frei0r_src              vsrc
+haldclutsrc             vsrc
+life                    vsrc
+mandelbrot              vsrc
+mptestsrc               vsrc
+nullsrc                 vsrc
+openclsrc               vsrc
+rgbtestsrc              vsrc
+smptebars               vsrc
+smptehdbars             vsrc
+testsrc                 vsrc
+testsrc2                vsrc
+yuvtestsrc              vsrc
+nullsink                vsink
+abitscope               avf
+adrawgraph              avf
+ahistogram              avf
+aphasemeter             avf
+avectorscope            avf
+concat                  avf
+showcqt                 avf
+showfreqs               avf
+showspectrum            avf
+showspectrumpic         avf
+showvolume              avf
+showwaves               avf
+showwavespic            avf
+spectrumsynth           vaf
+amovie                  avsrc
+movie                   avsrc
+"
+
+UNCONDITIONAL_FILTER_TABLE="
+abuffer         asrc
+buffer          vsrc
+abuffersink     asink
+buffersink      vsink
+afifo           af
+fifo            vf
+"
+
 afftfilt_filter_deps="avcodec"
 afftfilt_filter_select="fft"
 afir_filter_deps="avcodec"
@@ -3530,7 +3905,18 @@  MUXER_LIST=$(find_things    muxer    _MUX     libavformat/allformats.c)
 DEMUXER_LIST=$(find_things  demuxer  DEMUX    libavformat/allformats.c)
 OUTDEV_LIST=$(find_things   outdev   OUTDEV   libavdevice/alldevices.c)
 INDEV_LIST=$(find_things    indev    _IN      libavdevice/alldevices.c)
-FILTER_LIST=$(find_things   filter   FILTER   libavfilter/allfilters.c)
+
+extract_list_from_table(){
+    cols=$1
+    suffix=$2
+    shift 2
+    while test -n "$1"; do
+        echo "${1}${suffix}"
+        shift $cols
+    done
+}
+
+FILTER_LIST=$(extract_list_from_table 2 _filter $FILTER_TABLE)
 
 find_things_extern(){
     thing=$1
@@ -7030,6 +7416,58 @@  print_enabled_components(){
 print_enabled_components libavcodec/bsf_list.c AVBitStreamFilter bitstream_filters $BSF_LIST
 print_enabled_components libavformat/protocol_list.c URLProtocol url_protocols $PROTOCOL_LIST
 
+# filters
+extract_enabled_filter(){
+    while test -n "$1"; do
+        if enabled "${1}_filter"; then
+            echo "$1 $2"
+        fi
+        shift 2
+    done
+}
+
+extract_sorted_filter(){
+    while test -n "$1"; do
+        echo "$1 $2"
+        shift 2
+    done | sort
+}
+
+print_filter_extern(){
+    while test -n "$1"; do
+        echo "extern const AVFilter ff_${2}_${1};"
+        if test -n "$3"; then
+            echo "#define ff_next_${2}_${1} &ff_${4}_${3}"
+        else
+            echo "#define ff_next_${2}_${1} NULL"
+        fi
+        shift 2
+    done
+}
+
+print_filter_array(){
+    echo "static const AVFilter *const filter_list[] = {"
+    while test -n "$1"; do
+        echo "    &ff_${2}_${1},"
+        shift 2
+    done
+    echo "    NULL"
+    echo "};"
+}
+
+sorted_filter_table=$(extract_sorted_filter $(extract_enabled_filter $FILTER_TABLE) $UNCONDITIONAL_FILTER_TABLE)
+
+echo "/* Automatically generated by configure - do not modify! */" > $TMPH
+echo "#ifndef AVFILTER_FILTER_LIST_H" >> $TMPH
+echo "#define AVFILTER_FILTER_LIST_H" >> $TMPH
+print_filter_extern $sorted_filter_table >> $TMPH
+echo "#endif" >> $TMPH
+cp_if_changed $TMPH libavfilter/filter_list.h
+
+echo "/* Automatically generated by configure - do not modify! */" > $TMPH
+print_filter_array $sorted_filter_table >> $TMPH
+cp_if_changed $TMPH libavfilter/filter_list.c
+
 # Settings for pkg-config files
 
 cat > $TMPH <<EOF
diff --git a/libavfilter/aeval.c b/libavfilter/aeval.c
index cdddbaf31d..d5963367a1 100644
--- a/libavfilter/aeval.c
+++ b/libavfilter/aeval.c
@@ -322,7 +322,7 @@  static const AVFilterPad aevalsrc_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_asrc_aevalsrc = {
+const AVFilter ff_asrc_aevalsrc = {
     .name          = "aevalsrc",
     .description   = NULL_IF_CONFIG_SMALL("Generate an audio signal generated by an expression."),
     .query_formats = query_formats,
@@ -332,6 +332,7 @@  AVFilter ff_asrc_aevalsrc = {
     .inputs        = NULL,
     .outputs       = aevalsrc_outputs,
     .priv_class    = &aevalsrc_class,
+    .next          = ff_next_asrc_aevalsrc,
 };
 
 #endif /* CONFIG_AEVALSRC_FILTER */
@@ -475,7 +476,7 @@  static const AVFilterPad aeval_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_aeval = {
+const AVFilter ff_af_aeval = {
     .name          = "aeval",
     .description   = NULL_IF_CONFIG_SMALL("Filter audio signal according to a specified expression."),
     .query_formats = aeval_query_formats,
@@ -485,6 +486,7 @@  AVFilter ff_af_aeval = {
     .inputs        = aeval_inputs,
     .outputs       = aeval_outputs,
     .priv_class    = &aeval_class,
+    .next          = ff_next_af_aeval,
 };
 
 #endif /* CONFIG_AEVAL_FILTER */
diff --git a/libavfilter/af_acontrast.c b/libavfilter/af_acontrast.c
index e08053146e..fbbda2cfd0 100644
--- a/libavfilter/af_acontrast.c
+++ b/libavfilter/af_acontrast.c
@@ -208,7 +208,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_acontrast = {
+const AVFilter ff_af_acontrast = {
     .name           = "acontrast",
     .description    = NULL_IF_CONFIG_SMALL("Simple audio dynamic range compression/expansion filter."),
     .query_formats  = query_formats,
@@ -216,4 +216,5 @@  AVFilter ff_af_acontrast = {
     .priv_class     = &acontrast_class,
     .inputs         = inputs,
     .outputs        = outputs,
+    .next           = ff_next_af_acontrast,
 };
diff --git a/libavfilter/af_acopy.c b/libavfilter/af_acopy.c
index d849060966..ffee5cbce8 100644
--- a/libavfilter/af_acopy.c
+++ b/libavfilter/af_acopy.c
@@ -52,9 +52,10 @@  static const AVFilterPad acopy_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_acopy = {
+const AVFilter ff_af_acopy = {
     .name          = "acopy",
     .description   = NULL_IF_CONFIG_SMALL("Copy the input audio unchanged to the output."),
     .inputs        = acopy_inputs,
     .outputs       = acopy_outputs,
+    .next          = ff_next_af_acopy,
 };
diff --git a/libavfilter/af_acrusher.c b/libavfilter/af_acrusher.c
index ddce74465d..a2fcd1344c 100644
--- a/libavfilter/af_acrusher.c
+++ b/libavfilter/af_acrusher.c
@@ -350,7 +350,7 @@  static const AVFilterPad avfilter_af_acrusher_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_acrusher = {
+const AVFilter ff_af_acrusher = {
     .name          = "acrusher",
     .description   = NULL_IF_CONFIG_SMALL("Reduce audio bit resolution."),
     .priv_size     = sizeof(ACrusherContext),
@@ -359,4 +359,5 @@  AVFilter ff_af_acrusher = {
     .query_formats = query_formats,
     .inputs        = avfilter_af_acrusher_inputs,
     .outputs       = avfilter_af_acrusher_outputs,
+    .next          = ff_next_af_acrusher,
 };
diff --git a/libavfilter/af_adelay.c b/libavfilter/af_adelay.c
index d6d81ba7d8..9d829b61e5 100644
--- a/libavfilter/af_adelay.c
+++ b/libavfilter/af_adelay.c
@@ -275,7 +275,7 @@  static const AVFilterPad adelay_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_adelay = {
+const AVFilter ff_af_adelay = {
     .name          = "adelay",
     .description   = NULL_IF_CONFIG_SMALL("Delay one or more audio channels."),
     .query_formats = query_formats,
@@ -285,4 +285,5 @@  AVFilter ff_af_adelay = {
     .inputs        = adelay_inputs,
     .outputs       = adelay_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_af_adelay,
 };
diff --git a/libavfilter/af_aecho.c b/libavfilter/af_aecho.c
index b9ac18d3a4..9de1b21d40 100644
--- a/libavfilter/af_aecho.c
+++ b/libavfilter/af_aecho.c
@@ -352,7 +352,7 @@  static const AVFilterPad aecho_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_aecho = {
+const AVFilter ff_af_aecho = {
     .name          = "aecho",
     .description   = NULL_IF_CONFIG_SMALL("Add echoing to the audio."),
     .query_formats = query_formats,
@@ -362,4 +362,5 @@  AVFilter ff_af_aecho = {
     .uninit        = uninit,
     .inputs        = aecho_inputs,
     .outputs       = aecho_outputs,
+    .next          = ff_next_af_aecho,
 };
diff --git a/libavfilter/af_aemphasis.c b/libavfilter/af_aemphasis.c
index e1fa93affc..2dbfd517b4 100644
--- a/libavfilter/af_aemphasis.c
+++ b/libavfilter/af_aemphasis.c
@@ -357,7 +357,7 @@  static const AVFilterPad avfilter_af_aemphasis_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_aemphasis = {
+const AVFilter ff_af_aemphasis = {
     .name          = "aemphasis",
     .description   = NULL_IF_CONFIG_SMALL("Audio emphasis."),
     .priv_size     = sizeof(AudioEmphasisContext),
@@ -366,4 +366,5 @@  AVFilter ff_af_aemphasis = {
     .query_formats = query_formats,
     .inputs        = avfilter_af_aemphasis_inputs,
     .outputs       = avfilter_af_aemphasis_outputs,
+    .next          = ff_next_af_aemphasis,
 };
diff --git a/libavfilter/af_afade.c b/libavfilter/af_afade.c
index 285b5b6557..04880a062d 100644
--- a/libavfilter/af_afade.c
+++ b/libavfilter/af_afade.c
@@ -335,7 +335,7 @@  static const AVFilterPad avfilter_af_afade_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_afade = {
+const AVFilter ff_af_afade = {
     .name          = "afade",
     .description   = NULL_IF_CONFIG_SMALL("Fade in/out input audio."),
     .query_formats = query_formats,
@@ -345,6 +345,7 @@  AVFilter ff_af_afade = {
     .outputs       = avfilter_af_afade_outputs,
     .priv_class    = &afade_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_af_afade,
 };
 
 #endif /* CONFIG_AFADE_FILTER */
@@ -614,7 +615,7 @@  static const AVFilterPad avfilter_af_acrossfade_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_acrossfade = {
+const AVFilter ff_af_acrossfade = {
     .name          = "acrossfade",
     .description   = NULL_IF_CONFIG_SMALL("Cross fade two input audio streams."),
     .query_formats = query_formats,
@@ -623,6 +624,7 @@  AVFilter ff_af_acrossfade = {
     .priv_class    = &acrossfade_class,
     .inputs        = avfilter_af_acrossfade_inputs,
     .outputs       = avfilter_af_acrossfade_outputs,
+    .next          = ff_next_af_acrossfade,
 };
 
 #endif /* CONFIG_ACROSSFADE_FILTER */
diff --git a/libavfilter/af_afftfilt.c b/libavfilter/af_afftfilt.c
index 7f28e1f77b..f4c2e3492d 100644
--- a/libavfilter/af_afftfilt.c
+++ b/libavfilter/af_afftfilt.c
@@ -396,7 +396,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_afftfilt = {
+const AVFilter ff_af_afftfilt = {
     .name            = "afftfilt",
     .description     = NULL_IF_CONFIG_SMALL("Apply arbitrary expressions to samples in frequency domain."),
     .priv_size       = sizeof(AFFTFiltContext),
@@ -405,4 +405,5 @@  AVFilter ff_af_afftfilt = {
     .outputs         = outputs,
     .query_formats   = query_formats,
     .uninit          = uninit,
+    .next            = ff_next_af_afftfilt,
 };
diff --git a/libavfilter/af_afir.c b/libavfilter/af_afir.c
index 6687242631..2dcdcaf667 100644
--- a/libavfilter/af_afir.c
+++ b/libavfilter/af_afir.c
@@ -542,7 +542,7 @@  static const AVOption afir_options[] = {
 
 AVFILTER_DEFINE_CLASS(afir);
 
-AVFilter ff_af_afir = {
+const AVFilter ff_af_afir = {
     .name          = "afir",
     .description   = NULL_IF_CONFIG_SMALL("Apply Finite Impulse Response filter with supplied coefficients in 2nd stream."),
     .priv_size     = sizeof(AudioFIRContext),
@@ -553,4 +553,5 @@  AVFilter ff_af_afir = {
     .inputs        = afir_inputs,
     .outputs       = afir_outputs,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_af_afir,
 };
diff --git a/libavfilter/af_aformat.c b/libavfilter/af_aformat.c
index e43149561a..00759d463a 100644
--- a/libavfilter/af_aformat.c
+++ b/libavfilter/af_aformat.c
@@ -142,7 +142,7 @@  static const AVFilterPad avfilter_af_aformat_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_aformat = {
+const AVFilter ff_af_aformat = {
     .name          = "aformat",
     .description   = NULL_IF_CONFIG_SMALL("Convert the input audio to one of the specified formats."),
     .init          = init,
@@ -151,4 +151,5 @@  AVFilter ff_af_aformat = {
     .priv_class    = &aformat_class,
     .inputs        = avfilter_af_aformat_inputs,
     .outputs       = avfilter_af_aformat_outputs,
+    .next          = ff_next_af_aformat,
 };
diff --git a/libavfilter/af_agate.c b/libavfilter/af_agate.c
index ba96863a68..54ab5e0fdb 100644
--- a/libavfilter/af_agate.c
+++ b/libavfilter/af_agate.c
@@ -249,7 +249,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_agate = {
+const AVFilter ff_af_agate = {
     .name           = "agate",
     .description    = NULL_IF_CONFIG_SMALL("Audio gate."),
     .query_formats  = query_formats,
@@ -257,6 +257,7 @@  AVFilter ff_af_agate = {
     .priv_class     = &agate_class,
     .inputs         = inputs,
     .outputs        = outputs,
+    .next           = ff_next_af_agate,
 };
 
 #endif /* CONFIG_AGATE_FILTER */
@@ -424,7 +425,7 @@  static const AVFilterPad sidechaingate_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_sidechaingate = {
+const AVFilter ff_af_sidechaingate = {
     .name           = "sidechaingate",
     .description    = NULL_IF_CONFIG_SMALL("Audio sidechain gate."),
     .priv_size      = sizeof(AudioGateContext),
@@ -434,5 +435,6 @@  AVFilter ff_af_sidechaingate = {
     .uninit         = uninit,
     .inputs         = sidechaingate_inputs,
     .outputs        = sidechaingate_outputs,
+    .next           = ff_next_af_sidechaingate,
 };
 #endif  /* CONFIG_SIDECHAINGATE_FILTER */
diff --git a/libavfilter/af_aiir.c b/libavfilter/af_aiir.c
index 1f2a568c1d..d0eee122d8 100644
--- a/libavfilter/af_aiir.c
+++ b/libavfilter/af_aiir.c
@@ -835,7 +835,7 @@  static const AVOption aiir_options[] = {
 
 AVFILTER_DEFINE_CLASS(aiir);
 
-AVFilter ff_af_aiir = {
+const AVFilter ff_af_aiir = {
     .name          = "aiir",
     .description   = NULL_IF_CONFIG_SMALL("Apply Infinite Impulse Response filter with supplied coefficients."),
     .priv_size     = sizeof(AudioIIRContext),
@@ -846,4 +846,5 @@  AVFilter ff_af_aiir = {
     .inputs        = inputs,
     .outputs       = outputs,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_af_aiir,
 };
diff --git a/libavfilter/af_alimiter.c b/libavfilter/af_alimiter.c
index 0fc8e6baa3..d61819898c 100644
--- a/libavfilter/af_alimiter.c
+++ b/libavfilter/af_alimiter.c
@@ -357,7 +357,7 @@  static const AVFilterPad alimiter_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_alimiter = {
+const AVFilter ff_af_alimiter = {
     .name           = "alimiter",
     .description    = NULL_IF_CONFIG_SMALL("Audio lookahead limiter."),
     .priv_size      = sizeof(AudioLimiterContext),
@@ -367,4 +367,5 @@  AVFilter ff_af_alimiter = {
     .query_formats  = query_formats,
     .inputs         = alimiter_inputs,
     .outputs        = alimiter_outputs,
+    .next           = ff_next_af_alimiter,
 };
diff --git a/libavfilter/af_amerge.c b/libavfilter/af_amerge.c
index 09c660ef49..702149821d 100644
--- a/libavfilter/af_amerge.c
+++ b/libavfilter/af_amerge.c
@@ -354,7 +354,7 @@  static const AVFilterPad amerge_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_amerge = {
+const AVFilter ff_af_amerge = {
     .name          = "amerge",
     .description   = NULL_IF_CONFIG_SMALL("Merge two or more audio streams into "
                                           "a single multi-channel stream."),
@@ -366,4 +366,5 @@  AVFilter ff_af_amerge = {
     .outputs       = amerge_outputs,
     .priv_class    = &amerge_class,
     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next          = ff_next_af_amerge,
 };
diff --git a/libavfilter/af_amix.c b/libavfilter/af_amix.c
index ec764375c0..69c5dd5799 100644
--- a/libavfilter/af_amix.c
+++ b/libavfilter/af_amix.c
@@ -567,7 +567,7 @@  static const AVFilterPad avfilter_af_amix_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_amix = {
+const AVFilter ff_af_amix = {
     .name           = "amix",
     .description    = NULL_IF_CONFIG_SMALL("Audio mixing."),
     .priv_size      = sizeof(MixContext),
@@ -579,4 +579,5 @@  AVFilter ff_af_amix = {
     .inputs         = NULL,
     .outputs        = avfilter_af_amix_outputs,
     .flags          = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next           = ff_next_af_amix,
 };
diff --git a/libavfilter/af_anequalizer.c b/libavfilter/af_anequalizer.c
index 24034602fd..cb0bc1560c 100644
--- a/libavfilter/af_anequalizer.c
+++ b/libavfilter/af_anequalizer.c
@@ -747,7 +747,7 @@  static const AVFilterPad inputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_anequalizer = {
+const AVFilter ff_af_anequalizer = {
     .name          = "anequalizer",
     .description   = NULL_IF_CONFIG_SMALL("Apply high-order audio parametric multi band equalizer."),
     .priv_size     = sizeof(AudioNEqualizerContext),
@@ -759,4 +759,5 @@  AVFilter ff_af_anequalizer = {
     .outputs       = NULL,
     .flags         = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
     .process_command = process_command,
+    .next          = ff_next_af_anequalizer,
 };
diff --git a/libavfilter/af_anull.c b/libavfilter/af_anull.c
index 1d0af451dc..82efe848b0 100644
--- a/libavfilter/af_anull.c
+++ b/libavfilter/af_anull.c
@@ -43,9 +43,10 @@  static const AVFilterPad avfilter_af_anull_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_anull = {
+const AVFilter ff_af_anull = {
     .name          = "anull",
     .description   = NULL_IF_CONFIG_SMALL("Pass the source unchanged to the output."),
     .inputs        = avfilter_af_anull_inputs,
     .outputs       = avfilter_af_anull_outputs,
+    .next          = ff_next_af_anull,
 };
diff --git a/libavfilter/af_apad.c b/libavfilter/af_apad.c
index f7a4199c64..2238dee8c2 100644
--- a/libavfilter/af_apad.c
+++ b/libavfilter/af_apad.c
@@ -149,7 +149,7 @@  static const AVFilterPad apad_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_apad = {
+const AVFilter ff_af_apad = {
     .name          = "apad",
     .description   = NULL_IF_CONFIG_SMALL("Pad audio with silence."),
     .init          = init,
@@ -158,4 +158,5 @@  AVFilter ff_af_apad = {
     .outputs       = apad_outputs,
     .priv_class    = &apad_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_af_apad,
 };
diff --git a/libavfilter/af_aphaser.c b/libavfilter/af_aphaser.c
index bf46cc8fab..564f4b94d3 100644
--- a/libavfilter/af_aphaser.c
+++ b/libavfilter/af_aphaser.c
@@ -290,7 +290,7 @@  static const AVFilterPad aphaser_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_aphaser = {
+const AVFilter ff_af_aphaser = {
     .name          = "aphaser",
     .description   = NULL_IF_CONFIG_SMALL("Add a phasing effect to the audio."),
     .query_formats = query_formats,
@@ -300,4 +300,5 @@  AVFilter ff_af_aphaser = {
     .inputs        = aphaser_inputs,
     .outputs       = aphaser_outputs,
     .priv_class    = &aphaser_class,
+    .next          = ff_next_af_aphaser,
 };
diff --git a/libavfilter/af_apulsator.c b/libavfilter/af_apulsator.c
index 67711a28ce..87b5c62b98 100644
--- a/libavfilter/af_apulsator.c
+++ b/libavfilter/af_apulsator.c
@@ -246,7 +246,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_apulsator = {
+const AVFilter ff_af_apulsator = {
     .name          = "apulsator",
     .description   = NULL_IF_CONFIG_SMALL("Audio pulsator."),
     .priv_size     = sizeof(AudioPulsatorContext),
@@ -254,4 +254,5 @@  AVFilter ff_af_apulsator = {
     .query_formats = query_formats,
     .inputs        = inputs,
     .outputs       = outputs,
+    .next          = ff_next_af_apulsator,
 };
diff --git a/libavfilter/af_aresample.c b/libavfilter/af_aresample.c
index ef10621c35..6f14acf8c8 100644
--- a/libavfilter/af_aresample.c
+++ b/libavfilter/af_aresample.c
@@ -340,7 +340,7 @@  static const AVFilterPad aresample_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_aresample = {
+const AVFilter ff_af_aresample = {
     .name          = "aresample",
     .description   = NULL_IF_CONFIG_SMALL("Resample audio data."),
     .init_dict     = init_dict,
@@ -350,4 +350,5 @@  AVFilter ff_af_aresample = {
     .priv_class    = &aresample_class,
     .inputs        = aresample_inputs,
     .outputs       = aresample_outputs,
+    .next          = ff_next_af_aresample,
 };
diff --git a/libavfilter/af_asetnsamples.c b/libavfilter/af_asetnsamples.c
index ecb76e64db..71bfc5613f 100644
--- a/libavfilter/af_asetnsamples.c
+++ b/libavfilter/af_asetnsamples.c
@@ -186,7 +186,7 @@  static const AVFilterPad asetnsamples_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_asetnsamples = {
+const AVFilter ff_af_asetnsamples = {
     .name        = "asetnsamples",
     .description = NULL_IF_CONFIG_SMALL("Set the number of samples for each output audio frames."),
     .priv_size   = sizeof(ASNSContext),
@@ -195,4 +195,5 @@  AVFilter ff_af_asetnsamples = {
     .uninit      = uninit,
     .inputs      = asetnsamples_inputs,
     .outputs     = asetnsamples_outputs,
+    .next        = ff_next_af_asetnsamples,
 };
diff --git a/libavfilter/af_asetrate.c b/libavfilter/af_asetrate.c
index 50a5f43749..ebc102bdda 100644
--- a/libavfilter/af_asetrate.c
+++ b/libavfilter/af_asetrate.c
@@ -106,7 +106,7 @@  static const AVFilterPad asetrate_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_asetrate = {
+const AVFilter ff_af_asetrate = {
     .name          = "asetrate",
     .description   = NULL_IF_CONFIG_SMALL("Change the sample rate without "
                                           "altering the data."),
@@ -115,4 +115,5 @@  AVFilter ff_af_asetrate = {
     .inputs        = asetrate_inputs,
     .outputs       = asetrate_outputs,
     .priv_class    = &asetrate_class,
+    .next          = ff_next_af_asetrate,
 };
diff --git a/libavfilter/af_ashowinfo.c b/libavfilter/af_ashowinfo.c
index 9046e8d84a..7a4c18e75a 100644
--- a/libavfilter/af_ashowinfo.c
+++ b/libavfilter/af_ashowinfo.c
@@ -253,11 +253,12 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_ashowinfo = {
+const AVFilter ff_af_ashowinfo = {
     .name        = "ashowinfo",
     .description = NULL_IF_CONFIG_SMALL("Show textual information for each audio frame."),
     .priv_size   = sizeof(AShowInfoContext),
     .uninit      = uninit,
     .inputs      = inputs,
     .outputs     = outputs,
+    .next        = ff_next_af_ashowinfo,
 };
diff --git a/libavfilter/af_astats.c b/libavfilter/af_astats.c
index 2922da9f44..3ffb583205 100644
--- a/libavfilter/af_astats.c
+++ b/libavfilter/af_astats.c
@@ -535,7 +535,7 @@  static const AVFilterPad astats_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_astats = {
+const AVFilter ff_af_astats = {
     .name          = "astats",
     .description   = NULL_IF_CONFIG_SMALL("Show time domain statistics about audio frames."),
     .query_formats = query_formats,
@@ -544,4 +544,5 @@  AVFilter ff_af_astats = {
     .uninit        = uninit,
     .inputs        = astats_inputs,
     .outputs       = astats_outputs,
+    .next          = ff_next_af_astats,
 };
diff --git a/libavfilter/af_atempo.c b/libavfilter/af_atempo.c
index 8b214bccd7..30658872e6 100644
--- a/libavfilter/af_atempo.c
+++ b/libavfilter/af_atempo.c
@@ -1197,7 +1197,7 @@  static const AVFilterPad atempo_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_atempo = {
+const AVFilter ff_af_atempo = {
     .name            = "atempo",
     .description     = NULL_IF_CONFIG_SMALL("Adjust audio tempo."),
     .init            = init,
@@ -1208,4 +1208,5 @@  AVFilter ff_af_atempo = {
     .priv_class      = &atempo_class,
     .inputs          = atempo_inputs,
     .outputs         = atempo_outputs,
+    .next            = ff_next_af_atempo,
 };
diff --git a/libavfilter/af_biquads.c b/libavfilter/af_biquads.c
index d5c3823e64..4ca365bb0b 100644
--- a/libavfilter/af_biquads.c
+++ b/libavfilter/af_biquads.c
@@ -600,7 +600,7 @@  static av_cold int name_##_init(AVFilterContext *ctx) \
     return init(ctx);                                             \
 }                                                                       \
                                                          \
-AVFilter ff_af_##name_ = {                         \
+const AVFilter ff_af_##name_ = {                         \
     .name          = #name_,                             \
     .description   = NULL_IF_CONFIG_SMALL(description_), \
     .priv_size     = sizeof(BiquadsContext),             \
@@ -611,6 +611,7 @@  AVFilter ff_af_##name_ = {                         \
     .outputs       = outputs,                            \
     .priv_class    = &name_##_class,                     \
     .process_command = process_command,                  \
+    .next          = ff_next_af_##name_,                 \
 }
 
 #if CONFIG_EQUALIZER_FILTER
diff --git a/libavfilter/af_bs2b.c b/libavfilter/af_bs2b.c
index c01b983cd3..5e4e4c2eb7 100644
--- a/libavfilter/af_bs2b.c
+++ b/libavfilter/af_bs2b.c
@@ -211,7 +211,7 @@  static const AVFilterPad bs2b_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_bs2b = {
+const AVFilter ff_af_bs2b = {
     .name           = "bs2b",
     .description    = NULL_IF_CONFIG_SMALL("Bauer stereo-to-binaural filter."),
     .query_formats  = query_formats,
@@ -221,4 +221,5 @@  AVFilter ff_af_bs2b = {
     .uninit         = uninit,
     .inputs         = bs2b_inputs,
     .outputs        = bs2b_outputs,
+    .next           = ff_next_af_bs2b,
 };
diff --git a/libavfilter/af_channelmap.c b/libavfilter/af_channelmap.c
index 285d76a3ef..07dc45081e 100644
--- a/libavfilter/af_channelmap.c
+++ b/libavfilter/af_channelmap.c
@@ -408,7 +408,7 @@  static const AVFilterPad avfilter_af_channelmap_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_channelmap = {
+const AVFilter ff_af_channelmap = {
     .name          = "channelmap",
     .description   = NULL_IF_CONFIG_SMALL("Remap audio channels."),
     .init          = channelmap_init,
@@ -417,4 +417,5 @@  AVFilter ff_af_channelmap = {
     .priv_class    = &channelmap_class,
     .inputs        = avfilter_af_channelmap_inputs,
     .outputs       = avfilter_af_channelmap_outputs,
+    .next          = ff_next_af_channelmap,
 };
diff --git a/libavfilter/af_channelsplit.c b/libavfilter/af_channelsplit.c
index 8c6b00fe4f..8b273835f6 100644
--- a/libavfilter/af_channelsplit.c
+++ b/libavfilter/af_channelsplit.c
@@ -141,7 +141,7 @@  static const AVFilterPad avfilter_af_channelsplit_inputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_channelsplit = {
+const AVFilter ff_af_channelsplit = {
     .name           = "channelsplit",
     .description    = NULL_IF_CONFIG_SMALL("Split audio into per-channel streams."),
     .priv_size      = sizeof(ChannelSplitContext),
@@ -151,4 +151,5 @@  AVFilter ff_af_channelsplit = {
     .inputs         = avfilter_af_channelsplit_inputs,
     .outputs        = NULL,
     .flags          = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+    .next           = ff_next_af_channelsplit,
 };
diff --git a/libavfilter/af_chorus.c b/libavfilter/af_chorus.c
index 29c47ab14a..95388acf94 100644
--- a/libavfilter/af_chorus.c
+++ b/libavfilter/af_chorus.c
@@ -369,7 +369,7 @@  static const AVFilterPad chorus_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_chorus = {
+const AVFilter ff_af_chorus = {
     .name          = "chorus",
     .description   = NULL_IF_CONFIG_SMALL("Add a chorus effect to the audio."),
     .query_formats = query_formats,
@@ -379,4 +379,5 @@  AVFilter ff_af_chorus = {
     .uninit        = uninit,
     .inputs        = chorus_inputs,
     .outputs       = chorus_outputs,
+    .next          = ff_next_af_chorus,
 };
diff --git a/libavfilter/af_compand.c b/libavfilter/af_compand.c
index c138f0b1d8..7e6e104c57 100644
--- a/libavfilter/af_compand.c
+++ b/libavfilter/af_compand.c
@@ -584,7 +584,7 @@  static const AVFilterPad compand_outputs[] = {
 };
 
 
-AVFilter ff_af_compand = {
+const AVFilter ff_af_compand = {
     .name           = "compand",
     .description    = NULL_IF_CONFIG_SMALL(
             "Compress or expand audio dynamic range."),
@@ -595,4 +595,5 @@  AVFilter ff_af_compand = {
     .uninit         = uninit,
     .inputs         = compand_inputs,
     .outputs        = compand_outputs,
+    .next           = ff_next_af_compand,
 };
diff --git a/libavfilter/af_compensationdelay.c b/libavfilter/af_compensationdelay.c
index 05285cd297..4d0741b424 100644
--- a/libavfilter/af_compensationdelay.c
+++ b/libavfilter/af_compensationdelay.c
@@ -186,7 +186,7 @@  static const AVFilterPad compensationdelay_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_compensationdelay = {
+const AVFilter ff_af_compensationdelay = {
     .name          = "compensationdelay",
     .description   = NULL_IF_CONFIG_SMALL("Audio Compensation Delay Line."),
     .query_formats = query_formats,
@@ -195,4 +195,5 @@  AVFilter ff_af_compensationdelay = {
     .uninit        = uninit,
     .inputs        = compensationdelay_inputs,
     .outputs       = compensationdelay_outputs,
+    .next          = ff_next_af_compensationdelay,
 };
diff --git a/libavfilter/af_crossfeed.c b/libavfilter/af_crossfeed.c
index a0af280432..631f112a78 100644
--- a/libavfilter/af_crossfeed.c
+++ b/libavfilter/af_crossfeed.c
@@ -158,7 +158,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_crossfeed = {
+const AVFilter ff_af_crossfeed = {
     .name           = "crossfeed",
     .description    = NULL_IF_CONFIG_SMALL("Apply headphone crossfeed filter."),
     .query_formats  = query_formats,
@@ -166,4 +166,5 @@  AVFilter ff_af_crossfeed = {
     .priv_class     = &crossfeed_class,
     .inputs         = inputs,
     .outputs        = outputs,
+    .next           = ff_next_af_crossfeed,
 };
diff --git a/libavfilter/af_crystalizer.c b/libavfilter/af_crystalizer.c
index 5b27e1fb79..19486788bf 100644
--- a/libavfilter/af_crystalizer.c
+++ b/libavfilter/af_crystalizer.c
@@ -245,7 +245,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_crystalizer = {
+const AVFilter ff_af_crystalizer = {
     .name           = "crystalizer",
     .description    = NULL_IF_CONFIG_SMALL("Simple expand audio dynamic range filter."),
     .query_formats  = query_formats,
@@ -254,4 +254,5 @@  AVFilter ff_af_crystalizer = {
     .uninit         = uninit,
     .inputs         = inputs,
     .outputs        = outputs,
+    .next           = ff_next_af_crystalizer,
 };
diff --git a/libavfilter/af_dcshift.c b/libavfilter/af_dcshift.c
index e007efe05e..e77bdc63ea 100644
--- a/libavfilter/af_dcshift.c
+++ b/libavfilter/af_dcshift.c
@@ -160,7 +160,7 @@  static const AVFilterPad dcshift_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_dcshift = {
+const AVFilter ff_af_dcshift = {
     .name           = "dcshift",
     .description    = NULL_IF_CONFIG_SMALL("Apply a DC shift to the audio."),
     .query_formats  = query_formats,
@@ -170,4 +170,5 @@  AVFilter ff_af_dcshift = {
     .inputs         = dcshift_inputs,
     .outputs        = dcshift_outputs,
     .flags          = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next           = ff_next_af_dcshift,
 };
diff --git a/libavfilter/af_dynaudnorm.c b/libavfilter/af_dynaudnorm.c
index 5919304267..ce9bd02435 100644
--- a/libavfilter/af_dynaudnorm.c
+++ b/libavfilter/af_dynaudnorm.c
@@ -742,7 +742,7 @@  static const AVFilterPad avfilter_af_dynaudnorm_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_dynaudnorm = {
+const AVFilter ff_af_dynaudnorm = {
     .name          = "dynaudnorm",
     .description   = NULL_IF_CONFIG_SMALL("Dynamic Audio Normalizer."),
     .query_formats = query_formats,
@@ -752,4 +752,5 @@  AVFilter ff_af_dynaudnorm = {
     .inputs        = avfilter_af_dynaudnorm_inputs,
     .outputs       = avfilter_af_dynaudnorm_outputs,
     .priv_class    = &dynaudnorm_class,
+    .next          = ff_next_af_dynaudnorm,
 };
diff --git a/libavfilter/af_earwax.c b/libavfilter/af_earwax.c
index cdd2b4fc49..d65d15ab8f 100644
--- a/libavfilter/af_earwax.c
+++ b/libavfilter/af_earwax.c
@@ -164,11 +164,12 @@  static const AVFilterPad earwax_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_earwax = {
+const AVFilter ff_af_earwax = {
     .name           = "earwax",
     .description    = NULL_IF_CONFIG_SMALL("Widen the stereo image."),
     .query_formats  = query_formats,
     .priv_size      = sizeof(EarwaxContext),
     .inputs         = earwax_inputs,
     .outputs        = earwax_outputs,
+    .next           = ff_next_af_earwax,
 };
diff --git a/libavfilter/af_extrastereo.c b/libavfilter/af_extrastereo.c
index 13c6f47776..243e77ef19 100644
--- a/libavfilter/af_extrastereo.c
+++ b/libavfilter/af_extrastereo.c
@@ -120,7 +120,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_extrastereo = {
+const AVFilter ff_af_extrastereo = {
     .name           = "extrastereo",
     .description    = NULL_IF_CONFIG_SMALL("Increase difference between stereo audio channels."),
     .query_formats  = query_formats,
@@ -128,4 +128,5 @@  AVFilter ff_af_extrastereo = {
     .priv_class     = &extrastereo_class,
     .inputs         = inputs,
     .outputs        = outputs,
+    .next           = ff_next_af_extrastereo,
 };
diff --git a/libavfilter/af_firequalizer.c b/libavfilter/af_firequalizer.c
index 00ddc87341..71d5ac1b25 100644
--- a/libavfilter/af_firequalizer.c
+++ b/libavfilter/af_firequalizer.c
@@ -967,7 +967,7 @@  static const AVFilterPad firequalizer_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_firequalizer = {
+const AVFilter ff_af_firequalizer = {
     .name               = "firequalizer",
     .description        = NULL_IF_CONFIG_SMALL("Finite Impulse Response Equalizer."),
     .uninit             = uninit,
@@ -977,4 +977,5 @@  AVFilter ff_af_firequalizer = {
     .inputs             = firequalizer_inputs,
     .outputs            = firequalizer_outputs,
     .priv_class         = &firequalizer_class,
+    .next               = ff_next_af_firequalizer,
 };
diff --git a/libavfilter/af_flanger.c b/libavfilter/af_flanger.c
index b7497a12ed..e701c06604 100644
--- a/libavfilter/af_flanger.c
+++ b/libavfilter/af_flanger.c
@@ -233,7 +233,7 @@  static const AVFilterPad flanger_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_flanger = {
+const AVFilter ff_af_flanger = {
     .name          = "flanger",
     .description   = NULL_IF_CONFIG_SMALL("Apply a flanging effect to the audio."),
     .query_formats = query_formats,
@@ -243,4 +243,5 @@  AVFilter ff_af_flanger = {
     .uninit        = uninit,
     .inputs        = flanger_inputs,
     .outputs       = flanger_outputs,
+    .next          = ff_next_af_flanger,
 };
diff --git a/libavfilter/af_haas.c b/libavfilter/af_haas.c
index 0cfc93a7d1..034a5cd896 100644
--- a/libavfilter/af_haas.c
+++ b/libavfilter/af_haas.c
@@ -216,7 +216,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_haas = {
+const AVFilter ff_af_haas = {
     .name           = "haas",
     .description    = NULL_IF_CONFIG_SMALL("Apply Haas Stereo Enhancer."),
     .query_formats  = query_formats,
@@ -225,4 +225,5 @@  AVFilter ff_af_haas = {
     .uninit         = uninit,
     .inputs         = inputs,
     .outputs        = outputs,
+    .next           = ff_next_af_haas,
 };
diff --git a/libavfilter/af_hdcd.c b/libavfilter/af_hdcd.c
index fd17646f09..2839d4cc9d 100644
--- a/libavfilter/af_hdcd.c
+++ b/libavfilter/af_hdcd.c
@@ -1772,7 +1772,7 @@  static const AVFilterPad avfilter_af_hdcd_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_hdcd = {
+const AVFilter ff_af_hdcd = {
     .name          = "hdcd",
     .description   = NULL_IF_CONFIG_SMALL("Apply High Definition Compatible Digital (HDCD) decoding."),
     .priv_size     = sizeof(HDCDContext),
@@ -1782,4 +1782,5 @@  AVFilter ff_af_hdcd = {
     .query_formats = query_formats,
     .inputs        = avfilter_af_hdcd_inputs,
     .outputs       = avfilter_af_hdcd_outputs,
+    .next          = ff_next_af_hdcd,
 };
diff --git a/libavfilter/af_headphone.c b/libavfilter/af_headphone.c
index 2188f7ab24..3853a63262 100644
--- a/libavfilter/af_headphone.c
+++ b/libavfilter/af_headphone.c
@@ -813,7 +813,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_headphone = {
+const AVFilter ff_af_headphone = {
     .name          = "headphone",
     .description   = NULL_IF_CONFIG_SMALL("Apply headphone binaural spatialization with HRTFs in additional streams."),
     .priv_size     = sizeof(HeadphoneContext),
@@ -824,4 +824,5 @@  AVFilter ff_af_headphone = {
     .inputs        = NULL,
     .outputs       = outputs,
     .flags         = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next          = ff_next_af_headphone,
 };
diff --git a/libavfilter/af_join.c b/libavfilter/af_join.c
index cf5131e8dc..52255a7c44 100644
--- a/libavfilter/af_join.c
+++ b/libavfilter/af_join.c
@@ -521,7 +521,7 @@  static const AVFilterPad avfilter_af_join_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_join = {
+const AVFilter ff_af_join = {
     .name           = "join",
     .description    = NULL_IF_CONFIG_SMALL("Join multiple audio streams into "
                                            "multi-channel output."),
@@ -534,4 +534,5 @@  AVFilter ff_af_join = {
     .inputs         = NULL,
     .outputs        = avfilter_af_join_outputs,
     .flags          = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next           = ff_next_af_join,
 };
diff --git a/libavfilter/af_ladspa.c b/libavfilter/af_ladspa.c
index 3be26bc849..cf667f36f1 100644
--- a/libavfilter/af_ladspa.c
+++ b/libavfilter/af_ladspa.c
@@ -731,7 +731,7 @@  static const AVFilterPad ladspa_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_ladspa = {
+const AVFilter ff_af_ladspa = {
     .name          = "ladspa",
     .description   = NULL_IF_CONFIG_SMALL("Apply LADSPA effect."),
     .priv_size     = sizeof(LADSPAContext),
@@ -743,4 +743,5 @@  AVFilter ff_af_ladspa = {
     .inputs        = 0,
     .outputs       = ladspa_outputs,
     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next          = ff_next_af_ladspa,
 };
diff --git a/libavfilter/af_loudnorm.c b/libavfilter/af_loudnorm.c
index a7f11cbe6e..dac4e51d3b 100644
--- a/libavfilter/af_loudnorm.c
+++ b/libavfilter/af_loudnorm.c
@@ -917,7 +917,7 @@  static const AVFilterPad avfilter_af_loudnorm_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_loudnorm = {
+const AVFilter ff_af_loudnorm = {
     .name          = "loudnorm",
     .description   = NULL_IF_CONFIG_SMALL("EBU R128 loudness normalization"),
     .priv_size     = sizeof(LoudNormContext),
@@ -927,4 +927,5 @@  AVFilter ff_af_loudnorm = {
     .uninit        = uninit,
     .inputs        = avfilter_af_loudnorm_inputs,
     .outputs       = avfilter_af_loudnorm_outputs,
+    .next          = ff_next_af_loudnorm,
 };
diff --git a/libavfilter/af_lv2.c b/libavfilter/af_lv2.c
index 8a0a6fd888..fcaf4733c1 100644
--- a/libavfilter/af_lv2.c
+++ b/libavfilter/af_lv2.c
@@ -588,7 +588,7 @@  static const AVFilterPad lv2_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_lv2 = {
+const AVFilter ff_af_lv2 = {
     .name          = "lv2",
     .description   = NULL_IF_CONFIG_SMALL("Apply LV2 effect."),
     .priv_size     = sizeof(LV2Context),
@@ -599,4 +599,5 @@  AVFilter ff_af_lv2 = {
     .inputs        = 0,
     .outputs       = lv2_outputs,
     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next          = ff_next_af_lv2,
 };
diff --git a/libavfilter/af_mcompand.c b/libavfilter/af_mcompand.c
index 02f987a6a8..d45920dbea 100644
--- a/libavfilter/af_mcompand.c
+++ b/libavfilter/af_mcompand.c
@@ -676,7 +676,7 @@  static const AVFilterPad mcompand_outputs[] = {
 };
 
 
-AVFilter ff_af_mcompand = {
+const AVFilter ff_af_mcompand = {
     .name           = "mcompand",
     .description    = NULL_IF_CONFIG_SMALL(
             "Multiband Compress or expand audio dynamic range."),
@@ -686,4 +686,5 @@  AVFilter ff_af_mcompand = {
     .uninit         = uninit,
     .inputs         = mcompand_inputs,
     .outputs        = mcompand_outputs,
+    .next           = ff_next_af_mcompand,
 };
diff --git a/libavfilter/af_pan.c b/libavfilter/af_pan.c
index d8a63a7952..362b89c8c5 100644
--- a/libavfilter/af_pan.c
+++ b/libavfilter/af_pan.c
@@ -432,7 +432,7 @@  static const AVFilterPad pan_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_pan = {
+const AVFilter ff_af_pan = {
     .name          = "pan",
     .description   = NULL_IF_CONFIG_SMALL("Remix channels with coefficients (panning)."),
     .priv_size     = sizeof(PanContext),
@@ -442,4 +442,5 @@  AVFilter ff_af_pan = {
     .query_formats = query_formats,
     .inputs        = pan_inputs,
     .outputs       = pan_outputs,
+    .next          = ff_next_af_pan,
 };
diff --git a/libavfilter/af_replaygain.c b/libavfilter/af_replaygain.c
index 97617346ed..cdd44ebc91 100644
--- a/libavfilter/af_replaygain.c
+++ b/libavfilter/af_replaygain.c
@@ -604,7 +604,7 @@  static const AVFilterPad replaygain_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_replaygain = {
+const AVFilter ff_af_replaygain = {
     .name          = "replaygain",
     .description   = NULL_IF_CONFIG_SMALL("ReplayGain scanner."),
     .query_formats = query_formats,
@@ -612,4 +612,5 @@  AVFilter ff_af_replaygain = {
     .priv_size     = sizeof(ReplayGainContext),
     .inputs        = replaygain_inputs,
     .outputs       = replaygain_outputs,
+    .next          = ff_next_af_replaygain,
 };
diff --git a/libavfilter/af_resample.c b/libavfilter/af_resample.c
index e3c6a20696..4352019c68 100644
--- a/libavfilter/af_resample.c
+++ b/libavfilter/af_resample.c
@@ -344,7 +344,7 @@  static const AVFilterPad avfilter_af_resample_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_resample = {
+const AVFilter ff_af_resample = {
     .name          = "resample",
     .description   = NULL_IF_CONFIG_SMALL("Audio resampling and conversion."),
     .priv_size     = sizeof(ResampleContext),
@@ -354,4 +354,5 @@  AVFilter ff_af_resample = {
     .query_formats = query_formats,
     .inputs        = avfilter_af_resample_inputs,
     .outputs       = avfilter_af_resample_outputs,
+    .next          = ff_next_af_resample,
 };
diff --git a/libavfilter/af_rubberband.c b/libavfilter/af_rubberband.c
index ea6f4ff2c9..5fd8c65556 100644
--- a/libavfilter/af_rubberband.c
+++ b/libavfilter/af_rubberband.c
@@ -258,7 +258,7 @@  static const AVFilterPad rubberband_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_rubberband = {
+const AVFilter ff_af_rubberband = {
     .name          = "rubberband",
     .description   = NULL_IF_CONFIG_SMALL("Apply time-stretching and pitch-shifting."),
     .query_formats = query_formats,
@@ -268,4 +268,5 @@  AVFilter ff_af_rubberband = {
     .inputs        = rubberband_inputs,
     .outputs       = rubberband_outputs,
     .process_command = process_command,
+    .next          = ff_next_af_rubberband,
 };
diff --git a/libavfilter/af_sidechaincompress.c b/libavfilter/af_sidechaincompress.c
index 888049eaf0..063105dc7e 100644
--- a/libavfilter/af_sidechaincompress.c
+++ b/libavfilter/af_sidechaincompress.c
@@ -341,7 +341,7 @@  static const AVFilterPad sidechaincompress_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_sidechaincompress = {
+const AVFilter ff_af_sidechaincompress = {
     .name           = "sidechaincompress",
     .description    = NULL_IF_CONFIG_SMALL("Sidechain compressor."),
     .priv_size      = sizeof(SidechainCompressContext),
@@ -351,6 +351,7 @@  AVFilter ff_af_sidechaincompress = {
     .uninit         = uninit,
     .inputs         = sidechaincompress_inputs,
     .outputs        = sidechaincompress_outputs,
+    .next           = ff_next_af_sidechaincompress,
 };
 #endif  /* CONFIG_SIDECHAINCOMPRESS_FILTER */
 
@@ -436,7 +437,7 @@  static const AVFilterPad acompressor_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_acompressor = {
+const AVFilter ff_af_acompressor = {
     .name           = "acompressor",
     .description    = NULL_IF_CONFIG_SMALL("Audio compressor."),
     .priv_size      = sizeof(SidechainCompressContext),
@@ -444,5 +445,6 @@  AVFilter ff_af_acompressor = {
     .query_formats  = acompressor_query_formats,
     .inputs         = acompressor_inputs,
     .outputs        = acompressor_outputs,
+    .next           = ff_next_af_acompressor,
 };
 #endif  /* CONFIG_ACOMPRESSOR_FILTER */
diff --git a/libavfilter/af_silencedetect.c b/libavfilter/af_silencedetect.c
index b048d63738..404ef08e2e 100644
--- a/libavfilter/af_silencedetect.c
+++ b/libavfilter/af_silencedetect.c
@@ -204,7 +204,7 @@  static const AVFilterPad silencedetect_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_silencedetect = {
+const AVFilter ff_af_silencedetect = {
     .name          = "silencedetect",
     .description   = NULL_IF_CONFIG_SMALL("Detect silence."),
     .priv_size     = sizeof(SilenceDetectContext),
@@ -212,4 +212,5 @@  AVFilter ff_af_silencedetect = {
     .inputs        = silencedetect_inputs,
     .outputs       = silencedetect_outputs,
     .priv_class    = &silencedetect_class,
+    .next          = ff_next_af_silencedetect,
 };
diff --git a/libavfilter/af_silenceremove.c b/libavfilter/af_silenceremove.c
index d826a22e9d..a74f5072d9 100644
--- a/libavfilter/af_silenceremove.c
+++ b/libavfilter/af_silenceremove.c
@@ -542,7 +542,7 @@  static const AVFilterPad silenceremove_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_silenceremove = {
+const AVFilter ff_af_silenceremove = {
     .name          = "silenceremove",
     .description   = NULL_IF_CONFIG_SMALL("Remove silence."),
     .priv_size     = sizeof(SilenceRemoveContext),
@@ -552,4 +552,5 @@  AVFilter ff_af_silenceremove = {
     .query_formats = query_formats,
     .inputs        = silenceremove_inputs,
     .outputs       = silenceremove_outputs,
+    .next          = ff_next_af_silenceremove,
 };
diff --git a/libavfilter/af_sofalizer.c b/libavfilter/af_sofalizer.c
index d9098d7679..567793a393 100644
--- a/libavfilter/af_sofalizer.c
+++ b/libavfilter/af_sofalizer.c
@@ -900,7 +900,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_sofalizer = {
+const AVFilter ff_af_sofalizer = {
     .name          = "sofalizer",
     .description   = NULL_IF_CONFIG_SMALL("SOFAlizer (Spatially Oriented Format for Acoustics)."),
     .priv_size     = sizeof(SOFAlizerContext),
@@ -911,4 +911,5 @@  AVFilter ff_af_sofalizer = {
     .inputs        = inputs,
     .outputs       = outputs,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_af_sofalizer,
 };
diff --git a/libavfilter/af_stereotools.c b/libavfilter/af_stereotools.c
index 7e529783d5..105ec1d4b3 100644
--- a/libavfilter/af_stereotools.c
+++ b/libavfilter/af_stereotools.c
@@ -349,7 +349,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_stereotools = {
+const AVFilter ff_af_stereotools = {
     .name           = "stereotools",
     .description    = NULL_IF_CONFIG_SMALL("Apply various stereo tools."),
     .query_formats  = query_formats,
@@ -358,4 +358,5 @@  AVFilter ff_af_stereotools = {
     .uninit         = uninit,
     .inputs         = inputs,
     .outputs        = outputs,
+    .next           = ff_next_af_stereotools,
 };
diff --git a/libavfilter/af_stereowiden.c b/libavfilter/af_stereowiden.c
index ef16fcec73..3888641f54 100644
--- a/libavfilter/af_stereowiden.c
+++ b/libavfilter/af_stereowiden.c
@@ -150,7 +150,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_stereowiden = {
+const AVFilter ff_af_stereowiden = {
     .name           = "stereowiden",
     .description    = NULL_IF_CONFIG_SMALL("Apply stereo widening effect."),
     .query_formats  = query_formats,
@@ -159,4 +159,5 @@  AVFilter ff_af_stereowiden = {
     .uninit         = uninit,
     .inputs         = inputs,
     .outputs        = outputs,
+    .next           = ff_next_af_stereowiden,
 };
diff --git a/libavfilter/af_superequalizer.c b/libavfilter/af_superequalizer.c
index f38cca5c8b..452e7d98c3 100644
--- a/libavfilter/af_superequalizer.c
+++ b/libavfilter/af_superequalizer.c
@@ -356,7 +356,7 @@  static const AVOption superequalizer_options[] = {
 
 AVFILTER_DEFINE_CLASS(superequalizer);
 
-AVFilter ff_af_superequalizer = {
+const AVFilter ff_af_superequalizer = {
     .name          = "superequalizer",
     .description   = NULL_IF_CONFIG_SMALL("Apply 18 band equalization filter."),
     .priv_size     = sizeof(SuperEqualizerContext),
@@ -366,4 +366,5 @@  AVFilter ff_af_superequalizer = {
     .uninit        = uninit,
     .inputs        = superequalizer_inputs,
     .outputs       = superequalizer_outputs,
+    .next          = ff_next_af_superequalizer,
 };
diff --git a/libavfilter/af_surround.c b/libavfilter/af_surround.c
index 460e18cd5a..fc6e49a53a 100644
--- a/libavfilter/af_surround.c
+++ b/libavfilter/af_surround.c
@@ -1452,7 +1452,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_surround = {
+const AVFilter ff_af_surround = {
     .name           = "surround",
     .description    = NULL_IF_CONFIG_SMALL("Apply audio surround upmix filter."),
     .query_formats  = query_formats,
@@ -1463,4 +1463,5 @@  AVFilter ff_af_surround = {
     .inputs         = inputs,
     .outputs        = outputs,
     .flags          = AVFILTER_FLAG_SLICE_THREADS,
+    .next           = ff_next_af_surround,
 };
diff --git a/libavfilter/af_tremolo.c b/libavfilter/af_tremolo.c
index 8cbc79892d..3ba237ff6f 100644
--- a/libavfilter/af_tremolo.c
+++ b/libavfilter/af_tremolo.c
@@ -158,7 +158,7 @@  static const AVFilterPad avfilter_af_tremolo_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_tremolo = {
+const AVFilter ff_af_tremolo = {
     .name          = "tremolo",
     .description   = NULL_IF_CONFIG_SMALL("Apply tremolo effect."),
     .priv_size     = sizeof(TremoloContext),
@@ -167,4 +167,5 @@  AVFilter ff_af_tremolo = {
     .query_formats = query_formats,
     .inputs        = avfilter_af_tremolo_inputs,
     .outputs       = avfilter_af_tremolo_outputs,
+    .next          = ff_next_af_tremolo,
 };
diff --git a/libavfilter/af_vibrato.c b/libavfilter/af_vibrato.c
index 22bbab6239..472f67ab52 100644
--- a/libavfilter/af_vibrato.c
+++ b/libavfilter/af_vibrato.c
@@ -198,7 +198,7 @@  static const AVFilterPad avfilter_af_vibrato_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_vibrato = {
+const AVFilter ff_af_vibrato = {
     .name          = "vibrato",
     .description   = NULL_IF_CONFIG_SMALL("Apply vibrato effect."),
     .priv_size     = sizeof(VibratoContext),
@@ -207,4 +207,5 @@  AVFilter ff_af_vibrato = {
     .query_formats = query_formats,
     .inputs        = avfilter_af_vibrato_inputs,
     .outputs       = avfilter_af_vibrato_outputs,
+    .next          = ff_next_af_vibrato,
 };
diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c
index b106ed8cf4..c013230166 100644
--- a/libavfilter/af_volume.c
+++ b/libavfilter/af_volume.c
@@ -480,7 +480,7 @@  static const AVFilterPad avfilter_af_volume_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_volume = {
+const AVFilter ff_af_volume = {
     .name           = "volume",
     .description    = NULL_IF_CONFIG_SMALL("Change input volume."),
     .query_formats  = query_formats,
@@ -492,4 +492,5 @@  AVFilter ff_af_volume = {
     .outputs        = avfilter_af_volume_outputs,
     .flags          = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
     .process_command = process_command,
+    .next           = ff_next_af_volume,
 };
diff --git a/libavfilter/af_volumedetect.c b/libavfilter/af_volumedetect.c
index 43c1ebf02c..ca55ebd554 100644
--- a/libavfilter/af_volumedetect.c
+++ b/libavfilter/af_volumedetect.c
@@ -155,7 +155,7 @@  static const AVFilterPad volumedetect_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_volumedetect = {
+const AVFilter ff_af_volumedetect = {
     .name          = "volumedetect",
     .description   = NULL_IF_CONFIG_SMALL("Detect audio volume."),
     .priv_size     = sizeof(VolDetectContext),
@@ -163,4 +163,5 @@  AVFilter ff_af_volumedetect = {
     .uninit        = uninit,
     .inputs        = volumedetect_inputs,
     .outputs       = volumedetect_outputs,
+    .next          = ff_next_af_volumedetect,
 };
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 9adb1090b7..8bab79ff96 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -19,412 +19,59 @@ 
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/avassert.h"
 #include "libavutil/thread.h"
 #include "avfilter.h"
 #include "config.h"
 
+#include "libavfilter/filter_list.h"
+#include "libavfilter/filter_list.c"
 
-#define REGISTER_FILTER(X, x, y)                                        \
-    {                                                                   \
-        extern AVFilter ff_##y##_##x;                                   \
-        if (CONFIG_##X##_FILTER)                                        \
-            avfilter_register(&ff_##y##_##x);                           \
-    }
-
-#define REGISTER_FILTER_UNCONDITIONAL(x)                                \
-    {                                                                   \
-        extern AVFilter ff_##x;                                         \
-        avfilter_register(&ff_##x);                                     \
-    }
 
-static void register_all(void)
+#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1
+static void check_validity(void)
 {
-    REGISTER_FILTER(ABENCH,         abench,         af);
-    REGISTER_FILTER(ACOMPRESSOR,    acompressor,    af);
-    REGISTER_FILTER(ACONTRAST,      acontrast,      af);
-    REGISTER_FILTER(ACOPY,          acopy,          af);
-    REGISTER_FILTER(ACROSSFADE,     acrossfade,     af);
-    REGISTER_FILTER(ACRUSHER,       acrusher,       af);
-    REGISTER_FILTER(ADELAY,         adelay,         af);
-    REGISTER_FILTER(AECHO,          aecho,          af);
-    REGISTER_FILTER(AEMPHASIS,      aemphasis,      af);
-    REGISTER_FILTER(AEVAL,          aeval,          af);
-    REGISTER_FILTER(AFADE,          afade,          af);
-    REGISTER_FILTER(AFFTFILT,       afftfilt,       af);
-    REGISTER_FILTER(AFIR,           afir,           af);
-    REGISTER_FILTER(AFORMAT,        aformat,        af);
-    REGISTER_FILTER(AGATE,          agate,          af);
-    REGISTER_FILTER(AIIR,           aiir,           af);
-    REGISTER_FILTER(AINTERLEAVE,    ainterleave,    af);
-    REGISTER_FILTER(ALIMITER,       alimiter,       af);
-    REGISTER_FILTER(ALLPASS,        allpass,        af);
-    REGISTER_FILTER(ALOOP,          aloop,          af);
-    REGISTER_FILTER(AMERGE,         amerge,         af);
-    REGISTER_FILTER(AMETADATA,      ametadata,      af);
-    REGISTER_FILTER(AMIX,           amix,           af);
-    REGISTER_FILTER(ANEQUALIZER,    anequalizer,    af);
-    REGISTER_FILTER(ANULL,          anull,          af);
-    REGISTER_FILTER(APAD,           apad,           af);
-    REGISTER_FILTER(APERMS,         aperms,         af);
-    REGISTER_FILTER(APHASER,        aphaser,        af);
-    REGISTER_FILTER(APULSATOR,      apulsator,      af);
-    REGISTER_FILTER(AREALTIME,      arealtime,      af);
-    REGISTER_FILTER(ARESAMPLE,      aresample,      af);
-    REGISTER_FILTER(AREVERSE,       areverse,       af);
-    REGISTER_FILTER(ASELECT,        aselect,        af);
-    REGISTER_FILTER(ASENDCMD,       asendcmd,       af);
-    REGISTER_FILTER(ASETNSAMPLES,   asetnsamples,   af);
-    REGISTER_FILTER(ASETPTS,        asetpts,        af);
-    REGISTER_FILTER(ASETRATE,       asetrate,       af);
-    REGISTER_FILTER(ASETTB,         asettb,         af);
-    REGISTER_FILTER(ASHOWINFO,      ashowinfo,      af);
-    REGISTER_FILTER(ASIDEDATA,      asidedata,      af);
-    REGISTER_FILTER(ASPLIT,         asplit,         af);
-    REGISTER_FILTER(ASTATS,         astats,         af);
-    REGISTER_FILTER(ASTREAMSELECT,  astreamselect,  af);
-    REGISTER_FILTER(ATEMPO,         atempo,         af);
-    REGISTER_FILTER(ATRIM,          atrim,          af);
-    REGISTER_FILTER(AZMQ,           azmq,           af);
-    REGISTER_FILTER(BANDPASS,       bandpass,       af);
-    REGISTER_FILTER(BANDREJECT,     bandreject,     af);
-    REGISTER_FILTER(BASS,           bass,           af);
-    REGISTER_FILTER(BIQUAD,         biquad,         af);
-    REGISTER_FILTER(BS2B,           bs2b,           af);
-    REGISTER_FILTER(CHANNELMAP,     channelmap,     af);
-    REGISTER_FILTER(CHANNELSPLIT,   channelsplit,   af);
-    REGISTER_FILTER(CHORUS,         chorus,         af);
-    REGISTER_FILTER(COMPAND,        compand,        af);
-    REGISTER_FILTER(COMPENSATIONDELAY, compensationdelay, af);
-    REGISTER_FILTER(CROSSFEED,      crossfeed,      af);
-    REGISTER_FILTER(CRYSTALIZER,    crystalizer,    af);
-    REGISTER_FILTER(DCSHIFT,        dcshift,        af);
-    REGISTER_FILTER(DYNAUDNORM,     dynaudnorm,     af);
-    REGISTER_FILTER(EARWAX,         earwax,         af);
-    REGISTER_FILTER(EBUR128,        ebur128,        af);
-    REGISTER_FILTER(EQUALIZER,      equalizer,      af);
-    REGISTER_FILTER(EXTRASTEREO,    extrastereo,    af);
-    REGISTER_FILTER(FIREQUALIZER,   firequalizer,   af);
-    REGISTER_FILTER(FLANGER,        flanger,        af);
-    REGISTER_FILTER(HAAS,           haas,           af);
-    REGISTER_FILTER(HDCD,           hdcd,           af);
-    REGISTER_FILTER(HEADPHONE,      headphone,      af);
-    REGISTER_FILTER(HIGHPASS,       highpass,       af);
-    REGISTER_FILTER(JOIN,           join,           af);
-    REGISTER_FILTER(LADSPA,         ladspa,         af);
-    REGISTER_FILTER(LOUDNORM,       loudnorm,       af);
-    REGISTER_FILTER(LOWPASS,        lowpass,        af);
-    REGISTER_FILTER(LV2,            lv2,            af);
-    REGISTER_FILTER(MCOMPAND,       mcompand,       af);
-    REGISTER_FILTER(PAN,            pan,            af);
-    REGISTER_FILTER(REPLAYGAIN,     replaygain,     af);
-    REGISTER_FILTER(RESAMPLE,       resample,       af);
-    REGISTER_FILTER(RUBBERBAND,     rubberband,     af);
-    REGISTER_FILTER(SIDECHAINCOMPRESS, sidechaincompress, af);
-    REGISTER_FILTER(SIDECHAINGATE,  sidechaingate,  af);
-    REGISTER_FILTER(SILENCEDETECT,  silencedetect,  af);
-    REGISTER_FILTER(SILENCEREMOVE,  silenceremove,  af);
-    REGISTER_FILTER(SOFALIZER,      sofalizer,      af);
-    REGISTER_FILTER(STEREOTOOLS,    stereotools,    af);
-    REGISTER_FILTER(STEREOWIDEN,    stereowiden,    af);
-    REGISTER_FILTER(SUPEREQUALIZER, superequalizer, af);
-    REGISTER_FILTER(SURROUND,       surround,       af);
-    REGISTER_FILTER(TREBLE,         treble,         af);
-    REGISTER_FILTER(TREMOLO,        tremolo,        af);
-    REGISTER_FILTER(VIBRATO,        vibrato,        af);
-    REGISTER_FILTER(VOLUME,         volume,         af);
-    REGISTER_FILTER(VOLUMEDETECT,   volumedetect,   af);
-
-    REGISTER_FILTER(AEVALSRC,       aevalsrc,       asrc);
-    REGISTER_FILTER(ANOISESRC,      anoisesrc,      asrc);
-    REGISTER_FILTER(ANULLSRC,       anullsrc,       asrc);
-    REGISTER_FILTER(FLITE,          flite,          asrc);
-    REGISTER_FILTER(HILBERT,        hilbert,        asrc);
-    REGISTER_FILTER(SINE,           sine,           asrc);
-
-    REGISTER_FILTER(ANULLSINK,      anullsink,      asink);
-
-    REGISTER_FILTER(ALPHAEXTRACT,   alphaextract,   vf);
-    REGISTER_FILTER(ALPHAMERGE,     alphamerge,     vf);
-    REGISTER_FILTER(ASS,            ass,            vf);
-    REGISTER_FILTER(ATADENOISE,     atadenoise,     vf);
-    REGISTER_FILTER(AVGBLUR,        avgblur,        vf);
-    REGISTER_FILTER(BBOX,           bbox,           vf);
-    REGISTER_FILTER(BENCH,          bench,          vf);
-    REGISTER_FILTER(BITPLANENOISE,  bitplanenoise,  vf);
-    REGISTER_FILTER(BLACKDETECT,    blackdetect,    vf);
-    REGISTER_FILTER(BLACKFRAME,     blackframe,     vf);
-    REGISTER_FILTER(BLEND,          blend,          vf);
-    REGISTER_FILTER(BOXBLUR,        boxblur,        vf);
-    REGISTER_FILTER(BWDIF,          bwdif,          vf);
-    REGISTER_FILTER(CHROMAKEY,      chromakey,      vf);
-    REGISTER_FILTER(CIESCOPE,       ciescope,       vf);
-    REGISTER_FILTER(CODECVIEW,      codecview,      vf);
-    REGISTER_FILTER(COLORBALANCE,   colorbalance,   vf);
-    REGISTER_FILTER(COLORCHANNELMIXER, colorchannelmixer, vf);
-    REGISTER_FILTER(COLORKEY,       colorkey,       vf);
-    REGISTER_FILTER(COLORLEVELS,    colorlevels,    vf);
-    REGISTER_FILTER(COLORMATRIX,    colormatrix,    vf);
-    REGISTER_FILTER(COLORSPACE,     colorspace,     vf);
-    REGISTER_FILTER(CONVOLUTION,    convolution,    vf);
-    REGISTER_FILTER(CONVOLVE,       convolve,       vf);
-    REGISTER_FILTER(COPY,           copy,           vf);
-    REGISTER_FILTER(COREIMAGE,      coreimage,      vf);
-    REGISTER_FILTER(COVER_RECT,     cover_rect,     vf);
-    REGISTER_FILTER(CROP,           crop,           vf);
-    REGISTER_FILTER(CROPDETECT,     cropdetect,     vf);
-    REGISTER_FILTER(CURVES,         curves,         vf);
-    REGISTER_FILTER(DATASCOPE,      datascope,      vf);
-    REGISTER_FILTER(DCTDNOIZ,       dctdnoiz,       vf);
-    REGISTER_FILTER(DEBAND,         deband,         vf);
-    REGISTER_FILTER(DECIMATE,       decimate,       vf);
-    REGISTER_FILTER(DECONVOLVE,     deconvolve,     vf);
-    REGISTER_FILTER(DEFLATE,        deflate,        vf);
-    REGISTER_FILTER(DEFLICKER,      deflicker,      vf);
-    REGISTER_FILTER(DEINTERLACE_QSV,deinterlace_qsv,vf);
-    REGISTER_FILTER(DEINTERLACE_VAAPI, deinterlace_vaapi, vf);
-    REGISTER_FILTER(DEJUDDER,       dejudder,       vf);
-    REGISTER_FILTER(DELOGO,         delogo,         vf);
-    REGISTER_FILTER(DENOISE_VAAPI,  denoise_vaapi,  vf);
-    REGISTER_FILTER(DESHAKE,        deshake,        vf);
-    REGISTER_FILTER(DESPILL,        despill,        vf);
-    REGISTER_FILTER(DETELECINE,     detelecine,     vf);
-    REGISTER_FILTER(DILATION,       dilation,       vf);
-    REGISTER_FILTER(DISPLACE,       displace,       vf);
-    REGISTER_FILTER(DOUBLEWEAVE,    doubleweave,    vf);
-    REGISTER_FILTER(DRAWBOX,        drawbox,        vf);
-    REGISTER_FILTER(DRAWGRAPH,      drawgraph,      vf);
-    REGISTER_FILTER(DRAWGRID,       drawgrid,       vf);
-    REGISTER_FILTER(DRAWTEXT,       drawtext,       vf);
-    REGISTER_FILTER(EDGEDETECT,     edgedetect,     vf);
-    REGISTER_FILTER(ELBG,           elbg,           vf);
-    REGISTER_FILTER(ENTROPY,        entropy,        vf);
-    REGISTER_FILTER(EQ,             eq,             vf);
-    REGISTER_FILTER(EROSION,        erosion,        vf);
-    REGISTER_FILTER(EXTRACTPLANES,  extractplanes,  vf);
-    REGISTER_FILTER(FADE,           fade,           vf);
-    REGISTER_FILTER(FFTFILT,        fftfilt,        vf);
-    REGISTER_FILTER(FIELD,          field,          vf);
-    REGISTER_FILTER(FIELDHINT,      fieldhint,      vf);
-    REGISTER_FILTER(FIELDMATCH,     fieldmatch,     vf);
-    REGISTER_FILTER(FIELDORDER,     fieldorder,     vf);
-    REGISTER_FILTER(FILLBORDERS,    fillborders,    vf);
-    REGISTER_FILTER(FIND_RECT,      find_rect,      vf);
-    REGISTER_FILTER(FLOODFILL,      floodfill,      vf);
-    REGISTER_FILTER(FORMAT,         format,         vf);
-    REGISTER_FILTER(FPS,            fps,            vf);
-    REGISTER_FILTER(FRAMEPACK,      framepack,      vf);
-    REGISTER_FILTER(FRAMERATE,      framerate,      vf);
-    REGISTER_FILTER(FRAMESTEP,      framestep,      vf);
-    REGISTER_FILTER(FREI0R,         frei0r,         vf);
-    REGISTER_FILTER(FSPP,           fspp,           vf);
-    REGISTER_FILTER(GBLUR,          gblur,          vf);
-    REGISTER_FILTER(GEQ,            geq,            vf);
-    REGISTER_FILTER(GRADFUN,        gradfun,        vf);
-    REGISTER_FILTER(HALDCLUT,       haldclut,       vf);
-    REGISTER_FILTER(HFLIP,          hflip,          vf);
-    REGISTER_FILTER(HISTEQ,         histeq,         vf);
-    REGISTER_FILTER(HISTOGRAM,      histogram,      vf);
-    REGISTER_FILTER(HQDN3D,         hqdn3d,         vf);
-    REGISTER_FILTER(HQX,            hqx,            vf);
-    REGISTER_FILTER(HSTACK,         hstack,         vf);
-    REGISTER_FILTER(HUE,            hue,            vf);
-    REGISTER_FILTER(HWDOWNLOAD,     hwdownload,     vf);
-    REGISTER_FILTER(HWMAP,          hwmap,          vf);
-    REGISTER_FILTER(HWUPLOAD,       hwupload,       vf);
-    REGISTER_FILTER(HWUPLOAD_CUDA,  hwupload_cuda,  vf);
-    REGISTER_FILTER(HYSTERESIS,     hysteresis,     vf);
-    REGISTER_FILTER(IDET,           idet,           vf);
-    REGISTER_FILTER(IL,             il,             vf);
-    REGISTER_FILTER(INFLATE,        inflate,        vf);
-    REGISTER_FILTER(INTERLACE,      interlace,      vf);
-    REGISTER_FILTER(INTERLEAVE,     interleave,     vf);
-    REGISTER_FILTER(KERNDEINT,      kerndeint,      vf);
-    REGISTER_FILTER(LENSCORRECTION, lenscorrection, vf);
-    REGISTER_FILTER(LIBVMAF,        libvmaf,        vf);
-    REGISTER_FILTER(LIMITER,        limiter,        vf);
-    REGISTER_FILTER(LOOP,           loop,           vf);
-    REGISTER_FILTER(LUMAKEY,        lumakey,        vf);
-    REGISTER_FILTER(LUT,            lut,            vf);
-    REGISTER_FILTER(LUT2,           lut2,           vf);
-    REGISTER_FILTER(LUT3D,          lut3d,          vf);
-    REGISTER_FILTER(LUTRGB,         lutrgb,         vf);
-    REGISTER_FILTER(LUTYUV,         lutyuv,         vf);
-    REGISTER_FILTER(MASKEDCLAMP,    maskedclamp,    vf);
-    REGISTER_FILTER(MASKEDMERGE,    maskedmerge,    vf);
-    REGISTER_FILTER(MCDEINT,        mcdeint,        vf);
-    REGISTER_FILTER(MERGEPLANES,    mergeplanes,    vf);
-    REGISTER_FILTER(MESTIMATE,      mestimate,      vf);
-    REGISTER_FILTER(METADATA,       metadata,       vf);
-    REGISTER_FILTER(MIDEQUALIZER,   midequalizer,   vf);
-    REGISTER_FILTER(MINTERPOLATE,   minterpolate,   vf);
-    REGISTER_FILTER(MIX,            mix,            vf);
-    REGISTER_FILTER(MPDECIMATE,     mpdecimate,     vf);
-    REGISTER_FILTER(NEGATE,         negate,         vf);
-    REGISTER_FILTER(NLMEANS,        nlmeans,        vf);
-    REGISTER_FILTER(NNEDI,          nnedi,          vf);
-    REGISTER_FILTER(NOFORMAT,       noformat,       vf);
-    REGISTER_FILTER(NOISE,          noise,          vf);
-    REGISTER_FILTER(NORMALIZE,      normalize,      vf);
-    REGISTER_FILTER(NULL,           null,           vf);
-    REGISTER_FILTER(OCR,            ocr,            vf);
-    REGISTER_FILTER(OCV,            ocv,            vf);
-    REGISTER_FILTER(OSCILLOSCOPE,   oscilloscope,   vf);
-    REGISTER_FILTER(OVERLAY,        overlay,        vf);
-    REGISTER_FILTER(OVERLAY_OPENCL, overlay_opencl, vf);
-    REGISTER_FILTER(OVERLAY_QSV,    overlay_qsv,    vf);
-    REGISTER_FILTER(OWDENOISE,      owdenoise,      vf);
-    REGISTER_FILTER(PAD,            pad,            vf);
-    REGISTER_FILTER(PALETTEGEN,     palettegen,     vf);
-    REGISTER_FILTER(PALETTEUSE,     paletteuse,     vf);
-    REGISTER_FILTER(PERMS,          perms,          vf);
-    REGISTER_FILTER(PERSPECTIVE,    perspective,    vf);
-    REGISTER_FILTER(PHASE,          phase,          vf);
-    REGISTER_FILTER(PIXDESCTEST,    pixdesctest,    vf);
-    REGISTER_FILTER(PIXSCOPE,       pixscope,       vf);
-    REGISTER_FILTER(PP,             pp,             vf);
-    REGISTER_FILTER(PP7,            pp7,            vf);
-    REGISTER_FILTER(PREMULTIPLY,    premultiply,    vf);
-    REGISTER_FILTER(PREWITT,        prewitt,        vf);
-    REGISTER_FILTER(PROCAMP_VAAPI,  procamp_vaapi,  vf);
-    REGISTER_FILTER(PROGRAM_OPENCL, program_opencl, vf);
-    REGISTER_FILTER(PSEUDOCOLOR,    pseudocolor,    vf);
-    REGISTER_FILTER(PSNR,           psnr,           vf);
-    REGISTER_FILTER(PULLUP,         pullup,         vf);
-    REGISTER_FILTER(QP,             qp,             vf);
-    REGISTER_FILTER(RANDOM,         random,         vf);
-    REGISTER_FILTER(READEIA608,     readeia608,     vf);
-    REGISTER_FILTER(READVITC,       readvitc,       vf);
-    REGISTER_FILTER(REALTIME,       realtime,       vf);
-    REGISTER_FILTER(REMAP,          remap,          vf);
-    REGISTER_FILTER(REMOVEGRAIN,    removegrain,    vf);
-    REGISTER_FILTER(REMOVELOGO,     removelogo,     vf);
-    REGISTER_FILTER(REPEATFIELDS,   repeatfields,   vf);
-    REGISTER_FILTER(REVERSE,        reverse,        vf);
-    REGISTER_FILTER(ROBERTS,        roberts,        vf);
-    REGISTER_FILTER(ROTATE,         rotate,         vf);
-    REGISTER_FILTER(SAB,            sab,            vf);
-    REGISTER_FILTER(SCALE,          scale,          vf);
-    REGISTER_FILTER(SCALE_CUDA,     scale_cuda,     vf);
-    REGISTER_FILTER(SCALE_NPP,      scale_npp,      vf);
-    REGISTER_FILTER(SCALE_QSV,      scale_qsv,      vf);
-    REGISTER_FILTER(SCALE_VAAPI,    scale_vaapi,    vf);
-    REGISTER_FILTER(SCALE2REF,      scale2ref,      vf);
-    REGISTER_FILTER(SELECT,         select,         vf);
-    REGISTER_FILTER(SELECTIVECOLOR, selectivecolor, vf);
-    REGISTER_FILTER(SENDCMD,        sendcmd,        vf);
-    REGISTER_FILTER(SEPARATEFIELDS, separatefields, vf);
-    REGISTER_FILTER(SETDAR,         setdar,         vf);
-    REGISTER_FILTER(SETFIELD,       setfield,       vf);
-    REGISTER_FILTER(SETPTS,         setpts,         vf);
-    REGISTER_FILTER(SETRANGE,       setrange,       vf);
-    REGISTER_FILTER(SETSAR,         setsar,         vf);
-    REGISTER_FILTER(SETTB,          settb,          vf);
-    REGISTER_FILTER(SHARPNESS_VAAPI, sharpness_vaapi, vf);
-    REGISTER_FILTER(SHOWINFO,       showinfo,       vf);
-    REGISTER_FILTER(SHOWPALETTE,    showpalette,    vf);
-    REGISTER_FILTER(SHUFFLEFRAMES,  shuffleframes,  vf);
-    REGISTER_FILTER(SHUFFLEPLANES,  shuffleplanes,  vf);
-    REGISTER_FILTER(SIDEDATA,       sidedata,       vf);
-    REGISTER_FILTER(SIGNALSTATS,    signalstats,    vf);
-    REGISTER_FILTER(SIGNATURE,      signature,      vf);
-    REGISTER_FILTER(SMARTBLUR,      smartblur,      vf);
-    REGISTER_FILTER(SOBEL,          sobel,          vf);
-    REGISTER_FILTER(SPLIT,          split,          vf);
-    REGISTER_FILTER(SPP,            spp,            vf);
-    REGISTER_FILTER(SSIM,           ssim,           vf);
-    REGISTER_FILTER(STEREO3D,       stereo3d,       vf);
-    REGISTER_FILTER(STREAMSELECT,   streamselect,   vf);
-    REGISTER_FILTER(SUBTITLES,      subtitles,      vf);
-    REGISTER_FILTER(SUPER2XSAI,     super2xsai,     vf);
-    REGISTER_FILTER(SWAPRECT,       swaprect,       vf);
-    REGISTER_FILTER(SWAPUV,         swapuv,         vf);
-    REGISTER_FILTER(TBLEND,         tblend,         vf);
-    REGISTER_FILTER(TELECINE,       telecine,       vf);
-    REGISTER_FILTER(THRESHOLD,      threshold,      vf);
-    REGISTER_FILTER(THUMBNAIL,      thumbnail,      vf);
-    REGISTER_FILTER(THUMBNAIL_CUDA, thumbnail_cuda, vf);
-    REGISTER_FILTER(TILE,           tile,           vf);
-    REGISTER_FILTER(TINTERLACE,     tinterlace,     vf);
-    REGISTER_FILTER(TLUT2,          tlut2,          vf);
-    REGISTER_FILTER(TONEMAP,        tonemap,        vf);
-    REGISTER_FILTER(TRANSPOSE,      transpose,      vf);
-    REGISTER_FILTER(TRIM,           trim,           vf);
-    REGISTER_FILTER(UNPREMULTIPLY,  unpremultiply,  vf);
-    REGISTER_FILTER(UNSHARP,        unsharp,        vf);
-    REGISTER_FILTER(UNSHARP_OPENCL, unsharp_opencl, vf);
-    REGISTER_FILTER(USPP,           uspp,           vf);
-    REGISTER_FILTER(VAGUEDENOISER,  vaguedenoiser,  vf);
-    REGISTER_FILTER(VECTORSCOPE,    vectorscope,    vf);
-    REGISTER_FILTER(VFLIP,          vflip,          vf);
-    REGISTER_FILTER(VIDSTABDETECT,  vidstabdetect,  vf);
-    REGISTER_FILTER(VIDSTABTRANSFORM, vidstabtransform, vf);
-    REGISTER_FILTER(VIGNETTE,       vignette,       vf);
-    REGISTER_FILTER(VMAFMOTION,     vmafmotion,     vf);
-    REGISTER_FILTER(VPP_QSV,        vpp_qsv,        vf);
-    REGISTER_FILTER(VSTACK,         vstack,         vf);
-    REGISTER_FILTER(W3FDIF,         w3fdif,         vf);
-    REGISTER_FILTER(WAVEFORM,       waveform,       vf);
-    REGISTER_FILTER(WEAVE,          weave,          vf);
-    REGISTER_FILTER(XBR,            xbr,            vf);
-    REGISTER_FILTER(YADIF,          yadif,          vf);
-    REGISTER_FILTER(ZMQ,            zmq,            vf);
-    REGISTER_FILTER(ZOOMPAN,        zoompan,        vf);
-    REGISTER_FILTER(ZSCALE,         zscale,         vf);
-
-    REGISTER_FILTER(ALLRGB,         allrgb,         vsrc);
-    REGISTER_FILTER(ALLYUV,         allyuv,         vsrc);
-    REGISTER_FILTER(CELLAUTO,       cellauto,       vsrc);
-    REGISTER_FILTER(COLOR,          color,          vsrc);
-    REGISTER_FILTER(COREIMAGESRC,   coreimagesrc,   vsrc);
-    REGISTER_FILTER(FREI0R,         frei0r_src,     vsrc);
-    REGISTER_FILTER(HALDCLUTSRC,    haldclutsrc,    vsrc);
-    REGISTER_FILTER(LIFE,           life,           vsrc);
-    REGISTER_FILTER(MANDELBROT,     mandelbrot,     vsrc);
-    REGISTER_FILTER(MPTESTSRC,      mptestsrc,      vsrc);
-    REGISTER_FILTER(NULLSRC,        nullsrc,        vsrc);
-    REGISTER_FILTER(OPENCLSRC,      openclsrc,      vsrc);
-    REGISTER_FILTER(RGBTESTSRC,     rgbtestsrc,     vsrc);
-    REGISTER_FILTER(SMPTEBARS,      smptebars,      vsrc);
-    REGISTER_FILTER(SMPTEHDBARS,    smptehdbars,    vsrc);
-    REGISTER_FILTER(TESTSRC,        testsrc,        vsrc);
-    REGISTER_FILTER(TESTSRC2,       testsrc2,       vsrc);
-    REGISTER_FILTER(YUVTESTSRC,     yuvtestsrc,     vsrc);
+    int k;
+    for (k = 0; k < FF_ARRAY_ELEMS(filter_list) - 2; k++) {
+        av_assert2(filter_list[k]->next == filter_list[k+1] ||
+                   (av_log(NULL, AV_LOG_FATAL, "%s filter: invalid next pointer.\n", filter_list[k]->name),0));
+        av_assert2(strcmp(filter_list[k]->name, filter_list[k+1]->name) < 0 ||
+                   (av_log(NULL, AV_LOG_FATAL, "%s filter: unsorted with %s.\n", filter_list[k]->name, filter_list[k+1]->name),0));
+    }
+    av_assert2(!filter_list[k]->next);
+    av_assert2(!filter_list[k+1]);
+}
 
-    REGISTER_FILTER(NULLSINK,       nullsink,       vsink);
+static AVOnce check_validity_once = AV_ONCE_INIT;
+#define CHECK_VALIDITY() ff_thread_once(&check_validity_once, check_validity)
+#else
+#define CHECK_VALIDITY() ((void)0)
+#endif
 
-    /* multimedia filters */
-    REGISTER_FILTER(ABITSCOPE,      abitscope,      avf);
-    REGISTER_FILTER(ADRAWGRAPH,     adrawgraph,     avf);
-    REGISTER_FILTER(AHISTOGRAM,     ahistogram,     avf);
-    REGISTER_FILTER(APHASEMETER,    aphasemeter,    avf);
-    REGISTER_FILTER(AVECTORSCOPE,   avectorscope,   avf);
-    REGISTER_FILTER(CONCAT,         concat,         avf);
-    REGISTER_FILTER(SHOWCQT,        showcqt,        avf);
-    REGISTER_FILTER(SHOWFREQS,      showfreqs,      avf);
-    REGISTER_FILTER(SHOWSPECTRUM,   showspectrum,   avf);
-    REGISTER_FILTER(SHOWSPECTRUMPIC, showspectrumpic, avf);
-    REGISTER_FILTER(SHOWVOLUME,     showvolume,     avf);
-    REGISTER_FILTER(SHOWWAVES,      showwaves,      avf);
-    REGISTER_FILTER(SHOWWAVESPIC,   showwavespic,   avf);
-    REGISTER_FILTER(SPECTRUMSYNTH,  spectrumsynth,  vaf);
+void avfilter_register_all(void)
+{
+    CHECK_VALIDITY();
+}
 
-    /* multimedia sources */
-    REGISTER_FILTER(AMOVIE,         amovie,         avsrc);
-    REGISTER_FILTER(MOVIE,          movie,          avsrc);
+const AVFilter *avfilter_next(const AVFilter *prev)
+{
+    CHECK_VALIDITY();
+    return prev ? prev->next : filter_list[0];
+}
 
-    /* those filters are part of public or internal API => registered
-     * unconditionally */
-    REGISTER_FILTER_UNCONDITIONAL(asrc_abuffer);
-    REGISTER_FILTER_UNCONDITIONAL(vsrc_buffer);
-    REGISTER_FILTER_UNCONDITIONAL(asink_abuffer);
-    REGISTER_FILTER_UNCONDITIONAL(vsink_buffer);
-    REGISTER_FILTER_UNCONDITIONAL(af_afifo);
-    REGISTER_FILTER_UNCONDITIONAL(vf_fifo);
+static int compare_name(const void *key, const void *elem)
+{
+    const char *name = key;
+    const AVFilter *const *filter = elem;
+    return strcmp(name, (*filter)->name);
 }
 
-void avfilter_register_all(void)
+const AVFilter *avfilter_get_by_name(const char *name)
 {
-    static AVOnce control = AV_ONCE_INIT;
+    const AVFilter **filter;
 
-    ff_thread_once(&control, register_all);
+    CHECK_VALIDITY();
+    filter = bsearch(name, filter_list, FF_ARRAY_ELEMS(filter_list) - 1,
+                     sizeof(filter_list[0]), compare_name);
+    return filter ? *filter : NULL;
 }
diff --git a/libavfilter/asink_anullsink.c b/libavfilter/asink_anullsink.c
index 9b53d3fbc2..bc95b83ccb 100644
--- a/libavfilter/asink_anullsink.c
+++ b/libavfilter/asink_anullsink.c
@@ -37,7 +37,7 @@  static const AVFilterPad avfilter_asink_anullsink_inputs[] = {
     { NULL },
 };
 
-AVFilter ff_asink_anullsink = {
+const AVFilter ff_asink_anullsink = {
     .name        = "anullsink",
     .description = NULL_IF_CONFIG_SMALL("Do absolutely nothing with the input audio."),
 
@@ -45,4 +45,5 @@  AVFilter ff_asink_anullsink = {
 
     .inputs    = avfilter_asink_anullsink_inputs,
     .outputs   = NULL,
+    .next      = ff_next_asink_anullsink,
 };
diff --git a/libavfilter/asrc_anoisesrc.c b/libavfilter/asrc_anoisesrc.c
index 78f0af4fde..ead078957c 100644
--- a/libavfilter/asrc_anoisesrc.c
+++ b/libavfilter/asrc_anoisesrc.c
@@ -234,7 +234,7 @@  static const AVFilterPad anoisesrc_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_asrc_anoisesrc = {
+const AVFilter ff_asrc_anoisesrc = {
     .name          = "anoisesrc",
     .description   = NULL_IF_CONFIG_SMALL("Generate a noise audio signal."),
     .query_formats = query_formats,
@@ -242,4 +242,5 @@  AVFilter ff_asrc_anoisesrc = {
     .inputs        = NULL,
     .outputs       = anoisesrc_outputs,
     .priv_class    = &anoisesrc_class,
+    .next          = ff_next_asrc_anoisesrc,
 };
diff --git a/libavfilter/asrc_anullsrc.c b/libavfilter/asrc_anullsrc.c
index cb676947d8..6459b4c19a 100644
--- a/libavfilter/asrc_anullsrc.c
+++ b/libavfilter/asrc_anullsrc.c
@@ -136,7 +136,7 @@  static const AVFilterPad avfilter_asrc_anullsrc_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_asrc_anullsrc = {
+const AVFilter ff_asrc_anullsrc = {
     .name          = "anullsrc",
     .description   = NULL_IF_CONFIG_SMALL("Null audio source, return empty audio frames."),
     .init          = init,
@@ -145,4 +145,5 @@  AVFilter ff_asrc_anullsrc = {
     .inputs        = NULL,
     .outputs       = avfilter_asrc_anullsrc_outputs,
     .priv_class    = &anullsrc_class,
+    .next          = ff_next_asrc_anullsrc,
 };
diff --git a/libavfilter/asrc_flite.c b/libavfilter/asrc_flite.c
index c9619ebbae..e1fc604261 100644
--- a/libavfilter/asrc_flite.c
+++ b/libavfilter/asrc_flite.c
@@ -274,7 +274,7 @@  static const AVFilterPad flite_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_asrc_flite = {
+const AVFilter ff_asrc_flite = {
     .name          = "flite",
     .description   = NULL_IF_CONFIG_SMALL("Synthesize voice from text using libflite."),
     .query_formats = query_formats,
@@ -284,4 +284,5 @@  AVFilter ff_asrc_flite = {
     .inputs        = NULL,
     .outputs       = flite_outputs,
     .priv_class    = &flite_class,
+    .next          = ff_next_asrc_flite,
 };
diff --git a/libavfilter/asrc_hilbert.c b/libavfilter/asrc_hilbert.c
index a3a395254f..78890362e8 100644
--- a/libavfilter/asrc_hilbert.c
+++ b/libavfilter/asrc_hilbert.c
@@ -186,7 +186,7 @@  static const AVFilterPad hilbert_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_asrc_hilbert = {
+const AVFilter ff_asrc_hilbert = {
     .name          = "hilbert",
     .description   = NULL_IF_CONFIG_SMALL("Generate a Hilbert transform FIR coefficients."),
     .query_formats = query_formats,
@@ -196,4 +196,5 @@  AVFilter ff_asrc_hilbert = {
     .inputs        = NULL,
     .outputs       = hilbert_outputs,
     .priv_class    = &hilbert_class,
+    .next          = ff_next_asrc_hilbert,
 };
diff --git a/libavfilter/asrc_sine.c b/libavfilter/asrc_sine.c
index 3a87210b4b..fb00a4f824 100644
--- a/libavfilter/asrc_sine.c
+++ b/libavfilter/asrc_sine.c
@@ -269,7 +269,7 @@  static const AVFilterPad sine_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_asrc_sine = {
+const AVFilter ff_asrc_sine = {
     .name          = "sine",
     .description   = NULL_IF_CONFIG_SMALL("Generate sine wave audio signal."),
     .query_formats = query_formats,
@@ -279,4 +279,5 @@  AVFilter ff_asrc_sine = {
     .inputs        = NULL,
     .outputs       = sine_outputs,
     .priv_class    = &sine_class,
+    .next          = ff_next_asrc_sine,
 };
diff --git a/libavfilter/avf_abitscope.c b/libavfilter/avf_abitscope.c
index 0e3eaa422e..b5a397587c 100644
--- a/libavfilter/avf_abitscope.c
+++ b/libavfilter/avf_abitscope.c
@@ -241,7 +241,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_avf_abitscope = {
+const AVFilter ff_avf_abitscope = {
     .name          = "abitscope",
     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to audio bit scope video output."),
     .query_formats = query_formats,
@@ -249,4 +249,5 @@  AVFilter ff_avf_abitscope = {
     .inputs        = inputs,
     .outputs       = outputs,
     .priv_class    = &abitscope_class,
+    .next          = ff_next_avf_abitscope,
 };
diff --git a/libavfilter/avf_ahistogram.c b/libavfilter/avf_ahistogram.c
index 587415175b..6ba5f4d6de 100644
--- a/libavfilter/avf_ahistogram.c
+++ b/libavfilter/avf_ahistogram.c
@@ -401,7 +401,7 @@  static const AVFilterPad audiovectorscope_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_avf_ahistogram = {
+const AVFilter ff_avf_ahistogram = {
     .name          = "ahistogram",
     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to histogram video output."),
     .uninit        = uninit,
@@ -410,4 +410,5 @@  AVFilter ff_avf_ahistogram = {
     .inputs        = audiovectorscope_inputs,
     .outputs       = audiovectorscope_outputs,
     .priv_class    = &ahistogram_class,
+    .next          = ff_next_avf_ahistogram,
 };
diff --git a/libavfilter/avf_aphasemeter.c b/libavfilter/avf_aphasemeter.c
index 8cdee9464c..97afcf4cf7 100644
--- a/libavfilter/avf_aphasemeter.c
+++ b/libavfilter/avf_aphasemeter.c
@@ -266,7 +266,7 @@  static const AVFilterPad inputs[] = {
     { NULL }
 };
 
-AVFilter ff_avf_aphasemeter = {
+const AVFilter ff_avf_aphasemeter = {
     .name          = "aphasemeter",
     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to phase meter video output."),
     .init          = init,
@@ -277,4 +277,5 @@  AVFilter ff_avf_aphasemeter = {
     .outputs       = NULL,
     .priv_class    = &aphasemeter_class,
     .flags         = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+    .next          = ff_next_avf_aphasemeter,
 };
diff --git a/libavfilter/avf_avectorscope.c b/libavfilter/avf_avectorscope.c
index 725ac8bbda..5168fde70a 100644
--- a/libavfilter/avf_avectorscope.c
+++ b/libavfilter/avf_avectorscope.c
@@ -391,7 +391,7 @@  static const AVFilterPad audiovectorscope_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_avf_avectorscope = {
+const AVFilter ff_avf_avectorscope = {
     .name          = "avectorscope",
     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to vectorscope video output."),
     .uninit        = uninit,
@@ -400,4 +400,5 @@  AVFilter ff_avf_avectorscope = {
     .inputs        = audiovectorscope_inputs,
     .outputs       = audiovectorscope_outputs,
     .priv_class    = &avectorscope_class,
+    .next          = ff_next_avf_avectorscope,
 };
diff --git a/libavfilter/avf_concat.c b/libavfilter/avf_concat.c
index 6198a33d53..f7972bdfce 100644
--- a/libavfilter/avf_concat.c
+++ b/libavfilter/avf_concat.c
@@ -418,7 +418,7 @@  static av_cold void uninit(AVFilterContext *ctx)
     av_freep(&cat->in);
 }
 
-AVFilter ff_avf_concat = {
+const AVFilter ff_avf_concat = {
     .name          = "concat",
     .description   = NULL_IF_CONFIG_SMALL("Concatenate audio and video streams."),
     .init          = init,
@@ -429,4 +429,5 @@  AVFilter ff_avf_concat = {
     .outputs       = NULL,
     .priv_class    = &concat_class,
     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+    .next          = ff_next_avf_concat,
 };
diff --git a/libavfilter/avf_showcqt.c b/libavfilter/avf_showcqt.c
index 875ba48cee..cd67bacba2 100644
--- a/libavfilter/avf_showcqt.c
+++ b/libavfilter/avf_showcqt.c
@@ -1589,7 +1589,7 @@  static const AVFilterPad showcqt_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_avf_showcqt = {
+const AVFilter ff_avf_showcqt = {
     .name          = "showcqt",
     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to a CQT (Constant/Clamped Q Transform) spectrum video output."),
     .init          = init,
@@ -1599,4 +1599,5 @@  AVFilter ff_avf_showcqt = {
     .inputs        = showcqt_inputs,
     .outputs       = showcqt_outputs,
     .priv_class    = &showcqt_class,
+    .next          = ff_next_avf_showcqt,
 };
diff --git a/libavfilter/avf_showfreqs.c b/libavfilter/avf_showfreqs.c
index 22f28ec387..c86d47169b 100644
--- a/libavfilter/avf_showfreqs.c
+++ b/libavfilter/avf_showfreqs.c
@@ -513,7 +513,7 @@  static const AVFilterPad showfreqs_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_avf_showfreqs = {
+const AVFilter ff_avf_showfreqs = {
     .name          = "showfreqs",
     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to a frequencies video output."),
     .init          = init,
@@ -523,4 +523,5 @@  AVFilter ff_avf_showfreqs = {
     .inputs        = showfreqs_inputs,
     .outputs       = showfreqs_outputs,
     .priv_class    = &showfreqs_class,
+    .next          = ff_next_avf_showfreqs,
 };
diff --git a/libavfilter/avf_showspectrum.c b/libavfilter/avf_showspectrum.c
index 956f62f3ad..1cc81b7541 100644
--- a/libavfilter/avf_showspectrum.c
+++ b/libavfilter/avf_showspectrum.c
@@ -915,7 +915,7 @@  static const AVFilterPad showspectrum_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_avf_showspectrum = {
+const AVFilter ff_avf_showspectrum = {
     .name          = "showspectrum",
     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to a spectrum video output."),
     .uninit        = uninit,
@@ -925,6 +925,7 @@  AVFilter ff_avf_showspectrum = {
     .outputs       = showspectrum_outputs,
     .priv_class    = &showspectrum_class,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_avf_showspectrum,
 };
 #endif // CONFIG_SHOWSPECTRUM_FILTER
 
@@ -1302,7 +1303,7 @@  static const AVFilterPad showspectrumpic_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_avf_showspectrumpic = {
+const AVFilter ff_avf_showspectrumpic = {
     .name          = "showspectrumpic",
     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to a spectrum video output single picture."),
     .uninit        = uninit,
@@ -1312,6 +1313,7 @@  AVFilter ff_avf_showspectrumpic = {
     .outputs       = showspectrumpic_outputs,
     .priv_class    = &showspectrumpic_class,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_avf_showspectrumpic,
 };
 
 #endif // CONFIG_SHOWSPECTRUMPIC_FILTER
diff --git a/libavfilter/avf_showvolume.c b/libavfilter/avf_showvolume.c
index 897e5709b8..0a8cf1a619 100644
--- a/libavfilter/avf_showvolume.c
+++ b/libavfilter/avf_showvolume.c
@@ -351,7 +351,7 @@  static const AVFilterPad showvolume_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_avf_showvolume = {
+const AVFilter ff_avf_showvolume = {
     .name          = "showvolume",
     .description   = NULL_IF_CONFIG_SMALL("Convert input audio volume to video output."),
     .init          = init,
@@ -361,4 +361,5 @@  AVFilter ff_avf_showvolume = {
     .inputs        = showvolume_inputs,
     .outputs       = showvolume_outputs,
     .priv_class    = &showvolume_class,
+    .next          = ff_next_avf_showvolume,
 };
diff --git a/libavfilter/avf_showwaves.c b/libavfilter/avf_showwaves.c
index 0866967984..368d0917a9 100644
--- a/libavfilter/avf_showwaves.c
+++ b/libavfilter/avf_showwaves.c
@@ -649,7 +649,7 @@  static const AVFilterPad showwaves_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_avf_showwaves = {
+const AVFilter ff_avf_showwaves = {
     .name          = "showwaves",
     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to a video output."),
     .init          = init,
@@ -659,6 +659,7 @@  AVFilter ff_avf_showwaves = {
     .inputs        = showwaves_inputs,
     .outputs       = showwaves_outputs,
     .priv_class    = &showwaves_class,
+    .next          = ff_next_avf_showwaves,
 };
 
 #endif // CONFIG_SHOWWAVES_FILTER
@@ -757,7 +758,7 @@  static const AVFilterPad showwavespic_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_avf_showwavespic = {
+const AVFilter ff_avf_showwavespic = {
     .name          = "showwavespic",
     .description   = NULL_IF_CONFIG_SMALL("Convert input audio to a video output single picture."),
     .init          = init,
@@ -767,6 +768,7 @@  AVFilter ff_avf_showwavespic = {
     .inputs        = showwavespic_inputs,
     .outputs       = showwavespic_outputs,
     .priv_class    = &showwavespic_class,
+    .next          = ff_next_avf_showwavespic,
 };
 
 #endif // CONFIG_SHOWWAVESPIC_FILTER
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index ea75467a75..b89c28d57e 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -575,49 +575,10 @@  int avfilter_process_command(AVFilterContext *filter, const char *cmd, const cha
     return AVERROR(ENOSYS);
 }
 
-static AVFilter *first_filter;
-static AVFilter **last_filter = &first_filter;
-
-const AVFilter *avfilter_get_by_name(const char *name)
-{
-    const AVFilter *f = NULL;
-
-    if (!name)
-        return NULL;
-
-    while ((f = avfilter_next(f)))
-        if (!strcmp(f->name, name))
-            return (AVFilter *)f;
-
-    return NULL;
-}
-
-static AVMutex filter_register_mutex = AV_MUTEX_INITIALIZER;
-
 int avfilter_register(AVFilter *filter)
 {
-    AVFilter **f;
-
-    /* the filter must select generic or internal exclusively */
-    av_assert0((filter->flags & AVFILTER_FLAG_SUPPORT_TIMELINE) != AVFILTER_FLAG_SUPPORT_TIMELINE);
-
-    ff_mutex_lock(&filter_register_mutex);
-    f = last_filter;
-
-    while (*f)
-        f = &(*f)->next;
-    *f = filter;
-    filter->next = NULL;
-    last_filter = &filter->next;
-
-    ff_mutex_unlock(&filter_register_mutex);
-
-    return 0;
-}
-
-const AVFilter *avfilter_next(const AVFilter *prev)
-{
-    return prev ? prev->next : first_filter;
+    av_log(NULL, AV_LOG_ERROR, "External filter registration is currently unsupported.\n");
+    return AVERROR(EINVAL);
 }
 
 int avfilter_pad_count(const AVFilterPad *pads)
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index 62eed2168f..24e46a9b40 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -289,7 +289,7 @@  typedef struct AVFilter {
      * Used by the filter registration system. Must not be touched by any other
      * code.
      */
-    struct AVFilter *next;
+    const struct AVFilter *next;
 
     /**
      * Make the filter instance process a command.
diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c
index 0f87b5439a..f589f2487a 100644
--- a/libavfilter/buffersink.c
+++ b/libavfilter/buffersink.c
@@ -342,7 +342,7 @@  static const AVFilterPad avfilter_vsink_buffer_inputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsink_buffer = {
+const AVFilter ff_vsink_buffersink = {
     .name        = "buffersink",
     .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."),
     .priv_size   = sizeof(BufferSinkContext),
@@ -353,6 +353,7 @@  AVFilter ff_vsink_buffer = {
     .activate    = activate,
     .inputs      = avfilter_vsink_buffer_inputs,
     .outputs     = NULL,
+    .next        = ff_next_vsink_buffersink,
 };
 
 static const AVFilterPad avfilter_asink_abuffer_inputs[] = {
@@ -363,7 +364,7 @@  static const AVFilterPad avfilter_asink_abuffer_inputs[] = {
     { NULL }
 };
 
-AVFilter ff_asink_abuffer = {
+const AVFilter ff_asink_abuffersink = {
     .name        = "abuffersink",
     .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."),
     .priv_class  = &abuffersink_class,
@@ -374,4 +375,5 @@  AVFilter ff_asink_abuffer = {
     .activate    = activate,
     .inputs      = avfilter_asink_abuffer_inputs,
     .outputs     = NULL,
+    .next        = ff_next_asink_abuffersink,
 };
diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c
index cd56f8ca45..1a390fb688 100644
--- a/libavfilter/buffersrc.c
+++ b/libavfilter/buffersrc.c
@@ -493,7 +493,7 @@  static const AVFilterPad avfilter_vsrc_buffer_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsrc_buffer = {
+const AVFilter ff_vsrc_buffer = {
     .name      = "buffer",
     .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them accessible to the filterchain."),
     .priv_size = sizeof(BufferSourceContext),
@@ -505,6 +505,7 @@  AVFilter ff_vsrc_buffer = {
     .inputs    = NULL,
     .outputs   = avfilter_vsrc_buffer_outputs,
     .priv_class = &buffer_class,
+    .next      = ff_next_vsrc_buffer,
 };
 
 static const AVFilterPad avfilter_asrc_abuffer_outputs[] = {
@@ -518,7 +519,7 @@  static const AVFilterPad avfilter_asrc_abuffer_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_asrc_abuffer = {
+const AVFilter ff_asrc_abuffer = {
     .name          = "abuffer",
     .description   = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them accessible to the filterchain."),
     .priv_size     = sizeof(BufferSourceContext),
@@ -530,4 +531,5 @@  AVFilter ff_asrc_abuffer = {
     .inputs    = NULL,
     .outputs   = avfilter_asrc_abuffer_outputs,
     .priv_class = &abuffer_class,
+    .next      = ff_next_asrc_abuffer,
 };
diff --git a/libavfilter/f_bench.c b/libavfilter/f_bench.c
index f9e1006925..5f67a4ada4 100644
--- a/libavfilter/f_bench.c
+++ b/libavfilter/f_bench.c
@@ -107,7 +107,7 @@  static const AVFilterPad bench_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_bench = {
+const AVFilter ff_vf_bench = {
     .name          = "bench",
     .description   = NULL_IF_CONFIG_SMALL("Benchmark part of a filtergraph."),
     .priv_size     = sizeof(BenchContext),
@@ -115,6 +115,7 @@  AVFilter ff_vf_bench = {
     .inputs        = bench_inputs,
     .outputs       = bench_outputs,
     .priv_class    = &bench_class,
+    .next          = ff_next_vf_bench,
 };
 #endif /* CONFIG_BENCH_FILTER */
 
@@ -139,7 +140,7 @@  static const AVFilterPad abench_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_abench = {
+const AVFilter ff_af_abench = {
     .name          = "abench",
     .description   = NULL_IF_CONFIG_SMALL("Benchmark part of a filtergraph."),
     .priv_size     = sizeof(BenchContext),
@@ -147,5 +148,6 @@  AVFilter ff_af_abench = {
     .inputs        = abench_inputs,
     .outputs       = abench_outputs,
     .priv_class    = &abench_class,
+    .next          = ff_next_af_abench,
 };
 #endif /* CONFIG_ABENCH_FILTER */
diff --git a/libavfilter/f_drawgraph.c b/libavfilter/f_drawgraph.c
index 8be9b9f95a..abcb4e27d1 100644
--- a/libavfilter/f_drawgraph.c
+++ b/libavfilter/f_drawgraph.c
@@ -449,7 +449,7 @@  static const AVFilterPad drawgraph_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_drawgraph = {
+const AVFilter ff_vf_drawgraph = {
     .name          = "drawgraph",
     .description   = NULL_IF_CONFIG_SMALL("Draw a graph using input video metadata."),
     .priv_size     = sizeof(DrawGraphContext),
@@ -459,6 +459,7 @@  AVFilter ff_vf_drawgraph = {
     .uninit        = uninit,
     .inputs        = drawgraph_inputs,
     .outputs       = drawgraph_outputs,
+    .next          = ff_next_vf_drawgraph,
 };
 
 #endif // CONFIG_DRAWGRAPH_FILTER
@@ -487,7 +488,7 @@  static const AVFilterPad adrawgraph_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_avf_adrawgraph = {
+const AVFilter ff_avf_adrawgraph = {
     .name          = "adrawgraph",
     .description   = NULL_IF_CONFIG_SMALL("Draw a graph using input audio metadata."),
     .priv_size     = sizeof(DrawGraphContext),
@@ -497,5 +498,6 @@  AVFilter ff_avf_adrawgraph = {
     .uninit        = uninit,
     .inputs        = adrawgraph_inputs,
     .outputs       = adrawgraph_outputs,
+    .next          = ff_next_avf_adrawgraph,
 };
 #endif // CONFIG_ADRAWGRAPH_FILTER
diff --git a/libavfilter/f_ebur128.c b/libavfilter/f_ebur128.c
index 1e8b90fa2f..00ea22e8ab 100644
--- a/libavfilter/f_ebur128.c
+++ b/libavfilter/f_ebur128.c
@@ -937,7 +937,7 @@  static const AVFilterPad ebur128_inputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_ebur128 = {
+const AVFilter ff_af_ebur128 = {
     .name          = "ebur128",
     .description   = NULL_IF_CONFIG_SMALL("EBU R128 scanner."),
     .priv_size     = sizeof(EBUR128Context),
@@ -948,4 +948,5 @@  AVFilter ff_af_ebur128 = {
     .outputs       = NULL,
     .priv_class    = &ebur128_class,
     .flags         = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+    .next          = ff_next_af_ebur128,
 };
diff --git a/libavfilter/f_interleave.c b/libavfilter/f_interleave.c
index d8a73b52e5..a75bdea6c3 100644
--- a/libavfilter/f_interleave.c
+++ b/libavfilter/f_interleave.c
@@ -222,7 +222,7 @@  static const AVFilterPad interleave_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_interleave = {
+const AVFilter ff_vf_interleave = {
     .name        = "interleave",
     .description = NULL_IF_CONFIG_SMALL("Temporally interleave video inputs."),
     .priv_size   = sizeof(InterleaveContext),
@@ -231,6 +231,7 @@  AVFilter ff_vf_interleave = {
     .outputs     = interleave_outputs,
     .priv_class  = &interleave_class,
     .flags       = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next        = ff_next_vf_interleave,
 };
 
 #endif
@@ -250,7 +251,7 @@  static const AVFilterPad ainterleave_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_ainterleave = {
+const AVFilter ff_af_ainterleave = {
     .name        = "ainterleave",
     .description = NULL_IF_CONFIG_SMALL("Temporally interleave audio inputs."),
     .priv_size   = sizeof(InterleaveContext),
@@ -259,6 +260,7 @@  AVFilter ff_af_ainterleave = {
     .outputs     = ainterleave_outputs,
     .priv_class  = &ainterleave_class,
     .flags       = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next        = ff_next_af_ainterleave,
 };
 
 #endif
diff --git a/libavfilter/f_loop.c b/libavfilter/f_loop.c
index 255fe643da..327e54af9a 100644
--- a/libavfilter/f_loop.c
+++ b/libavfilter/f_loop.c
@@ -227,7 +227,7 @@  static const AVFilterPad aoutputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_aloop = {
+const AVFilter ff_af_aloop = {
     .name          = "aloop",
     .description   = NULL_IF_CONFIG_SMALL("Loop audio samples."),
     .priv_size     = sizeof(LoopContext),
@@ -235,6 +235,7 @@  AVFilter ff_af_aloop = {
     .uninit        = auninit,
     .inputs        = ainputs,
     .outputs       = aoutputs,
+    .next          = ff_next_af_aloop,
 };
 #endif /* CONFIG_ALOOP_FILTER */
 
@@ -369,7 +370,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_loop = {
+const AVFilter ff_vf_loop = {
     .name        = "loop",
     .description = NULL_IF_CONFIG_SMALL("Loop video frames."),
     .priv_size   = sizeof(LoopContext),
@@ -378,5 +379,6 @@  AVFilter ff_vf_loop = {
     .uninit      = uninit,
     .inputs      = inputs,
     .outputs     = outputs,
+    .next        = ff_next_vf_loop,
 };
 #endif /* CONFIG_LOOP_FILTER */
diff --git a/libavfilter/f_metadata.c b/libavfilter/f_metadata.c
index 523a94d38c..edd9166146 100644
--- a/libavfilter/f_metadata.c
+++ b/libavfilter/f_metadata.c
@@ -366,7 +366,7 @@  static const AVFilterPad aoutputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_ametadata = {
+const AVFilter ff_af_ametadata = {
     .name          = "ametadata",
     .description   = NULL_IF_CONFIG_SMALL("Manipulate audio frame metadata."),
     .priv_size     = sizeof(MetadataContext),
@@ -376,6 +376,7 @@  AVFilter ff_af_ametadata = {
     .inputs        = ainputs,
     .outputs       = aoutputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_af_ametadata,
 };
 #endif /* CONFIG_AMETADATA_FILTER */
 
@@ -401,7 +402,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_metadata = {
+const AVFilter ff_vf_metadata = {
     .name        = "metadata",
     .description = NULL_IF_CONFIG_SMALL("Manipulate video frame metadata."),
     .priv_size   = sizeof(MetadataContext),
@@ -411,5 +412,6 @@  AVFilter ff_vf_metadata = {
     .inputs      = inputs,
     .outputs     = outputs,
     .flags       = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next        = ff_next_vf_metadata,
 };
 #endif /* CONFIG_METADATA_FILTER */
diff --git a/libavfilter/f_perms.c b/libavfilter/f_perms.c
index dc6ecbbb53..b7ecf9ba6d 100644
--- a/libavfilter/f_perms.c
+++ b/libavfilter/f_perms.c
@@ -133,7 +133,7 @@  static const AVFilterPad aperms_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_aperms = {
+const AVFilter ff_af_aperms = {
     .name        = "aperms",
     .description = NULL_IF_CONFIG_SMALL("Set permissions for the output audio frame."),
     .init        = init,
@@ -141,6 +141,7 @@  AVFilter ff_af_aperms = {
     .inputs      = aperms_inputs,
     .outputs     = aperms_outputs,
     .priv_class  = &aperms_class,
+    .next        = ff_next_af_aperms,
 };
 #endif /* CONFIG_APERMS_FILTER */
 
@@ -166,7 +167,7 @@  static const AVFilterPad perms_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_perms = {
+const AVFilter ff_vf_perms = {
     .name        = "perms",
     .description = NULL_IF_CONFIG_SMALL("Set permissions for the output video frame."),
     .init        = init,
@@ -174,5 +175,6 @@  AVFilter ff_vf_perms = {
     .inputs      = perms_inputs,
     .outputs     = perms_outputs,
     .priv_class  = &perms_class,
+    .next        = ff_next_vf_perms,
 };
 #endif /* CONFIG_PERMS_FILTER */
diff --git a/libavfilter/f_realtime.c b/libavfilter/f_realtime.c
index 171c16aaaa..63d50a0676 100644
--- a/libavfilter/f_realtime.c
+++ b/libavfilter/f_realtime.c
@@ -89,13 +89,14 @@  static const AVFilterPad avfilter_vf_realtime_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_realtime = {
+const AVFilter ff_vf_realtime = {
     .name        = "realtime",
     .description = NULL_IF_CONFIG_SMALL("Slow down filtering to match realtime."),
     .priv_size   = sizeof(RealtimeContext),
     .priv_class  = &realtime_class,
     .inputs      = avfilter_vf_realtime_inputs,
     .outputs     = avfilter_vf_realtime_outputs,
+    .next        = ff_next_vf_realtime,
 };
 #endif /* CONFIG_REALTIME_FILTER */
 
@@ -121,12 +122,13 @@  static const AVFilterPad arealtime_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_arealtime = {
+const AVFilter ff_af_arealtime = {
     .name        = "arealtime",
     .description = NULL_IF_CONFIG_SMALL("Slow down filtering to match realtime."),
     .priv_size   = sizeof(RealtimeContext),
     .priv_class  = &arealtime_class,
     .inputs      = arealtime_inputs,
     .outputs     = arealtime_outputs,
+    .next        = ff_next_af_arealtime,
 };
 #endif /* CONFIG_AREALTIME_FILTER */
diff --git a/libavfilter/f_reverse.c b/libavfilter/f_reverse.c
index 5bf71b38ed..7fe5b465bd 100644
--- a/libavfilter/f_reverse.c
+++ b/libavfilter/f_reverse.c
@@ -127,7 +127,7 @@  static const AVFilterPad reverse_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_reverse = {
+const AVFilter ff_vf_reverse = {
     .name        = "reverse",
     .description = NULL_IF_CONFIG_SMALL("Reverse a clip."),
     .priv_size   = sizeof(ReverseContext),
@@ -135,6 +135,7 @@  AVFilter ff_vf_reverse = {
     .uninit      = uninit,
     .inputs      = reverse_inputs,
     .outputs     = reverse_outputs,
+    .next        = ff_next_vf_reverse,
 };
 
 #endif /* CONFIG_REVERSE_FILTER */
@@ -237,7 +238,7 @@  static const AVFilterPad areverse_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_areverse = {
+const AVFilter ff_af_areverse = {
     .name          = "areverse",
     .description   = NULL_IF_CONFIG_SMALL("Reverse an audio clip."),
     .query_formats = query_formats,
@@ -246,6 +247,7 @@  AVFilter ff_af_areverse = {
     .uninit        = uninit,
     .inputs        = areverse_inputs,
     .outputs       = areverse_outputs,
+    .next          = ff_next_af_areverse,
 };
 
 #endif /* CONFIG_AREVERSE_FILTER */
diff --git a/libavfilter/f_select.c b/libavfilter/f_select.c
index b1b2cbc21a..688735094e 100644
--- a/libavfilter/f_select.c
+++ b/libavfilter/f_select.c
@@ -484,7 +484,7 @@  static const AVFilterPad avfilter_af_aselect_inputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_aselect = {
+const AVFilter ff_af_aselect = {
     .name        = "aselect",
     .description = NULL_IF_CONFIG_SMALL("Select audio frames to pass in output."),
     .init        = aselect_init,
@@ -493,6 +493,7 @@  AVFilter ff_af_aselect = {
     .inputs      = avfilter_af_aselect_inputs,
     .priv_class  = &aselect_class,
     .flags       = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+    .next        = ff_next_af_aselect,
 };
 #endif /* CONFIG_ASELECT_FILTER */
 
@@ -521,7 +522,7 @@  static const AVFilterPad avfilter_vf_select_inputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_select = {
+const AVFilter ff_vf_select = {
     .name          = "select",
     .description   = NULL_IF_CONFIG_SMALL("Select video frames to pass in output."),
     .init          = select_init,
@@ -531,5 +532,6 @@  AVFilter ff_vf_select = {
     .priv_class    = &select_class,
     .inputs        = avfilter_vf_select_inputs,
     .flags         = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+    .next          = ff_next_vf_select,
 };
 #endif /* CONFIG_SELECT_FILTER */
diff --git a/libavfilter/f_sendcmd.c b/libavfilter/f_sendcmd.c
index b8740e8883..ac0c5eb511 100644
--- a/libavfilter/f_sendcmd.c
+++ b/libavfilter/f_sendcmd.c
@@ -537,7 +537,7 @@  static const AVFilterPad sendcmd_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_sendcmd = {
+const AVFilter ff_vf_sendcmd = {
     .name        = "sendcmd",
     .description = NULL_IF_CONFIG_SMALL("Send commands to filters."),
     .init        = init,
@@ -546,6 +546,7 @@  AVFilter ff_vf_sendcmd = {
     .inputs      = sendcmd_inputs,
     .outputs     = sendcmd_outputs,
     .priv_class  = &sendcmd_class,
+    .next        = ff_next_vf_sendcmd,
 };
 
 #endif
@@ -572,7 +573,7 @@  static const AVFilterPad asendcmd_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_asendcmd = {
+const AVFilter ff_af_asendcmd = {
     .name        = "asendcmd",
     .description = NULL_IF_CONFIG_SMALL("Send commands to filters."),
     .init        = init,
@@ -581,6 +582,7 @@  AVFilter ff_af_asendcmd = {
     .inputs      = asendcmd_inputs,
     .outputs     = asendcmd_outputs,
     .priv_class  = &asendcmd_class,
+    .next        = ff_next_af_asendcmd,
 };
 
 #endif
diff --git a/libavfilter/f_sidedata.c b/libavfilter/f_sidedata.c
index 45d246b732..7fce0bc299 100644
--- a/libavfilter/f_sidedata.c
+++ b/libavfilter/f_sidedata.c
@@ -133,7 +133,7 @@  static const AVFilterPad aoutputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_asidedata = {
+const AVFilter ff_af_asidedata = {
     .name          = "asidedata",
     .description   = NULL_IF_CONFIG_SMALL("Manipulate audio frame side data."),
     .priv_size     = sizeof(SideDataContext),
@@ -142,6 +142,7 @@  AVFilter ff_af_asidedata = {
     .inputs        = ainputs,
     .outputs       = aoutputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_af_asidedata,
 };
 #endif /* CONFIG_ASIDEDATA_FILTER */
 
@@ -167,7 +168,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_sidedata = {
+const AVFilter ff_vf_sidedata = {
     .name        = "sidedata",
     .description = NULL_IF_CONFIG_SMALL("Manipulate video frame side data."),
     .priv_size   = sizeof(SideDataContext),
@@ -176,5 +177,6 @@  AVFilter ff_vf_sidedata = {
     .inputs      = inputs,
     .outputs     = outputs,
     .flags       = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next        = ff_next_vf_sidedata,
 };
 #endif /* CONFIG_SIDEDATA_FILTER */
diff --git a/libavfilter/f_streamselect.c b/libavfilter/f_streamselect.c
index 923deb1a85..858368dbbe 100644
--- a/libavfilter/f_streamselect.c
+++ b/libavfilter/f_streamselect.c
@@ -316,7 +316,8 @@  static int query_formats(AVFilterContext *ctx)
     return 0;
 }
 
-AVFilter ff_vf_streamselect = {
+#if CONFIG_STREAMSELECT_FILTER
+const AVFilter ff_vf_streamselect = {
     .name            = "streamselect",
     .description     = NULL_IF_CONFIG_SMALL("Select video streams"),
     .init            = init,
@@ -327,12 +328,15 @@  AVFilter ff_vf_streamselect = {
     .priv_size       = sizeof(StreamSelectContext),
     .priv_class      = &streamselect_class,
     .flags           = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+    .next            = ff_next_vf_streamselect,
 };
+#endif /* CONFIG_STREAMSELECT_FILTER */
 
+#if CONFIG_ASTREAMSELECT_FILTER
 #define astreamselect_options streamselect_options
 AVFILTER_DEFINE_CLASS(astreamselect);
 
-AVFilter ff_af_astreamselect = {
+const AVFilter ff_af_astreamselect = {
     .name            = "astreamselect",
     .description     = NULL_IF_CONFIG_SMALL("Select audio streams"),
     .init            = init,
@@ -343,4 +347,6 @@  AVFilter ff_af_astreamselect = {
     .priv_size       = sizeof(StreamSelectContext),
     .priv_class      = &astreamselect_class,
     .flags           = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+    .next            = ff_next_af_astreamselect,
 };
+#endif /* CONFIG_ASTREAMSELECT_FILTER */
diff --git a/libavfilter/f_zmq.c b/libavfilter/f_zmq.c
index 89da5bef06..6bd8321927 100644
--- a/libavfilter/f_zmq.c
+++ b/libavfilter/f_zmq.c
@@ -225,7 +225,7 @@  static const AVFilterPad zmq_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_zmq = {
+const AVFilter ff_vf_zmq = {
     .name        = "zmq",
     .description = NULL_IF_CONFIG_SMALL("Receive commands through ZMQ and broker them to filters."),
     .init        = init,
@@ -234,6 +234,7 @@  AVFilter ff_vf_zmq = {
     .inputs      = zmq_inputs,
     .outputs     = zmq_outputs,
     .priv_class  = &zmq_class,
+    .next        = ff_next_vf_zmq,
 };
 
 #endif
@@ -260,7 +261,7 @@  static const AVFilterPad azmq_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_azmq = {
+const AVFilter ff_af_azmq = {
     .name        = "azmq",
     .description = NULL_IF_CONFIG_SMALL("Receive commands through ZMQ and broker them to filters."),
     .init        = init,
@@ -269,6 +270,7 @@  AVFilter ff_af_azmq = {
     .inputs      = azmq_inputs,
     .outputs     = azmq_outputs,
     .priv_class  = &azmq_class,
+    .next        = ff_next_af_azmq,
 };
 
 #endif
diff --git a/libavfilter/fifo.c b/libavfilter/fifo.c
index 0fa0f86cb3..1ab548c4f7 100644
--- a/libavfilter/fifo.c
+++ b/libavfilter/fifo.c
@@ -270,7 +270,7 @@  static const AVFilterPad avfilter_vf_fifo_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_fifo = {
+const AVFilter ff_vf_fifo = {
     .name      = "fifo",
     .description = NULL_IF_CONFIG_SMALL("Buffer input images and send them when they are requested."),
 
@@ -281,6 +281,7 @@  AVFilter ff_vf_fifo = {
 
     .inputs    = avfilter_vf_fifo_inputs,
     .outputs   = avfilter_vf_fifo_outputs,
+    .next      = ff_next_vf_fifo,
 };
 
 static const AVFilterPad avfilter_af_afifo_inputs[] = {
@@ -301,7 +302,7 @@  static const AVFilterPad avfilter_af_afifo_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_afifo = {
+const AVFilter ff_af_afifo = {
     .name        = "afifo",
     .description = NULL_IF_CONFIG_SMALL("Buffer input frames and send them when they are requested."),
 
@@ -312,4 +313,5 @@  AVFilter ff_af_afifo = {
 
     .inputs    = avfilter_af_afifo_inputs,
     .outputs   = avfilter_af_afifo_outputs,
+    .next      = ff_next_af_afifo,
 };
diff --git a/libavfilter/internal.h b/libavfilter/internal.h
index f9679ed1d7..3a467be5da 100644
--- a/libavfilter/internal.h
+++ b/libavfilter/internal.h
@@ -35,6 +35,8 @@ 
 #include "libavcodec/avcodec.h"
 #include "libavcodec/internal.h"
 
+#include "libavfilter/filter_list.h"
+
 typedef struct AVFilterCommand {
     double time;                ///< time expressed in seconds
     char *command;              ///< command
diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c
index 4505498bf3..9f381e1cf4 100644
--- a/libavfilter/setpts.c
+++ b/libavfilter/setpts.c
@@ -249,7 +249,7 @@  static const AVFilterPad avfilter_vf_setpts_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_setpts = {
+const AVFilter ff_vf_setpts = {
     .name      = "setpts",
     .description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."),
     .init      = init,
@@ -260,6 +260,7 @@  AVFilter ff_vf_setpts = {
 
     .inputs    = avfilter_vf_setpts_inputs,
     .outputs   = avfilter_vf_setpts_outputs,
+    .next      = ff_next_vf_setpts,
 };
 #endif /* CONFIG_SETPTS_FILTER */
 
@@ -286,7 +287,7 @@  static const AVFilterPad asetpts_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_asetpts = {
+const AVFilter ff_af_asetpts = {
     .name        = "asetpts",
     .description = NULL_IF_CONFIG_SMALL("Set PTS for the output audio frame."),
     .init        = init,
@@ -295,5 +296,6 @@  AVFilter ff_af_asetpts = {
     .priv_class  = &asetpts_class,
     .inputs      = asetpts_inputs,
     .outputs     = asetpts_outputs,
+    .next        = ff_next_af_asetpts,
 };
 #endif /* CONFIG_ASETPTS_FILTER */
diff --git a/libavfilter/settb.c b/libavfilter/settb.c
index 83616c1361..cac08b16ec 100644
--- a/libavfilter/settb.c
+++ b/libavfilter/settb.c
@@ -143,13 +143,14 @@  static const AVFilterPad avfilter_vf_settb_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_settb = {
+const AVFilter ff_vf_settb = {
     .name        = "settb",
     .description = NULL_IF_CONFIG_SMALL("Set timebase for the video output link."),
     .priv_size   = sizeof(SetTBContext),
     .priv_class  = &settb_class,
     .inputs      = avfilter_vf_settb_inputs,
     .outputs     = avfilter_vf_settb_outputs,
+    .next        = ff_next_vf_settb,
 };
 #endif /* CONFIG_SETTB_FILTER */
 
@@ -176,12 +177,13 @@  static const AVFilterPad avfilter_af_asettb_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_asettb = {
+const AVFilter ff_af_asettb = {
     .name        = "asettb",
     .description = NULL_IF_CONFIG_SMALL("Set timebase for the audio output link."),
     .priv_size   = sizeof(SetTBContext),
     .inputs      = avfilter_af_asettb_inputs,
     .outputs     = avfilter_af_asettb_outputs,
     .priv_class  = &asettb_class,
+    .next        = ff_next_af_asettb,
 };
 #endif /* CONFIG_ASETTB_FILTER */
diff --git a/libavfilter/split.c b/libavfilter/split.c
index 8b260a9ba3..ca298d082d 100644
--- a/libavfilter/split.c
+++ b/libavfilter/split.c
@@ -113,6 +113,7 @@  AVFILTER_DEFINE_CLASS(split);
 #define asplit_options options
 AVFILTER_DEFINE_CLASS(asplit);
 
+#if CONFIG_SPLIT_FILTER
 static const AVFilterPad avfilter_vf_split_inputs[] = {
     {
         .name         = "default",
@@ -122,7 +123,7 @@  static const AVFilterPad avfilter_vf_split_inputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_split = {
+const AVFilter ff_vf_split = {
     .name        = "split",
     .description = NULL_IF_CONFIG_SMALL("Pass on the input to N video outputs."),
     .priv_size   = sizeof(SplitContext),
@@ -132,8 +133,11 @@  AVFilter ff_vf_split = {
     .inputs      = avfilter_vf_split_inputs,
     .outputs     = NULL,
     .flags       = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+    .next        = ff_next_vf_split,
 };
+#endif /* CONFIG_SPLIT_FILTER */
 
+#if CONFIG_ASPLIT_FILTER
 static const AVFilterPad avfilter_af_asplit_inputs[] = {
     {
         .name         = "default",
@@ -143,7 +147,7 @@  static const AVFilterPad avfilter_af_asplit_inputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_asplit = {
+const AVFilter ff_af_asplit = {
     .name        = "asplit",
     .description = NULL_IF_CONFIG_SMALL("Pass on the audio input to N audio outputs."),
     .priv_size   = sizeof(SplitContext),
@@ -153,4 +157,6 @@  AVFilter ff_af_asplit = {
     .inputs      = avfilter_af_asplit_inputs,
     .outputs     = NULL,
     .flags       = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+    .next        = ff_next_af_asplit,
 };
+#endif /* CONFIG_ASPLIT_FILTER */
diff --git a/libavfilter/src_movie.c b/libavfilter/src_movie.c
index 258ba504a5..ddef7ba898 100644
--- a/libavfilter/src_movie.c
+++ b/libavfilter/src_movie.c
@@ -658,7 +658,7 @@  static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar
 
 AVFILTER_DEFINE_CLASS(movie);
 
-AVFilter ff_avsrc_movie = {
+const AVFilter ff_avsrc_movie = {
     .name          = "movie",
     .description   = NULL_IF_CONFIG_SMALL("Read from a movie source."),
     .priv_size     = sizeof(MovieContext),
@@ -670,7 +670,8 @@  AVFilter ff_avsrc_movie = {
     .inputs    = NULL,
     .outputs   = NULL,
     .flags     = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
-    .process_command = process_command
+    .process_command = process_command,
+    .next      = ff_next_avsrc_movie,
 };
 
 #endif  /* CONFIG_MOVIE_FILTER */
@@ -680,7 +681,7 @@  AVFilter ff_avsrc_movie = {
 #define amovie_options movie_options
 AVFILTER_DEFINE_CLASS(amovie);
 
-AVFilter ff_avsrc_amovie = {
+const AVFilter ff_avsrc_amovie = {
     .name          = "amovie",
     .description   = NULL_IF_CONFIG_SMALL("Read audio from a movie source."),
     .priv_size     = sizeof(MovieContext),
@@ -693,6 +694,7 @@  AVFilter ff_avsrc_amovie = {
     .priv_class = &amovie_class,
     .flags      = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
     .process_command = process_command,
+    .next       = ff_next_avsrc_amovie,
 };
 
 #endif /* CONFIG_AMOVIE_FILTER */
diff --git a/libavfilter/trim.c b/libavfilter/trim.c
index 1dbbabbb93..c494e4bbde 100644
--- a/libavfilter/trim.c
+++ b/libavfilter/trim.c
@@ -207,7 +207,7 @@  static const AVFilterPad trim_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_trim = {
+const AVFilter ff_vf_trim = {
     .name        = "trim",
     .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."),
     .init        = init,
@@ -215,6 +215,7 @@  AVFilter ff_vf_trim = {
     .priv_class  = &trim_class,
     .inputs      = trim_inputs,
     .outputs     = trim_outputs,
+    .next        = ff_next_vf_trim,
 };
 #endif // CONFIG_TRIM_FILTER
 
@@ -361,7 +362,7 @@  static const AVFilterPad atrim_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_af_atrim = {
+const AVFilter ff_af_atrim = {
     .name        = "atrim",
     .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."),
     .init        = init,
@@ -369,5 +370,6 @@  AVFilter ff_af_atrim = {
     .priv_class  = &atrim_class,
     .inputs      = atrim_inputs,
     .outputs     = atrim_outputs,
+    .next        = ff_next_af_atrim,
 };
 #endif // CONFIG_ATRIM_FILTER
diff --git a/libavfilter/vaf_spectrumsynth.c b/libavfilter/vaf_spectrumsynth.c
index fed2cbba03..1dfae713e0 100644
--- a/libavfilter/vaf_spectrumsynth.c
+++ b/libavfilter/vaf_spectrumsynth.c
@@ -531,7 +531,7 @@  static const AVFilterPad spectrumsynth_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vaf_spectrumsynth = {
+const AVFilter ff_vaf_spectrumsynth = {
     .name          = "spectrumsynth",
     .description   = NULL_IF_CONFIG_SMALL("Convert input spectrum videos to audio output."),
     .uninit        = uninit,
@@ -540,4 +540,5 @@  AVFilter ff_vaf_spectrumsynth = {
     .inputs        = spectrumsynth_inputs,
     .outputs       = spectrumsynth_outputs,
     .priv_class    = &spectrumsynth_class,
+    .next          = ff_next_vaf_spectrumsynth,
 };
diff --git a/libavfilter/vf_alphamerge.c b/libavfilter/vf_alphamerge.c
index 45fa340fcc..896345b6b4 100644
--- a/libavfilter/vf_alphamerge.c
+++ b/libavfilter/vf_alphamerge.c
@@ -205,7 +205,7 @@  static const AVFilterPad alphamerge_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_alphamerge = {
+const AVFilter ff_vf_alphamerge = {
     .name           = "alphamerge",
     .description    = NULL_IF_CONFIG_SMALL("Copy the luma value of the second "
                       "input into the alpha channel of the first input."),
@@ -214,4 +214,5 @@  AVFilter ff_vf_alphamerge = {
     .query_formats  = query_formats,
     .inputs         = alphamerge_inputs,
     .outputs        = alphamerge_outputs,
+    .next           = ff_next_vf_alphamerge,
 };
diff --git a/libavfilter/vf_aspect.c b/libavfilter/vf_aspect.c
index c042698ef7..c6174dd1cb 100644
--- a/libavfilter/vf_aspect.c
+++ b/libavfilter/vf_aspect.c
@@ -185,13 +185,14 @@  static const AVFilterPad avfilter_vf_setdar_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_setdar = {
+const AVFilter ff_vf_setdar = {
     .name        = "setdar",
     .description = NULL_IF_CONFIG_SMALL("Set the frame display aspect ratio."),
     .priv_size   = sizeof(AspectContext),
     .priv_class  = &setdar_class,
     .inputs      = avfilter_vf_setdar_inputs,
     .outputs     = avfilter_vf_setdar_outputs,
+    .next        = ff_next_vf_setdar,
 };
 
 #endif /* CONFIG_SETDAR_FILTER */
@@ -249,13 +250,14 @@  static const AVFilterPad avfilter_vf_setsar_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_setsar = {
+const AVFilter ff_vf_setsar = {
     .name        = "setsar",
     .description = NULL_IF_CONFIG_SMALL("Set the pixel sample aspect ratio."),
     .priv_size   = sizeof(AspectContext),
     .priv_class  = &setsar_class,
     .inputs      = avfilter_vf_setsar_inputs,
     .outputs     = avfilter_vf_setsar_outputs,
+    .next        = ff_next_vf_setsar,
 };
 
 #endif /* CONFIG_SETSAR_FILTER */
diff --git a/libavfilter/vf_atadenoise.c b/libavfilter/vf_atadenoise.c
index 03b772c674..fee149c56e 100644
--- a/libavfilter/vf_atadenoise.c
+++ b/libavfilter/vf_atadenoise.c
@@ -423,7 +423,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_atadenoise = {
+const AVFilter ff_vf_atadenoise = {
     .name          = "atadenoise",
     .description   = NULL_IF_CONFIG_SMALL("Apply an Adaptive Temporal Averaging Denoiser."),
     .priv_size     = sizeof(ATADenoiseContext),
@@ -434,4 +434,5 @@  AVFilter ff_vf_atadenoise = {
     .inputs        = inputs,
     .outputs       = outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_atadenoise,
 };
diff --git a/libavfilter/vf_avgblur.c b/libavfilter/vf_avgblur.c
index afd4a6ab79..0609a784d5 100644
--- a/libavfilter/vf_avgblur.c
+++ b/libavfilter/vf_avgblur.c
@@ -313,7 +313,7 @@  static const AVFilterPad avgblur_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_avgblur = {
+const AVFilter ff_vf_avgblur = {
     .name          = "avgblur",
     .description   = NULL_IF_CONFIG_SMALL("Apply Average Blur filter."),
     .priv_size     = sizeof(AverageBlurContext),
@@ -323,4 +323,5 @@  AVFilter ff_vf_avgblur = {
     .inputs        = avgblur_inputs,
     .outputs       = avgblur_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_avgblur,
 };
diff --git a/libavfilter/vf_bbox.c b/libavfilter/vf_bbox.c
index 4ac2980a6c..48a2535742 100644
--- a/libavfilter/vf_bbox.c
+++ b/libavfilter/vf_bbox.c
@@ -122,7 +122,7 @@  static const AVFilterPad bbox_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_bbox = {
+const AVFilter ff_vf_bbox = {
     .name          = "bbox",
     .description   = NULL_IF_CONFIG_SMALL("Compute bounding box for each frame."),
     .priv_size     = sizeof(BBoxContext),
@@ -131,4 +131,5 @@  AVFilter ff_vf_bbox = {
     .inputs        = bbox_inputs,
     .outputs       = bbox_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_bbox,
 };
diff --git a/libavfilter/vf_bitplanenoise.c b/libavfilter/vf_bitplanenoise.c
index dd6864bc5e..3a5bbd8a2f 100644
--- a/libavfilter/vf_bitplanenoise.c
+++ b/libavfilter/vf_bitplanenoise.c
@@ -214,7 +214,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_bitplanenoise = {
+const AVFilter ff_vf_bitplanenoise = {
     .name           = "bitplanenoise",
     .description    = NULL_IF_CONFIG_SMALL("Measure bit plane noise."),
     .priv_size      = sizeof(BPNContext),
@@ -223,4 +223,5 @@  AVFilter ff_vf_bitplanenoise = {
     .outputs        = outputs,
     .priv_class     = &bitplanenoise_class,
     .flags          = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next           = ff_next_vf_bitplanenoise,
 };
diff --git a/libavfilter/vf_blackdetect.c b/libavfilter/vf_blackdetect.c
index 06ef9988d1..9fdf6a3d6a 100644
--- a/libavfilter/vf_blackdetect.c
+++ b/libavfilter/vf_blackdetect.c
@@ -200,7 +200,7 @@  static const AVFilterPad blackdetect_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_blackdetect = {
+const AVFilter ff_vf_blackdetect = {
     .name          = "blackdetect",
     .description   = NULL_IF_CONFIG_SMALL("Detect video intervals that are (almost) black."),
     .priv_size     = sizeof(BlackDetectContext),
@@ -208,4 +208,5 @@  AVFilter ff_vf_blackdetect = {
     .inputs        = blackdetect_inputs,
     .outputs       = blackdetect_outputs,
     .priv_class    = &blackdetect_class,
+    .next          = ff_next_vf_blackdetect,
 };
diff --git a/libavfilter/vf_blackframe.c b/libavfilter/vf_blackframe.c
index 804965c42c..dba3c6c2c0 100644
--- a/libavfilter/vf_blackframe.c
+++ b/libavfilter/vf_blackframe.c
@@ -132,7 +132,7 @@  static const AVFilterPad avfilter_vf_blackframe_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_blackframe = {
+const AVFilter ff_vf_blackframe = {
     .name          = "blackframe",
     .description   = NULL_IF_CONFIG_SMALL("Detect frames that are (almost) black."),
     .priv_size     = sizeof(BlackFrameContext),
@@ -140,4 +140,5 @@  AVFilter ff_vf_blackframe = {
     .query_formats = query_formats,
     .inputs        = avfilter_vf_blackframe_inputs,
     .outputs       = avfilter_vf_blackframe_outputs,
+    .next          = ff_next_vf_blackframe,
 };
diff --git a/libavfilter/vf_blend.c b/libavfilter/vf_blend.c
index 70c37c75da..eb442d6347 100644
--- a/libavfilter/vf_blend.c
+++ b/libavfilter/vf_blend.c
@@ -605,7 +605,7 @@  static const AVFilterPad blend_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_blend = {
+const AVFilter ff_vf_blend = {
     .name          = "blend",
     .description   = NULL_IF_CONFIG_SMALL("Blend two video frames into each other."),
     .preinit       = blend_framesync_preinit,
@@ -618,6 +618,7 @@  AVFilter ff_vf_blend = {
     .outputs       = blend_outputs,
     .priv_class    = &blend_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_blend,
 };
 
 #endif
@@ -664,7 +665,7 @@  static const AVFilterPad tblend_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_tblend = {
+const AVFilter ff_vf_tblend = {
     .name          = "tblend",
     .description   = NULL_IF_CONFIG_SMALL("Blend successive frames."),
     .priv_size     = sizeof(BlendContext),
@@ -675,6 +676,7 @@  AVFilter ff_vf_tblend = {
     .inputs        = tblend_inputs,
     .outputs       = tblend_outputs,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_tblend,
 };
 
 #endif
diff --git a/libavfilter/vf_boxblur.c b/libavfilter/vf_boxblur.c
index 8e43986846..e29673f9da 100644
--- a/libavfilter/vf_boxblur.c
+++ b/libavfilter/vf_boxblur.c
@@ -399,7 +399,7 @@  static const AVFilterPad avfilter_vf_boxblur_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_boxblur = {
+const AVFilter ff_vf_boxblur = {
     .name          = "boxblur",
     .description   = NULL_IF_CONFIG_SMALL("Blur the input."),
     .priv_size     = sizeof(BoxBlurContext),
@@ -410,4 +410,5 @@  AVFilter ff_vf_boxblur = {
     .inputs        = avfilter_vf_boxblur_inputs,
     .outputs       = avfilter_vf_boxblur_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_boxblur,
 };
diff --git a/libavfilter/vf_bwdif.c b/libavfilter/vf_bwdif.c
index b691983611..f32fedaf0c 100644
--- a/libavfilter/vf_bwdif.c
+++ b/libavfilter/vf_bwdif.c
@@ -571,7 +571,7 @@  static const AVFilterPad avfilter_vf_bwdif_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_bwdif = {
+const AVFilter ff_vf_bwdif = {
     .name          = "bwdif",
     .description   = NULL_IF_CONFIG_SMALL("Deinterlace the input image."),
     .priv_size     = sizeof(BWDIFContext),
@@ -581,4 +581,5 @@  AVFilter ff_vf_bwdif = {
     .inputs        = avfilter_vf_bwdif_inputs,
     .outputs       = avfilter_vf_bwdif_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_bwdif,
 };
diff --git a/libavfilter/vf_chromakey.c b/libavfilter/vf_chromakey.c
index 88414783bc..bd8a3ea719 100644
--- a/libavfilter/vf_chromakey.c
+++ b/libavfilter/vf_chromakey.c
@@ -195,7 +195,7 @@  static const AVOption chromakey_options[] = {
 
 AVFILTER_DEFINE_CLASS(chromakey);
 
-AVFilter ff_vf_chromakey = {
+const AVFilter ff_vf_chromakey = {
     .name          = "chromakey",
     .description   = NULL_IF_CONFIG_SMALL("Turns a certain color into transparency. Operates on YUV colors."),
     .priv_size     = sizeof(ChromakeyContext),
@@ -205,4 +205,5 @@  AVFilter ff_vf_chromakey = {
     .inputs        = chromakey_inputs,
     .outputs       = chromakey_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_chromakey,
 };
diff --git a/libavfilter/vf_ciescope.c b/libavfilter/vf_ciescope.c
index 7c0cfed061..8fa33dd91a 100644
--- a/libavfilter/vf_ciescope.c
+++ b/libavfilter/vf_ciescope.c
@@ -1500,7 +1500,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_ciescope = {
+const AVFilter ff_vf_ciescope = {
     .name          = "ciescope",
     .description   = NULL_IF_CONFIG_SMALL("Video CIE scope."),
     .priv_size     = sizeof(CiescopeContext),
@@ -1509,4 +1509,5 @@  AVFilter ff_vf_ciescope = {
     .uninit        = uninit,
     .inputs        = inputs,
     .outputs       = outputs,
+    .next          = ff_next_vf_ciescope,
 };
diff --git a/libavfilter/vf_codecview.c b/libavfilter/vf_codecview.c
index 331bfba777..f7ee03d5c1 100644
--- a/libavfilter/vf_codecview.c
+++ b/libavfilter/vf_codecview.c
@@ -310,7 +310,7 @@  static const AVFilterPad codecview_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_codecview = {
+const AVFilter ff_vf_codecview = {
     .name          = "codecview",
     .description   = NULL_IF_CONFIG_SMALL("Visualize information about some codecs."),
     .priv_size     = sizeof(CodecViewContext),
@@ -319,4 +319,5 @@  AVFilter ff_vf_codecview = {
     .outputs       = codecview_outputs,
     .priv_class    = &codecview_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_codecview,
 };
diff --git a/libavfilter/vf_colorbalance.c b/libavfilter/vf_colorbalance.c
index f9965248fe..bc9d1cb199 100644
--- a/libavfilter/vf_colorbalance.c
+++ b/libavfilter/vf_colorbalance.c
@@ -202,7 +202,7 @@  static const AVFilterPad colorbalance_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_colorbalance = {
+const AVFilter ff_vf_colorbalance = {
     .name          = "colorbalance",
     .description   = NULL_IF_CONFIG_SMALL("Adjust the color balance."),
     .priv_size     = sizeof(ColorBalanceContext),
@@ -211,4 +211,5 @@  AVFilter ff_vf_colorbalance = {
     .inputs        = colorbalance_inputs,
     .outputs       = colorbalance_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_colorbalance,
 };
diff --git a/libavfilter/vf_colorchannelmixer.c b/libavfilter/vf_colorchannelmixer.c
index 2e068fa794..e2434713c8 100644
--- a/libavfilter/vf_colorchannelmixer.c
+++ b/libavfilter/vf_colorchannelmixer.c
@@ -349,7 +349,7 @@  static const AVFilterPad colorchannelmixer_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_colorchannelmixer = {
+const AVFilter ff_vf_colorchannelmixer = {
     .name          = "colorchannelmixer",
     .description   = NULL_IF_CONFIG_SMALL("Adjust colors by mixing color channels."),
     .priv_size     = sizeof(ColorChannelMixerContext),
@@ -359,4 +359,5 @@  AVFilter ff_vf_colorchannelmixer = {
     .inputs        = colorchannelmixer_inputs,
     .outputs       = colorchannelmixer_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_colorchannelmixer,
 };
diff --git a/libavfilter/vf_colorkey.c b/libavfilter/vf_colorkey.c
index 3d65e59d42..25dd50f0ee 100644
--- a/libavfilter/vf_colorkey.c
+++ b/libavfilter/vf_colorkey.c
@@ -157,7 +157,7 @@  static const AVOption colorkey_options[] = {
 
 AVFILTER_DEFINE_CLASS(colorkey);
 
-AVFilter ff_vf_colorkey = {
+const AVFilter ff_vf_colorkey = {
     .name          = "colorkey",
     .description   = NULL_IF_CONFIG_SMALL("Turns a certain color into transparency. Operates on RGB colors."),
     .priv_size     = sizeof(ColorkeyContext),
@@ -166,4 +166,5 @@  AVFilter ff_vf_colorkey = {
     .inputs        = colorkey_inputs,
     .outputs       = colorkey_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_colorkey,
 };
diff --git a/libavfilter/vf_colorlevels.c b/libavfilter/vf_colorlevels.c
index 5385a5e754..65129c95bd 100644
--- a/libavfilter/vf_colorlevels.c
+++ b/libavfilter/vf_colorlevels.c
@@ -244,7 +244,7 @@  static const AVFilterPad colorlevels_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_colorlevels = {
+const AVFilter ff_vf_colorlevels = {
     .name          = "colorlevels",
     .description   = NULL_IF_CONFIG_SMALL("Adjust the color levels."),
     .priv_size     = sizeof(ColorLevelsContext),
@@ -253,4 +253,5 @@  AVFilter ff_vf_colorlevels = {
     .inputs        = colorlevels_inputs,
     .outputs       = colorlevels_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_colorlevels,
 };
diff --git a/libavfilter/vf_colormatrix.c b/libavfilter/vf_colormatrix.c
index 3a02e2b86e..6dcda9da7d 100644
--- a/libavfilter/vf_colormatrix.c
+++ b/libavfilter/vf_colormatrix.c
@@ -507,7 +507,7 @@  static const AVFilterPad colormatrix_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_colormatrix = {
+const AVFilter ff_vf_colormatrix = {
     .name          = "colormatrix",
     .description   = NULL_IF_CONFIG_SMALL("Convert color matrix."),
     .priv_size     = sizeof(ColorMatrixContext),
@@ -517,4 +517,5 @@  AVFilter ff_vf_colormatrix = {
     .outputs       = colormatrix_outputs,
     .priv_class    = &colormatrix_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_colormatrix,
 };
diff --git a/libavfilter/vf_colorspace.c b/libavfilter/vf_colorspace.c
index 71ea08a20f..1b218a5db7 100644
--- a/libavfilter/vf_colorspace.c
+++ b/libavfilter/vf_colorspace.c
@@ -1202,7 +1202,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_colorspace = {
+const AVFilter ff_vf_colorspace = {
     .name            = "colorspace",
     .description     = NULL_IF_CONFIG_SMALL("Convert between colorspaces."),
     .init            = init,
@@ -1213,4 +1213,5 @@  AVFilter ff_vf_colorspace = {
     .inputs          = inputs,
     .outputs         = outputs,
     .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next            = ff_next_vf_colorspace,
 };
diff --git a/libavfilter/vf_convolution.c b/libavfilter/vf_convolution.c
index d7f8bf8a65..c5b4d112cb 100644
--- a/libavfilter/vf_convolution.c
+++ b/libavfilter/vf_convolution.c
@@ -1052,7 +1052,7 @@  static const AVFilterPad convolution_outputs[] = {
 
 #if CONFIG_CONVOLUTION_FILTER
 
-AVFilter ff_vf_convolution = {
+const AVFilter ff_vf_convolution = {
     .name          = "convolution",
     .description   = NULL_IF_CONFIG_SMALL("Apply convolution filter."),
     .priv_size     = sizeof(ConvolutionContext),
@@ -1063,6 +1063,7 @@  AVFilter ff_vf_convolution = {
     .inputs        = convolution_inputs,
     .outputs       = convolution_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_convolution,
 };
 
 #endif /* CONFIG_CONVOLUTION_FILTER */
@@ -1078,7 +1079,7 @@  static const AVOption prewitt_options[] = {
 
 AVFILTER_DEFINE_CLASS(prewitt);
 
-AVFilter ff_vf_prewitt = {
+const AVFilter ff_vf_prewitt = {
     .name          = "prewitt",
     .description   = NULL_IF_CONFIG_SMALL("Apply prewitt operator."),
     .priv_size     = sizeof(ConvolutionContext),
@@ -1089,6 +1090,7 @@  AVFilter ff_vf_prewitt = {
     .inputs        = convolution_inputs,
     .outputs       = convolution_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_prewitt,
 };
 
 #endif /* CONFIG_PREWITT_FILTER */
@@ -1104,7 +1106,7 @@  static const AVOption sobel_options[] = {
 
 AVFILTER_DEFINE_CLASS(sobel);
 
-AVFilter ff_vf_sobel = {
+const AVFilter ff_vf_sobel = {
     .name          = "sobel",
     .description   = NULL_IF_CONFIG_SMALL("Apply sobel operator."),
     .priv_size     = sizeof(ConvolutionContext),
@@ -1115,6 +1117,7 @@  AVFilter ff_vf_sobel = {
     .inputs        = convolution_inputs,
     .outputs       = convolution_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_sobel,
 };
 
 #endif /* CONFIG_SOBEL_FILTER */
@@ -1130,7 +1133,7 @@  static const AVOption roberts_options[] = {
 
 AVFILTER_DEFINE_CLASS(roberts);
 
-AVFilter ff_vf_roberts = {
+const AVFilter ff_vf_roberts = {
     .name          = "roberts",
     .description   = NULL_IF_CONFIG_SMALL("Apply roberts cross operator."),
     .priv_size     = sizeof(ConvolutionContext),
@@ -1141,6 +1144,7 @@  AVFilter ff_vf_roberts = {
     .inputs        = convolution_inputs,
     .outputs       = convolution_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_roberts,
 };
 
 #endif /* CONFIG_ROBERTS_FILTER */
diff --git a/libavfilter/vf_convolve.c b/libavfilter/vf_convolve.c
index 982eda1cbc..4ba0e18c9b 100644
--- a/libavfilter/vf_convolve.c
+++ b/libavfilter/vf_convolve.c
@@ -623,7 +623,7 @@  static const AVFilterPad convolve_outputs[] = {
 
 FRAMESYNC_DEFINE_CLASS(convolve, ConvolveContext, fs);
 
-AVFilter ff_vf_convolve = {
+const AVFilter ff_vf_convolve = {
     .name          = "convolve",
     .description   = NULL_IF_CONFIG_SMALL("Convolve first video stream with second video stream."),
     .preinit       = convolve_framesync_preinit,
@@ -636,6 +636,7 @@  AVFilter ff_vf_convolve = {
     .inputs        = convolve_inputs,
     .outputs       = convolve_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_convolve,
 };
 
 #endif /* CONFIG_CONVOLVE_FILTER */
@@ -653,7 +654,7 @@  static const AVOption deconvolve_options[] = {
 
 FRAMESYNC_DEFINE_CLASS(deconvolve, ConvolveContext, fs);
 
-AVFilter ff_vf_deconvolve = {
+const AVFilter ff_vf_deconvolve = {
     .name          = "deconvolve",
     .description   = NULL_IF_CONFIG_SMALL("Deconvolve first video stream with second video stream."),
     .preinit       = deconvolve_framesync_preinit,
@@ -666,6 +667,7 @@  AVFilter ff_vf_deconvolve = {
     .inputs        = convolve_inputs,
     .outputs       = convolve_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_deconvolve,
 };
 
 #endif /* CONFIG_DECONVOLVE_FILTER */
diff --git a/libavfilter/vf_copy.c b/libavfilter/vf_copy.c
index b0159cff00..57c0628823 100644
--- a/libavfilter/vf_copy.c
+++ b/libavfilter/vf_copy.c
@@ -76,10 +76,11 @@  static const AVFilterPad avfilter_vf_copy_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_copy = {
+const AVFilter ff_vf_copy = {
     .name        = "copy",
     .description = NULL_IF_CONFIG_SMALL("Copy the input video unchanged to the output."),
     .inputs      = avfilter_vf_copy_inputs,
     .outputs     = avfilter_vf_copy_outputs,
     .query_formats = query_formats,
+    .next        = ff_next_vf_copy,
 };
diff --git a/libavfilter/vf_coreimage.m b/libavfilter/vf_coreimage.m
index 323a28caa1..9c90f7fa21 100644
--- a/libavfilter/vf_coreimage.m
+++ b/libavfilter/vf_coreimage.m
@@ -654,7 +654,7 @@  static const AVOption coreimage_options[] = {
 
 AVFILTER_DEFINE_CLASS(coreimage);
 
-AVFilter ff_vf_coreimage = {
+const AVFilter ff_vf_coreimage = {
     .name          = "coreimage",
     .description   = NULL_IF_CONFIG_SMALL("Video filtering using CoreImage API."),
     .init          = init,
@@ -664,6 +664,7 @@  AVFilter ff_vf_coreimage = {
     .inputs        = vf_coreimage_inputs,
     .outputs       = vf_coreimage_outputs,
     .query_formats = query_formats,
+    .next          = ff_next_vf_coreimage,
 };
 
 // definitions for coreimagesrc video source
@@ -675,7 +676,7 @@  static const AVOption coreimagesrc_options[] = {
 
 AVFILTER_DEFINE_CLASS(coreimagesrc);
 
-AVFilter ff_vsrc_coreimagesrc = {
+const AVFilter ff_vsrc_coreimagesrc = {
     .name          = "coreimagesrc",
     .description   = NULL_IF_CONFIG_SMALL("Video source using image generators of CoreImage API."),
     .init          = init_src,
@@ -685,4 +686,5 @@  AVFilter ff_vsrc_coreimagesrc = {
     .inputs        = NULL,
     .outputs       = vsrc_coreimagesrc_outputs,
     .query_formats = query_formats_src,
+    .next          = ff_next_vsrc_coreimagesrc,
 };
diff --git a/libavfilter/vf_cover_rect.c b/libavfilter/vf_cover_rect.c
index f7f61038e3..7a0af21300 100644
--- a/libavfilter/vf_cover_rect.c
+++ b/libavfilter/vf_cover_rect.c
@@ -247,7 +247,7 @@  static const AVFilterPad cover_rect_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_cover_rect = {
+const AVFilter ff_vf_cover_rect = {
     .name            = "cover_rect",
     .description     = NULL_IF_CONFIG_SMALL("Find and cover a user specified object."),
     .priv_size       = sizeof(CoverContext),
@@ -257,4 +257,5 @@  AVFilter ff_vf_cover_rect = {
     .inputs          = cover_rect_inputs,
     .outputs         = cover_rect_outputs,
     .priv_class      = &cover_rect_class,
+    .next            = ff_next_vf_cover_rect,
 };
diff --git a/libavfilter/vf_crop.c b/libavfilter/vf_crop.c
index 7c31c1665d..b1cafa8289 100644
--- a/libavfilter/vf_crop.c
+++ b/libavfilter/vf_crop.c
@@ -377,7 +377,7 @@  static const AVFilterPad avfilter_vf_crop_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_crop = {
+const AVFilter ff_vf_crop = {
     .name            = "crop",
     .description     = NULL_IF_CONFIG_SMALL("Crop the input video."),
     .priv_size       = sizeof(CropContext),
@@ -387,4 +387,5 @@  AVFilter ff_vf_crop = {
     .inputs          = avfilter_vf_crop_inputs,
     .outputs         = avfilter_vf_crop_outputs,
     .process_command = process_command,
+    .next            = ff_next_vf_crop,
 };
diff --git a/libavfilter/vf_cropdetect.c b/libavfilter/vf_cropdetect.c
index 7c7d0b953a..c2097f57c7 100644
--- a/libavfilter/vf_cropdetect.c
+++ b/libavfilter/vf_cropdetect.c
@@ -272,7 +272,7 @@  static const AVFilterPad avfilter_vf_cropdetect_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_cropdetect = {
+const AVFilter ff_vf_cropdetect = {
     .name          = "cropdetect",
     .description   = NULL_IF_CONFIG_SMALL("Auto-detect crop size."),
     .priv_size     = sizeof(CropDetectContext),
@@ -282,4 +282,5 @@  AVFilter ff_vf_cropdetect = {
     .inputs        = avfilter_vf_cropdetect_inputs,
     .outputs       = avfilter_vf_cropdetect_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_cropdetect,
 };
diff --git a/libavfilter/vf_curves.c b/libavfilter/vf_curves.c
index 19ab789152..1ee47401ec 100644
--- a/libavfilter/vf_curves.c
+++ b/libavfilter/vf_curves.c
@@ -682,7 +682,7 @@  static const AVFilterPad curves_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_curves = {
+const AVFilter ff_vf_curves = {
     .name          = "curves",
     .description   = NULL_IF_CONFIG_SMALL("Adjust components curves."),
     .priv_size     = sizeof(CurvesContext),
@@ -693,4 +693,5 @@  AVFilter ff_vf_curves = {
     .outputs       = curves_outputs,
     .priv_class    = &curves_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_curves,
 };
diff --git a/libavfilter/vf_datascope.c b/libavfilter/vf_datascope.c
index 467663556e..bb9e2befe2 100644
--- a/libavfilter/vf_datascope.c
+++ b/libavfilter/vf_datascope.c
@@ -390,6 +390,7 @@  static int config_output(AVFilterLink *outlink)
     return 0;
 }
 
+#if CONFIG_DATASCOPE_FILTER
 static const AVFilterPad inputs[] = {
     {
         .name         = "default",
@@ -409,7 +410,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_datascope = {
+const AVFilter ff_vf_datascope = {
     .name          = "datascope",
     .description   = NULL_IF_CONFIG_SMALL("Video data analysis."),
     .priv_size     = sizeof(DatascopeContext),
@@ -418,7 +419,9 @@  AVFilter ff_vf_datascope = {
     .inputs        = inputs,
     .outputs       = outputs,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_datascope,
 };
+#endif /* CONFIG_DATASCOPE_FILTER */
 
 typedef struct PixscopeContext {
     const AVClass *class;
@@ -642,6 +645,7 @@  static int pixscope_filter_frame(AVFilterLink *inlink, AVFrame *in)
     return ff_filter_frame(outlink, out);
 }
 
+#if CONFIG_PIXSCOPE_FILTER
 static const AVFilterPad pixscope_inputs[] = {
     {
         .name           = "default",
@@ -660,7 +664,7 @@  static const AVFilterPad pixscope_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_pixscope = {
+const AVFilter ff_vf_pixscope = {
     .name          = "pixscope",
     .description   = NULL_IF_CONFIG_SMALL("Pixel data analysis."),
     .priv_size     = sizeof(PixscopeContext),
@@ -669,7 +673,9 @@  AVFilter ff_vf_pixscope = {
     .inputs        = pixscope_inputs,
     .outputs       = pixscope_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_pixscope,
 };
+#endif /* CONFIG_PIXSCOPE_FILTER */
 
 typedef struct PixelValues {
     uint16_t p[4];
@@ -1022,6 +1028,7 @@  static int oscilloscope_filter_frame(AVFilterLink *inlink, AVFrame *frame)
     return ff_filter_frame(outlink, frame);
 }
 
+#if CONFIG_OSCILLOSCOPE_FILTER
 static const AVFilterPad oscilloscope_inputs[] = {
     {
         .name           = "default",
@@ -1041,7 +1048,7 @@  static const AVFilterPad oscilloscope_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_oscilloscope = {
+const AVFilter ff_vf_oscilloscope = {
     .name          = "oscilloscope",
     .description   = NULL_IF_CONFIG_SMALL("2D Video Oscilloscope."),
     .priv_size     = sizeof(OscilloscopeContext),
@@ -1051,4 +1058,6 @@  AVFilter ff_vf_oscilloscope = {
     .inputs        = oscilloscope_inputs,
     .outputs       = oscilloscope_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_oscilloscope,
 };
+#endif /* CONFIG_OSCILLOSCOPE_FILTER */
diff --git a/libavfilter/vf_dctdnoiz.c b/libavfilter/vf_dctdnoiz.c
index cdbe5f853f..38228f472a 100644
--- a/libavfilter/vf_dctdnoiz.c
+++ b/libavfilter/vf_dctdnoiz.c
@@ -821,7 +821,7 @@  static const AVFilterPad dctdnoiz_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_dctdnoiz = {
+const AVFilter ff_vf_dctdnoiz = {
     .name          = "dctdnoiz",
     .description   = NULL_IF_CONFIG_SMALL("Denoise frames using 2D DCT."),
     .priv_size     = sizeof(DCTdnoizContext),
@@ -832,4 +832,5 @@  AVFilter ff_vf_dctdnoiz = {
     .outputs       = dctdnoiz_outputs,
     .priv_class    = &dctdnoiz_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_dctdnoiz,
 };
diff --git a/libavfilter/vf_deband.c b/libavfilter/vf_deband.c
index 713e80b049..5d3c05491b 100644
--- a/libavfilter/vf_deband.c
+++ b/libavfilter/vf_deband.c
@@ -457,7 +457,7 @@  static const AVFilterPad avfilter_vf_deband_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_deband = {
+const AVFilter ff_vf_deband = {
     .name          = "deband",
     .description   = NULL_IF_CONFIG_SMALL("Debands video."),
     .priv_size     = sizeof(DebandContext),
@@ -467,4 +467,5 @@  AVFilter ff_vf_deband = {
     .inputs        = avfilter_vf_deband_inputs,
     .outputs       = avfilter_vf_deband_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_deband,
 };
diff --git a/libavfilter/vf_decimate.c b/libavfilter/vf_decimate.c
index 53347c7f10..e9b9600c63 100644
--- a/libavfilter/vf_decimate.c
+++ b/libavfilter/vf_decimate.c
@@ -404,7 +404,7 @@  static const AVFilterPad decimate_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_decimate = {
+const AVFilter ff_vf_decimate = {
     .name          = "decimate",
     .description   = NULL_IF_CONFIG_SMALL("Decimate frames (post field matching filter)."),
     .init          = decimate_init,
@@ -414,4 +414,5 @@  AVFilter ff_vf_decimate = {
     .outputs       = decimate_outputs,
     .priv_class    = &decimate_class,
     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next          = ff_next_vf_decimate,
 };
diff --git a/libavfilter/vf_deflicker.c b/libavfilter/vf_deflicker.c
index 863a3508c1..f6d582285e 100644
--- a/libavfilter/vf_deflicker.c
+++ b/libavfilter/vf_deflicker.c
@@ -459,7 +459,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_deflicker = {
+const AVFilter ff_vf_deflicker = {
     .name          = "deflicker",
     .description   = NULL_IF_CONFIG_SMALL("Remove temporal frame luminance variations."),
     .priv_size     = sizeof(DeflickerContext),
@@ -468,4 +468,5 @@  AVFilter ff_vf_deflicker = {
     .query_formats = query_formats,
     .inputs        = inputs,
     .outputs       = outputs,
+    .next          = ff_next_vf_deflicker,
 };
diff --git a/libavfilter/vf_deinterlace_qsv.c b/libavfilter/vf_deinterlace_qsv.c
index 897e6bd622..99559d1fdb 100644
--- a/libavfilter/vf_deinterlace_qsv.c
+++ b/libavfilter/vf_deinterlace_qsv.c
@@ -572,7 +572,7 @@  static const AVFilterPad qsvdeint_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_deinterlace_qsv = {
+const AVFilter ff_vf_deinterlace_qsv = {
     .name      = "deinterlace_qsv",
     .description = NULL_IF_CONFIG_SMALL("QuickSync video deinterlacing"),
 
@@ -586,4 +586,5 @@  AVFilter ff_vf_deinterlace_qsv = {
     .outputs   = qsvdeint_outputs,
 
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next           = ff_next_vf_deinterlace_qsv,
 };
diff --git a/libavfilter/vf_deinterlace_vaapi.c b/libavfilter/vf_deinterlace_vaapi.c
index f7a262d0c6..21acf5cb21 100644
--- a/libavfilter/vf_deinterlace_vaapi.c
+++ b/libavfilter/vf_deinterlace_vaapi.c
@@ -403,7 +403,7 @@  static const AVFilterPad deint_vaapi_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_deinterlace_vaapi = {
+const AVFilter ff_vf_deinterlace_vaapi = {
     .name           = "deinterlace_vaapi",
     .description    = NULL_IF_CONFIG_SMALL("Deinterlacing of VAAPI surfaces"),
     .priv_size      = sizeof(DeintVAAPIContext),
@@ -414,4 +414,5 @@  AVFilter ff_vf_deinterlace_vaapi = {
     .outputs        = deint_vaapi_outputs,
     .priv_class     = &deint_vaapi_class,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next           = ff_next_vf_deinterlace_vaapi,
 };
diff --git a/libavfilter/vf_dejudder.c b/libavfilter/vf_dejudder.c
index 4705cb6121..df2357d539 100644
--- a/libavfilter/vf_dejudder.c
+++ b/libavfilter/vf_dejudder.c
@@ -175,7 +175,7 @@  static const AVFilterPad dejudder_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_dejudder = {
+const AVFilter ff_vf_dejudder = {
     .name        = "dejudder",
     .description = NULL_IF_CONFIG_SMALL("Remove judder produced by pullup."),
     .priv_size   = sizeof(DejudderContext),
@@ -184,4 +184,5 @@  AVFilter ff_vf_dejudder = {
     .outputs     = dejudder_outputs,
     .init        = dejudder_init,
     .uninit      = dejudder_uninit,
+    .next        = ff_next_vf_dejudder,
 };
diff --git a/libavfilter/vf_delogo.c b/libavfilter/vf_delogo.c
index 065d093641..6c1c2b20e3 100644
--- a/libavfilter/vf_delogo.c
+++ b/libavfilter/vf_delogo.c
@@ -311,7 +311,7 @@  static const AVFilterPad avfilter_vf_delogo_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_delogo = {
+const AVFilter ff_vf_delogo = {
     .name          = "delogo",
     .description   = NULL_IF_CONFIG_SMALL("Remove logo from input video."),
     .priv_size     = sizeof(DelogoContext),
@@ -321,4 +321,5 @@  AVFilter ff_vf_delogo = {
     .inputs        = avfilter_vf_delogo_inputs,
     .outputs       = avfilter_vf_delogo_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_delogo,
 };
diff --git a/libavfilter/vf_deshake.c b/libavfilter/vf_deshake.c
index fb4eb355b8..c976993019 100644
--- a/libavfilter/vf_deshake.c
+++ b/libavfilter/vf_deshake.c
@@ -545,7 +545,7 @@  static const AVFilterPad deshake_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_deshake = {
+const AVFilter ff_vf_deshake = {
     .name          = "deshake",
     .description   = NULL_IF_CONFIG_SMALL("Stabilize shaky video."),
     .priv_size     = sizeof(DeshakeContext),
@@ -555,4 +555,5 @@  AVFilter ff_vf_deshake = {
     .inputs        = deshake_inputs,
     .outputs       = deshake_outputs,
     .priv_class    = &deshake_class,
+    .next          = ff_next_vf_deshake,
 };
diff --git a/libavfilter/vf_despill.c b/libavfilter/vf_despill.c
index 64c27f4060..ae935e85c7 100644
--- a/libavfilter/vf_despill.c
+++ b/libavfilter/vf_despill.c
@@ -171,7 +171,7 @@  static const AVOption despill_options[] = {
 
 AVFILTER_DEFINE_CLASS(despill);
 
-AVFilter ff_vf_despill = {
+const AVFilter ff_vf_despill = {
     .name          = "despill",
     .description   = NULL_IF_CONFIG_SMALL("Despill video."),
     .priv_size     = sizeof(DespillContext),
@@ -180,4 +180,5 @@  AVFilter ff_vf_despill = {
     .inputs        = despill_inputs,
     .outputs       = despill_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_despill,
 };
diff --git a/libavfilter/vf_detelecine.c b/libavfilter/vf_detelecine.c
index 0d1e2f2ffb..1722740fed 100644
--- a/libavfilter/vf_detelecine.c
+++ b/libavfilter/vf_detelecine.c
@@ -373,7 +373,7 @@  static const AVFilterPad detelecine_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_detelecine = {
+const AVFilter ff_vf_detelecine = {
     .name          = "detelecine",
     .description   = NULL_IF_CONFIG_SMALL("Apply an inverse telecine pattern."),
     .priv_size     = sizeof(DetelecineContext),
@@ -383,4 +383,5 @@  AVFilter ff_vf_detelecine = {
     .query_formats = query_formats,
     .inputs        = detelecine_inputs,
     .outputs       = detelecine_outputs,
+    .next          = ff_next_vf_detelecine,
 };
diff --git a/libavfilter/vf_displace.c b/libavfilter/vf_displace.c
index 768af6def4..a8aec44768 100644
--- a/libavfilter/vf_displace.c
+++ b/libavfilter/vf_displace.c
@@ -398,7 +398,7 @@  static const AVFilterPad displace_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_displace = {
+const AVFilter ff_vf_displace = {
     .name          = "displace",
     .description   = NULL_IF_CONFIG_SMALL("Displace pixels."),
     .priv_size     = sizeof(DisplaceContext),
@@ -409,4 +409,5 @@  AVFilter ff_vf_displace = {
     .outputs       = displace_outputs,
     .priv_class    = &displace_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_vf_displace,
 };
diff --git a/libavfilter/vf_drawbox.c b/libavfilter/vf_drawbox.c
index c9cb63dbd1..72bcf79e92 100644
--- a/libavfilter/vf_drawbox.c
+++ b/libavfilter/vf_drawbox.c
@@ -312,7 +312,7 @@  static const AVFilterPad drawbox_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_drawbox = {
+const AVFilter ff_vf_drawbox = {
     .name          = "drawbox",
     .description   = NULL_IF_CONFIG_SMALL("Draw a colored box on the input video."),
     .priv_size     = sizeof(DrawBoxContext),
@@ -322,6 +322,7 @@  AVFilter ff_vf_drawbox = {
     .inputs        = drawbox_inputs,
     .outputs       = drawbox_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_drawbox,
 };
 #endif /* CONFIG_DRAWBOX_FILTER */
 
@@ -445,7 +446,7 @@  static const AVFilterPad drawgrid_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_drawgrid = {
+const AVFilter ff_vf_drawgrid = {
     .name          = "drawgrid",
     .description   = NULL_IF_CONFIG_SMALL("Draw a colored grid on the input video."),
     .priv_size     = sizeof(DrawBoxContext),
@@ -455,6 +456,7 @@  AVFilter ff_vf_drawgrid = {
     .inputs        = drawgrid_inputs,
     .outputs       = drawgrid_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_drawgrid,
 };
 
 #endif  /* CONFIG_DRAWGRID_FILTER */
diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
index f97a741b50..350fc9af47 100644
--- a/libavfilter/vf_drawtext.c
+++ b/libavfilter/vf_drawtext.c
@@ -1509,7 +1509,7 @@  static const AVFilterPad avfilter_vf_drawtext_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_drawtext = {
+const AVFilter ff_vf_drawtext = {
     .name          = "drawtext",
     .description   = NULL_IF_CONFIG_SMALL("Draw text on top of video frames using libfreetype library."),
     .priv_size     = sizeof(DrawTextContext),
@@ -1521,4 +1521,5 @@  AVFilter ff_vf_drawtext = {
     .outputs       = avfilter_vf_drawtext_outputs,
     .process_command = command,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_drawtext,
 };
diff --git a/libavfilter/vf_edgedetect.c b/libavfilter/vf_edgedetect.c
index 173f9fe161..739d11d9d9 100644
--- a/libavfilter/vf_edgedetect.c
+++ b/libavfilter/vf_edgedetect.c
@@ -385,7 +385,7 @@  static const AVFilterPad edgedetect_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_edgedetect = {
+const AVFilter ff_vf_edgedetect = {
     .name          = "edgedetect",
     .description   = NULL_IF_CONFIG_SMALL("Detect and draw edge."),
     .priv_size     = sizeof(EdgeDetectContext),
@@ -396,4 +396,5 @@  AVFilter ff_vf_edgedetect = {
     .outputs       = edgedetect_outputs,
     .priv_class    = &edgedetect_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_edgedetect,
 };
diff --git a/libavfilter/vf_elbg.c b/libavfilter/vf_elbg.c
index 396af82f77..dc83fb9cc0 100644
--- a/libavfilter/vf_elbg.c
+++ b/libavfilter/vf_elbg.c
@@ -251,7 +251,7 @@  static const AVFilterPad elbg_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_elbg = {
+const AVFilter ff_vf_elbg = {
     .name          = "elbg",
     .description   = NULL_IF_CONFIG_SMALL("Apply posterize effect, using the ELBG algorithm."),
     .priv_size     = sizeof(ELBGContext),
@@ -261,4 +261,5 @@  AVFilter ff_vf_elbg = {
     .uninit        = uninit,
     .inputs        = elbg_inputs,
     .outputs       = elbg_outputs,
+    .next          = ff_next_vf_elbg,
 };
diff --git a/libavfilter/vf_entropy.c b/libavfilter/vf_entropy.c
index e6002ce27a..b48217d91b 100644
--- a/libavfilter/vf_entropy.c
+++ b/libavfilter/vf_entropy.c
@@ -193,7 +193,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_entropy = {
+const AVFilter ff_vf_entropy = {
     .name           = "entropy",
     .description    = NULL_IF_CONFIG_SMALL("Measure video frames entropy."),
     .priv_size      = sizeof(EntropyContext),
@@ -203,4 +203,5 @@  AVFilter ff_vf_entropy = {
     .outputs        = outputs,
     .priv_class     = &entropy_class,
     .flags          = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next           = ff_next_vf_entropy,
 };
diff --git a/libavfilter/vf_eq.c b/libavfilter/vf_eq.c
index 2c4c7e4d54..2710192c6b 100644
--- a/libavfilter/vf_eq.c
+++ b/libavfilter/vf_eq.c
@@ -376,7 +376,7 @@  static const AVOption eq_options[] = {
 
 AVFILTER_DEFINE_CLASS(eq);
 
-AVFilter ff_vf_eq = {
+const AVFilter ff_vf_eq = {
     .name            = "eq",
     .description     = NULL_IF_CONFIG_SMALL("Adjust brightness, contrast, gamma, and saturation."),
     .priv_size       = sizeof(EQContext),
@@ -388,4 +388,5 @@  AVFilter ff_vf_eq = {
     .init            = initialize,
     .uninit          = uninit,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next            = ff_next_vf_eq,
 };
diff --git a/libavfilter/vf_extractplanes.c b/libavfilter/vf_extractplanes.c
index fc676a25fa..20411809f9 100644
--- a/libavfilter/vf_extractplanes.c
+++ b/libavfilter/vf_extractplanes.c
@@ -392,7 +392,7 @@  static const AVFilterPad extractplanes_inputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_extractplanes = {
+const AVFilter ff_vf_extractplanes = {
     .name          = "extractplanes",
     .description   = NULL_IF_CONFIG_SMALL("Extract planes as grayscale frames."),
     .priv_size     = sizeof(ExtractPlanesContext),
@@ -403,6 +403,7 @@  AVFilter ff_vf_extractplanes = {
     .inputs        = extractplanes_inputs,
     .outputs       = NULL,
     .flags         = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+    .next          = ff_next_vf_extractplanes,
 };
 
 #if CONFIG_ALPHAEXTRACT_FILTER
@@ -416,7 +417,7 @@  static av_cold int init_alphaextract(AVFilterContext *ctx)
     return init(ctx);
 }
 
-AVFilter ff_vf_alphaextract = {
+const AVFilter ff_vf_alphaextract = {
     .name           = "alphaextract",
     .description    = NULL_IF_CONFIG_SMALL("Extract an alpha channel as a "
                       "grayscale image component."),
@@ -427,5 +428,6 @@  AVFilter ff_vf_alphaextract = {
     .inputs         = extractplanes_inputs,
     .outputs        = NULL,
     .flags          = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
+    .next           = ff_next_vf_alphaextract,
 };
 #endif  /* CONFIG_ALPHAEXTRACT_FILTER */
diff --git a/libavfilter/vf_fade.c b/libavfilter/vf_fade.c
index c30c41db0d..ddfc427b15 100644
--- a/libavfilter/vf_fade.c
+++ b/libavfilter/vf_fade.c
@@ -419,7 +419,7 @@  static const AVFilterPad avfilter_vf_fade_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_fade = {
+const AVFilter ff_vf_fade = {
     .name          = "fade",
     .description   = NULL_IF_CONFIG_SMALL("Fade in/out input video."),
     .init          = init,
@@ -429,4 +429,5 @@  AVFilter ff_vf_fade = {
     .inputs        = avfilter_vf_fade_inputs,
     .outputs       = avfilter_vf_fade_outputs,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_fade,
 };
diff --git a/libavfilter/vf_fftfilt.c b/libavfilter/vf_fftfilt.c
index af44b1e22e..4b6d2b46c7 100644
--- a/libavfilter/vf_fftfilt.c
+++ b/libavfilter/vf_fftfilt.c
@@ -428,7 +428,7 @@  static const AVFilterPad fftfilt_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_fftfilt = {
+const AVFilter ff_vf_fftfilt = {
     .name            = "fftfilt",
     .description     = NULL_IF_CONFIG_SMALL("Apply arbitrary expressions to pixels in frequency domain."),
     .priv_size       = sizeof(FFTFILTContext),
@@ -439,4 +439,5 @@  AVFilter ff_vf_fftfilt = {
     .init            = initialize,
     .uninit          = uninit,
     .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next            = ff_next_vf_fftfilt,
 };
diff --git a/libavfilter/vf_field.c b/libavfilter/vf_field.c
index 2f4eb679ea..1e3f22c976 100644
--- a/libavfilter/vf_field.c
+++ b/libavfilter/vf_field.c
@@ -101,11 +101,12 @@  static const AVFilterPad field_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_field = {
+const AVFilter ff_vf_field = {
     .name        = "field",
     .description = NULL_IF_CONFIG_SMALL("Extract a field from the input video."),
     .priv_size   = sizeof(FieldContext),
     .inputs      = field_inputs,
     .outputs     = field_outputs,
     .priv_class  = &field_class,
+    .next        = ff_next_vf_field,
 };
diff --git a/libavfilter/vf_fieldhint.c b/libavfilter/vf_fieldhint.c
index 3cfeb20a38..2b6f79e431 100644
--- a/libavfilter/vf_fieldhint.c
+++ b/libavfilter/vf_fieldhint.c
@@ -271,7 +271,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_fieldhint = {
+const AVFilter ff_vf_fieldhint = {
     .name          = "fieldhint",
     .description   = NULL_IF_CONFIG_SMALL("Field matching using hints."),
     .priv_size     = sizeof(FieldHintContext),
@@ -281,4 +281,5 @@  AVFilter ff_vf_fieldhint = {
     .query_formats = query_formats,
     .inputs        = inputs,
     .outputs       = outputs,
+    .next          = ff_next_vf_fieldhint,
 };
diff --git a/libavfilter/vf_fieldmatch.c b/libavfilter/vf_fieldmatch.c
index 3694f26d33..bbe677e827 100644
--- a/libavfilter/vf_fieldmatch.c
+++ b/libavfilter/vf_fieldmatch.c
@@ -981,7 +981,7 @@  static const AVFilterPad fieldmatch_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_fieldmatch = {
+const AVFilter ff_vf_fieldmatch = {
     .name           = "fieldmatch",
     .description    = NULL_IF_CONFIG_SMALL("Field matching for inverse telecine."),
     .query_formats  = query_formats,
@@ -992,4 +992,5 @@  AVFilter ff_vf_fieldmatch = {
     .outputs        = fieldmatch_outputs,
     .priv_class     = &fieldmatch_class,
     .flags          = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next           = ff_next_vf_fieldmatch,
 };
diff --git a/libavfilter/vf_fieldorder.c b/libavfilter/vf_fieldorder.c
index ca55ff1f66..9fea529ebd 100644
--- a/libavfilter/vf_fieldorder.c
+++ b/libavfilter/vf_fieldorder.c
@@ -184,7 +184,7 @@  static const AVFilterPad avfilter_vf_fieldorder_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_fieldorder = {
+const AVFilter ff_vf_fieldorder = {
     .name          = "fieldorder",
     .description   = NULL_IF_CONFIG_SMALL("Set the field order."),
     .priv_size     = sizeof(FieldOrderContext),
@@ -193,4 +193,5 @@  AVFilter ff_vf_fieldorder = {
     .inputs        = avfilter_vf_fieldorder_inputs,
     .outputs       = avfilter_vf_fieldorder_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_fieldorder,
 };
diff --git a/libavfilter/vf_fillborders.c b/libavfilter/vf_fillborders.c
index df883bc62e..5e43188828 100644
--- a/libavfilter/vf_fillborders.c
+++ b/libavfilter/vf_fillborders.c
@@ -382,7 +382,7 @@  static const AVFilterPad fillborders_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_fillborders = {
+const AVFilter ff_vf_fillborders = {
     .name          = "fillborders",
     .description   = NULL_IF_CONFIG_SMALL("Fill borders of the input video."),
     .priv_size     = sizeof(FillBordersContext),
@@ -391,4 +391,5 @@  AVFilter ff_vf_fillborders = {
     .inputs        = fillborders_inputs,
     .outputs       = fillborders_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_fillborders,
 };
diff --git a/libavfilter/vf_find_rect.c b/libavfilter/vf_find_rect.c
index d7e6579af7..8ffc9d6fbf 100644
--- a/libavfilter/vf_find_rect.c
+++ b/libavfilter/vf_find_rect.c
@@ -292,7 +292,7 @@  static const AVFilterPad foc_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_find_rect = {
+const AVFilter ff_vf_find_rect = {
     .name            = "find_rect",
     .description     = NULL_IF_CONFIG_SMALL("Find a user specified object."),
     .priv_size       = sizeof(FOCContext),
@@ -302,4 +302,5 @@  AVFilter ff_vf_find_rect = {
     .inputs          = foc_inputs,
     .outputs         = foc_outputs,
     .priv_class      = &find_rect_class,
+    .next            = ff_next_vf_find_rect,
 };
diff --git a/libavfilter/vf_floodfill.c b/libavfilter/vf_floodfill.c
index 323dd0e2fa..ba59083fbc 100644
--- a/libavfilter/vf_floodfill.c
+++ b/libavfilter/vf_floodfill.c
@@ -420,7 +420,7 @@  static const AVOption floodfill_options[] = {
 
 AVFILTER_DEFINE_CLASS(floodfill);
 
-AVFilter ff_vf_floodfill = {
+const AVFilter ff_vf_floodfill = {
     .name          = "floodfill",
     .description   = NULL_IF_CONFIG_SMALL("Fill area with same color with another color."),
     .priv_size     = sizeof(FloodfillContext),
@@ -430,4 +430,5 @@  AVFilter ff_vf_floodfill = {
     .inputs        = floodfill_inputs,
     .outputs       = floodfill_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_floodfill,
 };
diff --git a/libavfilter/vf_format.c b/libavfilter/vf_format.c
index a57c99d797..4cfc593446 100644
--- a/libavfilter/vf_format.c
+++ b/libavfilter/vf_format.c
@@ -166,7 +166,7 @@  static const AVFilterPad avfilter_vf_format_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_format = {
+const AVFilter ff_vf_format = {
     .name          = "format",
     .description   = NULL_IF_CONFIG_SMALL("Convert the input video to one of the specified pixel formats."),
 
@@ -180,6 +180,7 @@  AVFilter ff_vf_format = {
 
     .inputs        = avfilter_vf_format_inputs,
     .outputs       = avfilter_vf_format_outputs,
+    .next          = ff_next_vf_format,
 };
 #endif /* CONFIG_FORMAT_FILTER */
 
@@ -205,7 +206,7 @@  static const AVFilterPad avfilter_vf_noformat_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_noformat = {
+const AVFilter ff_vf_noformat = {
     .name          = "noformat",
     .description   = NULL_IF_CONFIG_SMALL("Force libavfilter not to use any of the specified pixel formats for the input to the next filter."),
 
@@ -219,5 +220,6 @@  AVFilter ff_vf_noformat = {
 
     .inputs        = avfilter_vf_noformat_inputs,
     .outputs       = avfilter_vf_noformat_outputs,
+    .next          = ff_next_vf_noformat,
 };
 #endif /* CONFIG_NOFORMAT_FILTER */
diff --git a/libavfilter/vf_fps.c b/libavfilter/vf_fps.c
index dbafd2c35a..7989dadc2c 100644
--- a/libavfilter/vf_fps.c
+++ b/libavfilter/vf_fps.c
@@ -330,7 +330,7 @@  static const AVFilterPad avfilter_vf_fps_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_fps = {
+const AVFilter ff_vf_fps = {
     .name        = "fps",
     .description = NULL_IF_CONFIG_SMALL("Force constant framerate."),
     .init        = init,
@@ -339,4 +339,5 @@  AVFilter ff_vf_fps = {
     .priv_class  = &fps_class,
     .inputs      = avfilter_vf_fps_inputs,
     .outputs     = avfilter_vf_fps_outputs,
+    .next        = ff_next_vf_fps,
 };
diff --git a/libavfilter/vf_framepack.c b/libavfilter/vf_framepack.c
index 12a29964c4..dbe6f27325 100644
--- a/libavfilter/vf_framepack.c
+++ b/libavfilter/vf_framepack.c
@@ -409,7 +409,7 @@  static const AVFilterPad framepack_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_framepack = {
+const AVFilter ff_vf_framepack = {
     .name          = "framepack",
     .description   = NULL_IF_CONFIG_SMALL("Generate a frame packed stereoscopic video."),
     .priv_size     = sizeof(FramepackContext),
@@ -418,4 +418,5 @@  AVFilter ff_vf_framepack = {
     .inputs        = framepack_inputs,
     .outputs       = framepack_outputs,
     .uninit        = framepack_uninit,
+    .next          = ff_next_vf_framepack,
 };
diff --git a/libavfilter/vf_framerate.c b/libavfilter/vf_framerate.c
index 3e2615be5e..3a7fdcd74d 100644
--- a/libavfilter/vf_framerate.c
+++ b/libavfilter/vf_framerate.c
@@ -495,7 +495,7 @@  static const AVFilterPad framerate_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_framerate = {
+const AVFilter ff_vf_framerate = {
     .name          = "framerate",
     .description   = NULL_IF_CONFIG_SMALL("Upsamples or downsamples progressive source between specified frame rates."),
     .priv_size     = sizeof(FrameRateContext),
@@ -506,4 +506,5 @@  AVFilter ff_vf_framerate = {
     .inputs        = framerate_inputs,
     .outputs       = framerate_outputs,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_framerate,
 };
diff --git a/libavfilter/vf_framestep.c b/libavfilter/vf_framestep.c
index 8102e7c719..8fbd8abbb2 100644
--- a/libavfilter/vf_framestep.c
+++ b/libavfilter/vf_framestep.c
@@ -89,7 +89,7 @@  static const AVFilterPad framestep_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_framestep = {
+const AVFilter ff_vf_framestep = {
     .name        = "framestep",
     .description = NULL_IF_CONFIG_SMALL("Select one frame every N frames."),
     .priv_size   = sizeof(FrameStepContext),
@@ -97,4 +97,5 @@  AVFilter ff_vf_framestep = {
     .inputs      = framestep_inputs,
     .outputs     = framestep_outputs,
     .flags       = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next        = ff_next_vf_framestep,
 };
diff --git a/libavfilter/vf_frei0r.c b/libavfilter/vf_frei0r.c
index 8aeac08519..fc6c424dc2 100644
--- a/libavfilter/vf_frei0r.c
+++ b/libavfilter/vf_frei0r.c
@@ -366,6 +366,7 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     return ff_filter_frame(outlink, out);
 }
 
+#if CONFIG_FREI0R_FILTER
 #define OFFSET(x) offsetof(Frei0rContext, x)
 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
 static const AVOption frei0r_options[] = {
@@ -394,7 +395,7 @@  static const AVFilterPad avfilter_vf_frei0r_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_frei0r = {
+const AVFilter ff_vf_frei0r = {
     .name          = "frei0r",
     .description   = NULL_IF_CONFIG_SMALL("Apply a frei0r effect."),
     .query_formats = query_formats,
@@ -404,8 +405,11 @@  AVFilter ff_vf_frei0r = {
     .priv_class    = &frei0r_class,
     .inputs        = avfilter_vf_frei0r_inputs,
     .outputs       = avfilter_vf_frei0r_outputs,
+    .next          = ff_next_vf_frei0r,
 };
+#endif /* CONFIG_FREI0R_FILTER */
 
+#if CONFIG_FREI0R_SRC_FILTER
 static av_cold int source_init(AVFilterContext *ctx)
 {
     Frei0rContext *s = ctx->priv;
@@ -480,7 +484,7 @@  static const AVFilterPad avfilter_vsrc_frei0r_src_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsrc_frei0r_src = {
+const AVFilter ff_vsrc_frei0r_src = {
     .name          = "frei0r_src",
     .description   = NULL_IF_CONFIG_SMALL("Generate a frei0r source."),
     .priv_size     = sizeof(Frei0rContext),
@@ -490,4 +494,6 @@  AVFilter ff_vsrc_frei0r_src = {
     .query_formats = query_formats,
     .inputs        = NULL,
     .outputs       = avfilter_vsrc_frei0r_src_outputs,
+    .next          = ff_next_vsrc_frei0r_src,
 };
+#endif /* CONFIG_FREI0R_SRC_FILTER */
diff --git a/libavfilter/vf_fspp.c b/libavfilter/vf_fspp.c
index c6989046c4..1a94a34526 100644
--- a/libavfilter/vf_fspp.c
+++ b/libavfilter/vf_fspp.c
@@ -680,7 +680,7 @@  static const AVFilterPad fspp_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_fspp = {
+const AVFilter ff_vf_fspp = {
     .name            = "fspp",
     .description     = NULL_IF_CONFIG_SMALL("Apply Fast Simple Post-processing filter."),
     .priv_size       = sizeof(FSPPContext),
@@ -690,4 +690,5 @@  AVFilter ff_vf_fspp = {
     .outputs         = fspp_outputs,
     .priv_class      = &fspp_class,
     .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next            = ff_next_vf_fspp,
 };
diff --git a/libavfilter/vf_gblur.c b/libavfilter/vf_gblur.c
index fd901e20d4..0a50147567 100644
--- a/libavfilter/vf_gblur.c
+++ b/libavfilter/vf_gblur.c
@@ -354,7 +354,7 @@  static const AVFilterPad gblur_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_gblur = {
+const AVFilter ff_vf_gblur = {
     .name          = "gblur",
     .description   = NULL_IF_CONFIG_SMALL("Apply Gaussian Blur filter."),
     .priv_size     = sizeof(GBlurContext),
@@ -364,4 +364,5 @@  AVFilter ff_vf_gblur = {
     .inputs        = gblur_inputs,
     .outputs       = gblur_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_gblur,
 };
diff --git a/libavfilter/vf_geq.c b/libavfilter/vf_geq.c
index 2aa1259c9a..1a3d4d68a1 100644
--- a/libavfilter/vf_geq.c
+++ b/libavfilter/vf_geq.c
@@ -362,7 +362,7 @@  static const AVFilterPad geq_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_geq = {
+const AVFilter ff_vf_geq = {
     .name          = "geq",
     .description   = NULL_IF_CONFIG_SMALL("Apply generic equation to each pixel."),
     .priv_size     = sizeof(GEQContext),
@@ -373,4 +373,5 @@  AVFilter ff_vf_geq = {
     .outputs       = geq_outputs,
     .priv_class    = &geq_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_geq,
 };
diff --git a/libavfilter/vf_gradfun.c b/libavfilter/vf_gradfun.c
index f63128d72e..dd5901dd06 100644
--- a/libavfilter/vf_gradfun.c
+++ b/libavfilter/vf_gradfun.c
@@ -251,7 +251,7 @@  static const AVFilterPad avfilter_vf_gradfun_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_gradfun = {
+const AVFilter ff_vf_gradfun = {
     .name          = "gradfun",
     .description   = NULL_IF_CONFIG_SMALL("Debands video quickly using gradients."),
     .priv_size     = sizeof(GradFunContext),
@@ -262,4 +262,5 @@  AVFilter ff_vf_gradfun = {
     .inputs        = avfilter_vf_gradfun_inputs,
     .outputs       = avfilter_vf_gradfun_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_gradfun,
 };
diff --git a/libavfilter/vf_hflip.c b/libavfilter/vf_hflip.c
index b77afc77fc..4d1b66b70b 100644
--- a/libavfilter/vf_hflip.c
+++ b/libavfilter/vf_hflip.c
@@ -243,7 +243,7 @@  static const AVFilterPad avfilter_vf_hflip_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_hflip = {
+const AVFilter ff_vf_hflip = {
     .name          = "hflip",
     .description   = NULL_IF_CONFIG_SMALL("Horizontally flip the input video."),
     .priv_size     = sizeof(FlipContext),
@@ -252,4 +252,5 @@  AVFilter ff_vf_hflip = {
     .inputs        = avfilter_vf_hflip_inputs,
     .outputs       = avfilter_vf_hflip_outputs,
     .flags         = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_hflip,
 };
diff --git a/libavfilter/vf_histeq.c b/libavfilter/vf_histeq.c
index 2449e10dea..8457fe9510 100644
--- a/libavfilter/vf_histeq.c
+++ b/libavfilter/vf_histeq.c
@@ -270,7 +270,7 @@  static const AVFilterPad histeq_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_histeq = {
+const AVFilter ff_vf_histeq = {
     .name          = "histeq",
     .description   = NULL_IF_CONFIG_SMALL("Apply global color histogram equalization."),
     .priv_size     = sizeof(HisteqContext),
@@ -280,4 +280,5 @@  AVFilter ff_vf_histeq = {
     .outputs       = histeq_outputs,
     .priv_class    = &histeq_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_histeq,
 };
diff --git a/libavfilter/vf_histogram.c b/libavfilter/vf_histogram.c
index 5185992de6..695ef31815 100644
--- a/libavfilter/vf_histogram.c
+++ b/libavfilter/vf_histogram.c
@@ -375,7 +375,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_histogram = {
+const AVFilter ff_vf_histogram = {
     .name          = "histogram",
     .description   = NULL_IF_CONFIG_SMALL("Compute and draw a histogram."),
     .priv_size     = sizeof(HistogramContext),
@@ -383,4 +383,5 @@  AVFilter ff_vf_histogram = {
     .inputs        = inputs,
     .outputs       = outputs,
     .priv_class    = &histogram_class,
+    .next          = ff_next_vf_histogram,
 };
diff --git a/libavfilter/vf_hqdn3d.c b/libavfilter/vf_hqdn3d.c
index d6c14bb3d8..fe82f5c9ee 100644
--- a/libavfilter/vf_hqdn3d.c
+++ b/libavfilter/vf_hqdn3d.c
@@ -360,7 +360,7 @@  static const AVFilterPad avfilter_vf_hqdn3d_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_hqdn3d = {
+const AVFilter ff_vf_hqdn3d = {
     .name          = "hqdn3d",
     .description   = NULL_IF_CONFIG_SMALL("Apply a High Quality 3D Denoiser."),
     .priv_size     = sizeof(HQDN3DContext),
@@ -371,4 +371,5 @@  AVFilter ff_vf_hqdn3d = {
     .inputs        = avfilter_vf_hqdn3d_inputs,
     .outputs       = avfilter_vf_hqdn3d_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_vf_hqdn3d,
 };
diff --git a/libavfilter/vf_hqx.c b/libavfilter/vf_hqx.c
index 16a1be7bd4..ec77d6ead0 100644
--- a/libavfilter/vf_hqx.c
+++ b/libavfilter/vf_hqx.c
@@ -553,7 +553,7 @@  static const AVFilterPad hqx_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_hqx = {
+const AVFilter ff_vf_hqx = {
     .name          = "hqx",
     .description   = NULL_IF_CONFIG_SMALL("Scale the input by 2, 3 or 4 using the hq*x magnification algorithm."),
     .priv_size     = sizeof(HQXContext),
@@ -563,4 +563,5 @@  AVFilter ff_vf_hqx = {
     .outputs       = hqx_outputs,
     .priv_class    = &hqx_class,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_hqx,
 };
diff --git a/libavfilter/vf_hue.c b/libavfilter/vf_hue.c
index 45a5a1a92f..2f82c9fda1 100644
--- a/libavfilter/vf_hue.c
+++ b/libavfilter/vf_hue.c
@@ -438,7 +438,7 @@  static const AVFilterPad hue_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_hue = {
+const AVFilter ff_vf_hue = {
     .name            = "hue",
     .description     = NULL_IF_CONFIG_SMALL("Adjust the hue and saturation of the input video."),
     .priv_size       = sizeof(HueContext),
@@ -450,4 +450,5 @@  AVFilter ff_vf_hue = {
     .outputs         = hue_outputs,
     .priv_class      = &hue_class,
     .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next            = ff_next_vf_hue,
 };
diff --git a/libavfilter/vf_hwdownload.c b/libavfilter/vf_hwdownload.c
index 33af30cf40..951c517571 100644
--- a/libavfilter/vf_hwdownload.c
+++ b/libavfilter/vf_hwdownload.c
@@ -205,7 +205,7 @@  static const AVFilterPad hwdownload_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_hwdownload = {
+const AVFilter ff_vf_hwdownload = {
     .name          = "hwdownload",
     .description   = NULL_IF_CONFIG_SMALL("Download a hardware frame to a normal frame"),
     .uninit        = hwdownload_uninit,
@@ -215,4 +215,5 @@  AVFilter ff_vf_hwdownload = {
     .inputs        = hwdownload_inputs,
     .outputs       = hwdownload_outputs,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next          = ff_next_vf_hwdownload,
 };
diff --git a/libavfilter/vf_hwmap.c b/libavfilter/vf_hwmap.c
index d5fc3c46e6..5422cc62aa 100644
--- a/libavfilter/vf_hwmap.c
+++ b/libavfilter/vf_hwmap.c
@@ -414,7 +414,7 @@  static const AVFilterPad hwmap_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_hwmap = {
+const AVFilter ff_vf_hwmap = {
     .name           = "hwmap",
     .description    = NULL_IF_CONFIG_SMALL("Map hardware frames"),
     .uninit         = hwmap_uninit,
@@ -424,4 +424,5 @@  AVFilter ff_vf_hwmap = {
     .inputs         = hwmap_inputs,
     .outputs        = hwmap_outputs,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next           = ff_next_vf_hwmap,
 };
diff --git a/libavfilter/vf_hwupload.c b/libavfilter/vf_hwupload.c
index 157686b7b3..0284f51756 100644
--- a/libavfilter/vf_hwupload.c
+++ b/libavfilter/vf_hwupload.c
@@ -222,7 +222,7 @@  static const AVFilterPad hwupload_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_hwupload = {
+const AVFilter ff_vf_hwupload = {
     .name          = "hwupload",
     .description   = NULL_IF_CONFIG_SMALL("Upload a normal frame to a hardware frame"),
     .uninit        = hwupload_uninit,
@@ -232,4 +232,5 @@  AVFilter ff_vf_hwupload = {
     .inputs        = hwupload_inputs,
     .outputs       = hwupload_outputs,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next          = ff_next_vf_hwupload,
 };
diff --git a/libavfilter/vf_hwupload_cuda.c b/libavfilter/vf_hwupload_cuda.c
index 063f0285c3..5771dcd1ee 100644
--- a/libavfilter/vf_hwupload_cuda.c
+++ b/libavfilter/vf_hwupload_cuda.c
@@ -174,7 +174,7 @@  static const AVFilterPad cudaupload_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_hwupload_cuda = {
+const AVFilter ff_vf_hwupload_cuda = {
     .name        = "hwupload_cuda",
     .description = NULL_IF_CONFIG_SMALL("Upload a system memory frame to a CUDA device."),
 
@@ -190,4 +190,5 @@  AVFilter ff_vf_hwupload_cuda = {
     .outputs   = cudaupload_outputs,
 
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next           = ff_next_vf_hwupload_cuda,
 };
diff --git a/libavfilter/vf_hysteresis.c b/libavfilter/vf_hysteresis.c
index 551b33f332..8653e60e18 100644
--- a/libavfilter/vf_hysteresis.c
+++ b/libavfilter/vf_hysteresis.c
@@ -372,7 +372,7 @@  static const AVFilterPad hysteresis_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_hysteresis = {
+const AVFilter ff_vf_hysteresis = {
     .name          = "hysteresis",
     .description   = NULL_IF_CONFIG_SMALL("Grow first stream into second stream by connecting components."),
     .preinit       = hysteresis_framesync_preinit,
@@ -384,4 +384,5 @@  AVFilter ff_vf_hysteresis = {
     .outputs       = hysteresis_outputs,
     .priv_class    = &hysteresis_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_vf_hysteresis,
 };
diff --git a/libavfilter/vf_idet.c b/libavfilter/vf_idet.c
index 02ae2edcb9..da42e5171d 100644
--- a/libavfilter/vf_idet.c
+++ b/libavfilter/vf_idet.c
@@ -441,7 +441,7 @@  static const AVFilterPad idet_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_idet = {
+const AVFilter ff_vf_idet = {
     .name          = "idet",
     .description   = NULL_IF_CONFIG_SMALL("Interlace detect Filter."),
     .priv_size     = sizeof(IDETContext),
@@ -451,4 +451,5 @@  AVFilter ff_vf_idet = {
     .inputs        = idet_inputs,
     .outputs       = idet_outputs,
     .priv_class    = &idet_class,
+    .next          = ff_next_vf_idet,
 };
diff --git a/libavfilter/vf_il.c b/libavfilter/vf_il.c
index ae0cc1938a..e22ffe2363 100644
--- a/libavfilter/vf_il.c
+++ b/libavfilter/vf_il.c
@@ -201,7 +201,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_il = {
+const AVFilter ff_vf_il = {
     .name          = "il",
     .description   = NULL_IF_CONFIG_SMALL("Deinterleave or interleave fields."),
     .priv_size     = sizeof(IlContext),
@@ -210,4 +210,5 @@  AVFilter ff_vf_il = {
     .outputs       = outputs,
     .priv_class    = &il_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_il,
 };
diff --git a/libavfilter/vf_interlace.c b/libavfilter/vf_interlace.c
index 24c422ded2..52af3e07dd 100644
--- a/libavfilter/vf_interlace.c
+++ b/libavfilter/vf_interlace.c
@@ -354,7 +354,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_interlace = {
+const AVFilter ff_vf_interlace = {
     .name          = "interlace",
     .description   = NULL_IF_CONFIG_SMALL("Convert progressive video into interlaced."),
     .uninit        = uninit,
@@ -363,4 +363,5 @@  AVFilter ff_vf_interlace = {
     .query_formats = query_formats,
     .inputs        = inputs,
     .outputs       = outputs,
+    .next          = ff_next_vf_interlace,
 };
diff --git a/libavfilter/vf_kerndeint.c b/libavfilter/vf_kerndeint.c
index 057d15f9cb..add3a30ace 100644
--- a/libavfilter/vf_kerndeint.c
+++ b/libavfilter/vf_kerndeint.c
@@ -307,7 +307,7 @@  static const AVFilterPad kerndeint_outputs[] = {
 };
 
 
-AVFilter ff_vf_kerndeint = {
+const AVFilter ff_vf_kerndeint = {
     .name          = "kerndeint",
     .description   = NULL_IF_CONFIG_SMALL("Apply kernel deinterlacing to the input."),
     .priv_size     = sizeof(KerndeintContext),
@@ -316,4 +316,5 @@  AVFilter ff_vf_kerndeint = {
     .query_formats = query_formats,
     .inputs        = kerndeint_inputs,
     .outputs       = kerndeint_outputs,
+    .next          = ff_next_vf_kerndeint,
 };
diff --git a/libavfilter/vf_lenscorrection.c b/libavfilter/vf_lenscorrection.c
index 239fe195bd..9e61a78a16 100644
--- a/libavfilter/vf_lenscorrection.c
+++ b/libavfilter/vf_lenscorrection.c
@@ -218,7 +218,7 @@  static const AVFilterPad lenscorrection_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_lenscorrection = {
+const AVFilter ff_vf_lenscorrection = {
     .name          = "lenscorrection",
     .description   = NULL_IF_CONFIG_SMALL("Rectify the image by correcting for lens distortion."),
     .priv_size     = sizeof(LenscorrectionCtx),
@@ -228,4 +228,5 @@  AVFilter ff_vf_lenscorrection = {
     .priv_class    = &lenscorrection_class,
     .uninit        = uninit,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_lenscorrection,
 };
diff --git a/libavfilter/vf_libopencv.c b/libavfilter/vf_libopencv.c
index 8128030b8c..37b063137e 100644
--- a/libavfilter/vf_libopencv.c
+++ b/libavfilter/vf_libopencv.c
@@ -427,7 +427,7 @@  static const AVFilterPad avfilter_vf_ocv_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_ocv = {
+const AVFilter ff_vf_ocv = {
     .name          = "ocv",
     .description   = NULL_IF_CONFIG_SMALL("Apply transform using libopencv."),
     .priv_size     = sizeof(OCVContext),
@@ -437,4 +437,5 @@  AVFilter ff_vf_ocv = {
     .uninit        = uninit,
     .inputs        = avfilter_vf_ocv_inputs,
     .outputs       = avfilter_vf_ocv_outputs,
+    .next          = ff_next_vf_ocv,
 };
diff --git a/libavfilter/vf_libvmaf.c b/libavfilter/vf_libvmaf.c
index dfe474c40c..54692883e0 100644
--- a/libavfilter/vf_libvmaf.c
+++ b/libavfilter/vf_libvmaf.c
@@ -345,7 +345,7 @@  static const AVFilterPad libvmaf_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_libvmaf = {
+const AVFilter ff_vf_libvmaf = {
     .name          = "libvmaf",
     .description   = NULL_IF_CONFIG_SMALL("Calculate the VMAF between two video streams."),
     .preinit       = libvmaf_framesync_preinit,
@@ -357,4 +357,5 @@  AVFilter ff_vf_libvmaf = {
     .priv_class    = &libvmaf_class,
     .inputs        = libvmaf_inputs,
     .outputs       = libvmaf_outputs,
+    .next          = ff_next_vf_libvmaf,
 };
diff --git a/libavfilter/vf_limiter.c b/libavfilter/vf_limiter.c
index 9c62b11884..4cc38f0e54 100644
--- a/libavfilter/vf_limiter.c
+++ b/libavfilter/vf_limiter.c
@@ -218,7 +218,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_limiter = {
+const AVFilter ff_vf_limiter = {
     .name          = "limiter",
     .description   = NULL_IF_CONFIG_SMALL("Limit pixels components to the specified range."),
     .priv_size     = sizeof(LimiterContext),
@@ -228,4 +228,5 @@  AVFilter ff_vf_limiter = {
     .inputs        = inputs,
     .outputs       = outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_limiter,
 };
diff --git a/libavfilter/vf_lumakey.c b/libavfilter/vf_lumakey.c
index 418538f929..44a7c96a9e 100644
--- a/libavfilter/vf_lumakey.c
+++ b/libavfilter/vf_lumakey.c
@@ -189,7 +189,7 @@  static const AVOption lumakey_options[] = {
 
 AVFILTER_DEFINE_CLASS(lumakey);
 
-AVFilter ff_vf_lumakey = {
+const AVFilter ff_vf_lumakey = {
     .name          = "lumakey",
     .description   = NULL_IF_CONFIG_SMALL("Turns a certain luma into transparency."),
     .priv_size     = sizeof(LumakeyContext),
@@ -198,4 +198,5 @@  AVFilter ff_vf_lumakey = {
     .inputs        = lumakey_inputs,
     .outputs       = lumakey_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_lumakey,
 };
diff --git a/libavfilter/vf_lut.c b/libavfilter/vf_lut.c
index 26f2945c84..b92a120cf9 100644
--- a/libavfilter/vf_lut.c
+++ b/libavfilter/vf_lut.c
@@ -498,7 +498,7 @@  static const AVFilterPad outputs[] = {
 };
 
 #define DEFINE_LUT_FILTER(name_, description_)                          \
-    AVFilter ff_vf_##name_ = {                                          \
+    const AVFilter ff_vf_##name_ = {                                    \
         .name          = #name_,                                        \
         .description   = NULL_IF_CONFIG_SMALL(description_),            \
         .priv_size     = sizeof(LutContext),                            \
@@ -509,6 +509,7 @@  static const AVFilterPad outputs[] = {
         .inputs        = inputs,                                        \
         .outputs       = outputs,                                       \
         .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,        \
+        .next          = ff_next_vf_##name_,                             \
     }
 
 #if CONFIG_LUT_FILTER
diff --git a/libavfilter/vf_lut2.c b/libavfilter/vf_lut2.c
index 1385cba9b1..de0001484f 100644
--- a/libavfilter/vf_lut2.c
+++ b/libavfilter/vf_lut2.c
@@ -374,7 +374,7 @@  static const AVFilterPad outputs[] = {
 
 FRAMESYNC_DEFINE_CLASS(lut2, LUT2Context, fs);
 
-AVFilter ff_vf_lut2 = {
+const AVFilter ff_vf_lut2 = {
     .name          = "lut2",
     .description   = NULL_IF_CONFIG_SMALL("Compute and apply a lookup table from two video inputs."),
     .preinit       = lut2_framesync_preinit,
@@ -386,6 +386,7 @@  AVFilter ff_vf_lut2 = {
     .inputs        = inputs,
     .outputs       = outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_vf_lut2,
 };
 
 #if CONFIG_TLUT2_FILTER
@@ -444,7 +445,7 @@  static const AVFilterPad tlut2_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_tlut2 = {
+const AVFilter ff_vf_tlut2 = {
     .name          = "tlut2",
     .description   = NULL_IF_CONFIG_SMALL("Compute and apply a lookup table from two successive frames."),
     .priv_size     = sizeof(LUT2Context),
@@ -454,6 +455,7 @@  AVFilter ff_vf_tlut2 = {
     .uninit        = uninit,
     .inputs        = tlut2_inputs,
     .outputs       = tlut2_outputs,
+    .next          = ff_next_vf_tlut2,
 };
 
 #endif
diff --git a/libavfilter/vf_lut3d.c b/libavfilter/vf_lut3d.c
index c9b72249aa..ed787175d1 100644
--- a/libavfilter/vf_lut3d.c
+++ b/libavfilter/vf_lut3d.c
@@ -625,7 +625,7 @@  static const AVFilterPad lut3d_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_lut3d = {
+const AVFilter ff_vf_lut3d = {
     .name          = "lut3d",
     .description   = NULL_IF_CONFIG_SMALL("Adjust colors using a 3D LUT."),
     .priv_size     = sizeof(LUT3DContext),
@@ -635,6 +635,7 @@  AVFilter ff_vf_lut3d = {
     .outputs       = lut3d_outputs,
     .priv_class    = &lut3d_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_lut3d,
 };
 #endif
 
@@ -806,7 +807,7 @@  static const AVFilterPad haldclut_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_haldclut = {
+const AVFilter ff_vf_haldclut = {
     .name          = "haldclut",
     .description   = NULL_IF_CONFIG_SMALL("Adjust colors using a Hald CLUT."),
     .priv_size     = sizeof(LUT3DContext),
@@ -819,5 +820,6 @@  AVFilter ff_vf_haldclut = {
     .outputs       = haldclut_outputs,
     .priv_class    = &haldclut_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_haldclut,
 };
 #endif
diff --git a/libavfilter/vf_maskedclamp.c b/libavfilter/vf_maskedclamp.c
index 9812e08533..b59f932734 100644
--- a/libavfilter/vf_maskedclamp.c
+++ b/libavfilter/vf_maskedclamp.c
@@ -314,7 +314,7 @@  static const AVFilterPad maskedclamp_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_maskedclamp = {
+const AVFilter ff_vf_maskedclamp = {
     .name          = "maskedclamp",
     .description   = NULL_IF_CONFIG_SMALL("Clamp first stream with second stream and third stream."),
     .priv_size     = sizeof(MaskedClampContext),
@@ -325,4 +325,5 @@  AVFilter ff_vf_maskedclamp = {
     .outputs       = maskedclamp_outputs,
     .priv_class    = &maskedclamp_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_vf_maskedclamp,
 };
diff --git a/libavfilter/vf_maskedmerge.c b/libavfilter/vf_maskedmerge.c
index 86559abdac..c16fda9c6f 100644
--- a/libavfilter/vf_maskedmerge.c
+++ b/libavfilter/vf_maskedmerge.c
@@ -281,7 +281,7 @@  static const AVFilterPad maskedmerge_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_maskedmerge = {
+const AVFilter ff_vf_maskedmerge = {
     .name          = "maskedmerge",
     .description   = NULL_IF_CONFIG_SMALL("Merge first stream with second stream using third stream as mask."),
     .priv_size     = sizeof(MaskedMergeContext),
@@ -292,4 +292,5 @@  AVFilter ff_vf_maskedmerge = {
     .outputs       = maskedmerge_outputs,
     .priv_class    = &maskedmerge_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_vf_maskedmerge,
 };
diff --git a/libavfilter/vf_mcdeint.c b/libavfilter/vf_mcdeint.c
index 9cdec6308d..a9561847ca 100644
--- a/libavfilter/vf_mcdeint.c
+++ b/libavfilter/vf_mcdeint.c
@@ -301,7 +301,7 @@  static const AVFilterPad mcdeint_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_mcdeint = {
+const AVFilter ff_vf_mcdeint = {
     .name          = "mcdeint",
     .description   = NULL_IF_CONFIG_SMALL("Apply motion compensating deinterlacing."),
     .priv_size     = sizeof(MCDeintContext),
@@ -310,4 +310,5 @@  AVFilter ff_vf_mcdeint = {
     .inputs        = mcdeint_inputs,
     .outputs       = mcdeint_outputs,
     .priv_class    = &mcdeint_class,
+    .next          = ff_next_vf_mcdeint,
 };
diff --git a/libavfilter/vf_mergeplanes.c b/libavfilter/vf_mergeplanes.c
index 4b4f91be5d..f24430e86e 100644
--- a/libavfilter/vf_mergeplanes.c
+++ b/libavfilter/vf_mergeplanes.c
@@ -296,7 +296,7 @@  static const AVFilterPad mergeplanes_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_mergeplanes = {
+const AVFilter ff_vf_mergeplanes = {
     .name          = "mergeplanes",
     .description   = NULL_IF_CONFIG_SMALL("Merge planes."),
     .priv_size     = sizeof(MergePlanesContext),
@@ -308,4 +308,5 @@  AVFilter ff_vf_mergeplanes = {
     .inputs        = NULL,
     .outputs       = mergeplanes_outputs,
     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next          = ff_next_vf_mergeplanes,
 };
diff --git a/libavfilter/vf_mestimate.c b/libavfilter/vf_mestimate.c
index 7ecfe7da60..fd0c713150 100644
--- a/libavfilter/vf_mestimate.c
+++ b/libavfilter/vf_mestimate.c
@@ -365,7 +365,7 @@  static const AVFilterPad mestimate_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_mestimate = {
+const AVFilter ff_vf_mestimate = {
     .name          = "mestimate",
     .description   = NULL_IF_CONFIG_SMALL("Generate motion vectors."),
     .priv_size     = sizeof(MEContext),
@@ -374,4 +374,5 @@  AVFilter ff_vf_mestimate = {
     .query_formats = query_formats,
     .inputs        = mestimate_inputs,
     .outputs       = mestimate_outputs,
+    .next          = ff_next_vf_mestimate,
 };
diff --git a/libavfilter/vf_midequalizer.c b/libavfilter/vf_midequalizer.c
index c03814a24f..0adc03dc83 100644
--- a/libavfilter/vf_midequalizer.c
+++ b/libavfilter/vf_midequalizer.c
@@ -368,7 +368,7 @@  static const AVFilterPad midequalizer_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_midequalizer = {
+const AVFilter ff_vf_midequalizer = {
     .name          = "midequalizer",
     .description   = NULL_IF_CONFIG_SMALL("Apply Midway Equalization."),
     .priv_size     = sizeof(MidEqualizerContext),
@@ -379,4 +379,5 @@  AVFilter ff_vf_midequalizer = {
     .outputs       = midequalizer_outputs,
     .priv_class    = &midequalizer_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_vf_midequalizer,
 };
diff --git a/libavfilter/vf_minterpolate.c b/libavfilter/vf_minterpolate.c
index d53431593d..d9ee868073 100644
--- a/libavfilter/vf_minterpolate.c
+++ b/libavfilter/vf_minterpolate.c
@@ -1260,7 +1260,7 @@  static const AVFilterPad minterpolate_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_minterpolate = {
+const AVFilter ff_vf_minterpolate = {
     .name          = "minterpolate",
     .description   = NULL_IF_CONFIG_SMALL("Frame rate conversion using Motion Interpolation."),
     .priv_size     = sizeof(MIContext),
@@ -1269,4 +1269,5 @@  AVFilter ff_vf_minterpolate = {
     .query_formats = query_formats,
     .inputs        = minterpolate_inputs,
     .outputs       = minterpolate_outputs,
+    .next          = ff_next_vf_minterpolate,
 };
diff --git a/libavfilter/vf_misc_vaapi.c b/libavfilter/vf_misc_vaapi.c
index 8b179fe215..e90f5a3376 100644
--- a/libavfilter/vf_misc_vaapi.c
+++ b/libavfilter/vf_misc_vaapi.c
@@ -262,7 +262,8 @@  static const AVFilterPad misc_vaapi_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_denoise_vaapi = {
+#if CONFIG_DENOISE_VAAPI_FILTER
+const AVFilter ff_vf_denoise_vaapi = {
     .name          = "denoise_vaapi",
     .description   = NULL_IF_CONFIG_SMALL("VAAPI VPP for de-noise"),
     .priv_size     = sizeof(DenoiseVAAPIContext),
@@ -273,9 +274,12 @@  AVFilter ff_vf_denoise_vaapi = {
     .outputs       = misc_vaapi_outputs,
     .priv_class    = &denoise_vaapi_class,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next          = ff_next_vf_denoise_vaapi,
 };
+#endif
 
-AVFilter ff_vf_sharpness_vaapi = {
+#if CONFIG_SHARPNESS_VAAPI_FILTER
+const AVFilter ff_vf_sharpness_vaapi = {
     .name          = "sharpness_vaapi",
     .description   = NULL_IF_CONFIG_SMALL("VAAPI VPP for sharpness"),
     .priv_size     = sizeof(SharpnessVAAPIContext),
@@ -286,4 +290,6 @@  AVFilter ff_vf_sharpness_vaapi = {
     .outputs       = misc_vaapi_outputs,
     .priv_class    = &sharpness_vaapi_class,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next          = ff_next_vf_sharpness_vaapi,
 };
+#endif
diff --git a/libavfilter/vf_mix.c b/libavfilter/vf_mix.c
index 261ab066ef..39640ecb81 100644
--- a/libavfilter/vf_mix.c
+++ b/libavfilter/vf_mix.c
@@ -269,7 +269,7 @@  static const AVFilterPad outputs[] = {
 
 AVFILTER_DEFINE_CLASS(mix);
 
-AVFilter ff_vf_mix = {
+const AVFilter ff_vf_mix = {
     .name          = "mix",
     .description   = NULL_IF_CONFIG_SMALL("Mix video inputs."),
     .priv_size     = sizeof(MixContext),
@@ -280,4 +280,5 @@  AVFilter ff_vf_mix = {
     .uninit        = uninit,
     .activate      = activate,
     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next          = ff_next_vf_mix,
 };
diff --git a/libavfilter/vf_mpdecimate.c b/libavfilter/vf_mpdecimate.c
index c9a9cb3a8e..ab9d51d4d3 100644
--- a/libavfilter/vf_mpdecimate.c
+++ b/libavfilter/vf_mpdecimate.c
@@ -242,7 +242,7 @@  static const AVFilterPad mpdecimate_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_mpdecimate = {
+const AVFilter ff_vf_mpdecimate = {
     .name          = "mpdecimate",
     .description   = NULL_IF_CONFIG_SMALL("Remove near-duplicate frames."),
     .init          = init,
@@ -252,4 +252,5 @@  AVFilter ff_vf_mpdecimate = {
     .query_formats = query_formats,
     .inputs        = mpdecimate_inputs,
     .outputs       = mpdecimate_outputs,
+    .next          = ff_next_vf_mpdecimate,
 };
diff --git a/libavfilter/vf_neighbor.c b/libavfilter/vf_neighbor.c
index de4a12f048..22e3f2d9fc 100644
--- a/libavfilter/vf_neighbor.c
+++ b/libavfilter/vf_neighbor.c
@@ -252,7 +252,7 @@  static const AVFilterPad neighbor_outputs[] = {
 #define DEFINE_NEIGHBOR_FILTER(name_, description_)          \
 AVFILTER_DEFINE_CLASS(name_);                                \
                                                              \
-AVFilter ff_vf_##name_ = {                                   \
+const AVFilter ff_vf_##name_ = {                             \
     .name          = #name_,                                 \
     .description   = NULL_IF_CONFIG_SMALL(description_),     \
     .priv_size     = sizeof(NContext),                       \
@@ -262,6 +262,7 @@  AVFilter ff_vf_##name_ = {                                   \
     .inputs        = neighbor_inputs,                        \
     .outputs       = neighbor_outputs,                       \
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, \
+    .next          = ff_next_vf_##name_,                     \
 }
 
 #if CONFIG_EROSION_FILTER
diff --git a/libavfilter/vf_nlmeans.c b/libavfilter/vf_nlmeans.c
index e4952e187e..dd0b958361 100644
--- a/libavfilter/vf_nlmeans.c
+++ b/libavfilter/vf_nlmeans.c
@@ -537,7 +537,7 @@  static const AVFilterPad nlmeans_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_nlmeans = {
+const AVFilter ff_vf_nlmeans = {
     .name          = "nlmeans",
     .description   = NULL_IF_CONFIG_SMALL("Non-local means denoiser."),
     .priv_size     = sizeof(NLMeansContext),
@@ -548,4 +548,5 @@  AVFilter ff_vf_nlmeans = {
     .outputs       = nlmeans_outputs,
     .priv_class    = &nlmeans_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_nlmeans,
 };
diff --git a/libavfilter/vf_nnedi.c b/libavfilter/vf_nnedi.c
index b14aa64c04..8f42164aae 100644
--- a/libavfilter/vf_nnedi.c
+++ b/libavfilter/vf_nnedi.c
@@ -1197,7 +1197,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_nnedi = {
+const AVFilter ff_vf_nnedi = {
     .name          = "nnedi",
     .description   = NULL_IF_CONFIG_SMALL("Apply neural network edge directed interpolation intra-only deinterlacer."),
     .priv_size     = sizeof(NNEDIContext),
@@ -1208,4 +1208,5 @@  AVFilter ff_vf_nnedi = {
     .inputs        = inputs,
     .outputs       = outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_vf_nnedi,
 };
diff --git a/libavfilter/vf_noise.c b/libavfilter/vf_noise.c
index abdf04708b..2714fd060b 100644
--- a/libavfilter/vf_noise.c
+++ b/libavfilter/vf_noise.c
@@ -337,7 +337,7 @@  static const AVFilterPad noise_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_noise = {
+const AVFilter ff_vf_noise = {
     .name          = "noise",
     .description   = NULL_IF_CONFIG_SMALL("Add noise."),
     .priv_size     = sizeof(NoiseContext),
@@ -348,4 +348,5 @@  AVFilter ff_vf_noise = {
     .outputs       = noise_outputs,
     .priv_class    = &noise_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_noise,
 };
diff --git a/libavfilter/vf_normalize.c b/libavfilter/vf_normalize.c
index 5c1fe98c60..e092530825 100644
--- a/libavfilter/vf_normalize.c
+++ b/libavfilter/vf_normalize.c
@@ -374,7 +374,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_normalize = {
+const AVFilter ff_vf_normalize = {
     .name          = "normalize",
     .description   = NULL_IF_CONFIG_SMALL("Normalize RGB video."),
     .priv_size     = sizeof(NormalizeContext),
@@ -383,4 +383,5 @@  AVFilter ff_vf_normalize = {
     .query_formats = query_formats,
     .inputs        = inputs,
     .outputs       = outputs,
+    .next          = ff_next_vf_normalize,
 };
diff --git a/libavfilter/vf_null.c b/libavfilter/vf_null.c
index 2355615229..e9400fe567 100644
--- a/libavfilter/vf_null.c
+++ b/libavfilter/vf_null.c
@@ -42,9 +42,10 @@  static const AVFilterPad avfilter_vf_null_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_null = {
+const AVFilter ff_vf_null = {
     .name        = "null",
     .description = NULL_IF_CONFIG_SMALL("Pass the source unchanged to the output."),
     .inputs      = avfilter_vf_null_inputs,
     .outputs     = avfilter_vf_null_outputs,
+    .next        = ff_next_vf_null,
 };
diff --git a/libavfilter/vf_ocr.c b/libavfilter/vf_ocr.c
index abfff49438..39a6a4611e 100644
--- a/libavfilter/vf_ocr.c
+++ b/libavfilter/vf_ocr.c
@@ -136,7 +136,7 @@  static const AVFilterPad ocr_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_ocr = {
+const AVFilter ff_vf_ocr = {
     .name          = "ocr",
     .description   = NULL_IF_CONFIG_SMALL("Optical Character Recognition."),
     .priv_size     = sizeof(OCRContext),
@@ -146,4 +146,5 @@  AVFilter ff_vf_ocr = {
     .uninit        = uninit,
     .inputs        = ocr_inputs,
     .outputs       = ocr_outputs,
+    .next          = ff_next_vf_ocr,
 };
diff --git a/libavfilter/vf_overlay.c b/libavfilter/vf_overlay.c
index aa5835ae3a..4dd63a0314 100644
--- a/libavfilter/vf_overlay.c
+++ b/libavfilter/vf_overlay.c
@@ -978,7 +978,7 @@  static const AVFilterPad avfilter_vf_overlay_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_overlay = {
+const AVFilter ff_vf_overlay = {
     .name          = "overlay",
     .description   = NULL_IF_CONFIG_SMALL("Overlay a video source on top of the input."),
     .preinit       = overlay_framesync_preinit,
@@ -992,4 +992,5 @@  AVFilter ff_vf_overlay = {
     .inputs        = avfilter_vf_overlay_inputs,
     .outputs       = avfilter_vf_overlay_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_vf_overlay,
 };
diff --git a/libavfilter/vf_overlay_opencl.c b/libavfilter/vf_overlay_opencl.c
index ee8381dfee..827d9ac155 100644
--- a/libavfilter/vf_overlay_opencl.c
+++ b/libavfilter/vf_overlay_opencl.c
@@ -346,7 +346,7 @@  static const AVFilterPad overlay_opencl_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_overlay_opencl = {
+const AVFilter ff_vf_overlay_opencl = {
     .name            = "overlay_opencl",
     .description     = NULL_IF_CONFIG_SMALL("Overlay one video on top of another"),
     .priv_size       = sizeof(OverlayOpenCLContext),
@@ -358,4 +358,5 @@  AVFilter ff_vf_overlay_opencl = {
     .inputs          = overlay_opencl_inputs,
     .outputs         = overlay_opencl_outputs,
     .flags_internal  = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next            = ff_next_vf_overlay_opencl,
 };
diff --git a/libavfilter/vf_overlay_qsv.c b/libavfilter/vf_overlay_qsv.c
index 6c3efdbeb5..d5f75c0197 100644
--- a/libavfilter/vf_overlay_qsv.c
+++ b/libavfilter/vf_overlay_qsv.c
@@ -479,7 +479,7 @@  static const AVFilterPad overlay_qsv_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_overlay_qsv = {
+const AVFilter ff_vf_overlay_qsv = {
     .name           = "overlay_qsv",
     .description    = NULL_IF_CONFIG_SMALL("Quick Sync Video overlay."),
     .priv_size      = sizeof(QSVOverlayContext),
@@ -490,4 +490,5 @@  AVFilter ff_vf_overlay_qsv = {
     .outputs        = overlay_qsv_outputs,
     .priv_class     = &overlay_qsv_class,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next           = ff_next_vf_overlay_qsv,
 };
diff --git a/libavfilter/vf_owdenoise.c b/libavfilter/vf_owdenoise.c
index 6d6c2a3d53..afdf78becb 100644
--- a/libavfilter/vf_owdenoise.c
+++ b/libavfilter/vf_owdenoise.c
@@ -364,7 +364,7 @@  static const AVFilterPad owdenoise_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_owdenoise = {
+const AVFilter ff_vf_owdenoise = {
     .name          = "owdenoise",
     .description   = NULL_IF_CONFIG_SMALL("Denoise using wavelets."),
     .priv_size     = sizeof(OWDenoiseContext),
@@ -374,4 +374,5 @@  AVFilter ff_vf_owdenoise = {
     .outputs       = owdenoise_outputs,
     .priv_class    = &owdenoise_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_owdenoise,
 };
diff --git a/libavfilter/vf_pad.c b/libavfilter/vf_pad.c
index ed155578e1..8c975af7ac 100644
--- a/libavfilter/vf_pad.c
+++ b/libavfilter/vf_pad.c
@@ -453,7 +453,7 @@  static const AVFilterPad avfilter_vf_pad_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_pad = {
+const AVFilter ff_vf_pad = {
     .name          = "pad",
     .description   = NULL_IF_CONFIG_SMALL("Pad the input video."),
     .priv_size     = sizeof(PadContext),
@@ -461,4 +461,5 @@  AVFilter ff_vf_pad = {
     .query_formats = query_formats,
     .inputs        = avfilter_vf_pad_inputs,
     .outputs       = avfilter_vf_pad_outputs,
+    .next          = ff_next_vf_pad,
 };
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 5ff73e6b2b..2ff5c1958c 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -570,7 +570,7 @@  static const AVFilterPad palettegen_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_palettegen = {
+const AVFilter ff_vf_palettegen = {
     .name          = "palettegen",
     .description   = NULL_IF_CONFIG_SMALL("Find the optimal palette for a given stream."),
     .priv_size     = sizeof(PaletteGenContext),
@@ -579,4 +579,5 @@  AVFilter ff_vf_palettegen = {
     .inputs        = palettegen_inputs,
     .outputs       = palettegen_outputs,
     .priv_class    = &palettegen_class,
+    .next          = ff_next_vf_palettegen,
 };
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index 604a8af29c..7a2883877d 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -1138,7 +1138,7 @@  static const AVFilterPad paletteuse_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_paletteuse = {
+const AVFilter ff_vf_paletteuse = {
     .name          = "paletteuse",
     .description   = NULL_IF_CONFIG_SMALL("Use a palette to downsample an input video stream."),
     .priv_size     = sizeof(PaletteUseContext),
@@ -1149,4 +1149,5 @@  AVFilter ff_vf_paletteuse = {
     .inputs        = paletteuse_inputs,
     .outputs       = paletteuse_outputs,
     .priv_class    = &paletteuse_class,
+    .next          = ff_next_vf_paletteuse,
 };
diff --git a/libavfilter/vf_perspective.c b/libavfilter/vf_perspective.c
index 92495097cc..ee2789d3aa 100644
--- a/libavfilter/vf_perspective.c
+++ b/libavfilter/vf_perspective.c
@@ -511,7 +511,7 @@  static const AVFilterPad perspective_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_perspective = {
+const AVFilter ff_vf_perspective = {
     .name          = "perspective",
     .description   = NULL_IF_CONFIG_SMALL("Correct the perspective of video."),
     .priv_size     = sizeof(PerspectiveContext),
@@ -522,4 +522,5 @@  AVFilter ff_vf_perspective = {
     .outputs       = perspective_outputs,
     .priv_class    = &perspective_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_perspective,
 };
diff --git a/libavfilter/vf_phase.c b/libavfilter/vf_phase.c
index fadeb6266d..a2eb6cd2cc 100644
--- a/libavfilter/vf_phase.c
+++ b/libavfilter/vf_phase.c
@@ -320,7 +320,7 @@  static const AVFilterPad phase_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_phase = {
+const AVFilter ff_vf_phase = {
     .name          = "phase",
     .description   = NULL_IF_CONFIG_SMALL("Phase shift fields."),
     .priv_size     = sizeof(PhaseContext),
@@ -330,4 +330,5 @@  AVFilter ff_vf_phase = {
     .inputs        = phase_inputs,
     .outputs       = phase_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_vf_phase,
 };
diff --git a/libavfilter/vf_pixdesctest.c b/libavfilter/vf_pixdesctest.c
index d6423acb91..1f0be4468b 100644
--- a/libavfilter/vf_pixdesctest.c
+++ b/libavfilter/vf_pixdesctest.c
@@ -125,11 +125,12 @@  static const AVFilterPad avfilter_vf_pixdesctest_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_pixdesctest = {
+const AVFilter ff_vf_pixdesctest = {
     .name        = "pixdesctest",
     .description = NULL_IF_CONFIG_SMALL("Test pixel format definitions."),
     .priv_size   = sizeof(PixdescTestContext),
     .uninit      = uninit,
     .inputs      = avfilter_vf_pixdesctest_inputs,
     .outputs     = avfilter_vf_pixdesctest_outputs,
+    .next        = ff_next_vf_pixdesctest,
 };
diff --git a/libavfilter/vf_pp.c b/libavfilter/vf_pp.c
index 524ef1bb0a..310fc7646f 100644
--- a/libavfilter/vf_pp.c
+++ b/libavfilter/vf_pp.c
@@ -181,7 +181,7 @@  static const AVFilterPad pp_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_pp = {
+const AVFilter ff_vf_pp = {
     .name            = "pp",
     .description     = NULL_IF_CONFIG_SMALL("Filter video using libpostproc."),
     .priv_size       = sizeof(PPFilterContext),
@@ -193,4 +193,5 @@  AVFilter ff_vf_pp = {
     .process_command = pp_process_command,
     .priv_class      = &pp_class,
     .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next            = ff_next_vf_pp,
 };
diff --git a/libavfilter/vf_pp7.c b/libavfilter/vf_pp7.c
index 570a1c90b9..f4c0673d2a 100644
--- a/libavfilter/vf_pp7.c
+++ b/libavfilter/vf_pp7.c
@@ -393,7 +393,7 @@  static const AVFilterPad pp7_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_pp7 = {
+const AVFilter ff_vf_pp7 = {
     .name            = "pp7",
     .description     = NULL_IF_CONFIG_SMALL("Apply Postprocessing 7 filter."),
     .priv_size       = sizeof(PP7Context),
@@ -403,4 +403,5 @@  AVFilter ff_vf_pp7 = {
     .outputs         = pp7_outputs,
     .priv_class      = &pp7_class,
     .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next            = ff_next_vf_pp7,
 };
diff --git a/libavfilter/vf_premultiply.c b/libavfilter/vf_premultiply.c
index df4f26578d..01f17eab34 100644
--- a/libavfilter/vf_premultiply.c
+++ b/libavfilter/vf_premultiply.c
@@ -683,7 +683,7 @@  static const AVFilterPad premultiply_outputs[] = {
 
 #if CONFIG_PREMULTIPLY_FILTER
 
-AVFilter ff_vf_premultiply = {
+const AVFilter ff_vf_premultiply = {
     .name          = "premultiply",
     .description   = NULL_IF_CONFIG_SMALL("PreMultiply first stream with first plane of second stream."),
     .priv_size     = sizeof(PreMultiplyContext),
@@ -696,6 +696,7 @@  AVFilter ff_vf_premultiply = {
     .priv_class    = &premultiply_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL |
                      AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next          = ff_next_vf_premultiply,
 };
 
 #endif /* CONFIG_PREMULTIPLY_FILTER */
@@ -705,7 +706,7 @@  AVFilter ff_vf_premultiply = {
 #define unpremultiply_options options
 AVFILTER_DEFINE_CLASS(unpremultiply);
 
-AVFilter ff_vf_unpremultiply = {
+const AVFilter ff_vf_unpremultiply = {
     .name          = "unpremultiply",
     .description   = NULL_IF_CONFIG_SMALL("UnPreMultiply first stream with first plane of second stream."),
     .priv_size     = sizeof(PreMultiplyContext),
@@ -718,6 +719,7 @@  AVFilter ff_vf_unpremultiply = {
     .priv_class    = &unpremultiply_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL |
                      AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next          = ff_next_vf_unpremultiply,
 };
 
 #endif /* CONFIG_UNPREMULTIPLY_FILTER */
diff --git a/libavfilter/vf_procamp_vaapi.c b/libavfilter/vf_procamp_vaapi.c
index 45a3120b23..1d93bb6527 100644
--- a/libavfilter/vf_procamp_vaapi.c
+++ b/libavfilter/vf_procamp_vaapi.c
@@ -256,7 +256,7 @@  static const AVFilterPad procamp_vaapi_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_procamp_vaapi = {
+const AVFilter ff_vf_procamp_vaapi = {
     .name          = "procamp_vaapi",
     .description   = NULL_IF_CONFIG_SMALL("ProcAmp (color balance) adjustments for hue, saturation, brightness, contrast"),
     .priv_size     = sizeof(ProcampVAAPIContext),
@@ -267,4 +267,5 @@  AVFilter ff_vf_procamp_vaapi = {
     .outputs       = procamp_vaapi_outputs,
     .priv_class    = &procamp_vaapi_class,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next          = ff_next_vf_procamp_vaapi,
 };
diff --git a/libavfilter/vf_program_opencl.c b/libavfilter/vf_program_opencl.c
index 4ee9668236..e2421bbcbb 100644
--- a/libavfilter/vf_program_opencl.c
+++ b/libavfilter/vf_program_opencl.c
@@ -375,7 +375,7 @@  static const AVFilterPad program_opencl_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_program_opencl = {
+const AVFilter ff_vf_program_opencl = {
     .name           = "program_opencl",
     .description    = NULL_IF_CONFIG_SMALL("Filter video using an OpenCL program"),
     .priv_size      = sizeof(ProgramOpenCLContext),
@@ -388,6 +388,7 @@  AVFilter ff_vf_program_opencl = {
     .inputs         = NULL,
     .outputs        = program_opencl_outputs,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next           = ff_next_vf_program_opencl,
 };
 
 #endif
@@ -428,7 +429,7 @@  static const AVFilterPad openclsrc_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsrc_openclsrc = {
+const AVFilter ff_vsrc_openclsrc = {
     .name           = "openclsrc",
     .description    = NULL_IF_CONFIG_SMALL("Generate video using an OpenCL program"),
     .priv_size      = sizeof(ProgramOpenCLContext),
@@ -439,6 +440,7 @@  AVFilter ff_vsrc_openclsrc = {
     .inputs         = NULL,
     .outputs        = openclsrc_outputs,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next           = ff_next_vsrc_openclsrc,
 };
 
 #endif
diff --git a/libavfilter/vf_pseudocolor.c b/libavfilter/vf_pseudocolor.c
index 2e7a3a95f8..4243aafb86 100644
--- a/libavfilter/vf_pseudocolor.c
+++ b/libavfilter/vf_pseudocolor.c
@@ -658,7 +658,7 @@  static av_cold void uninit(AVFilterContext *ctx)
 
 AVFILTER_DEFINE_CLASS(pseudocolor);
 
-AVFilter ff_vf_pseudocolor = {
+const AVFilter ff_vf_pseudocolor = {
     .name          = "pseudocolor",
     .description   = NULL_IF_CONFIG_SMALL("Make pseudocolored video frames."),
     .priv_size     = sizeof(PseudoColorContext),
@@ -668,4 +668,5 @@  AVFilter ff_vf_pseudocolor = {
     .inputs        = inputs,
     .outputs       = outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_pseudocolor,
 };
diff --git a/libavfilter/vf_psnr.c b/libavfilter/vf_psnr.c
index 153d694b7f..1ff18736f6 100644
--- a/libavfilter/vf_psnr.c
+++ b/libavfilter/vf_psnr.c
@@ -407,7 +407,7 @@  static const AVFilterPad psnr_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_psnr = {
+const AVFilter ff_vf_psnr = {
     .name          = "psnr",
     .description   = NULL_IF_CONFIG_SMALL("Calculate the PSNR between two video streams."),
     .preinit       = psnr_framesync_preinit,
@@ -419,4 +419,5 @@  AVFilter ff_vf_psnr = {
     .priv_class    = &psnr_class,
     .inputs        = psnr_inputs,
     .outputs       = psnr_outputs,
+    .next          = ff_next_vf_psnr,
 };
diff --git a/libavfilter/vf_pullup.c b/libavfilter/vf_pullup.c
index fa76caad03..667a52c95a 100644
--- a/libavfilter/vf_pullup.c
+++ b/libavfilter/vf_pullup.c
@@ -764,7 +764,7 @@  static const AVFilterPad pullup_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_pullup = {
+const AVFilter ff_vf_pullup = {
     .name          = "pullup",
     .description   = NULL_IF_CONFIG_SMALL("Pullup from field sequence to frames."),
     .priv_size     = sizeof(PullupContext),
@@ -773,4 +773,5 @@  AVFilter ff_vf_pullup = {
     .query_formats = query_formats,
     .inputs        = pullup_inputs,
     .outputs       = pullup_outputs,
+    .next          = ff_next_vf_pullup,
 };
diff --git a/libavfilter/vf_qp.c b/libavfilter/vf_qp.c
index 33d39493bc..a3e89d6e9a 100644
--- a/libavfilter/vf_qp.c
+++ b/libavfilter/vf_qp.c
@@ -172,7 +172,7 @@  static const AVFilterPad qp_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_qp = {
+const AVFilter ff_vf_qp = {
     .name          = "qp",
     .description   = NULL_IF_CONFIG_SMALL("Change video quantization parameters."),
     .priv_size     = sizeof(QPContext),
@@ -180,4 +180,5 @@  AVFilter ff_vf_qp = {
     .outputs       = qp_outputs,
     .priv_class    = &qp_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_vf_qp,
 };
diff --git a/libavfilter/vf_random.c b/libavfilter/vf_random.c
index 373a7db053..bbf1826781 100644
--- a/libavfilter/vf_random.c
+++ b/libavfilter/vf_random.c
@@ -126,7 +126,7 @@  static const AVFilterPad random_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_random = {
+const AVFilter ff_vf_random = {
     .name        = "random",
     .description = NULL_IF_CONFIG_SMALL("Return random frames."),
     .priv_size   = sizeof(RandomContext),
@@ -134,4 +134,5 @@  AVFilter ff_vf_random = {
     .init        = init,
     .inputs      = random_inputs,
     .outputs     = random_outputs,
+    .next        = ff_next_vf_random,
 };
diff --git a/libavfilter/vf_readeia608.c b/libavfilter/vf_readeia608.c
index bc3abe7c4d..2cd616802b 100644
--- a/libavfilter/vf_readeia608.c
+++ b/libavfilter/vf_readeia608.c
@@ -256,7 +256,7 @@  static const AVFilterPad readeia608_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_readeia608 = {
+const AVFilter ff_vf_readeia608 = {
     .name          = "readeia608",
     .description   = NULL_IF_CONFIG_SMALL("Read EIA-608 Closed Caption codes from input video and write them to frame metadata."),
     .priv_size     = sizeof(ReadEIA608Context),
@@ -265,4 +265,5 @@  AVFilter ff_vf_readeia608 = {
     .inputs        = readeia608_inputs,
     .outputs       = readeia608_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_readeia608,
 };
diff --git a/libavfilter/vf_readvitc.c b/libavfilter/vf_readvitc.c
index 7ef8cdae58..6ad77141c1 100644
--- a/libavfilter/vf_readvitc.c
+++ b/libavfilter/vf_readvitc.c
@@ -246,7 +246,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_readvitc = {
+const AVFilter ff_vf_readvitc = {
     .name          = "readvitc",
     .description   = NULL_IF_CONFIG_SMALL("Read vertical interval timecode and write it to frame metadata."),
     .priv_size     = sizeof(ReadVitcContext),
@@ -255,4 +255,5 @@  AVFilter ff_vf_readvitc = {
     .outputs       = outputs,
     .init          = init,
     .query_formats = query_formats,
+    .next          = ff_next_vf_readvitc,
 };
diff --git a/libavfilter/vf_remap.c b/libavfilter/vf_remap.c
index d24284703b..fd58d44e8e 100644
--- a/libavfilter/vf_remap.c
+++ b/libavfilter/vf_remap.c
@@ -396,7 +396,7 @@  static const AVFilterPad remap_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_remap = {
+const AVFilter ff_vf_remap = {
     .name          = "remap",
     .description   = NULL_IF_CONFIG_SMALL("Remap pixels."),
     .priv_size     = sizeof(RemapContext),
@@ -407,4 +407,5 @@  AVFilter ff_vf_remap = {
     .outputs       = remap_outputs,
     .priv_class    = &remap_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_remap,
 };
diff --git a/libavfilter/vf_removegrain.c b/libavfilter/vf_removegrain.c
index bc45076baa..bafef498c6 100644
--- a/libavfilter/vf_removegrain.c
+++ b/libavfilter/vf_removegrain.c
@@ -648,7 +648,7 @@  static const AVFilterPad removegrain_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_removegrain = {
+const AVFilter ff_vf_removegrain = {
     .name          = "removegrain",
     .description   = NULL_IF_CONFIG_SMALL("Remove grain."),
     .priv_size     = sizeof(RemoveGrainContext),
@@ -657,4 +657,5 @@  AVFilter ff_vf_removegrain = {
     .outputs       = removegrain_outputs,
     .priv_class    = &removegrain_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_removegrain,
 };
diff --git a/libavfilter/vf_removelogo.c b/libavfilter/vf_removelogo.c
index aff2d12a17..64d57529c4 100644
--- a/libavfilter/vf_removelogo.c
+++ b/libavfilter/vf_removelogo.c
@@ -573,7 +573,7 @@  static const AVFilterPad removelogo_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_removelogo = {
+const AVFilter ff_vf_removelogo = {
     .name          = "removelogo",
     .description   = NULL_IF_CONFIG_SMALL("Remove a TV logo based on a mask image."),
     .priv_size     = sizeof(RemovelogoContext),
@@ -584,4 +584,5 @@  AVFilter ff_vf_removelogo = {
     .outputs       = removelogo_outputs,
     .priv_class    = &removelogo_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_removelogo,
 };
diff --git a/libavfilter/vf_repeatfields.c b/libavfilter/vf_repeatfields.c
index 3ac432b5bc..99c1caf64b 100644
--- a/libavfilter/vf_repeatfields.c
+++ b/libavfilter/vf_repeatfields.c
@@ -181,7 +181,7 @@  static const AVFilterPad repeatfields_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_repeatfields = {
+const AVFilter ff_vf_repeatfields = {
     .name          = "repeatfields",
     .description   = NULL_IF_CONFIG_SMALL("Hard repeat fields based on MPEG repeat field flag."),
     .priv_size     = sizeof(RepeatFieldsContext),
@@ -189,4 +189,5 @@  AVFilter ff_vf_repeatfields = {
     .inputs        = repeatfields_inputs,
     .outputs       = repeatfields_outputs,
     .query_formats = query_formats,
+    .next          = ff_next_vf_repeatfields,
 };
diff --git a/libavfilter/vf_rotate.c b/libavfilter/vf_rotate.c
index 371ff7f722..7a91c4c35f 100644
--- a/libavfilter/vf_rotate.c
+++ b/libavfilter/vf_rotate.c
@@ -601,7 +601,7 @@  static const AVFilterPad rotate_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_rotate = {
+const AVFilter ff_vf_rotate = {
     .name          = "rotate",
     .description   = NULL_IF_CONFIG_SMALL("Rotate the input image."),
     .priv_size     = sizeof(RotContext),
@@ -613,4 +613,5 @@  AVFilter ff_vf_rotate = {
     .outputs       = rotate_outputs,
     .priv_class    = &rotate_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_rotate,
 };
diff --git a/libavfilter/vf_sab.c b/libavfilter/vf_sab.c
index 6f63654161..62bce8cce5 100644
--- a/libavfilter/vf_sab.c
+++ b/libavfilter/vf_sab.c
@@ -323,7 +323,7 @@  static const AVFilterPad sab_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_sab = {
+const AVFilter ff_vf_sab = {
     .name          = "sab",
     .description   = NULL_IF_CONFIG_SMALL("Apply shape adaptive blur."),
     .priv_size     = sizeof(SabContext),
@@ -334,4 +334,5 @@  AVFilter ff_vf_sab = {
     .outputs       = sab_outputs,
     .priv_class    = &sab_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_sab,
 };
diff --git a/libavfilter/vf_scale.c b/libavfilter/vf_scale.c
index 9f45032e85..4ea05c5265 100644
--- a/libavfilter/vf_scale.c
+++ b/libavfilter/vf_scale.c
@@ -93,8 +93,6 @@  typedef struct ScaleContext {
 
 } ScaleContext;
 
-AVFilter ff_vf_scale2ref;
-
 static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
 {
     ScaleContext *scale = ctx->priv;
@@ -602,6 +600,7 @@  static const AVOption scale_options[] = {
     { NULL }
 };
 
+#if CONFIG_SCALE_FILTER
 static const AVClass scale_class = {
     .class_name       = "scale",
     .item_name        = av_default_item_name,
@@ -629,7 +628,7 @@  static const AVFilterPad avfilter_vf_scale_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_scale = {
+const AVFilter ff_vf_scale = {
     .name            = "scale",
     .description     = NULL_IF_CONFIG_SMALL("Scale the input video size and/or convert the image format."),
     .init_dict       = init_dict,
@@ -640,8 +639,11 @@  AVFilter ff_vf_scale = {
     .inputs          = avfilter_vf_scale_inputs,
     .outputs         = avfilter_vf_scale_outputs,
     .process_command = process_command,
+    .next            = ff_next_vf_scale,
 };
+#endif /* CONFIG_SCALE_FILTER */
 
+#if CONFIG_SCALE2REF_FILTER
 static const AVClass scale2ref_class = {
     .class_name       = "scale2ref",
     .item_name        = av_default_item_name,
@@ -681,7 +683,7 @@  static const AVFilterPad avfilter_vf_scale2ref_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_scale2ref = {
+const AVFilter ff_vf_scale2ref = {
     .name            = "scale2ref",
     .description     = NULL_IF_CONFIG_SMALL("Scale the input video size and/or convert the image format to the given reference."),
     .init_dict       = init_dict,
@@ -692,4 +694,6 @@  AVFilter ff_vf_scale2ref = {
     .inputs          = avfilter_vf_scale2ref_inputs,
     .outputs         = avfilter_vf_scale2ref_outputs,
     .process_command = process_command,
+    .next            = ff_next_vf_scale2ref,
 };
+#endif /* CONFIG_SCALE2REF_FILTER */
diff --git a/libavfilter/vf_scale_cuda.c b/libavfilter/vf_scale_cuda.c
index 23ac27a7dc..e3b1c44b1e 100644
--- a/libavfilter/vf_scale_cuda.c
+++ b/libavfilter/vf_scale_cuda.c
@@ -538,7 +538,7 @@  static const AVFilterPad cudascale_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_scale_cuda = {
+const AVFilter ff_vf_scale_cuda = {
     .name      = "scale_cuda",
     .description = NULL_IF_CONFIG_SMALL("GPU accelerated video resizer"),
 
@@ -553,4 +553,5 @@  AVFilter ff_vf_scale_cuda = {
     .outputs   = cudascale_outputs,
 
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next           = ff_next_vf_scale_cuda,
 };
diff --git a/libavfilter/vf_scale_npp.c b/libavfilter/vf_scale_npp.c
index 8a277ce8e1..d3840c3ed3 100644
--- a/libavfilter/vf_scale_npp.c
+++ b/libavfilter/vf_scale_npp.c
@@ -580,7 +580,7 @@  static const AVFilterPad nppscale_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_scale_npp = {
+const AVFilter ff_vf_scale_npp = {
     .name      = "scale_npp",
     .description = NULL_IF_CONFIG_SMALL("NVIDIA Performance Primitives video "
                                         "scaling and format conversion"),
@@ -596,4 +596,5 @@  AVFilter ff_vf_scale_npp = {
     .outputs   = nppscale_outputs,
 
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next           = ff_next_vf_scale_npp,
 };
diff --git a/libavfilter/vf_scale_qsv.c b/libavfilter/vf_scale_qsv.c
index a5f5be7d66..c8a70ba8eb 100644
--- a/libavfilter/vf_scale_qsv.c
+++ b/libavfilter/vf_scale_qsv.c
@@ -615,7 +615,7 @@  static const AVFilterPad qsvscale_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_scale_qsv = {
+const AVFilter ff_vf_scale_qsv = {
     .name      = "scale_qsv",
     .description = NULL_IF_CONFIG_SMALL("QuickSync video scaling and format conversion"),
 
@@ -630,4 +630,5 @@  AVFilter ff_vf_scale_qsv = {
     .outputs   = qsvscale_outputs,
 
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next           = ff_next_vf_scale_qsv,
 };
diff --git a/libavfilter/vf_scale_vaapi.c b/libavfilter/vf_scale_vaapi.c
index c19e23ccd0..e0daa15aa8 100644
--- a/libavfilter/vf_scale_vaapi.c
+++ b/libavfilter/vf_scale_vaapi.c
@@ -198,7 +198,7 @@  static const AVFilterPad scale_vaapi_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_scale_vaapi = {
+const AVFilter ff_vf_scale_vaapi = {
     .name          = "scale_vaapi",
     .description   = NULL_IF_CONFIG_SMALL("Scale to/from VAAPI surfaces."),
     .priv_size     = sizeof(ScaleVAAPIContext),
@@ -209,4 +209,5 @@  AVFilter ff_vf_scale_vaapi = {
     .outputs       = scale_vaapi_outputs,
     .priv_class    = &scale_vaapi_class,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next          = ff_next_vf_scale_vaapi,
 };
diff --git a/libavfilter/vf_selectivecolor.c b/libavfilter/vf_selectivecolor.c
index c4d51bb70d..ec8a4c6b4a 100644
--- a/libavfilter/vf_selectivecolor.c
+++ b/libavfilter/vf_selectivecolor.c
@@ -470,7 +470,7 @@  static const AVFilterPad selectivecolor_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_selectivecolor = {
+const AVFilter ff_vf_selectivecolor = {
     .name          = "selectivecolor",
     .description   = NULL_IF_CONFIG_SMALL("Apply CMYK adjustments to specific color ranges."),
     .priv_size     = sizeof(SelectiveColorContext),
@@ -479,4 +479,5 @@  AVFilter ff_vf_selectivecolor = {
     .outputs       = selectivecolor_outputs,
     .priv_class    = &selectivecolor_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_selectivecolor,
 };
diff --git a/libavfilter/vf_separatefields.c b/libavfilter/vf_separatefields.c
index 3ed222f271..064b561a21 100644
--- a/libavfilter/vf_separatefields.c
+++ b/libavfilter/vf_separatefields.c
@@ -144,11 +144,12 @@  static const AVFilterPad separatefields_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_separatefields = {
+const AVFilter ff_vf_separatefields = {
     .name        = "separatefields",
     .description = NULL_IF_CONFIG_SMALL("Split input video frames into fields."),
     .priv_size   = sizeof(SeparateFieldsContext),
     .uninit      = uninit,
     .inputs      = separatefields_inputs,
     .outputs     = separatefields_outputs,
+    .next        = ff_next_vf_separatefields,
 };
diff --git a/libavfilter/vf_setfield.c b/libavfilter/vf_setfield.c
index f4dc33d7e5..db3b6ebe60 100644
--- a/libavfilter/vf_setfield.c
+++ b/libavfilter/vf_setfield.c
@@ -84,11 +84,12 @@  static const AVFilterPad setfield_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_setfield = {
+const AVFilter ff_vf_setfield = {
     .name        = "setfield",
     .description = NULL_IF_CONFIG_SMALL("Force field for the output video frame."),
     .priv_size   = sizeof(SetFieldContext),
     .priv_class  = &setfield_class,
     .inputs      = setfield_inputs,
     .outputs     = setfield_outputs,
+    .next        = ff_next_vf_setfield,
 };
diff --git a/libavfilter/vf_setparams.c b/libavfilter/vf_setparams.c
index 8427f98ba8..f742b8c26e 100644
--- a/libavfilter/vf_setparams.c
+++ b/libavfilter/vf_setparams.c
@@ -73,11 +73,12 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_setrange = {
+const AVFilter ff_vf_setrange = {
     .name        = "setrange",
     .description = NULL_IF_CONFIG_SMALL("Force color range for the output video frame."),
     .priv_size   = sizeof(SetParamsContext),
     .priv_class  = &setrange_class,
     .inputs      = inputs,
     .outputs     = outputs,
+    .next        = ff_next_vf_setrange,
 };
diff --git a/libavfilter/vf_showinfo.c b/libavfilter/vf_showinfo.c
index d1d1415c0b..0d879cf6b2 100644
--- a/libavfilter/vf_showinfo.c
+++ b/libavfilter/vf_showinfo.c
@@ -235,9 +235,10 @@  static const AVFilterPad avfilter_vf_showinfo_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_showinfo = {
+const AVFilter ff_vf_showinfo = {
     .name        = "showinfo",
     .description = NULL_IF_CONFIG_SMALL("Show textual information for each video frame."),
     .inputs      = avfilter_vf_showinfo_inputs,
     .outputs     = avfilter_vf_showinfo_outputs,
+    .next        = ff_next_vf_showinfo,
 };
diff --git a/libavfilter/vf_showpalette.c b/libavfilter/vf_showpalette.c
index d886ab87a7..2c3d7735ec 100644
--- a/libavfilter/vf_showpalette.c
+++ b/libavfilter/vf_showpalette.c
@@ -128,7 +128,7 @@  static const AVFilterPad showpalette_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_showpalette = {
+const AVFilter ff_vf_showpalette = {
     .name          = "showpalette",
     .description   = NULL_IF_CONFIG_SMALL("Display frame palette."),
     .priv_size     = sizeof(ShowPaletteContext),
@@ -137,4 +137,5 @@  AVFilter ff_vf_showpalette = {
     .outputs       = showpalette_outputs,
     .priv_class    = &showpalette_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_showpalette,
 };
diff --git a/libavfilter/vf_shuffleframes.c b/libavfilter/vf_shuffleframes.c
index 8e595111b8..3138c226e4 100644
--- a/libavfilter/vf_shuffleframes.c
+++ b/libavfilter/vf_shuffleframes.c
@@ -156,7 +156,7 @@  static const AVFilterPad shuffleframes_outputs[] = {
     { NULL },
 };
 
-AVFilter ff_vf_shuffleframes = {
+const AVFilter ff_vf_shuffleframes = {
     .name          = "shuffleframes",
     .description   = NULL_IF_CONFIG_SMALL("Shuffle video frames."),
     .priv_size     = sizeof(ShuffleFramesContext),
@@ -166,4 +166,5 @@  AVFilter ff_vf_shuffleframes = {
     .inputs        = shuffleframes_inputs,
     .outputs       = shuffleframes_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_shuffleframes,
 };
diff --git a/libavfilter/vf_shuffleplanes.c b/libavfilter/vf_shuffleplanes.c
index 4bc7b79f87..b3aec1e261 100644
--- a/libavfilter/vf_shuffleplanes.c
+++ b/libavfilter/vf_shuffleplanes.c
@@ -155,7 +155,7 @@  static const AVFilterPad shuffleplanes_outputs[] = {
     { NULL },
 };
 
-AVFilter ff_vf_shuffleplanes = {
+const AVFilter ff_vf_shuffleplanes = {
     .name         = "shuffleplanes",
     .description  = NULL_IF_CONFIG_SMALL("Shuffle video planes."),
 
@@ -164,4 +164,5 @@  AVFilter ff_vf_shuffleplanes = {
 
     .inputs       = shuffleplanes_inputs,
     .outputs      = shuffleplanes_outputs,
+    .next         = ff_next_vf_shuffleplanes,
 };
diff --git a/libavfilter/vf_signalstats.c b/libavfilter/vf_signalstats.c
index 298881bc72..686e7dc46e 100644
--- a/libavfilter/vf_signalstats.c
+++ b/libavfilter/vf_signalstats.c
@@ -1010,7 +1010,7 @@  static const AVFilterPad signalstats_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_signalstats = {
+const AVFilter ff_vf_signalstats = {
     .name          = "signalstats",
     .description   = "Generate statistics from video analysis.",
     .init          = init,
@@ -1021,4 +1021,5 @@  AVFilter ff_vf_signalstats = {
     .outputs       = signalstats_outputs,
     .priv_class    = &signalstats_class,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_signalstats,
 };
diff --git a/libavfilter/vf_signature.c b/libavfilter/vf_signature.c
index f0078ba1a6..ea089b4e05 100644
--- a/libavfilter/vf_signature.c
+++ b/libavfilter/vf_signature.c
@@ -756,7 +756,7 @@  static const AVFilterPad signature_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_signature = {
+const AVFilter ff_vf_signature = {
     .name          = "signature",
     .description   = NULL_IF_CONFIG_SMALL("Calculate the MPEG-7 video signature"),
     .priv_size     = sizeof(SignatureContext),
@@ -767,4 +767,5 @@  AVFilter ff_vf_signature = {
     .outputs       = signature_outputs,
     .inputs        = NULL,
     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next          = ff_next_vf_signature,
 };
diff --git a/libavfilter/vf_smartblur.c b/libavfilter/vf_smartblur.c
index 1955ac43d8..fc859bd33c 100644
--- a/libavfilter/vf_smartblur.c
+++ b/libavfilter/vf_smartblur.c
@@ -291,7 +291,7 @@  static const AVFilterPad smartblur_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_smartblur = {
+const AVFilter ff_vf_smartblur = {
     .name          = "smartblur",
     .description   = NULL_IF_CONFIG_SMALL("Blur the input video without impacting the outlines."),
     .priv_size     = sizeof(SmartblurContext),
@@ -302,4 +302,5 @@  AVFilter ff_vf_smartblur = {
     .outputs       = smartblur_outputs,
     .priv_class    = &smartblur_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_smartblur,
 };
diff --git a/libavfilter/vf_spp.c b/libavfilter/vf_spp.c
index fe579cedb1..81e21828fb 100644
--- a/libavfilter/vf_spp.c
+++ b/libavfilter/vf_spp.c
@@ -514,7 +514,7 @@  static const AVFilterPad spp_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_spp = {
+const AVFilter ff_vf_spp = {
     .name            = "spp",
     .description     = NULL_IF_CONFIG_SMALL("Apply a simple post processing filter."),
     .priv_size       = sizeof(SPPContext),
@@ -526,4 +526,5 @@  AVFilter ff_vf_spp = {
     .process_command = process_command,
     .priv_class      = &spp_class,
     .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next            = ff_next_vf_spp,
 };
diff --git a/libavfilter/vf_ssim.c b/libavfilter/vf_ssim.c
index 4dcdc0548b..2373f8cfd4 100644
--- a/libavfilter/vf_ssim.c
+++ b/libavfilter/vf_ssim.c
@@ -498,7 +498,7 @@  static const AVFilterPad ssim_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_ssim = {
+const AVFilter ff_vf_ssim = {
     .name          = "ssim",
     .description   = NULL_IF_CONFIG_SMALL("Calculate the SSIM between two video streams."),
     .preinit       = ssim_framesync_preinit,
@@ -510,4 +510,5 @@  AVFilter ff_vf_ssim = {
     .priv_class    = &ssim_class,
     .inputs        = ssim_inputs,
     .outputs       = ssim_outputs,
+    .next          = ff_next_vf_ssim,
 };
diff --git a/libavfilter/vf_stack.c b/libavfilter/vf_stack.c
index b2b8c68041..d933f87482 100644
--- a/libavfilter/vf_stack.c
+++ b/libavfilter/vf_stack.c
@@ -242,7 +242,7 @@  static const AVFilterPad outputs[] = {
 #define hstack_options stack_options
 AVFILTER_DEFINE_CLASS(hstack);
 
-AVFilter ff_vf_hstack = {
+const AVFilter ff_vf_hstack = {
     .name          = "hstack",
     .description   = NULL_IF_CONFIG_SMALL("Stack video inputs horizontally."),
     .priv_size     = sizeof(StackContext),
@@ -253,6 +253,7 @@  AVFilter ff_vf_hstack = {
     .uninit        = uninit,
     .activate      = activate,
     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next          = ff_next_vf_hstack,
 };
 
 #endif /* CONFIG_HSTACK_FILTER */
@@ -262,7 +263,7 @@  AVFilter ff_vf_hstack = {
 #define vstack_options stack_options
 AVFILTER_DEFINE_CLASS(vstack);
 
-AVFilter ff_vf_vstack = {
+const AVFilter ff_vf_vstack = {
     .name          = "vstack",
     .description   = NULL_IF_CONFIG_SMALL("Stack video inputs vertically."),
     .priv_size     = sizeof(StackContext),
@@ -273,6 +274,7 @@  AVFilter ff_vf_vstack = {
     .uninit        = uninit,
     .activate      = activate,
     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
+    .next          = ff_next_vf_vstack,
 };
 
 #endif /* CONFIG_VSTACK_FILTER */
diff --git a/libavfilter/vf_stereo3d.c b/libavfilter/vf_stereo3d.c
index 8b22f880ca..36243c6912 100644
--- a/libavfilter/vf_stereo3d.c
+++ b/libavfilter/vf_stereo3d.c
@@ -1105,7 +1105,7 @@  static const AVFilterPad stereo3d_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_stereo3d = {
+const AVFilter ff_vf_stereo3d = {
     .name          = "stereo3d",
     .description   = NULL_IF_CONFIG_SMALL("Convert video stereoscopic 3D view."),
     .priv_size     = sizeof(Stereo3DContext),
@@ -1115,4 +1115,5 @@  AVFilter ff_vf_stereo3d = {
     .outputs       = stereo3d_outputs,
     .priv_class    = &stereo3d_class,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_stereo3d,
 };
diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c
index a7b02461f2..fedb4c9fc7 100644
--- a/libavfilter/vf_subtitles.c
+++ b/libavfilter/vf_subtitles.c
@@ -246,7 +246,7 @@  static av_cold int init_ass(AVFilterContext *ctx)
     return 0;
 }
 
-AVFilter ff_vf_ass = {
+const AVFilter ff_vf_ass = {
     .name          = "ass",
     .description   = NULL_IF_CONFIG_SMALL("Render ASS subtitles onto input video using the libass library."),
     .priv_size     = sizeof(AssContext),
@@ -256,6 +256,7 @@  AVFilter ff_vf_ass = {
     .inputs        = ass_inputs,
     .outputs       = ass_outputs,
     .priv_class    = &ass_class,
+    .next          = ff_next_vf_ass,
 };
 #endif
 
@@ -484,7 +485,7 @@  end:
     return ret;
 }
 
-AVFilter ff_vf_subtitles = {
+const AVFilter ff_vf_subtitles = {
     .name          = "subtitles",
     .description   = NULL_IF_CONFIG_SMALL("Render text subtitles onto input video using the libass library."),
     .priv_size     = sizeof(AssContext),
@@ -494,5 +495,6 @@  AVFilter ff_vf_subtitles = {
     .inputs        = ass_inputs,
     .outputs       = ass_outputs,
     .priv_class    = &subtitles_class,
+    .next          = ff_next_vf_subtitles,
 };
 #endif
diff --git a/libavfilter/vf_super2xsai.c b/libavfilter/vf_super2xsai.c
index 87eec04da8..3475fdd486 100644
--- a/libavfilter/vf_super2xsai.c
+++ b/libavfilter/vf_super2xsai.c
@@ -344,11 +344,12 @@  static const AVFilterPad super2xsai_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_super2xsai = {
+const AVFilter ff_vf_super2xsai = {
     .name          = "super2xsai",
     .description   = NULL_IF_CONFIG_SMALL("Scale the input by 2x using the Super2xSaI pixel art algorithm."),
     .priv_size     = sizeof(Super2xSaIContext),
     .query_formats = query_formats,
     .inputs        = super2xsai_inputs,
     .outputs       = super2xsai_outputs,
+    .next          = ff_next_vf_super2xsai,
 };
diff --git a/libavfilter/vf_swaprect.c b/libavfilter/vf_swaprect.c
index f96f897818..959fe35e72 100644
--- a/libavfilter/vf_swaprect.c
+++ b/libavfilter/vf_swaprect.c
@@ -243,7 +243,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_swaprect = {
+const AVFilter ff_vf_swaprect = {
     .name          = "swaprect",
     .description   = NULL_IF_CONFIG_SMALL("Swap 2 rectangular objects in video."),
     .priv_size     = sizeof(SwapRectContext),
@@ -253,4 +253,5 @@  AVFilter ff_vf_swaprect = {
     .inputs        = inputs,
     .outputs       = outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_swaprect,
 };
diff --git a/libavfilter/vf_swapuv.c b/libavfilter/vf_swapuv.c
index 8d62c48c4f..498d87882a 100644
--- a/libavfilter/vf_swapuv.c
+++ b/libavfilter/vf_swapuv.c
@@ -117,7 +117,7 @@  static const AVFilterPad swapuv_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_swapuv = {
+const AVFilter ff_vf_swapuv = {
     .name          = "swapuv",
     .description   = NULL_IF_CONFIG_SMALL("Swap U and V components."),
     .query_formats = query_formats,
@@ -126,4 +126,5 @@  AVFilter ff_vf_swapuv = {
     .inputs        = swapuv_inputs,
     .outputs       = swapuv_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_swapuv,
 };
diff --git a/libavfilter/vf_telecine.c b/libavfilter/vf_telecine.c
index 62599a7a3a..c2e13f2c5c 100644
--- a/libavfilter/vf_telecine.c
+++ b/libavfilter/vf_telecine.c
@@ -282,7 +282,7 @@  static const AVFilterPad telecine_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_telecine = {
+const AVFilter ff_vf_telecine = {
     .name          = "telecine",
     .description   = NULL_IF_CONFIG_SMALL("Apply a telecine pattern."),
     .priv_size     = sizeof(TelecineContext),
@@ -292,4 +292,5 @@  AVFilter ff_vf_telecine = {
     .query_formats = query_formats,
     .inputs        = telecine_inputs,
     .outputs       = telecine_outputs,
+    .next          = ff_next_vf_telecine,
 };
diff --git a/libavfilter/vf_threshold.c b/libavfilter/vf_threshold.c
index 58b5d14836..96ad50cb74 100644
--- a/libavfilter/vf_threshold.c
+++ b/libavfilter/vf_threshold.c
@@ -314,7 +314,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_threshold = {
+const AVFilter ff_vf_threshold = {
     .name          = "threshold",
     .description   = NULL_IF_CONFIG_SMALL("Threshold first video stream using other video streams."),
     .priv_size     = sizeof(ThresholdContext),
@@ -325,4 +325,5 @@  AVFilter ff_vf_threshold = {
     .inputs        = inputs,
     .outputs       = outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next          = ff_next_vf_threshold,
 };
diff --git a/libavfilter/vf_thumbnail.c b/libavfilter/vf_thumbnail.c
index 0effdc91e9..ee68abab2e 100644
--- a/libavfilter/vf_thumbnail.c
+++ b/libavfilter/vf_thumbnail.c
@@ -224,7 +224,7 @@  static const AVFilterPad thumbnail_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_thumbnail = {
+const AVFilter ff_vf_thumbnail = {
     .name          = "thumbnail",
     .description   = NULL_IF_CONFIG_SMALL("Select the most representative frame in a given sequence of consecutive frames."),
     .priv_size     = sizeof(ThumbContext),
@@ -234,4 +234,5 @@  AVFilter ff_vf_thumbnail = {
     .inputs        = thumbnail_inputs,
     .outputs       = thumbnail_outputs,
     .priv_class    = &thumbnail_class,
+    .next          = ff_next_vf_thumbnail,
 };
diff --git a/libavfilter/vf_thumbnail_cuda.c b/libavfilter/vf_thumbnail_cuda.c
index 09377ca7f4..f7bc2cedea 100644
--- a/libavfilter/vf_thumbnail_cuda.c
+++ b/libavfilter/vf_thumbnail_cuda.c
@@ -431,7 +431,7 @@  static const AVFilterPad thumbnail_cuda_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_thumbnail_cuda = {
+const AVFilter ff_vf_thumbnail_cuda = {
     .name          = "thumbnail_cuda",
     .description   = NULL_IF_CONFIG_SMALL("Select the most representative frame in a given sequence of consecutive frames."),
     .priv_size     = sizeof(ThumbnailCudaContext),
@@ -442,4 +442,5 @@  AVFilter ff_vf_thumbnail_cuda = {
     .outputs       = thumbnail_cuda_outputs,
     .priv_class    = &thumbnail_cuda_class,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next          = ff_next_vf_thumbnail_cuda,
 };
diff --git a/libavfilter/vf_tile.c b/libavfilter/vf_tile.c
index 439689a14d..87a8febba2 100644
--- a/libavfilter/vf_tile.c
+++ b/libavfilter/vf_tile.c
@@ -284,7 +284,7 @@  static const AVFilterPad tile_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_tile = {
+const AVFilter ff_vf_tile = {
     .name          = "tile",
     .description   = NULL_IF_CONFIG_SMALL("Tile several successive frames together."),
     .init          = init,
@@ -294,4 +294,5 @@  AVFilter ff_vf_tile = {
     .inputs        = tile_inputs,
     .outputs       = tile_outputs,
     .priv_class    = &tile_class,
+    .next          = ff_next_vf_tile,
 };
diff --git a/libavfilter/vf_tinterlace.c b/libavfilter/vf_tinterlace.c
index f13791d254..1ecf73d0f6 100644
--- a/libavfilter/vf_tinterlace.c
+++ b/libavfilter/vf_tinterlace.c
@@ -515,7 +515,7 @@  static const AVFilterPad tinterlace_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_tinterlace = {
+const AVFilter ff_vf_tinterlace = {
     .name          = "tinterlace",
     .description   = NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."),
     .priv_size     = sizeof(TInterlaceContext),
@@ -524,4 +524,5 @@  AVFilter ff_vf_tinterlace = {
     .inputs        = tinterlace_inputs,
     .outputs       = tinterlace_outputs,
     .priv_class    = &tinterlace_class,
+    .next          = ff_next_vf_tinterlace,
 };
diff --git a/libavfilter/vf_tonemap.c b/libavfilter/vf_tonemap.c
index 10308bdb16..8c976fc684 100644
--- a/libavfilter/vf_tonemap.c
+++ b/libavfilter/vf_tonemap.c
@@ -342,7 +342,7 @@  static const AVFilterPad tonemap_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_tonemap = {
+const AVFilter ff_vf_tonemap = {
     .name            = "tonemap",
     .description     = NULL_IF_CONFIG_SMALL("Conversion to/from different dynamic ranges."),
     .init            = init,
@@ -351,4 +351,5 @@  AVFilter ff_vf_tonemap = {
     .priv_class      = &tonemap_class,
     .inputs          = tonemap_inputs,
     .outputs         = tonemap_outputs,
+    .next            = ff_next_vf_tonemap,
 };
diff --git a/libavfilter/vf_transpose.c b/libavfilter/vf_transpose.c
index 3ff4cb4249..5dc7ee8e89 100644
--- a/libavfilter/vf_transpose.c
+++ b/libavfilter/vf_transpose.c
@@ -401,7 +401,7 @@  static const AVFilterPad avfilter_vf_transpose_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_transpose = {
+const AVFilter ff_vf_transpose = {
     .name          = "transpose",
     .description   = NULL_IF_CONFIG_SMALL("Transpose input video."),
     .priv_size     = sizeof(TransContext),
@@ -410,4 +410,5 @@  AVFilter ff_vf_transpose = {
     .inputs        = avfilter_vf_transpose_inputs,
     .outputs       = avfilter_vf_transpose_outputs,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_transpose,
 };
diff --git a/libavfilter/vf_unsharp.c b/libavfilter/vf_unsharp.c
index 41ccc56942..5c38d880b2 100644
--- a/libavfilter/vf_unsharp.c
+++ b/libavfilter/vf_unsharp.c
@@ -284,7 +284,7 @@  static const AVFilterPad avfilter_vf_unsharp_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_unsharp = {
+const AVFilter ff_vf_unsharp = {
     .name          = "unsharp",
     .description   = NULL_IF_CONFIG_SMALL("Sharpen or blur the input video."),
     .priv_size     = sizeof(UnsharpContext),
@@ -295,4 +295,5 @@  AVFilter ff_vf_unsharp = {
     .inputs        = avfilter_vf_unsharp_inputs,
     .outputs       = avfilter_vf_unsharp_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_unsharp,
 };
diff --git a/libavfilter/vf_unsharp_opencl.c b/libavfilter/vf_unsharp_opencl.c
index 6a453c014b..ffa531ee53 100644
--- a/libavfilter/vf_unsharp_opencl.c
+++ b/libavfilter/vf_unsharp_opencl.c
@@ -469,7 +469,7 @@  static const AVFilterPad unsharp_opencl_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_unsharp_opencl = {
+const AVFilter ff_vf_unsharp_opencl = {
     .name           = "unsharp_opencl",
     .description    = NULL_IF_CONFIG_SMALL("Apply unsharp mask to input video"),
     .priv_size      = sizeof(UnsharpOpenCLContext),
@@ -480,4 +480,5 @@  AVFilter ff_vf_unsharp_opencl = {
     .inputs         = unsharp_opencl_inputs,
     .outputs        = unsharp_opencl_outputs,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next           = ff_next_vf_unsharp_opencl,
 };
diff --git a/libavfilter/vf_uspp.c b/libavfilter/vf_uspp.c
index da4029f4b2..df5b2d1c13 100644
--- a/libavfilter/vf_uspp.c
+++ b/libavfilter/vf_uspp.c
@@ -496,7 +496,7 @@  static const AVFilterPad uspp_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_uspp = {
+const AVFilter ff_vf_uspp = {
     .name            = "uspp",
     .description     = NULL_IF_CONFIG_SMALL("Apply Ultra Simple / Slow Post-processing filter."),
     .priv_size       = sizeof(USPPContext),
@@ -506,4 +506,5 @@  AVFilter ff_vf_uspp = {
     .outputs         = uspp_outputs,
     .priv_class      = &uspp_class,
     .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
+    .next            = ff_next_vf_uspp,
 };
diff --git a/libavfilter/vf_vaguedenoiser.c b/libavfilter/vf_vaguedenoiser.c
index 2b93e70e57..b0de2ba782 100644
--- a/libavfilter/vf_vaguedenoiser.c
+++ b/libavfilter/vf_vaguedenoiser.c
@@ -569,7 +569,7 @@  static const AVFilterPad vaguedenoiser_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_vaguedenoiser = {
+const AVFilter ff_vf_vaguedenoiser = {
     .name          = "vaguedenoiser",
     .description   = NULL_IF_CONFIG_SMALL("Apply a Wavelet based Denoiser."),
     .priv_size     = sizeof(VagueDenoiserContext),
@@ -580,4 +580,5 @@  AVFilter ff_vf_vaguedenoiser = {
     .inputs        = vaguedenoiser_inputs,
     .outputs       = vaguedenoiser_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_vaguedenoiser,
 };
diff --git a/libavfilter/vf_vectorscope.c b/libavfilter/vf_vectorscope.c
index e3e00797d0..ecfb183a4b 100644
--- a/libavfilter/vf_vectorscope.c
+++ b/libavfilter/vf_vectorscope.c
@@ -1347,7 +1347,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_vectorscope = {
+const AVFilter ff_vf_vectorscope = {
     .name          = "vectorscope",
     .description   = NULL_IF_CONFIG_SMALL("Video vectorscope."),
     .priv_size     = sizeof(VectorscopeContext),
@@ -1356,4 +1356,5 @@  AVFilter ff_vf_vectorscope = {
     .uninit        = uninit,
     .inputs        = inputs,
     .outputs       = outputs,
+    .next          = ff_next_vf_vectorscope,
 };
diff --git a/libavfilter/vf_vflip.c b/libavfilter/vf_vflip.c
index c7c39d3341..346b993d16 100644
--- a/libavfilter/vf_vflip.c
+++ b/libavfilter/vf_vflip.c
@@ -110,7 +110,7 @@  static const AVFilterPad avfilter_vf_vflip_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_vflip = {
+const AVFilter ff_vf_vflip = {
     .name        = "vflip",
     .description = NULL_IF_CONFIG_SMALL("Flip the input video vertically."),
     .priv_size   = sizeof(FlipContext),
@@ -118,4 +118,5 @@  AVFilter ff_vf_vflip = {
     .inputs      = avfilter_vf_vflip_inputs,
     .outputs     = avfilter_vf_vflip_outputs,
     .flags       = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next        = ff_next_vf_vflip,
 };
diff --git a/libavfilter/vf_vidstabdetect.c b/libavfilter/vf_vidstabdetect.c
index fd7ff3be24..10eb5bb6a3 100644
--- a/libavfilter/vf_vidstabdetect.c
+++ b/libavfilter/vf_vidstabdetect.c
@@ -207,7 +207,7 @@  static const AVFilterPad avfilter_vf_vidstabdetect_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_vidstabdetect = {
+const AVFilter ff_vf_vidstabdetect = {
     .name          = "vidstabdetect",
     .description   = NULL_IF_CONFIG_SMALL("Extract relative transformations, "
                                           "pass 1 of 2 for stabilization "
@@ -219,4 +219,5 @@  AVFilter ff_vf_vidstabdetect = {
     .inputs        = avfilter_vf_vidstabdetect_inputs,
     .outputs       = avfilter_vf_vidstabdetect_outputs,
     .priv_class    = &vidstabdetect_class,
+    .next          = ff_next_vf_vidstabdetect,
 };
diff --git a/libavfilter/vf_vidstabtransform.c b/libavfilter/vf_vidstabtransform.c
index d1ec1391cb..e7c14b7a74 100644
--- a/libavfilter/vf_vidstabtransform.c
+++ b/libavfilter/vf_vidstabtransform.c
@@ -308,7 +308,7 @@  static const AVFilterPad avfilter_vf_vidstabtransform_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_vidstabtransform = {
+const AVFilter ff_vf_vidstabtransform = {
     .name          = "vidstabtransform",
     .description   = NULL_IF_CONFIG_SMALL("Transform the frames, "
                                           "pass 2 of 2 for stabilization "
@@ -320,4 +320,5 @@  AVFilter ff_vf_vidstabtransform = {
     .inputs        = avfilter_vf_vidstabtransform_inputs,
     .outputs       = avfilter_vf_vidstabtransform_outputs,
     .priv_class    = &vidstabtransform_class,
+    .next          = ff_next_vf_vidstabtransform,
 };
diff --git a/libavfilter/vf_vignette.c b/libavfilter/vf_vignette.c
index 47b59e5ba0..c19ef9e092 100644
--- a/libavfilter/vf_vignette.c
+++ b/libavfilter/vf_vignette.c
@@ -345,7 +345,7 @@  static const AVFilterPad vignette_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_vignette = {
+const AVFilter ff_vf_vignette = {
     .name          = "vignette",
     .description   = NULL_IF_CONFIG_SMALL("Make or reverse a vignette effect."),
     .priv_size     = sizeof(VignetteContext),
@@ -356,4 +356,5 @@  AVFilter ff_vf_vignette = {
     .outputs       = vignette_outputs,
     .priv_class    = &vignette_class,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+    .next          = ff_next_vf_vignette,
 };
diff --git a/libavfilter/vf_vmafmotion.c b/libavfilter/vf_vmafmotion.c
index 9bcc4ff16f..6a9848ab3c 100644
--- a/libavfilter/vf_vmafmotion.c
+++ b/libavfilter/vf_vmafmotion.c
@@ -356,7 +356,7 @@  static const AVFilterPad vmafmotion_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_vmafmotion = {
+const AVFilter ff_vf_vmafmotion = {
     .name          = "vmafmotion",
     .description   = NULL_IF_CONFIG_SMALL("Calculate the VMAF Motion score."),
     .init          = init,
@@ -366,4 +366,5 @@  AVFilter ff_vf_vmafmotion = {
     .priv_class    = &vmafmotion_class,
     .inputs        = vmafmotion_inputs,
     .outputs       = vmafmotion_outputs,
+    .next          = ff_next_vf_vmafmotion,
 };
diff --git a/libavfilter/vf_vpp_qsv.c b/libavfilter/vf_vpp_qsv.c
index bd5fc32299..c576a4f025 100644
--- a/libavfilter/vf_vpp_qsv.c
+++ b/libavfilter/vf_vpp_qsv.c
@@ -393,7 +393,7 @@  static const AVFilterPad vpp_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_vpp_qsv = {
+const AVFilter ff_vf_vpp_qsv = {
     .name          = "vpp_qsv",
     .description   = NULL_IF_CONFIG_SMALL("Quick Sync Video VPP."),
     .priv_size     = sizeof(VPPContext),
@@ -403,4 +403,5 @@  AVFilter ff_vf_vpp_qsv = {
     .outputs       = vpp_outputs,
     .priv_class    = &vpp_class,
     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
+    .next          = ff_next_vf_vpp_qsv,
 };
diff --git a/libavfilter/vf_w3fdif.c b/libavfilter/vf_w3fdif.c
index c6a6550778..6f411b5bff 100644
--- a/libavfilter/vf_w3fdif.c
+++ b/libavfilter/vf_w3fdif.c
@@ -583,7 +583,7 @@  static const AVFilterPad w3fdif_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_w3fdif = {
+const AVFilter ff_vf_w3fdif = {
     .name          = "w3fdif",
     .description   = NULL_IF_CONFIG_SMALL("Apply Martin Weston three field deinterlace."),
     .priv_size     = sizeof(W3FDIFContext),
@@ -593,4 +593,5 @@  AVFilter ff_vf_w3fdif = {
     .inputs        = w3fdif_inputs,
     .outputs       = w3fdif_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_w3fdif,
 };
diff --git a/libavfilter/vf_waveform.c b/libavfilter/vf_waveform.c
index 02a7046f33..c2707ea17a 100644
--- a/libavfilter/vf_waveform.c
+++ b/libavfilter/vf_waveform.c
@@ -2829,7 +2829,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_waveform = {
+const AVFilter ff_vf_waveform = {
     .name          = "waveform",
     .description   = NULL_IF_CONFIG_SMALL("Video waveform monitor."),
     .priv_size     = sizeof(WaveformContext),
@@ -2838,4 +2838,5 @@  AVFilter ff_vf_waveform = {
     .uninit        = uninit,
     .inputs        = inputs,
     .outputs       = outputs,
+    .next          = ff_next_vf_waveform,
 };
diff --git a/libavfilter/vf_weave.c b/libavfilter/vf_weave.c
index 037f5d1cf2..16d906f7da 100644
--- a/libavfilter/vf_weave.c
+++ b/libavfilter/vf_weave.c
@@ -139,6 +139,7 @@  static av_cold void uninit(AVFilterContext *ctx)
     av_frame_free(&s->prev);
 }
 
+#if CONFIG_WEAVE_FILTER
 static const AVFilterPad weave_inputs[] = {
     {
         .name             = "default",
@@ -157,7 +158,7 @@  static const AVFilterPad weave_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_weave = {
+const AVFilter ff_vf_weave = {
     .name          = "weave",
     .description   = NULL_IF_CONFIG_SMALL("Weave input video fields into frames."),
     .priv_size     = sizeof(WeaveContext),
@@ -165,8 +166,11 @@  AVFilter ff_vf_weave = {
     .uninit        = uninit,
     .inputs        = weave_inputs,
     .outputs       = weave_outputs,
+    .next          = ff_next_vf_weave,
 };
+#endif /* CONFIG_WEAVE_FILTER */
 
+#if CONFIG_DOUBLEWEAVE_FILTER
 static av_cold int init(AVFilterContext *ctx)
 {
     WeaveContext *s = ctx->priv;
@@ -180,7 +184,7 @@  static av_cold int init(AVFilterContext *ctx)
 #define doubleweave_options weave_options
 AVFILTER_DEFINE_CLASS(doubleweave);
 
-AVFilter ff_vf_doubleweave = {
+const AVFilter ff_vf_doubleweave = {
     .name          = "doubleweave",
     .description   = NULL_IF_CONFIG_SMALL("Weave input video fields into double number of frames."),
     .priv_size     = sizeof(WeaveContext),
@@ -189,4 +193,6 @@  AVFilter ff_vf_doubleweave = {
     .uninit        = uninit,
     .inputs        = weave_inputs,
     .outputs       = weave_outputs,
+    .next          = ff_next_vf_doubleweave,
 };
+#endif /* CONFIG_DOUBLEWEAVE_FILTER */
diff --git a/libavfilter/vf_xbr.c b/libavfilter/vf_xbr.c
index 78094e0287..3df0254bc2 100644
--- a/libavfilter/vf_xbr.c
+++ b/libavfilter/vf_xbr.c
@@ -425,7 +425,7 @@  static const AVFilterPad xbr_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_xbr = {
+const AVFilter ff_vf_xbr = {
     .name          = "xbr",
     .description   = NULL_IF_CONFIG_SMALL("Scale the input using xBR algorithm."),
     .inputs        = xbr_inputs,
@@ -435,4 +435,5 @@  AVFilter ff_vf_xbr = {
     .priv_class    = &xbr_class,
     .init          = init,
     .flags         = AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_xbr,
 };
diff --git a/libavfilter/vf_yadif.c b/libavfilter/vf_yadif.c
index f58d8ac2bc..e4a4ba41fa 100644
--- a/libavfilter/vf_yadif.c
+++ b/libavfilter/vf_yadif.c
@@ -552,7 +552,7 @@  static const AVFilterPad avfilter_vf_yadif_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_yadif = {
+const AVFilter ff_vf_yadif = {
     .name          = "yadif",
     .description   = NULL_IF_CONFIG_SMALL("Deinterlace the input image."),
     .priv_size     = sizeof(YADIFContext),
@@ -562,4 +562,5 @@  AVFilter ff_vf_yadif = {
     .inputs        = avfilter_vf_yadif_inputs,
     .outputs       = avfilter_vf_yadif_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
+    .next          = ff_next_vf_yadif,
 };
diff --git a/libavfilter/vf_zoompan.c b/libavfilter/vf_zoompan.c
index 0635171b57..0dadf593ad 100644
--- a/libavfilter/vf_zoompan.c
+++ b/libavfilter/vf_zoompan.c
@@ -363,7 +363,7 @@  static const AVFilterPad outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_zoompan = {
+const AVFilter ff_vf_zoompan = {
     .name          = "zoompan",
     .description   = NULL_IF_CONFIG_SMALL("Apply Zoom & Pan effect."),
     .priv_size     = sizeof(ZPContext),
@@ -374,4 +374,5 @@  AVFilter ff_vf_zoompan = {
     .activate      = activate,
     .inputs        = inputs,
     .outputs       = outputs,
+    .next          = ff_next_vf_zoompan,
 };
diff --git a/libavfilter/vf_zscale.c b/libavfilter/vf_zscale.c
index 6e1d36cb4c..a742582d64 100644
--- a/libavfilter/vf_zscale.c
+++ b/libavfilter/vf_zscale.c
@@ -885,7 +885,7 @@  static const AVFilterPad avfilter_vf_zscale_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vf_zscale = {
+const AVFilter ff_vf_zscale = {
     .name            = "zscale",
     .description     = NULL_IF_CONFIG_SMALL("Apply resizing, colorspace and bit depth conversion."),
     .init_dict       = init_dict,
@@ -896,4 +896,5 @@  AVFilter ff_vf_zscale = {
     .inputs          = avfilter_vf_zscale_inputs,
     .outputs         = avfilter_vf_zscale_outputs,
     .process_command = process_command,
+    .next            = ff_next_vf_zscale,
 };
diff --git a/libavfilter/vsink_nullsink.c b/libavfilter/vsink_nullsink.c
index 281721bc55..7840d887f5 100644
--- a/libavfilter/vsink_nullsink.c
+++ b/libavfilter/vsink_nullsink.c
@@ -35,7 +35,7 @@  static const AVFilterPad avfilter_vsink_nullsink_inputs[] = {
     { NULL },
 };
 
-AVFilter ff_vsink_nullsink = {
+const AVFilter ff_vsink_nullsink = {
     .name        = "nullsink",
     .description = NULL_IF_CONFIG_SMALL("Do absolutely nothing with the input video."),
 
@@ -43,4 +43,5 @@  AVFilter ff_vsink_nullsink = {
 
     .inputs    = avfilter_vsink_nullsink_inputs,
     .outputs   = NULL,
+    .next      = ff_next_vsink_nullsink,
 };
diff --git a/libavfilter/vsrc_cellauto.c b/libavfilter/vsrc_cellauto.c
index 7a6d9659f7..5f541035b1 100644
--- a/libavfilter/vsrc_cellauto.c
+++ b/libavfilter/vsrc_cellauto.c
@@ -327,7 +327,7 @@  static const AVFilterPad cellauto_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsrc_cellauto = {
+const AVFilter ff_vsrc_cellauto = {
     .name          = "cellauto",
     .description   = NULL_IF_CONFIG_SMALL("Create pattern generated by an elementary cellular automaton."),
     .priv_size     = sizeof(CellAutoContext),
@@ -337,4 +337,5 @@  AVFilter ff_vsrc_cellauto = {
     .query_formats = query_formats,
     .inputs        = NULL,
     .outputs       = cellauto_outputs,
+    .next          = ff_next_vsrc_cellauto,
 };
diff --git a/libavfilter/vsrc_life.c b/libavfilter/vsrc_life.c
index a87ceef15d..8a97229535 100644
--- a/libavfilter/vsrc_life.c
+++ b/libavfilter/vsrc_life.c
@@ -441,7 +441,7 @@  static const AVFilterPad life_outputs[] = {
     { NULL}
 };
 
-AVFilter ff_vsrc_life = {
+const AVFilter ff_vsrc_life = {
     .name          = "life",
     .description   = NULL_IF_CONFIG_SMALL("Create life."),
     .priv_size     = sizeof(LifeContext),
@@ -451,4 +451,5 @@  AVFilter ff_vsrc_life = {
     .query_formats = query_formats,
     .inputs        = NULL,
     .outputs       = life_outputs,
+    .next          = ff_next_vsrc_life,
 };
diff --git a/libavfilter/vsrc_mandelbrot.c b/libavfilter/vsrc_mandelbrot.c
index 6ad108151f..fb205c09e1 100644
--- a/libavfilter/vsrc_mandelbrot.c
+++ b/libavfilter/vsrc_mandelbrot.c
@@ -419,7 +419,7 @@  static const AVFilterPad mandelbrot_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsrc_mandelbrot = {
+const AVFilter ff_vsrc_mandelbrot = {
     .name          = "mandelbrot",
     .description   = NULL_IF_CONFIG_SMALL("Render a Mandelbrot fractal."),
     .priv_size     = sizeof(MBContext),
@@ -429,4 +429,5 @@  AVFilter ff_vsrc_mandelbrot = {
     .query_formats = query_formats,
     .inputs        = NULL,
     .outputs       = mandelbrot_outputs,
+    .next          = ff_next_vsrc_mandelbrot,
 };
diff --git a/libavfilter/vsrc_mptestsrc.c b/libavfilter/vsrc_mptestsrc.c
index c5fdea75dc..4ac4e42e4b 100644
--- a/libavfilter/vsrc_mptestsrc.c
+++ b/libavfilter/vsrc_mptestsrc.c
@@ -351,7 +351,7 @@  static const AVFilterPad mptestsrc_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsrc_mptestsrc = {
+const AVFilter ff_vsrc_mptestsrc = {
     .name          = "mptestsrc",
     .description   = NULL_IF_CONFIG_SMALL("Generate various test pattern."),
     .priv_size     = sizeof(MPTestContext),
@@ -360,4 +360,5 @@  AVFilter ff_vsrc_mptestsrc = {
     .query_formats = query_formats,
     .inputs        = NULL,
     .outputs       = mptestsrc_outputs,
+    .next          = ff_next_vsrc_mptestsrc,
 };
diff --git a/libavfilter/vsrc_testsrc.c b/libavfilter/vsrc_testsrc.c
index a790974d14..b5ed79bcd1 100644
--- a/libavfilter/vsrc_testsrc.c
+++ b/libavfilter/vsrc_testsrc.c
@@ -262,7 +262,7 @@  static const AVFilterPad color_outputs[] = {
     {  NULL }
 };
 
-AVFilter ff_vsrc_color = {
+const AVFilter ff_vsrc_color = {
     .name            = "color",
     .description     = NULL_IF_CONFIG_SMALL("Provide an uniformly colored input."),
     .priv_class      = &color_class,
@@ -273,6 +273,7 @@  AVFilter ff_vsrc_color = {
     .inputs          = NULL,
     .outputs         = color_outputs,
     .process_command = color_process_command,
+    .next            = ff_next_vsrc_color,
 };
 
 #endif /* CONFIG_COLOR_FILTER */
@@ -396,7 +397,7 @@  static const AVFilterPad haldclutsrc_outputs[] = {
     {  NULL }
 };
 
-AVFilter ff_vsrc_haldclutsrc = {
+const AVFilter ff_vsrc_haldclutsrc = {
     .name          = "haldclutsrc",
     .description   = NULL_IF_CONFIG_SMALL("Provide an identity Hald CLUT."),
     .priv_class    = &haldclutsrc_class,
@@ -406,6 +407,7 @@  AVFilter ff_vsrc_haldclutsrc = {
     .query_formats = haldclutsrc_query_formats,
     .inputs        = NULL,
     .outputs       = haldclutsrc_outputs,
+    .next          = ff_next_vsrc_haldclutsrc,
 };
 #endif /* CONFIG_HALDCLUTSRC_FILTER */
 
@@ -434,7 +436,7 @@  static const AVFilterPad nullsrc_outputs[] = {
     { NULL },
 };
 
-AVFilter ff_vsrc_nullsrc = {
+const AVFilter ff_vsrc_nullsrc = {
     .name        = "nullsrc",
     .description = NULL_IF_CONFIG_SMALL("Null video source, return unprocessed video frames."),
     .init        = nullsrc_init,
@@ -443,6 +445,7 @@  AVFilter ff_vsrc_nullsrc = {
     .priv_class  = &nullsrc_class,
     .inputs      = NULL,
     .outputs     = nullsrc_outputs,
+    .next        = ff_next_vsrc_nullsrc,
 };
 
 #endif /* CONFIG_NULLSRC_FILTER */
@@ -670,7 +673,7 @@  static const AVFilterPad avfilter_vsrc_testsrc_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsrc_testsrc = {
+const AVFilter ff_vsrc_testsrc = {
     .name          = "testsrc",
     .description   = NULL_IF_CONFIG_SMALL("Generate test pattern."),
     .priv_size     = sizeof(TestSourceContext),
@@ -680,6 +683,7 @@  AVFilter ff_vsrc_testsrc = {
     .query_formats = test_query_formats,
     .inputs        = NULL,
     .outputs       = avfilter_vsrc_testsrc_outputs,
+    .next          = ff_next_vsrc_testsrc,
 };
 
 #endif /* CONFIG_TESTSRC_FILTER */
@@ -943,7 +947,7 @@  static const AVFilterPad avfilter_vsrc_testsrc2_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsrc_testsrc2 = {
+const AVFilter ff_vsrc_testsrc2 = {
     .name          = "testsrc2",
     .description   = NULL_IF_CONFIG_SMALL("Generate another test pattern."),
     .priv_size     = sizeof(TestSourceContext),
@@ -953,6 +957,7 @@  AVFilter ff_vsrc_testsrc2 = {
     .query_formats = test2_query_formats,
     .inputs        = NULL,
     .outputs       = avfilter_vsrc_testsrc2_outputs,
+    .next          = ff_next_vsrc_testsrc2,
 };
 
 #endif /* CONFIG_TESTSRC2_FILTER */
@@ -1062,7 +1067,7 @@  static const AVFilterPad avfilter_vsrc_rgbtestsrc_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsrc_rgbtestsrc = {
+const AVFilter ff_vsrc_rgbtestsrc = {
     .name          = "rgbtestsrc",
     .description   = NULL_IF_CONFIG_SMALL("Generate RGB test pattern."),
     .priv_size     = sizeof(TestSourceContext),
@@ -1072,6 +1077,7 @@  AVFilter ff_vsrc_rgbtestsrc = {
     .query_formats = rgbtest_query_formats,
     .inputs        = NULL,
     .outputs       = avfilter_vsrc_rgbtestsrc_outputs,
+    .next          = ff_next_vsrc_rgbtestsrc,
 };
 
 #endif /* CONFIG_RGBTESTSRC_FILTER */
@@ -1238,7 +1244,7 @@  static const AVFilterPad avfilter_vsrc_yuvtestsrc_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsrc_yuvtestsrc = {
+const AVFilter ff_vsrc_yuvtestsrc = {
     .name          = "yuvtestsrc",
     .description   = NULL_IF_CONFIG_SMALL("Generate YUV test pattern."),
     .priv_size     = sizeof(TestSourceContext),
@@ -1248,6 +1254,7 @@  AVFilter ff_vsrc_yuvtestsrc = {
     .query_formats = yuvtest_query_formats,
     .inputs        = NULL,
     .outputs       = avfilter_vsrc_yuvtestsrc_outputs,
+    .next          = ff_next_vsrc_yuvtestsrc,
 };
 
 #endif /* CONFIG_YUVTESTSRC_FILTER */
@@ -1424,7 +1431,7 @@  static av_cold int smptebars_init(AVFilterContext *ctx)
     return init(ctx);
 }
 
-AVFilter ff_vsrc_smptebars = {
+const AVFilter ff_vsrc_smptebars = {
     .name          = "smptebars",
     .description   = NULL_IF_CONFIG_SMALL("Generate SMPTE color bars."),
     .priv_size     = sizeof(TestSourceContext),
@@ -1434,6 +1441,7 @@  AVFilter ff_vsrc_smptebars = {
     .query_formats = smptebars_query_formats,
     .inputs        = NULL,
     .outputs       = smptebars_outputs,
+    .next          = ff_next_vsrc_smptebars,
 };
 
 #endif  /* CONFIG_SMPTEBARS_FILTER */
@@ -1529,7 +1537,7 @@  static av_cold int smptehdbars_init(AVFilterContext *ctx)
     return init(ctx);
 }
 
-AVFilter ff_vsrc_smptehdbars = {
+const AVFilter ff_vsrc_smptehdbars = {
     .name          = "smptehdbars",
     .description   = NULL_IF_CONFIG_SMALL("Generate SMPTE HD color bars."),
     .priv_size     = sizeof(TestSourceContext),
@@ -1539,6 +1547,7 @@  AVFilter ff_vsrc_smptehdbars = {
     .query_formats = smptebars_query_formats,
     .inputs        = NULL,
     .outputs       = smptebars_outputs,
+    .next          = ff_next_vsrc_smptehdbars,
 };
 
 #endif  /* CONFIG_SMPTEHDBARS_FILTER */
@@ -1611,7 +1620,7 @@  static const AVFilterPad avfilter_vsrc_allyuv_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsrc_allyuv = {
+const AVFilter ff_vsrc_allyuv = {
     .name          = "allyuv",
     .description   = NULL_IF_CONFIG_SMALL("Generate all yuv colors."),
     .priv_size     = sizeof(TestSourceContext),
@@ -1621,6 +1630,7 @@  AVFilter ff_vsrc_allyuv = {
     .query_formats = allyuv_query_formats,
     .inputs        = NULL,
     .outputs       = avfilter_vsrc_allyuv_outputs,
+    .next          = ff_next_vsrc_allyuv,
 };
 
 #endif /* CONFIG_ALLYUV_FILTER */
@@ -1692,7 +1702,7 @@  static const AVFilterPad avfilter_vsrc_allrgb_outputs[] = {
     { NULL }
 };
 
-AVFilter ff_vsrc_allrgb = {
+const AVFilter ff_vsrc_allrgb = {
     .name          = "allrgb",
     .description   = NULL_IF_CONFIG_SMALL("Generate all RGB colors."),
     .priv_size     = sizeof(TestSourceContext),
@@ -1702,6 +1712,7 @@  AVFilter ff_vsrc_allrgb = {
     .query_formats = allrgb_query_formats,
     .inputs        = NULL,
     .outputs       = avfilter_vsrc_allrgb_outputs,
+    .next          = ff_next_vsrc_allrgb,
 };
 
 #endif /* CONFIG_ALLRGB_FILTER */
diff --git a/tests/checkasm/Makefile b/tests/checkasm/Makefile
index afbd09b940..4a96159c1a 100644
--- a/tests/checkasm/Makefile
+++ b/tests/checkasm/Makefile
@@ -61,7 +61,7 @@  tests/checkasm/checkasm.o: CFLAGS += -Umain
 CHECKASM := tests/checkasm/checkasm$(EXESUF)
 
 $(CHECKASM): $(CHECKASMOBJS) $(FF_STATIC_DEP_LIBS)
-	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(CHECKASMOBJS) $(FF_STATIC_DEP_LIBS) $(EXTRALIBS-avcodec) $(EXTRALIBS-avfilter) $(EXTRALIBS-avutil) $(EXTRALIBS)
+	$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(CHECKASMOBJS) $(FF_STATIC_DEP_LIBS) $(EXTRALIBS-avcodec) $(EXTRALIBS-avformat) $(EXTRALIBS-avfilter) $(EXTRALIBS-avutil) $(EXTRALIBS)
 
 checkasm: $(CHECKASM)