Repository: conan-io/conan-center-index

Issue:
[question] ffmpeg configure codecs, muxers, etc.
<!-- What is your question? Please be as specific as possible! -->
Hey,

This isn't a question as such, but it's not really a bug either!

The ffmpeg recipe doesn't currently allow you to explicitly configure the codecs etc. that are included in the build. To replace my manual build I need to be able to restrict what's included.

ffmpeg  supports a large number different codecs, containers, etc. and trying to provide a comprehensive list and maintain parity when new ones are added etc. doesn't seem like it would scale and would add a maintenance burden. 

I'd like to put in a PR to allow these to be configured in some way, and in light of the above, I propose that boolean options for "disable everything" type flags be added, for example `--disable-everything` or `--disable-encoders`, along with free-form options for flags which take a value such as `--enable-encoder=`, it would be left to the autotools build system to complain about any incorrect values passed for these free-form options.  e.g.
```
options = {
# existing options ...
"disable-everything": [True, False],
"disable-encoders": [True, False],
"disable-decoders": [True, False],
"enable-encoder": "ANY",
"enable-decoder": "ANY",
"enable-muxer": "ANY",
"enable-demuxer":, "ANY",
}
```

This would then allow you enable / disable specific codecs by name or create a minimalist build, for example to avoid licencing/patent issues and only allow MP3 decoding:
```
[options]
ffmpeg:disable-everything=True
ffmpeg:enable-decoder="mp3"
ffmpeg:enable-decoder="opus"
ffmpeg:enable-demuxer="mp3"
ffmpeg:enable-demuxer="ogg"
``` 

Another option would be to instead allow a list of codecs etc. to be passed into the recipe, which would reduce the length of the options list in conanfiles, but at the expense of extra parsing logic in the recipe and potentially very long conanfile option lines. Given the trade off I think I'm leaning towards the above example rather than the one below, but I don't feel strongly about this if more people prefer the list approach.
```
[options]
ffmpeg:disable-everything=True
ffmpeg:enable-decoders="mp3, mp3float, opus"
ffmpeg:enable-demuxer="mp3, ogg"
```

Adding this functionality might also be useful for other purposes, such as mentioned in https://github.com/conan-io/conan-center-index/issues/10571, e.g:
```
[options]
ffmpeg:disable-decoders=True
ffmpeg:disable-demuxers=True
# etc
``` 

I'd like to start working on a PR for this, I'm just looking for some feedback on how to add it to the recipe, is this approach sensible or would another approach be preferred? Individual `enable-thing` options or `enable-things` lists? Any other suggestions?

Thanks for reading!


Discussion:
It's a fair point. As ffmpeg is big and can accept many different codecs, I agree that is does not scale and a generic option could help with it. We would need to a set a default value with a minimal support, otherwise we would distribute an useless package.

As more contributors already helped with ffmpeg recipe, I'm pinging them here @SpaceIm @madebr @SSE4 @MartinDelille to give their thoughts.
Can you even do this:
```
"enable-encoder": "ogg",
"enable-encoder": "opus",
```
Isn't that a set / dict, ie the keys have to be unique?

I normally see features listed like so:
```
"enable-ogg": True,
"enable-opus": True
```

There might be an array of encoders, ie ['ogg','opus']
and a loop to add them into the options {} and default_options {},
like... for each, options.append( "enable-" + name )   etc etc

I think it's related to #10571
in general, I am not opposed to make recipes more configurable - contributions are always welcomed.
`ffmpeg` has literally hundreeds of possible encoders, decoders, filters to turn on/off one-by-one.
there are several ways on how it could be implemented.

