From 7486f878b982867667892611e181dc4eab3298c8 Mon Sep 17 00:00:00 2001 From: Leandro Santiago <leandrosansilva@gmail.com> Date: Wed, 12 Mar 2025 13:42:42 +0100 Subject: [PATCH] avfilter: Proof of Concept: enable out-of-tree filters This is a POC/prototype that aims to enable out of tree filters on FFmpeg. Here is how to test it, with an example filter: ``` mkdir -p ext/libavfilter pushd ext/libavfilter git clone https://gitlab.com/leandrosansilva/ffmpeg-extra-filter-example example popd ``` Then compile ffmpeg as usual: ``` ./configure && make && make install ``` Now you can use the filters: ``` ffplay /path/to/file.webm -vf 'foo,bar' ``` What works: - Building C based filters with no extra, or simple dependencies. - Multiple filters in the same object file. What is ugly: - The filter metadata is hidden in the Makefile comments, and this is needed because at `./configure` time, executed before make, some code needs to be generated and, although such generation could in theory be done via make, this feels like too much change at the moment. - Among the metadata, there are linker and compiler flags, which would look much better if they were simple make variables. - At the moment it's possible for only one `check` metadata entry to be included in the Makefile, but we should support multiple of them, for the case when there are multiple extra dependencies. What was not implemented: - I believe it would be useful to check if the license of the filter is compatible with the license used to build FFmpeg. - Only extra filters written in C (maybe C++?) are supported for now. One of my goals is to enable Rust as well. - There should be a way to have optional dependencies, for filters that can have multiple "backends", for instance, to be chosen at build time. Signed-off-by: Leandro Santiago <leandrosansilva@gmail.com> --- .gitignore | 4 +++ configure | 75 ++++++++++++++++++++++++++++++++++++++++ libavfilter/Makefile | 4 +++ libavfilter/allfilters.c | 1 + 4 files changed, 84 insertions(+) diff --git a/.gitignore b/.gitignore index 430abaf91b..4eae911379 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,7 @@ /tools/python/__pycache__/ /libavcodec/vulkan/*.c /libavfilter/vulkan/*.c +/ffbuild/external-filters.txt +/ffbuild/external-filters.mak +/libavfilter/external_filters_extern.h +/ext diff --git a/configure b/configure index d84e32196d..f7bb2cc38a 100755 --- a/configure +++ b/configure @@ -1798,6 +1798,7 @@ AVDEVICE_COMPONENTS=" AVFILTER_COMPONENTS=" filters + external_filters " AVFORMAT_COMPONENTS=" @@ -4489,6 +4490,50 @@ for opt do esac done +symbols_from_external_filter_makefile() { + grep '^#.*symbol:' < "$1" | sed -e 's|#\s*symbol: \(.*\)|\1|g' +} + +list_external_filter_makefiles() { + [ ! -d "ext/libavfilter" ] && return + + for f in ext/libavfilter/*; do + [ -f "$f/Makefile" ] && echo $f/Makefile + done +} + +list_external_filter_makefiles > ffbuild/external-filters.txt + +find_external_filters_extern() { + # TODO: handle invalid filter + while read Makefile; do + symbols_from_external_filter_makefile "$Makefile" | sed 's/^ff_[avfsinkrc]\{2,5\}_\([[:alnum:]_]\{1,\}\)/\1_filter/' + done < ffbuild/external-filters.txt +} + +EXTERNAL_FILTER_LIST=$(find_external_filters_extern) + +for n in external_filters; do + v=$(toupper ${n%s})_LIST + eval enable \$$v + eval ${n}_if_any="\$$v" +done + +FILTER_LIST=" + $FILTER_LIST + $EXTERNAL_FILTER_LIST +" + +AVFILTER_COMPONENTS_LIST=" + $AVFILTER_COMPONENTS_LIST + $EXTERNAL_FILTER_LIST +" + +ALL_COMPONENTS=" + $ALL_COMPONENTS + $EXTERNAL_FILTER_LIST +" + for e in $env; do eval "export $e" done @@ -7173,6 +7218,11 @@ enabled rkmpp && { require_pkg_config rkmpp rockchip_mpp rockchip/r } enabled vapoursynth && require_headers "vapoursynth/VSScript4.h vapoursynth/VapourSynth4.h" +# Check for the dependencies of the external filters +while read Makefile; do + # NOTE: this eval is dangerous, as it allows arbitrary code execution! + eval $(grep '^#.*check:' "$Makefile" | sed 's|#\s*check: \(.*\)|\1|g') +done < ffbuild/external-filters.txt if enabled gcrypt; then GCRYPT_CONFIG="${cross_prefix}libgcrypt-config" @@ -8250,12 +8300,23 @@ IGNORE_TESTS=$ignore_tests VERSION_TRACKING=$version_tracking EOF +while read Makefile; do + # NOTE: this eval is dangerous, as it allows arbitrary code execution! + eval "avfilter_extralibs=\"\$avfilter_extralibs $(grep '^#.*ldflags:' "$Makefile" | sed 's|#\s*ldflags: \(.*\)|\1|g')\"" +done < ffbuild/external-filters.txt + map 'eval echo "${v}_FFLIBS=\$${v}_deps" >> ffbuild/config.mak' $LIBRARY_LIST for entry in $LIBRARY_LIST $PROGRAM_LIST $EXTRALIBS_LIST; do eval echo "EXTRALIBS-${entry}=\$${entry}_extralibs" >> ffbuild/config.mak done +echo "" > ffbuild/external-filters.mak + +while read Makefile; do + echo "include $Makefile" >> ffbuild/external-filters.mak +done < ffbuild/external-filters.txt + cat > $TMPH <<EOF /* Automatically generated by configure - do not modify! */ #ifndef FFMPEG_CONFIG_H @@ -8343,6 +8404,20 @@ cp_if_changed $TMPH libavutil/avconfig.h # ... eval "$(sed -n "s/^extern const FFFilter ff_\([avfsinkrc]\{2,5\}\)_\(.*\);/full_filter_name_\2=\1_\2/p" $source_path/libavfilter/allfilters.c)" +rm -f libavfilter/external_filters_extern.h + +# register the symbols of the external filters +while read Makefile; do + eval "$(symbols_from_external_filter_makefile "$Makefile" \ + | sed 's/^ff_\([avfsinkrc]\{2,5\}\)_\([[:alnum:]]\{1,\}\)$/full_filter_name_\2=\1_\2/')" + + symbols_from_external_filter_makefile "$Makefile" | while read symbol; do + echo "extern const FFFilter $symbol;" >> libavfilter/external_filters_extern.h + echo "EXTERNAL_FILTER_$(echo $symbol | sed 's/^ff_[avfsinkrc]\{2,5\}_\([[:alnum:]]\{1,\}\)$/\1/' | tr a-z A-Z)_LOCATION = $f" \ + >> ffbuild/external-filters.mak + done +done < ffbuild/external-filters.txt + # generate the lists of enabled components print_enabled_components(){ file=$1 diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 7c0d879ec9..877b24c30f 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -27,6 +27,10 @@ OBJS = allfilters.o \ include $(SRC_PATH)/libavfilter/dnn/Makefile include $(SRC_PATH)/libavfilter/vulkan/Makefile +# external filters handling +include $(SRC_PATH)/ffbuild/external-filters.mak +EXTERNAL_FILTER_FLAGS = -I$(PWD) -I$(PWD)/libavfilter + OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o OBJS-$(HAVE_THREADS) += pthread.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 740d9ab265..c2d576e4be 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -621,6 +621,7 @@ extern const FFFilter ff_vsrc_buffer; extern const FFFilter ff_asink_abuffer; extern const FFFilter ff_vsink_buffer; +#include "libavfilter/external_filters_extern.h" #include "libavfilter/filter_list.c" -- 2.48.1