* [FFmpeg-devel] [PATCH] avfilter: POC: enable out-of-tree filters
@ 2025-03-13 12:18 Leandro Santiago
2025-03-13 12:20 ` Leandro Santiago
0 siblings, 1 reply; 2+ messages in thread
From: Leandro Santiago @ 2025-03-13 12:18 UTC (permalink / raw)
To: ffmpeg-devel
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 <leandrosansilva@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-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [FFmpeg-devel] [PATCH] avfilter: POC: enable out-of-tree filters
2025-03-13 12:18 [FFmpeg-devel] [PATCH] avfilter: POC: enable out-of-tree filters Leandro Santiago
@ 2025-03-13 12:20 ` Leandro Santiago
0 siblings, 0 replies; 2+ messages in thread
From: Leandro Santiago @ 2025-03-13 12:20 UTC (permalink / raw)
To: ffmpeg-devel
This is a follow-up to the my previous message:
https://ffmpeg.org/pipermail/ffmpeg-devel/2025-March/340895.html
On 3/13/25 13:18, Leandro Santiago wrote:
> 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 <leandrosansilva@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"
>
>
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2025-03-13 12:20 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-03-13 12:18 [FFmpeg-devel] [PATCH] avfilter: POC: enable out-of-tree filters Leandro Santiago
2025-03-13 12:20 ` Leandro Santiago
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git