1. allow arbitrary configure flags to be passed. the same way we did it in [boost](https://github.com/conan-io/conan-center-index/blob/master/recipes/boost/all/conanfile.py#L94), for instance.
so, something like that may do the trick:
```
# option
"extra_configure_flags": "ANY",  # custom configure flags
...
# default 
"extra_configure_flags": "None",
..
# apply
        if self.options.extra_configure_flags:
            args.extend(shlex.split(str(self.options.extra_configure_flags)))
```
and then use it like:
```
ffmpeg:extra_configure_flags=--disable-everything --disable-encoders=mp3
```
2. something more specific, e.g. as in your previous example:
```
ffmpeg:disable-everything=True
ffmpeg:enable-decoders="mp3, mp3float, opus"
ffmpeg:enable-demuxer="mp3, ogg"
```
3. add fine-grained options for each decoder/encoder/filter/etc:
```
enable_mp3_decoder: [True, False],
enable_ogg_decoder: [True, False], 
```
might be very tedious to maintain. most likely, these options have to be generated programmatically. they also will be different for each release of ffmpeg.

> Isn't that a set / dict, ie the keys have to be unique?

Ahh yes, good point, at the Conan level I think you are right, so they would need to be lists like
`ffmpeg:enable-decoders="mp3, mp3float, opus"`, that would work.

>There might be an array of encoders, ie ['ogg','opus']
and a loop to add them into the options {} and default_options {},
like... for each, options.append( "enable-" + name ) etc etc

I think there are just too many supported options to make this approach maintainable tbh.

>allow arbitrary configure flags to be passed. the same way we did it in [boost](https://github.com/conan-io/conan-center-index/blob/master/recipes/boost/all/conanfile.py#L94), for instance.
so, something like that may do the trick:

I think this is potentially more complicated than more specific list based options because allowing any flag at all to be passed could conflict with other options provided by the recipe and also any flags the recipe silently passes, for example where the `gpl` flag is disabled when `with_libx264` is set. 

The recipe might then need to try and filter out these conflicting flags and any flags for which the recipe provides explicit options, it feels like it could become a bit of a headache.

So far I think the specific list options for each type of ffmpeg flag is probably the best compromise, it avoids issues with conflicting flags / options as I mentioned above by restricting where we pass free-form user input to the ffmpeg build system directly, but is not so fine-grained that it would be difficult to maintain between ffmpeg versions because we're leaving the ffmpeg build system responsible for validating the specific encoders etc. that are supported. e.g.
```
ffmpeg:disable-everything=True
ffmpeg:enable-decoders="mp3, mp3float, opus"
ffmpeg:enable-demuxer="mp3, ogg"
```



Resolving patch:
diff --git a/recipes/ffmpeg/all/conanfile.py b/recipes/ffmpeg/all/conanfile.py
--- a/recipes/ffmpeg/all/conanfile.py
+++ b/recipes/ffmpeg/all/conanfile.py
@@ -18,7 +18,7 @@ class FFMpegConan(ConanFile):
     license = ("LGPL-2.1-or-later", "GPL-2.0-or-later")
     homepage = "https://ffmpeg.org"
     topics = ("ffmpeg", "multimedia", "audio", "video", "encoder", "decoder", "encoding", "decoding",
-             "transcoding", "multiplexer", "demultiplexer", "streaming")
+              "transcoding", "multiplexer", "demultiplexer", "streaming")
 
     settings = "os", "arch", "compiler", "build_type"
     options = {
@@ -61,6 +61,41 @@ class FFMpegConan(ConanFile):
         "with_audiotoolbox": [True, False],
         "with_videotoolbox": [True, False],
         "with_programs": [True, False],
+        "disable_everything": [True, False],
+        "disable_all_encoders": [True, False],
+        "disable_encoders": "ANY",
+        "enable_encoders": "ANY",
+        "disable_all_decoders": [True, False],
+        "disable_decoders": "ANY",
+        "enable_decoders": "ANY",
+        "disable_all_hardware_accelerators": [True, False],
+        "disable_hardware_accelerators": "ANY",
+        "enable_hardware_accelerators": "ANY",
+        "disable_all_muxers": [True, False],
+        "disable_muxers": "ANY",
+        "enable_muxers": "ANY",
+        "disable_all_demuxers": [True, False],
+        "disable_demuxers": "ANY",
+        "enable_demuxers": "ANY",
+        "disable_all_parsers": [True, False],
+        "disable_parsers": "ANY",
+        "enable_parsers": "ANY",
+        "disable_all_bitstream_filters": [True, False],
+        "disable_bitstream_filters": "ANY",
+        "enable_bitstream_filters": "ANY",
+        "disable_all_protocols": [True, False],
+        "disable_protocols": "ANY",
+        "enable_protocols": "ANY",
+        "disable_all_devices": [True, False],
+        "disable_all_input_devices": [True, False],
+        "disable_input_devices": "ANY",
+        "enable_input_devices": "ANY",
+        "disable_all_output_devices": [True, False],
+        "disable_output_devices": "ANY",
+        "enable_output_devices": "ANY",
+        "disable_all_filters": [True, False],
+        "disable_filters": "ANY",
+        "enable_filters": "ANY",
     }
     default_options = {
         "shared": False,
@@ -102,6 +137,41 @@ class FFMpegConan(ConanFile):
         "with_audiotoolbox": True,
         "with_videotoolbox": True,
         "with_programs": True,
+        "disable_everything": False,
+        "disable_all_encoders": False,
+        "disable_encoders": None,
+        "enable_encoders": None,
+        "disable_all_decoders": False,
+        "disable_decoders": None,
+        "enable_decoders": None,
+        "disable_all_hardware_accelerators": False,
+        "disable_hardware_accelerators": None,
+        "enable_hardware_accelerators": None,
+        "disable_all_muxers": False,
+        "disable_muxers": None,
+        "enable_muxers": None,
+        "disable_all_demuxers": False,
+        "disable_demuxers": None,
+        "enable_demuxers": None,
+        "disable_all_parsers": False,
+        "disable_parsers": None,
+        "enable_parsers": None,
+        "disable_all_bitstream_filters": False,
+        "disable_bitstream_filters": None,
+        "enable_bitstream_filters": None,
+        "disable_all_protocols": False,
+        "disable_protocols": None,
+        "enable_protocols": None,
+        "disable_all_devices": False,
+        "disable_all_input_devices": False,
+        "disable_input_devices": None,
+        "enable_input_devices": None,
+        "disable_all_output_devices": False,
+        "disable_output_devices": None,
+        "enable_output_devices": None,
+        "disable_all_filters": False,
+        "disable_filters": None,
+        "enable_filters": None,
     }
 
     generators = "pkg_config"
@@ -211,7 +281,7 @@ def requirements(self):
         if self.options.with_libwebp:
             self.requires("libwebp/1.2.2")
         if self.options.with_ssl == "openssl":
-            self.requires("openssl/1.1.1n")
+            self.requires("openssl/1.1.1o")
         if self.options.get_safe("with_libalsa"):
             self.requires("libalsa/1.2.5.1")
         if self.options.get_safe("with_xcb") or self.options.get_safe("with_vaapi"):
@@ -227,7 +297,8 @@ def requirements(self):
 
     def validate(self):
         if self.options.with_ssl == "securetransport" and not tools.is_apple_os(self.settings.os):
-            raise ConanInvalidConfiguration("securetransport is only available on Apple")
+            raise ConanInvalidConfiguration(
+                "securetransport is only available on Apple")
 
         for dependency, features in self._dependencies.items():
             if not self.options.get_safe(dependency):
@@ -236,7 +307,8 @@ def validate(self):
             for feature in features:
                 used = used or self.options.get_safe(feature)
             if not used:
-                raise ConanInvalidConfiguration("FFmpeg '{}' option requires '{}' option to be enabled".format(dependency, "' or '".join(features)))
+                raise ConanInvalidConfiguration("FFmpeg '{}' option requires '{}' option to be enabled".format(
+                    dependency, "' or '".join(features)))
 
     def build_requirements(self):
         if self.settings.arch in ("x86", "x86_64"):
@@ -252,7 +324,8 @@ def source(self):
     @property
     def _target_arch(self):
         target_arch, _, _ = tools.get_gnu_triplet(
-            "Macos" if tools.is_apple_os(self.settings.os) else str(self.settings.os),
+            "Macos" if tools.is_apple_os(
+                self.settings.os) else str(self.settings.os),
             str(self.settings.arch),
             str(self.settings.compiler) if self.settings.os == "Windows" else None,
         ).split("-")
@@ -264,7 +337,8 @@ def _target_os(self):
             return "win32"
         else:
             _, _, target_os = tools.get_gnu_triplet(
-                "Macos" if tools.is_apple_os(self.settings.os) else str(self.settings.os),
+                "Macos" if tools.is_apple_os(
+                    self.settings.os) else str(self.settings.os),
                 str(self.settings.arch),
                 str(self.settings.compiler) if self.settings.os == "Windows" else None,
             ).split("-")
@@ -279,7 +353,8 @@ def _patch_sources(self):
                                   "#define X264_API_IMPORTS 1", "")
         if self.options.with_ssl == "openssl":
             # https://trac.ffmpeg.org/ticket/5675
