Build from source¶
You don't need to build: every release ships both variants across the profiles, as WASM modules and native drivers. But the whole pipeline is MIT and reproducible, so building it yourself is easy.
Prerequisites¶
- Docker (the build runs in a
wasi-sdkimage; nothing is installed on your host). - ~2 GB of disk and a few minutes.
Build a variant¶
The module lands at dist/ffmpeg-wasi-<variant>.wasm. The upstream FFmpeg it builds comes from
build/ffmpeg-version.txt; to try a different one for a one-off experiment, add
--build-arg FFMPEG_VERSION=n9.0.1 (any FFmpeg release tag). Changing it for real means editing
that file, which is what CI and both Dockerfiles read.
Build the intermediate profile¶
Add --build-arg PROFILE=intermediate (default lean) to build the full software-codec
module: the LGPL encoders, the native codec/container batches, and text/subtitle burn-in:
docker build -f build/Dockerfile \
--build-arg VARIANT=lgpl --build-arg PROFILE=intermediate \
--target artifact -o dist .
It lands at dist/ffmpeg-wasi-intermediate-<variant>.wasm, the same asset the release
publishes. See variants & profiles.
With just:
Build the native driver (Backend B)¶
The same engine also builds to a native ELF (spec 0028) via build/Dockerfile.native. The
subprocess afmpeg's native backend
drives for native-speed encode. It takes the same VARIANT/PROFILE args, and adds a third
profile, full (native-only): intermediate + AV1 (SVT-AV1, both variants) and HEVC (x265,
gpl only).
docker build -f build/Dockerfile.native \
--build-arg VARIANT=gpl --build-arg PROFILE=full \
--target artifact -o dist-native .
It lands at dist-native/driver, published as ffmpeg-wasi-driver-linux-amd64-full-gpl. Use
PROFILE=lean or intermediate for the lighter native drivers; lgpl full builds AV1 but not
HEVC (x265 is GPL). linux/amd64 only for now.
Run it¶
The repo bundles a tiny wazero harness that loads the module and runs it (it provides the
env setjmp/longjmp imports and the WebAssembly feature set the build needs):
You'll see the engine's capability report (the FFmpeg version and the available codecs/muxers/filters) confirming it links and runs:
ffmpeg-wasi engine
vocab_version: 9
ffmpeg: n9.0.1
libavcodec 4070502 libavformat 4066406 libavfilter 724582
encoders:
libopenh264 yes
libx264 no # gpl variant only
mjpeg yes
aac yes
...
decoders:
h264 yes
...
The report probes a fixed handful of codecs: libopenh264, libx264, mjpeg, aac, flac and
pcm_s16le for encode; h264, hevc, vp9, aac, mp3, opus and flac for decode. It is a
build smoke test, not an inventory: for the full set see codecs.
Check what you built¶
The conformance suite (spec 0036) drives built artifacts through the driver ABI and checks them against what this repository declares. Point it at a directory of artifacts:
Artifacts are discovered by filename: ffmpeg-wasi-<profile>-<variant>.wasm and
ffmpeg-wasi-driver-linux-amd64-<profile>-<variant>, with the lean profile keeping the shorter
legacy name. A file whose name does not parse is ignored rather than guessed at, so pointing at a
directory holding other things is safe. Every artifact-backed test skips when the directory is
absent, because go test ./... should never require an FFmpeg build:
Three things are asserted, all against every artifact found:
- Capabilities: every component
build/enable-lists.shclaims for that (profile, variant) is actually linked into the binary, read from--capabilities. A component the build asked for and did not get is a failure; one present but never asked for is only noted, since FFmpeg pulls dependencies in of its own accord. Absences upstream gates behind--enable-gplare expected in an lgpl build and listed as such. - The ABI: the four ops dispatch, a malformed request exits
2, a too-new vocabulary exits3, a processing failure exits1, stdout carries exactly one line of JSON and nothing else, andversionreports the FFmpeg versionbuild/ffmpeg-version.txtnames alongside the vocabulary versionsrc/driver.cdeclares. - Behaviour: that
probe,processandframesactually do the thing: stream counts, codecs, dimensions, sample rates, durations within tolerance, and extracted frames landing on disk as decodable images. The media is generated in pure Go, so "the engine read 2.0 seconds" is checked against arithmetic rather than against the engine's own earlier output.
The version assertion is worth knowing about when a result surprises you: if version disagrees with
the files, the usual cause is a stale artifact: the directory holds an engine built from a
different version of this repository, not a defect in the build.
Some behavioural tests skip on a lean build, saying so and why: WebM muxing needs a VP8/VP9/AV1 or Opus/Vorbis encoder that only the richer profiles carry. A skip means "this build cannot do that", never "this was not checked".
All three run in CI after the build stage, against all ten artifacts. The suite deliberately reports properties, not checksums: a byte-golden test would go red on every FFmpeg bump, which is the opposite of the job this suite exists to do.
Running the suite against a released artifact
The suite asserts the ABI as documented, so it goes red against a release whose behaviour has
since been corrected: two process validation failures exited 0 rather than 2 up to
n8.1.2-12 (errors & exit codes). The
capability half needs --capabilities, which no release before that carries at all. Build the
artifacts you want to check.
To see one side of the capability check on its own:
just show-claims lean lgpl wasm # what the build asks configure for
just capabilities dist/ffmpeg-wasi-lgpl.wasm # what the artifact carries
What the build does¶
Five small scripts under build/, orchestrated by build/Dockerfile:
deps.sh: clones/cross-compiles the external codec libraries into$PREFIX: openh264 (+ libx264 on gpl), and for the intermediate/full profiles Opus/MP3/Vorbis/WebP/VP8-9, freetype/harfbuzz/libass, AV1 decode (libdav1d), and, for native full only, x265 / SVT-AV1.enable-lists.sh: the component allowlist: which decoders, encoders, muxers, demuxers, filters, bitstream filters and protocols each (profile, variant) asks for. Sourced bylibav.sh, and read directly by the conformance suite, which asserts the built artifact really carries what this file claims (spec 0036). One definition, two consumers: a test that re-implemented the composition would drift from the build silently.libav.sh: clones FFmpeg, configures it single-threaded forwasm32-wasi(libraries only), andmakes thelibav*archives.driver.sh: links the engine (src/driver.c) + the wasi compat shims against those archives into one.wasmcommand module.toolchain.sh: the shared wasi-sdk/clang cross-compile environment.
They branch on TARGET (wasm by default, native for the driver), so one build system
produces both artifacts. See The build for what makes it work
(single-threaded config, setjmp/longjmp lowering, the POSIX/WASI compat shims) and
The native driver for the TARGET=native
path.