This is a POC/prototype that aims to enable out of tree filters on FFmpeg. Here I name them "extra filters".
It introduces the program `jq` as a new build dependency. To test it, create a directory, for instance, /tmp/my-shiny-filter/ and inside it, create the following files: `filter.json`, with the content: ``` { "check": "require_pkg_config json json-c json-c/json.h json_c_version_num", "symbols": ["ff_vf_foo", "ff_vf_bar"] } ``` `filter.mak`, with the content: ``` OBJS += vf_shiny.o LIBOBJS += vf_shiny.o libavfilter/vf_shiny.o: $(CC) $(EXTRA_FILTER_FLAGS) -c -o $@ $(EXTRA_FILTER_FOO_LOCATION)/vf_shiny.c ``` `vf_shiny.c` file, with the content: ``` #include "libavutil/internal.h" #include "avfilter.h" #include "filters.h" #include "video.h" const FFFilter ff_vf_bar = { .p.name = "bar", .p.description = NULL_IF_CONFIG_SMALL("Example filter Baz"), .p.flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(ff_video_default_filterpad), }; const FFFilter ff_vf_foo = { .p.name = "foo", .p.description = NULL_IF_CONFIG_SMALL("Another foo filter"), .p.flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(ff_video_default_filterpad), }; ``` Then, from the ffmpeg source tree, run configure specifying where the extra filter is located: ``` ./configure --extra-filter=/tmp/my-shiny-filter make ffplay ``` Now you can use the filters: ``` ./ffplay /path/to/file.webm -vf 'foo,baz' ``` What works: - Building C based filters with no extra dependencies. - Multiple filters in the same object file. What does not work: - The extra filters will not use the same CC flags used to build the built-in filters as I could get it to work yet. - Due to the above limitation, you cannot include headers of extra dependencies, for instance, `json.h` in the example. - You can pass arbitrary CFLAGS or LDFLAGS in the filter.json file, but they should be passed only then building/linking `libavfilter`, instead of other libraries. 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. Signed-off-by: Leandro Santiago <leandrosansi...@gmail.com> --- .gitignore | 3 ++ configure | 61 ++++++++++++++++++++++++++++++++++++++++ libavfilter/Makefile | 4 +++ libavfilter/allfilters.c | 1 + 4 files changed, 69 insertions(+) diff --git a/.gitignore b/.gitignore index 9cfc78b414..4963e90191 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,6 @@ /tools/python/__pycache__/ /libavcodec/vulkan/*.c /libavfilter/vulkan/*.c +/ffbuild/extra-filters.txt +/ffbuild/extra-filters.mak +/libavfilter/extra_filters_extern.h diff --git a/configure b/configure index 750c99e3b9..6a2adc6c05 100755 --- a/configure +++ b/configure @@ -179,6 +179,7 @@ Individual component options: --enable-filter=NAME enable filter NAME --disable-filter=NAME disable filter NAME --disable-filters disable all filters + --extra-filter=/foo/bar add extra filter from directory. This option can be used multiple times External library support: @@ -1798,6 +1799,7 @@ AVDEVICE_COMPONENTS=" AVFILTER_COMPONENTS=" filters + extra_filters " AVFORMAT_COMPONENTS=" @@ -4382,6 +4384,8 @@ do_random(){ $action $(rand_list "$@" | awk "BEGIN { srand($random_seed) } \$1 == \"prob\" { prob = \$2; next } rand() < prob { print }") } +rm -f ffbuild/extra-filters.txt + # deprecated components (disabled by default) disable sonic_encoder sonic_ls_encoder @@ -4457,6 +4461,10 @@ for opt do die_unknown $opt fi ;; + --extra-filter=*) + filter_path="${opt#--extra-filter=}" + echo "$filter_path" >> ffbuild/extra-filters.txt + ;; --list-*) NAME="${opt#--list-}" is_in $NAME $COMPONENT_LIST || die_unknown $opt @@ -4487,6 +4495,36 @@ for opt do esac done +find_extra_filters_extern() { + # TODO: handle invalid filter + while read f; do + jq -r '.symbols[]' < "$f/filter.json" | sed 's/^ff_[avfsinkrc]\{2,5\}_\([[:alnum:]_]\{1,\}\)/\1_filter/' + done < ffbuild/extra-filters.txt +} + +EXTRA_FILTER_LIST=$(find_extra_filters_extern) + +for n in extra_filters; do + v=$(toupper ${n%s})_LIST + eval enable \$$v + eval ${n}_if_any="\$$v" +done + +FILTER_LIST=" + $FILTER_LIST + $EXTRA_FILTER_LIST +" + +AVFILTER_COMPONENTS_LIST=" + $AVFILTER_COMPONENTS_LIST + $EXTRA_FILTER_LIST +" + +ALL_COMPONENTS=" + $ALL_COMPONENTS + $EXTRA_FILTER_LIST +" + for e in $env; do eval "export $e" done @@ -7165,6 +7203,10 @@ enabled rkmpp && { require_pkg_config rkmpp rockchip_mpp rockchip/r } enabled vapoursynth && require_headers "vapoursynth/VSScript4.h vapoursynth/VapourSynth4.h" +while read f; do + # NOTE: this eval is dangerous, as it allows arbitrary code execution! + eval $(jq -r '.check // true' < "$f/filter.json") +done < ffbuild/extra-filters.txt if enabled gcrypt; then GCRYPT_CONFIG="${cross_prefix}libgcrypt-config" @@ -8243,6 +8285,12 @@ for entry in $LIBRARY_LIST $PROGRAM_LIST $EXTRALIBS_LIST; do eval echo "EXTRALIBS-${entry}=\$${entry}_extralibs" >> ffbuild/config.mak done +echo "" > ffbuild/extra-filters.mak + +while read f; do + echo "include $f/filter.mak" >> ffbuild/extra-filters.mak +done < ffbuild/extra-filters.txt + cat > $TMPH <<EOF /* Automatically generated by configure - do not modify! */ #ifndef FFMPEG_CONFIG_H @@ -8330,6 +8378,19 @@ 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/extra_filters_extern.h + +# Handle extra filters +while read f; do + eval "$(jq -r '.symbols[]' < "$f/filter.json" | sed 's/^ff_\([avfsinkrc]\{2,5\}\)_\([[:alnum:]]\{1,\}\)$/full_filter_name_\2=\1_\2/')" + jq -r '.symbols[]' < "$f/filter.json" | while read symbol; do + echo "extern const FFFilter $symbol;" >> libavfilter/extra_filters_extern.h + echo "EXTRA_FILTER_$(echo $symbol | sed 's/^ff_[avfsinkrc]\{2,5\}_\([[:alnum:]]\{1,\}\)$/\1/' | tr a-z A-Z)_LOCATION = $f" >> ffbuild/extra-filters.mak + echo "LDFLAGS += $(jq -r '.ldflags // ""' < "$f/filter.json")" >> ffbuild/extra-filters.mak + echo "CFLAGS += $(jq -r '.cflags // ""' < "$f/filter.json")" >> ffbuild/extra-filters.mak + done +done < ffbuild/extra-filters.txt + # generate the lists of enabled components print_enabled_components(){ file=$1 diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 7c0d879ec9..9b22aece3a 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 +# extra filters handling +include $(SRC_PATH)/ffbuild/extra-filters.mak +EXTRA_FILTER_FLAGS = -I$(PWD) -I$(PWD)/libavfilter $(CPPFLAGS) $(CC_DEPFLAGS) + OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o OBJS-$(HAVE_THREADS) += pthread.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 740d9ab265..e8565de5b0 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/extra_filters_extern.h" #include "libavfilter/filter_list.c" -- 2.48.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".