-            openssl_libraries = " ".join(["-l%s" % lib for lib in self.deps_cpp_info["openssl"].libs])
+            openssl_libraries = " ".join(
+                ["-l%s" % lib for lib in self.deps_cpp_info["openssl"].libs])
             tools.replace_in_file(os.path.join(self._source_subfolder, "configure"),
                                   "check_lib openssl openssl/ssl.h SSL_library_init -lssl -lcrypto -lws2_32 -lgdi32 ||",
                                   "check_lib openssl openssl/ssl.h OPENSSL_init_ssl %s || " % openssl_libraries)
@@ -295,9 +370,17 @@ def _build_context(self):
     def _configure_autotools(self):
         if self._autotools:
             return self._autotools
-        self._autotools = AutoToolsBuildEnvironment(self, win_bash=tools.os_info.is_windows)
+        self._autotools = AutoToolsBuildEnvironment(
+            self, win_bash=tools.os_info.is_windows)
         self._autotools.libs = []
-        opt_enable_disable = lambda what, v: "--{}-{}".format("enable" if v else "disable", what)
+
+        def opt_enable_disable(
+            what, v): return "--{}-{}".format("enable" if v else "disable", what)
+
+        def opt_append_disable_if_set(args, what, v):
+            if v:
+                args.append("--disable-{}".format(what))
+
         args = [
             "--pkg-config-flags=--static",
             "--disable-doc",
@@ -306,6 +389,7 @@ def _configure_autotools(self):
             opt_enable_disable("shared", self.options.shared),
             opt_enable_disable("static", not self.options.shared),
             opt_enable_disable("pic", self.options.get_safe("fPIC", True)),
+            # Components
             opt_enable_disable("avdevice", self.options.avdevice),
             opt_enable_disable("avcodec", self.options.avcodec),
             opt_enable_disable("avformat", self.options.avformat),
@@ -313,6 +397,7 @@ def _configure_autotools(self):
             opt_enable_disable("swscale", self.options.swscale),
             opt_enable_disable("postproc", self.options.postproc),
             opt_enable_disable("avfilter", self.options.avfilter),
+
             # Dependencies
             opt_enable_disable("bzlib", self.options.with_bzip2),
             opt_enable_disable("zlib", self.options.with_zlib),
@@ -332,27 +417,100 @@ def _configure_autotools(self):
             opt_enable_disable("libwebp", self.options.with_libwebp),
             opt_enable_disable("openssl", self.options.with_ssl == "openssl"),
             opt_enable_disable("alsa", self.options.get_safe("with_libalsa")),
-            opt_enable_disable("libpulse", self.options.get_safe("with_pulse")),
+            opt_enable_disable(
+                "libpulse", self.options.get_safe("with_pulse")),
             opt_enable_disable("vaapi", self.options.get_safe("with_vaapi")),
             opt_enable_disable("vdpau", self.options.get_safe("with_vdpau")),
             opt_enable_disable("libxcb", self.options.get_safe("with_xcb")),
-            opt_enable_disable("libxcb-shm", self.options.get_safe("with_xcb")),
-            opt_enable_disable("libxcb-shape", self.options.get_safe("with_xcb")),
-            opt_enable_disable("libxcb-xfixes", self.options.get_safe("with_xcb")),
+            opt_enable_disable(
+                "libxcb-shm", self.options.get_safe("with_xcb")),
+            opt_enable_disable(
+                "libxcb-shape", self.options.get_safe("with_xcb")),
+            opt_enable_disable(
+                "libxcb-xfixes", self.options.get_safe("with_xcb")),
             opt_enable_disable("appkit", self.options.get_safe("with_appkit")),
-            opt_enable_disable("avfoundation", self.options.get_safe("with_avfoundation")),
-            opt_enable_disable("coreimage", self.options.get_safe("with_coreimage")),
-            opt_enable_disable("audiotoolbox", self.options.get_safe("with_audiotoolbox")),
-            opt_enable_disable("videotoolbox", self.options.get_safe("with_videotoolbox")),
-            opt_enable_disable("securetransport", self.options.with_ssl == "securetransport"),
+            opt_enable_disable(
+                "avfoundation", self.options.get_safe("with_avfoundation")),
+            opt_enable_disable(
+                "coreimage", self.options.get_safe("with_coreimage")),
+            opt_enable_disable(
+                "audiotoolbox", self.options.get_safe("with_audiotoolbox")),
+            opt_enable_disable(
+                "videotoolbox", self.options.get_safe("with_videotoolbox")),
+            opt_enable_disable("securetransport",
+                               self.options.with_ssl == "securetransport"),
             "--disable-cuda",  # FIXME: CUDA support
             "--disable-cuvid",  # FIXME: CUVID support
             # Licenses
-            opt_enable_disable("nonfree", self.options.with_libfdk_aac or ( self.options.with_ssl and ( self.options.with_libx264 or self.options.with_libx265 or self.options.postproc ) ) ),
-            opt_enable_disable("gpl", self.options.with_libx264 or self.options.with_libx265 or self.options.postproc)
+            opt_enable_disable("nonfree", self.options.with_libfdk_aac or (self.options.with_ssl and (
+                self.options.with_libx264 or self.options.with_libx265 or self.options.postproc))),
+            opt_enable_disable(
+                "gpl", self.options.with_libx264 or self.options.with_libx265 or self.options.postproc)
         ]
