A POSIX shell build system that compiles FFmpeg from source with ~80 modular dependency recipes. Supports multiple FFmpeg versions via build profiles.
- ~80 modular recipes — each dependency is a self-contained shell file
- 64 enabled FFmpeg features — video, audio, subtitle, image, streaming, HW acceleration
- Version profiles — pin all dependency versions per FFmpeg release (8.0.1, 7.1, 7.0, 6.1)
- License tiers — free, GPL, and non-free codec selection
- Cross-platform — Linux and macOS (including Apple Silicon)
- Full static binaries — optional fully static build on Linux
- Install/uninstall — manifest-tracked installation of binaries, libraries, and headers
- Update checker — compare installed versions against GitHub releases
- Zero system pollution — all build artifacts are isolated until explicit install
| Dependency | Purpose |
|---|---|
| POSIX shell | sh, dash, bash, or zsh |
make |
Build system |
g++ |
C/C++ compiler (Linux). On macOS, clang++ via Xcode is used instead |
curl |
Downloading source tarballs |
These enable additional codecs. If missing, the corresponding recipes are skipped automatically with a warning.
| Dependency | Enables |
|---|---|
cargo |
rav1e (AV1 encoder) |
python3 |
dav1d, lv2, glslang |
meson + ninja |
dav1d, lv2, fribidi, harfbuzz, fontconfig, openh264, rubberband, librist |
nvcc (CUDA toolkit) |
NVIDIA CUDA filters (nvenc/nvdec work without it) |
git |
librtmp (cloned from git.ffmpeg.org, no tarball available) |
Install optional dependencies on Arch Linux:
sudo pacman -S rust python meson ninja cudaOn Ubuntu/Debian:
sudo apt install cargo python3 meson ninja-build nvidia-cuda-toolkit# Build FFmpeg with free codecs
./mediaforge.sh build
# Build with GPL codecs (x264, x265, xvidcore, vid.stab)
./mediaforge.sh build --enable-gpl
# Build with GPL + non-free codecs (adds openssl, fdk-aac)
./mediaforge.sh build --enable-nonfree
# Full static binary with all codecs
./mediaforge.sh build --enable-nonfree --enable-staticUsage: mediaforge.sh <command> [options]
Commands:
build Build FFmpeg and dependencies
clean Remove all build artifacts
install Install built binaries and libraries
uninstall Remove installed files
check-updates Check for newer dependency versions
check-shadowers Audit workspace .pc files for system-version shadowing
list-profiles List available version profiles
help Show help
version Show version
Build options:
-g, --enable-gpl Enable GPL-licensed codecs
-G, --enable-nonfree Enable non-free codecs (implies GPL)
-L, --disable-lv2 Skip LV2 plugin chain
-s, --enable-static Full static binary (Linux only)
-m, --enable-small Minimal build
--enable-lto Enable LTO in recipes that support it
(default: off; archives may break on GCC major bumps)
--disable-lto Force LTO off (default)
-p, --profile=X.Y Use version profile
-j, --jobs=N Parallel job count (default: auto)
-u, --rebuild-outdated Rebuild stale dependencies
-I, --no-install Skip post-build install
-y, --yes Non-interactive mode
--menu Interactive selector (whiptail or POSIX fallback)
-v, --verbose Show build commands (-vv for more)
-q, --quiet Errors only
-n, --dry-run Show what would build
-k, --keep-going Continue on recipe failure
Codec / backend selectors (mutually exclusive within each group):
--tls=BACKEND TLS backend: openssl|gnutls|mbedtls|libressl|none
(default: gnutls)
--aac=IMPL AAC encoder: fdk_aac|native
(default: native; nonfree -> fdk_aac)
--flac=IMPL FLAC encoder: libflac|native (default: native)
--h264=IMPL H.264 encoder: x264|openh264 (default: x264)
--h265=IMPL H.265 encoder: x265|kvazaar (default: x265)
--av1-enc=IMPL AV1 encoder: svtav1|rav1e|av1 (default: svtav1)
--flite-audio=BACKEND flite audio output: none|alsa|pulseaudio|oss|sun
(default: none; FFmpeg filter does not invoke it)
Recipe overrides:
--disable=PKG Disable a recipe by name (repeatable, comma-separated ok)
--enable=PKG Force-enable a recipe that defaults to off
--list-pkgs Print every recipe with category and mutex group
--clean-choices Delete the stored choice matrix and exit
Install/uninstall options:
--prefix=PATH Install/uninstall location (default: interactive prompt)
-y, --yes Non-interactive mode
# Build with short flags
./mediaforge.sh build -Gs # nonfree + static
./mediaforge.sh build -g -j 4 # GPL, 4 jobs
./mediaforge.sh build -n # dry run
# Version profiles
./mediaforge.sh list-profiles
./mediaforge.sh build --profile=7.1
./mediaforge.sh build --profile=6.1 --rebuild-outdated
# Install/uninstall
./mediaforge.sh install # interactive menu
./mediaforge.sh install --prefix=/opt/ffmpeg
./mediaforge.sh install --prefix=$HOME/.local/mediaforge # isolated user prefix
./mediaforge.sh uninstall # discovers installs via manifest
./mediaforge.sh uninstall --prefix=/opt/ffmpeg
# Update checking
./mediaforge.sh check-updates
./mediaforge.sh check-updates --profile=7.1
GITHUB_TOKEN=ghp_xxx ./mediaforge.sh check-updates
# Audit pkgconfig drift (after build, before release)
./mediaforge.sh check-shadowers # warn-only, exit 0
./mediaforge.sh check-shadowers --strict # exit 1 on new shadowers (CI)
# Clean
./mediaforge.sh cleanThe installer elevates with sudo automatically when the target prefix
requires it (e.g. /usr/local). Do not wrap ./mediaforge.sh install
with sudo yourself when targeting a user-owned prefix
(~/.local/mediaforge, ~/opt/...). Files inherit the running process's
UID — running as root leaves root-owned files in your home that you
can't modify or delete without sudo on every operation.
| You are | Targeting | Run install as | Resulting ownership |
|---|---|---|---|
| user | ~/.local/mediaforge |
yourself (no sudo) | you:you ✓ |
| user | /usr/local |
yourself (installer wraps sudo) |
root:root ✓ |
| root | /usr/local |
yourself | root:root ✓ |
| root | /home/<user>/.local/... |
DON'T | root:root ✗ |
Same rule for uninstall.
If anything else on your system links against mediaforge (e.g. a Rust
crate consuming FFmpeg via pkg-config), install to an isolated
subdirectory rather than over the shared user/system prefix:
./mediaforge.sh install --prefix=$HOME/.local/mediaforgeThe isolated dir keeps mediaforge's 94 workspace .pc files out of the shared
~/.local/lib/pkgconfig/. The install layer also drops 19 specific
transitive-dep .pc files that would shadow newer system versions
(fontconfig, freetype2, harfbuzz, expat, gnutls, libpng,
bzip2, libxml-2.0, fribidi, gmp, nettle, hogweed, brotli*,
lzma, zlib). The .a archives still install — FFmpeg's static link
still references their symbols — but their .pc files are absent from
the install prefix, so a downstream consumer's pkg-config fontconfig
falls through to the system's newer version.
Downstream consumers point PKG_CONFIG_PATH at mediaforge's prefix
first, then the system path. This is the canonical Linux side-install
pattern (Homebrew's /opt/homebrew/, MacPorts' /opt/local/, GNU stow,
NixOS profiles).
When you add a new recipe or want to verify the workspace doesn't ship
a new shadowing .pc file:
./mediaforge.sh check-shadowersThe command probes every .pc in the workspace against the system
pkgconfig path and classifies each match as [expected] (already in
the install-time stop-list) or [NEW SHADOW] (not in the stop-list —
review whether to add it). Exits 0 by default (warn-only, matches
rpmlint/lintian/brew audit convention); --strict exits 1 for
CI gating. The stop-list lives in lib/install.sh:_PKGCONFIG_SHADOWERS.
Profiles pin all ~80 dependency versions to a known-good set for a specific FFmpeg release:
| Profile | FFmpeg | Release |
|---|---|---|
8.0.1 |
8.0.1 | 2025 |
7.1 |
7.1 | Sep 2024 |
7.0 |
7.0 | Apr 2024 |
6.1 |
6.1 | Nov 2023 |
Without --profile, recipes use their built-in default versions (equivalent to the 8.0.1 profile).
mediaforge.sh Main driver — subcommand dispatch, recipe orchestration
lib/
utils.sh Core utilities (logging, stamp gating, run)
platform.sh OS/arch detection (Linux, macOS, Apple Silicon)
framework.sh Recipe lifecycle (run_recipe, reset, guards, phases)
download.sh Tarball fetch with cache and exponential backoff
cleanup.sh Signal trap handler
install.sh Install/uninstall with manifest tracking
updates.sh GitHub API update checker
patches/
giflib-makefile.patch Remove doc/man build targets
libjxl-static-linking.patch Fix jxl_threads static linking
libvorbis-cpusubtype.patch Remove macOS -force_cpusubtype_ALL
libzmq-stats-proxy.patch GCC 15 C23 aggregate init fix
profiles/
ffmpeg-8.0.1.conf Version pins for FFmpeg 8.0.1
ffmpeg-7.1.conf Version pins for FFmpeg 7.1
ffmpeg-7.0.conf Version pins for FFmpeg 7.0
ffmpeg-6.1.conf Version pins for FFmpeg 6.1
recipes/
_order.conf Declarative build order
ffmpeg.sh Final FFmpeg build
tools/ Build tools (cmake, nasm, pkg-config, zlib, ...)
crypto/ Crypto libraries (openssl, gnutls, gmp, nettle)
video/ Video codecs (x264, x265, libvpx, dav1d, svtav1, ...)
audio/ Audio codecs (opus, lame, fdk-aac, vorbis, ...)
image/ Image libraries (libpng, libjxl, libwebp, ...)
hwaccel/ HW acceleration (vaapi, vulkan, nvcodec, opencl, ...)
other/ Other libraries (freetype, srt, libzmq, ...)
mediaforge.shsources alllib/*.shand dispatches the subcommandbuildparses options, loads the version profile if specified- Iterates
recipes/_order.conf, callingrun_recipe()for each entry - Each recipe sets
PKG_*variables and optionally overrides build phases - Stamp files in
workspace/.stamps/track what's already built - After all recipes, accumulated flags are applied and FFmpeg is built
- Binaries and libraries are optionally installed with manifest tracking
Each recipe is a shell file that sets variables and optionally overrides phase functions:
PKG_NAME="mylib"
PKG_VERSION="${PKG_VERSION_MYLIB:-1.0.0}"
PKG_URL="https://example.com/mylib-${PKG_VERSION}.tar.gz"
PKG_FFMPEG_OPT="--enable-mylib"
# Optional: override any build phase
pkg_configure() {
run ./configure --prefix="$PREFIX" --disable-shared --enable-static
}Available guards: PKG_GPL, PKG_NONFREE, PKG_LINUX_ONLY, PKG_SKIP_ON_ARCH, PKG_REQUIRES_CMD, PKG_REQUIRES_MESON.
A full --enable-nonfree build enables 64 features across these categories:
Video codecs: x264, x265, libvpx (VP8/VP9), aom (AV1), dav1d (AV1 decode), svtav1 (AV1 encode), rav1e (AV1 encode), xvidcore, kvazaar (HEVC), openh264, vid.stab, zimg
Audio codecs: opus, lame (MP3), vorbis, theora, fdk-aac, opencore-amr, soxr, speex, twolame (MP2), gsm, libilbc, vo-amrwbenc, libshine (MP3), lv2
Subtitle/text: libass, harfbuzz, fribidi, fontconfig, freetype
Image formats: libjxl (JPEG XL), libwebp, libpng, openjpeg (JPEG 2000)
Media formats: libbluray, librtmp, libxml2 (DASH), libsrt, librist
Audio processing: rubberband (time-stretch), libmysofa (HRTF), bs2b (crossfeed), chromaprint (fingerprinting)
Filter plugins: frei0r, ladspa
HW acceleration: vaapi, vulkan, glslang, AMF, OpenCL, NVENC/NVDEC/CUVID
Miscellaneous: libcaca (ASCII output), codec2, flite (TTS), libgme (game music), libopenmpt (tracker music), libsnappy, libzmq, vapoursynth, libzvbi
MIT License. See LICENSE for details.
FFmpeg itself is licensed under LGPL 2.1+, with optional GPL and non-free components enabled via --enable-gpl and --enable-nonfree flags. See individual recipe files for dependency licenses.