Reproducible, LGPL-only FFmpeg binaries for closed-source applications.
Built and published by @serversideup so that downstream projects can consume FFmpeg without inheriting GPL or non-free obligations.
These builds run on Depot's Apple Silicon macOS runners. Depot sponsors our CI so we can publish reproducible, multi-arch FFmpeg releases on real Apple hardware without paying the macOS-runner tax ourselves. If you need fast, cache-aware GitHub Actions runners (Linux, Windows, or macOS), give them a look. Huge thanks to the Depot team. 🙏
Each release ships a pair of binaries — ffmpeg and ffprobe — produced from the same configure pass and therefore inheriting the same flag profile:
--disable-gpl— no GPL-licensed code linked in (nolibx264, nolibx265, nolibxvid, nolibpostproc)--disable-nonfree— no non-free code (nolibfdk_aac)--disable-version3— stays on LGPLv2.1 (avoids dependencies that would relicense the bundle to v3)--disable-autodetect— no Homebrew or system optional libs sneak in; only what we explicitly enable--disable-programs --enable-ffmpeg --enable-ffprobe— ship the two CLI tools downstream consumers need; skipffplay(drags in SDL)
Hardware-accelerated encoders are enabled per-platform from LGPL-clean SDK headers (the encoders themselves are GPU-vendor implementations; the SDK headers we link against are BSD/MIT and don't impose copyleft):
- macOS: VideoToolbox (
h264_videotoolbox,hevc_videotoolbox) - Windows: NVIDIA NVENC (
h264_nvenc,hevc_nvenc), Intel Quick Sync via oneVPL (h264_qsv,hevc_qsv), AMD AMF (h264_amf,hevc_amf)
Windows also enables libopenh264 (--enable-libopenh264), a software (CPU) H.264 encoder used as a last-resort fallback on hosts with no usable GPU encoder. OpenH264 is BSD-2-Clause, so it stays LGPL-clean and doesn't trip the forbidden-flag check (it is not --enable-gpl/--enable-nonfree/--enable-version3). It is provided by the MSYS2 mingw-w64-x86_64-openh264 package and ships as libopenh264-7.dll bundled alongside ffmpeg.exe (with its BSD-2-Clause license in LIBOPENH264-LICENSE.txt), the same way libvpl-2.dll is; the C++ runtime it needs, libstdc++-6.dll, is already in the bundle closure via libvpl.
The build is verified post-link to confirm none of the banned flags ended up enabled and that the linkage matches the platform's expectations:
- macOS: both binaries link only to system frameworks under
/System/and/usr/lib/ - Windows: each DLL in the import table must either be present in
C:\Windows\System32\(= Windows itself provides it; no redistribution obligation) or be one of the two DLLs we explicitly bundle alongsideffmpeg.exe—libvpl-2.dll(Intel's oneVPL dispatcher; MIT) andlibwinpthread-1.dll(mingw-w64 pthread runtime; BSD-style permissive). Both bundled DLLs are required because libvpl is dispatcher-only (no static archive) and libwinpthread is injected late in FFmpeg's link command in a way that can't be redirected to the static archive without breaking libvpl. Both carry LGPL-compatible permissive licenses, so bundling them adds no copyleft beyond what the FFmpeg LGPL already imposes
A consumer that bundles one of these binaries into a closed-source product still owes its end users the corresponding FFmpeg source under LGPL § 6. Since this repo's VERSION file pins an upstream FFmpeg release whose source is publicly available at https://ffmpeg.org/releases/, the source-availability obligation is satisfied by pointing users at that URL plus the SHA256 recorded in each release's RELEASE-NOTES.md. Consumers should reproduce this notice in their own distribution.
A release is cut whenever VERSION or the build script changes on main (or via manual workflow_dispatch for a one-off rebuild). The GitHub Actions workflow at .github/workflows/build.yml runs the platform-specific build script for every supported target, uploads the resulting binaries as a tagged release, and writes a RELEASE-NOTES.md recording the SHA256 of each artifact.
Tag format: v<ffmpeg-version>-<run-number>, e.g. v8.1.2-42, where <run-number> is the GitHub Actions workflow run number (monotonic, so every push or dispatch produces a fresh distinct tag — no counter file to keep in sync).
Per release, per target, the published artifacts are:
ffmpeg-<version>-<triple>.tar.gz— contains theffmpeg(orffmpeg.exe) andffprobebinaries,COPYING.LGPLv2.1, andSOURCE.txt(provenance record). macOS builds additionally includeOPENSSL-LICENSE(OpenSSL 3 is statically linked in as the TLS backend, Apache-2.0-licensed). Windows builds additionally includelibvpl-2.dll(Intel oneVPL dispatcher, MIT-licensed) +LIBVPL-LICENSE.txt,libwinpthread-1.dll(mingw-w64 pthread runtime, BSD-style permissive) +LIBWINPTHREAD-LICENSE.txt, andlibopenh264-7.dll(Cisco OpenH264 software encoder, BSD-2-Clause) +LIBOPENH264-LICENSE.txt.ffmpeg-<version>-<triple>.tar.gz.sha256
TLS backend per platform (for rtmps/https): macOS = OpenSSL 3 (Apache-2.0, statically linked), Windows = Schannel (system SSPI), Linux = GnuTLS (dynamic, distro-provided). macOS deliberately does not use SecureTransport or LibreSSL/libtls: FFmpeg's RTMP layer probes its transport with nonblocking reads, and both of those backends turn the probe into a blocking read that waits on the server's next ack — RTMPS throughput collapses to ~2.5% of real time (measured identically on both; OpenSSL and GnuTLS handle the probe correctly). The macOS and Linux builds gate every ship on an RTMPS throughput smoke test: 10s of 1080p60 at ~8 Mbps pushed to a local MediaMTX over TLS must sustain ≥ 0.9x real time — a handshake-only check passes on a backend that is unusable for streaming.
Currently supported targets:
aarch64-apple-darwin(macOS, Apple Silicon)x86_64-apple-darwin(macOS, Intel — cross-compiled from Apple Silicon)x86_64-pc-windows-msvc(Windows x64, built with mingw-w64 gcc via MSYS2 — the "msvc" in the triple is a consumer convention for "Windows x64"; the resulting PE executable is ABI-compatible regardless of caller toolchain)x86_64-unknown-linux-gnu(Linux x64, for Docker). Unlike the desktop targets, this binary is not self-contained: it links gnutls / libva / libopenh264 dynamically against the libraries provided by the consuming container's distribution packages. Encoders:h264_nvenc(NVIDIA, runtime lib injected by the NVIDIA Container Toolkit),h264_vaapi(Intel iGPU + AMD),libopenh264(CPU fallback),aac. QSV/oneVPL is omitted for now — VAAPI covers Intel iGPUs.aarch64-unknown-linux-gnu(Linux ARM, for Docker — e.g. Ampere/Graviton VPSes). Software-only:libopenh264(CPU) + nativeaac, gnutls TLS, plus stream passthrough. arm64 servers have no NVENC or Intel VAAPI, so there are no GPU encoders; same dynamic-link model as the x64 Linux build.
- Look up the new upstream version at https://ffmpeg.org/releases/.
- Verify the tarball's SHA256 against the upstream signed checksum file.
- Edit
VERSIONto the new value, e.g.8.2.0. - Add a case branch for the new version to all three of
scripts/build-macos.sh,scripts/build-windows.sh, andscripts/build-linux.shwith the pinned tarball SHA256 (defence-in-depth check on each platform). - Open a PR. CI runs the full matrix on the PR for verification.
- Merge to
main. The push triggers the release workflow, producingv<new-version>-<run-number>.
Edit scripts/build-macos.sh or scripts/build-windows.sh and merge to main. The push triggers a new release with the same VERSION and the next run number, e.g. v8.1.2-43. Release notes call out the flag delta.
Run the workflow manually from the Actions tab (workflow_dispatch). Produces a fresh tag at the next run number without any commit.
Given a macOS artifact ffmpeg-8.1.2-aarch64-apple-darwin.tar.gz:
# Confirm the SHA256 matches the published .sha256
shasum -a 256 -c ffmpeg-8.1.2-aarch64-apple-darwin.tar.gz.sha256
# Confirm LGPL flags in the binary itself (ffprobe came from the same configure
# pass, so checking ffmpeg covers both)
tar -xzf ffmpeg-8.1.2-aarch64-apple-darwin.tar.gz
./ffmpeg -version | grep configuration | tr ' ' '\n' | grep -E '^--' | sort
# Should NOT contain: --enable-gpl, --enable-nonfree, --enable-version3
# Should contain: --disable-gpl, --disable-nonfree, --disable-version3Given a Windows artifact ffmpeg-8.1.2-x86_64-pc-windows-msvc.tar.gz (verify from PowerShell, cmd, or an MSYS2 shell):
# Confirm the SHA256 matches the published .sha256
Get-FileHash -Algorithm SHA256 ffmpeg-8.1.2-x86_64-pc-windows-msvc.tar.gz
# Confirm LGPL flags + expected hardware encoders
tar -xzf ffmpeg-8.1.2-x86_64-pc-windows-msvc.tar.gz
.\ffmpeg.exe -version
.\ffmpeg.exe -hide_banner -encoders | Select-String 'nvenc|qsv|amf|aac'
# Should list h264_nvenc, hevc_nvenc, h264_qsv, hevc_qsv, h264_amf, hevc_amf, aac
# (which encoders can actually encode at runtime depends on the host's GPU
# drivers — the binary always *contains* all of them)To reproduce from source, check out this repo at the tag and run the platform's build script:
- macOS:
scripts/build-macos.sh --target aarch64-apple-darwinon an Apple Silicon Mac with Xcode CLT installed. - Windows:
scripts/build-windows.sh --target x86_64-pc-windows-msvcfrom inside an MSYS2 MINGW64 shell with the toolchain + encoder header packages installed (see.github/workflows/build.ymlfor the exactpacman -Slist).
The resulting dist/<triple>/{ffmpeg,ffprobe} (or .exe) binaries should be bit-identical to the published artifact (modulo embedded timestamps).
This repository — both the build scripts and the FFmpeg binaries they produce — is distributed under LGPL-2.1. See LICENSE for the full text. The build script also bakes a copy of COPYING.LGPLv2.1 (taken verbatim from the FFmpeg source tree) into every released tarball, so downstream consumers receive the license alongside the binary as LGPL § 6 requires.