+
+        # Individual Component Options
+        opt_append_disable_if_set(args, "everything", self.options.disable_everything)
+        opt_append_disable_if_set(args, "encoders", self.options.disable_all_encoders)
+        opt_append_disable_if_set(args, "decoders", self.options.disable_all_decoders)
+        opt_append_disable_if_set(args, "hwaccels", self.options.disable_all_hardware_accelerators)
+        opt_append_disable_if_set(args, "muxers", self.options.disable_all_muxers)
+        opt_append_disable_if_set(args, "demuxers", self.options.disable_all_demuxers)
+        opt_append_disable_if_set(args, "parsers", self.options.disable_all_parsers)
+        opt_append_disable_if_set(args, "bsfs", self.options.disable_all_bitstream_filters)
+        opt_append_disable_if_set(args, "protocols", self.options.disable_all_protocols)
+        opt_append_disable_if_set(args, "devices", self.options.disable_all_devices)
+        opt_append_disable_if_set(args, "indevs", self.options.disable_all_input_devices)
+        opt_append_disable_if_set(args, "outdevs", self.options.disable_all_output_devices)
+        opt_append_disable_if_set(args, "filters", self.options.disable_all_filters)
+
+        args.extend(self._split_and_format_options_string(
+            "enable-encoder", self.options.enable_encoders))
+        args.extend(self._split_and_format_options_string(
+            "disable-encoder", self.options.disable_encoders))
+        args.extend(self._split_and_format_options_string(
+            "enable-decoder", self.options.enable_decoders))
+        args.extend(self._split_and_format_options_string(
+            "disable-decoder", self.options.disable_decoders))
+        args.extend(self._split_and_format_options_string(
+            "enable-hwaccel", self.options.enable_hardware_accelerators))
+        args.extend(self._split_and_format_options_string(
+            "disable-hwaccel", self.options.disable_hardware_accelerators))
+        args.extend(self._split_and_format_options_string(
+            "enable-muxer", self.options.enable_muxers))
+        args.extend(self._split_and_format_options_string(
+            "disable-muxer", self.options.disable_muxers))
+        args.extend(self._split_and_format_options_string(
+            "enable-demuxer", self.options.enable_demuxers))
+        args.extend(self._split_and_format_options_string(
+            "disable-demuxer", self.options.disable_demuxers))
+        args.extend(self._split_and_format_options_string(
+            "enable-parser", self.options.enable_parsers))
+        args.extend(self._split_and_format_options_string(
+            "disable-parser", self.options.disable_parsers))
+        args.extend(self._split_and_format_options_string(
+            "enable-bsf", self.options.enable_bitstream_filters))
+        args.extend(self._split_and_format_options_string(
+            "disable-bsf", self.options.disable_bitstream_filters))
+        args.extend(self._split_and_format_options_string(
+            "enable-protocol", self.options.enable_protocols))
+        args.extend(self._split_and_format_options_string(
+            "disable-protocol", self.options.disable_protocols))
+        args.extend(self._split_and_format_options_string(
+            "enable-indev", self.options.enable_input_devices))
+        args.extend(self._split_and_format_options_string(
+            "disable-indev", self.options.disable_input_devices))
+        args.extend(self._split_and_format_options_string(
+            "enable-outdev", self.options.enable_output_devices))
+        args.extend(self._split_and_format_options_string(
+            "disable-outdev", self.options.disable_output_devices))
+        args.extend(self._split_and_format_options_string(
+            "enable-filter", self.options.enable_filters))
+        args.extend(self._split_and_format_options_string(
+            "disable-filter", self.options.disable_filters))
+
         if self._version_supports_vulkan():
-            args.append(opt_enable_disable("vulkan", self.options.get_safe("with_vulkan")))
+            args.append(opt_enable_disable(
+                "vulkan", self.options.get_safe("with_vulkan")))
         if tools.is_apple_os(self.settings.os):
             # relocatable shared libs
             args.append("--install-name-dir=@rpath")
@@ -376,8 +534,10 @@ def _configure_autotools(self):
         extra_cflags = []
         extra_ldflags = []
         if tools.is_apple_os(self.settings.os) and self.settings.os.version:
-            extra_cflags.append(tools.apple_deployment_target_flag(self.settings.os, self.settings.os.version))
-            extra_ldflags.append(tools.apple_deployment_target_flag(self.settings.os, self.settings.os.version))
+            extra_cflags.append(tools.apple_deployment_target_flag(
+                self.settings.os, self.settings.os.version))
+            extra_ldflags.append(tools.apple_deployment_target_flag(
+                self.settings.os, self.settings.os.version))
         if self._is_msvc:
             args.append("--pkg-config={}".format(tools.get_env("PKG_CONFIG")))
             args.append("--toolchain=msvc")
@@ -389,19 +549,35 @@ def _configure_autotools(self):
                 args.append("--target-os=none")
             else:
                 args.append("--target-os={}".format(self._target_os))
-            
+
             if tools.is_apple_os(self.settings.os):
                 xcrun = tools.XCRun(self.settings)
                 apple_arch = tools.to_apple_arch(str(self.settings.arch))
-                extra_cflags.extend(["-arch {}".format(apple_arch), "-isysroot {}".format(xcrun.sdk_path)])
-                extra_ldflags.extend(["-arch {}".format(apple_arch), "-isysroot {}".format(xcrun.sdk_path)])
+                extra_cflags.extend(
+                    ["-arch {}".format(apple_arch), "-isysroot {}".format(xcrun.sdk_path)])
+                extra_ldflags.extend(
+                    ["-arch {}".format(apple_arch), "-isysroot {}".format(xcrun.sdk_path)])
 
         args.append("--extra-cflags={}".format(" ".join(extra_cflags)))
         args.append("--extra-ldflags={}".format(" ".join(extra_ldflags)))
 
-        self._autotools.configure(args=args, configure_dir=self._source_subfolder, build=False, host=False, target=False)
+        self._autotools.configure(
+            args=args, configure_dir=self._source_subfolder, build=False, host=False, target=False)
         return self._autotools
 
+    def _split_and_format_options_string(self, flag_name, options_list):
+        if not options_list:
+            return []
+
+        def _format_options_list_item(flag_name, options_item):
+            return "--{}={}".format(flag_name, options_item)
+
+        def _split_options_string(options_string):
+            return list(filter(None, "".join(options_string.split()).split(",")))
+
+        options_string = str(options_list)
+        return [_format_options_list_item(flag_name, item) for item in _split_options_string(options_string)]
+
     def build(self):
         self._patch_sources()
         tools.replace_in_file(os.path.join(self._source_subfolder, "configure"),
@@ -427,8 +603,9 @@ def package(self):
                 for fn in os.listdir(os.path.join(self.package_folder, "bin")):
                     if fn.endswith(".lib"):
                         rename(self, os.path.join(self.package_folder, "bin", fn),
-                                     os.path.join(self.package_folder, "lib", fn))
-                tools.remove_files_by_mask(os.path.join(self.package_folder, "lib"), "*.def")
+                               os.path.join(self.package_folder, "lib", fn))
+                tools.remove_files_by_mask(os.path.join(
+                    self.package_folder, "lib"), "*.def")
             else:
                 # ffmpeg produces `.a` files that are actually `.lib` files
                 with tools.chdir(os.path.join(self.package_folder, "lib")):
@@ -457,89 +634,108 @@ def _set_component_version(self, component_name):
             self.cpp_info.components[component_name].version = version
         else:
             self.output.warn("cannot determine version of "
-                  "lib%s packaged with ffmpeg!" % component_name)
+                             "lib%s packaged with ffmpeg!" % component_name)
 
     def package_info(self):
         if self.options.with_programs:
             if self.options.with_sdl:
-                self.cpp_info.components["programs"].requires = ["sdl::libsdl2"]
+                self.cpp_info.components["programs"].requires = [
+                    "sdl::libsdl2"]
 
         if self.options.avdevice:
-            self.cpp_info.components["avdevice"].set_property("pkg_config_name", "libavdevice")
+            self.cpp_info.components["avdevice"].set_property(
+                "pkg_config_name", "libavdevice")
             self.cpp_info.components["avdevice"].libs = ["avdevice"]
             self.cpp_info.components["avdevice"].requires = ["avutil"]
             if self.options.avfilter:
-                self.cpp_info.components["avdevice"].requires.append("avfilter")
+                self.cpp_info.components["avdevice"].requires.append(
+                    "avfilter")
             if self.options.swscale:
                 self.cpp_info.components["avdevice"].requires.append("swscale")
             if self.options.avformat:
-                self.cpp_info.components["avdevice"].requires.append("avformat")
+                self.cpp_info.components["avdevice"].requires.append(
+                    "avformat")
             if self.options.avcodec:
                 self.cpp_info.components["avdevice"].requires.append("avcodec")
             if self.options.swresample:
-                self.cpp_info.components["avdevice"].requires.append("swresample")
+                self.cpp_info.components["avdevice"].requires.append(
+                    "swresample")
             if self.options.postproc:
-                self.cpp_info.components["avdevice"].requires.append("postproc")
+                self.cpp_info.components["avdevice"].requires.append(
+                    "postproc")
             self._set_component_version("avdevice")
 
         if self.options.avfilter:
-            self.cpp_info.components["avfilter"].set_property("pkg_config_name", "libavfilter")
+            self.cpp_info.components["avfilter"].set_property(
+                "pkg_config_name", "libavfilter")
             self.cpp_info.components["avfilter"].libs = ["avfilter"]
             self.cpp_info.components["avfilter"].requires = ["avutil"]
             if self.options.swscale:
                 self.cpp_info.components["avfilter"].requires.append("swscale")
             if self.options.avformat:
-                self.cpp_info.components["avfilter"].requires.append("avformat")
+                self.cpp_info.components["avfilter"].requires.append(
+                    "avformat")
             if self.options.avcodec:
                 self.cpp_info.components["avfilter"].requires.append("avcodec")
             if self.options.swresample:
-                self.cpp_info.components["avfilter"].requires.append("swresample")
+                self.cpp_info.components["avfilter"].requires.append(
+                    "swresample")
             if self.options.postproc:
-                self.cpp_info.components["avfilter"].requires.append("postproc")
+                self.cpp_info.components["avfilter"].requires.append(
+                    "postproc")
             self._set_component_version("avfilter")
 
         if self.options.avformat:
-            self.cpp_info.components["avformat"].set_property("pkg_config_name", "libavformat")
+            self.cpp_info.components["avformat"].set_property(
+                "pkg_config_name", "libavformat")
             self.cpp_info.components["avformat"].libs = ["avformat"]
             self.cpp_info.components["avformat"].requires = ["avutil"]
             if self.options.avcodec:
                 self.cpp_info.components["avformat"].requires.append("avcodec")
             if self.options.swresample:
-                self.cpp_info.components["avformat"].requires.append("swresample")
+                self.cpp_info.components["avformat"].requires.append(
+                    "swresample")
             self._set_component_version("avformat")
 
         if self.options.avcodec:
-            self.cpp_info.components["avcodec"].set_property("pkg_config_name", "libavcodec")
+            self.cpp_info.components["avcodec"].set_property(
+                "pkg_config_name", "libavcodec")
             self.cpp_info.components["avcodec"].libs = ["avcodec"]
             self.cpp_info.components["avcodec"].requires = ["avutil"]
             if self.options.swresample:
-                self.cpp_info.components["avcodec"].requires.append("swresample")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "swresample")
             self._set_component_version("avcodec")
 
         if self.options.swscale:
-            self.cpp_info.components["swscale"].set_property("pkg_config_name", "libswscale")
+            self.cpp_info.components["swscale"].set_property(
+                "pkg_config_name", "libswscale")
             self.cpp_info.components["swscale"].libs = ["swscale"]
             self.cpp_info.components["swscale"].requires = ["avutil"]
             self._set_component_version("swscale")
 
         if self.options.swresample:
-            self.cpp_info.components["swresample"].set_property("pkg_config_name", "libswresample")
+            self.cpp_info.components["swresample"].set_property(
+                "pkg_config_name", "libswresample")
             self.cpp_info.components["swresample"].libs = ["swresample"]
             self.cpp_info.components["swresample"].requires = ["avutil"]
             self._set_component_version("swresample")
 
         if self.options.postproc:
-            self.cpp_info.components["postproc"].set_property("pkg_config_name", "libpostproc")
+            self.cpp_info.components["postproc"].set_property(
+                "pkg_config_name", "libpostproc")
             self.cpp_info.components["postproc"].libs = ["postproc"]
             self.cpp_info.components["postproc"].requires = ["avutil"]
             self._set_component_version("postproc")
 
-        self.cpp_info.components["avutil"].set_property("pkg_config_name", "libavutil")
+        self.cpp_info.components["avutil"].set_property(
+            "pkg_config_name", "libavutil")
         self.cpp_info.components["avutil"].libs = ["avutil"]
         self._set_component_version("avutil")
 
         if self.settings.os in ("FreeBSD", "Linux"):
-            self.cpp_info.components["avutil"].system_libs = ["pthread", "m", "dl"]
+            self.cpp_info.components["avutil"].system_libs = [
+                "pthread", "m", "dl"]
             if self.options.swresample:
                 self.cpp_info.components["swresample"].system_libs = ["m"]
             if self.options.swscale:
@@ -551,107 +747,151 @@ def package_info(self):
                     # https://trac.ffmpeg.org/ticket/1713
                     # https://ffmpeg.org/platform.html#Advanced-linking-configuration
                     # https://ffmpeg.org/pipermail/libav-user/2014-December/007719.html
-                    self.cpp_info.components["avcodec"].exelinkflags.append("-Wl,-Bsymbolic")
-                    self.cpp_info.components["avcodec"].sharedlinkflags.append("-Wl,-Bsymbolic")
+                    self.cpp_info.components["avcodec"].exelinkflags.append(
+                        "-Wl,-Bsymbolic")
+                    self.cpp_info.components["avcodec"].sharedlinkflags.append(
+                        "-Wl,-Bsymbolic")
             if self.options.avformat:
                 self.cpp_info.components["avformat"].system_libs = ["m"]
             if self.options.avfilter:
-                self.cpp_info.components["avfilter"].system_libs = ["m", "pthread"]
+                self.cpp_info.components["avfilter"].system_libs = [
+                    "m", "pthread"]
             if self.options.avdevice:
                 self.cpp_info.components["avdevice"].system_libs = ["m"]
         elif self.settings.os == "Windows":
             if self.options.avcodec:
-                self.cpp_info.components["avcodec"].system_libs = ["Mfplat", "Mfuuid", "strmiids"]
+                self.cpp_info.components["avcodec"].system_libs = [
+                    "Mfplat", "Mfuuid", "strmiids"]
             if self.options.avdevice:
-                self.cpp_info.components["avdevice"].system_libs = ["ole32", "psapi", "strmiids", "uuid", "oleaut32", "shlwapi", "gdi32", "vfw32"]
-            self.cpp_info.components["avutil"].system_libs = ["user32", "bcrypt"]
+                self.cpp_info.components["avdevice"].system_libs = [
+                    "ole32", "psapi", "strmiids", "uuid", "oleaut32", "shlwapi", "gdi32", "vfw32"]
+            self.cpp_info.components["avutil"].system_libs = [
+                "user32", "bcrypt"]
         elif tools.is_apple_os(self.settings.os):
             if self.options.avdevice:
-                self.cpp_info.components["avdevice"].frameworks = ["CoreFoundation", "Foundation", "CoreGraphics"]
+                self.cpp_info.components["avdevice"].frameworks = [
+                    "CoreFoundation", "Foundation", "CoreGraphics"]
             if self.options.avfilter:
-                self.cpp_info.components["avfilter"].frameworks = ["CoreGraphics"]
+                self.cpp_info.components["avfilter"].frameworks = [
+                    "CoreGraphics"]
             if self.options.avcodec:
-                self.cpp_info.components["avcodec"].frameworks = ["CoreVideo", "CoreMedia"]
+                self.cpp_info.components["avcodec"].frameworks = [
+                    "CoreVideo", "CoreMedia"]
             if self.settings.os == "Macos":
                 if self.options.avdevice:
-                    self.cpp_info.components["avdevice"].frameworks.append("OpenGL")
+                    self.cpp_info.components["avdevice"].frameworks.append(
+                        "OpenGL")
                 if self.options.avfilter:
-                    self.cpp_info.components["avfilter"].frameworks.append("OpenGL")
+                    self.cpp_info.components["avfilter"].frameworks.append(
+                        "OpenGL")
 
         if self.options.avdevice:
             if self.options.get_safe("with_libalsa"):
-                self.cpp_info.components["avdevice"].requires.append("libalsa::libalsa")
+                self.cpp_info.components["avdevice"].requires.append(
+                    "libalsa::libalsa")
             if self.options.get_safe("with_xcb"):
-                self.cpp_info.components["avdevice"].requires.append("xorg::xcb")
+                self.cpp_info.components["avdevice"].requires.append(
+                    "xorg::xcb")
             if self.options.get_safe("with_pulse"):
-                self.cpp_info.components["avdevice"].requires.append("pulseaudio::pulseaudio")
+                self.cpp_info.components["avdevice"].requires.append(
+                    "pulseaudio::pulseaudio")
             if self.options.get_safe("with_appkit"):
-                self.cpp_info.components["avdevice"].frameworks.append("AppKit")
+                self.cpp_info.components["avdevice"].frameworks.append(
+                    "AppKit")
             if self.options.get_safe("with_avfoundation"):
-                self.cpp_info.components["avdevice"].frameworks.append("AVFoundation")
+                self.cpp_info.components["avdevice"].frameworks.append(
+                    "AVFoundation")
             if self.options.get_safe("with_audiotoolbox"):
-                self.cpp_info.components["avdevice"].frameworks.append("CoreAudio")
+                self.cpp_info.components["avdevice"].frameworks.append(
+                    "CoreAudio")
 
         if self.options.avcodec:
             if self.options.with_zlib:
-                self.cpp_info.components["avcodec"].requires.append("zlib::zlib")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "zlib::zlib")
             if self.options.with_lzma:
-                self.cpp_info.components["avcodec"].requires.append("xz_utils::xz_utils")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "xz_utils::xz_utils")
             if self.options.with_libiconv:
-                self.cpp_info.components["avcodec"].requires.append("libiconv::libiconv")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "libiconv::libiconv")
             if self.options.with_openjpeg:
-                self.cpp_info.components["avcodec"].requires.append("openjpeg::openjpeg")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "openjpeg::openjpeg")
             if self.options.with_openh264:
-                self.cpp_info.components["avcodec"].requires.append("openh264::openh264")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "openh264::openh264")
             if self.options.with_vorbis:
-                self.cpp_info.components["avcodec"].requires.append("vorbis::vorbis")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "vorbis::vorbis")
             if self.options.with_opus:
-                self.cpp_info.components["avcodec"].requires.append("opus::opus")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "opus::opus")
             if self.options.with_libx264:
-                self.cpp_info.components["avcodec"].requires.append("libx264::libx264")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "libx264::libx264")
             if self.options.with_libx265:
-                self.cpp_info.components["avcodec"].requires.append("libx265::libx265")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "libx265::libx265")
             if self.options.with_libvpx:
-                self.cpp_info.components["avcodec"].requires.append("libvpx::libvpx")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "libvpx::libvpx")
             if self.options.with_libmp3lame:
-                self.cpp_info.components["avcodec"].requires.append("libmp3lame::libmp3lame")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "libmp3lame::libmp3lame")
             if self.options.with_libfdk_aac:
-                self.cpp_info.components["avcodec"].requires.append("libfdk_aac::libfdk_aac")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "libfdk_aac::libfdk_aac")
             if self.options.with_libwebp:
-                self.cpp_info.components["avcodec"].requires.append("libwebp::libwebp")
+                self.cpp_info.components["avcodec"].requires.append(
+                    "libwebp::libwebp")
             if self.options.get_safe("with_audiotoolbox"):
-                self.cpp_info.components["avcodec"].frameworks.append("AudioToolbox")
+                self.cpp_info.components["avcodec"].frameworks.append(
+                    "AudioToolbox")
             if self.options.get_safe("with_videotoolbox"):
-                self.cpp_info.components["avcodec"].frameworks.append("VideoToolbox")
+                self.cpp_info.components["avcodec"].frameworks.append(
+                    "VideoToolbox")
 
         if self.options.avformat:
             if self.options.with_bzip2:
-                self.cpp_info.components["avformat"].requires.append("bzip2::bzip2")
+                self.cpp_info.components["avformat"].requires.append(
+                    "bzip2::bzip2")
             if self.options.with_zeromq:
-                self.cpp_info.components["avformat"].requires.append("zeromq::libzmq")
+                self.cpp_info.components["avformat"].requires.append(
+                    "zeromq::libzmq")
             if self.options.with_ssl == "openssl":
-                self.cpp_info.components["avformat"].requires.append("openssl::ssl")
+                self.cpp_info.components["avformat"].requires.append(
+                    "openssl::ssl")
             elif self.options.with_ssl == "securetransport":
-                self.cpp_info.components["avformat"].frameworks.append("Security")
+                self.cpp_info.components["avformat"].frameworks.append(
+                    "Security")
 
         if self.options.avfilter:
             if self.options.with_freetype:
-                self.cpp_info.components["avfilter"].requires.append("freetype::freetype")
+                self.cpp_info.components["avfilter"].requires.append(
+                    "freetype::freetype")
             if self.options.with_zeromq:
-                self.cpp_info.components["avfilter"].requires.append("zeromq::libzmq")
+                self.cpp_info.components["avfilter"].requires.append(
+                    "zeromq::libzmq")
             if self.options.get_safe("with_appkit"):
-                self.cpp_info.components["avfilter"].frameworks.append("AppKit")
+                self.cpp_info.components["avfilter"].frameworks.append(
+                    "AppKit")
             if self.options.get_safe("with_coreimage"):
-                self.cpp_info.components["avfilter"].frameworks.append("CoreImage")
+                self.cpp_info.components["avfilter"].frameworks.append(
+                    "CoreImage")
             if tools.Version(self.version) >= "5.0" and tools.is_apple_os(self.settings.os):
                 self.cpp_info.components["avfilter"].frameworks.append("Metal")
 
         if self.options.get_safe("with_vaapi"):
-            self.cpp_info.components["avutil"].requires.extend(["vaapi::vaapi", "xorg::x11"])
+            self.cpp_info.components["avutil"].requires.extend(
+                ["vaapi::vaapi", "xorg::x11"])
+
         if self.options.get_safe("with_vdpau"):
             self.cpp_info.components["avutil"].requires.append("vdpau::vdpau")
+
         if self._version_supports_vulkan() and self.options.get_safe("with_vulkan"):
-            self.cpp_info.components["avutil"].requires.append("vulkan-loader::vulkan-loader")
+            self.cpp_info.components["avutil"].requires.append(
+                "vulkan-loader::vulkan-loader")
 
     def _version_supports_vulkan(self):
         return tools.Version(self.version) >= "4.3.0"


Test patch:
diff --git a/recipes/ffmpeg/all/test_package/test_package.c b/recipes/ffmpeg/all/test_package/test_package.c
--- a/recipes/ffmpeg/all/test_package/test_package.c
+++ b/recipes/ffmpeg/all/test_package/test_package.c
@@ -26,10 +26,6 @@ int main()
     #ifdef HAVE_FFMPEG_AVCODEC
         printf("configuration: %s\n", avcodec_configuration());
         printf("avcodec version: %d.%d.%d\n", AV_VERSION_MAJOR(avcodec_version()), AV_VERSION_MINOR(avcodec_version()), AV_VERSION_MICRO(avcodec_version()));
-        if (avcodec_find_encoder_by_name("rawvideo") == NULL) {
-            printf("Unable to find rawvideo encoder\n");
-            return EXIT_FAILURE;
-        }
     #else
         printf("avcodec is disabled!\n");
     #endif