Configuration & presets
How codec, filter, demuxer, and muxer selection works — and how to add your own preset.
Every codec, filter, protocol, and format decision lives in src/cli/configure.mjs. It
generates the configure-<target>.sh scripts that scripts/build.sh runs. Never edit
the generated scripts — they’re overwritten on every build.
Presets
| Preset | For | Contents |
|---|---|---|
minimal | fast dev iteration | H.264/VP8 decode, AAC/MJPEG/PNG encode, basic filters |
standard | broad dev build | wide decode set, common encoders, all pipeline filters |
lgpl | the shipped wasmpeg | comprehensive decode, native encoders — all FFmpeg built-ins, safe for commercial use |
gpl | the shipped wasmpeg-full | everything in lgpl plus libx264/libx265 encode |
gpl inherits the entire lgpl set and extends it, so the two never drift — it’s literally
built as [...PRESETS.lgpl.decoders, ...extras] for each category.
What a preset controls
A preset is a plain object. Each field maps directly onto a family of --enable-* flags
that the configurator emits:
| Field | FFmpeg flag emitted | What it selects |
|---|---|---|
decoders | --enable-decoder=<name> | Codecs you can read from |
encoders | --enable-encoder=<name> | Codecs you can write to |
demuxers | --enable-demuxer=<name> | Input container/format parsers |
muxers | --enable-muxer=<name> | Output container/format writers |
parsers | --enable-parser=<name> | Bitstream parsers (split packets from raw/elementary streams) |
protocols | --enable-protocol=<name> | I/O backends — wasmpeg only needs file |
filters | --enable-filter=<name> | libavfilter filters |
extraFlags | passed verbatim | License/library switches like --enable-gpl |
Everything else is fixed by the configurator: it always passes --disable-everything
first, then re-enables the six libraries (avcodec, avformat, avfilter, avutil,
swscale, swresample), zlib, and only the components your preset lists. Nothing leaks in
from FFmpeg’s defaults, so the component list in codecs is exactly your preset.
Add a preset
Add an entry to the PRESETS object in configure.mjs:
custom: {
decoders: ['h264', 'vp9', 'aac', 'opus'],
encoders: ['aac'],
demuxers: ['mov', 'matroska', 'image2', 'image2pipe', 'image_png_pipe'],
muxers: ['mp4', 'null'],
parsers: ['h264', 'vp9', 'aac', 'opus', 'png'],
protocols: ['file'],
filters: ['scale', 'format'],
},
Build it with PRESET=custom TARGET=cpu bash scripts/build.sh.
Find the right component name
The vendored FFmpeg source is the authoritative list:
cd vendor/ffmpeg
./configure --list-decoders
./configure --list-encoders
./configure --list-demuxers
./configure --list-muxers
./configure --list-filters
./configure --list-protocols
After rebuilding, confirm a component compiled in:
grep CONFIG_AV1_DECODER vendor/ffmpeg/config_components.h
# expect: #define CONFIG_AV1_DECODER 1
Image pipe demuxers have a naming split that silently fails. The --enable-demuxer=
flag uses the configure name; av_find_input_format() at runtime uses a different
name:
| Format | --enable-demuxer= | Runtime name |
|---|---|---|
| PNG pipe | image_png_pipe | png_pipe |
| JPEG pipe | image_jpeg_pipe | jpeg_pipe |
| File images | image2 | image2 |
Using --enable-demuxer=png_pipe enables nothing and produces no error — it fails at
runtime.
External libraries
Some codecs need an external library, supplied via Emscripten ports.
| Library | FFmpeg flag | emcc flag | Used by |
|---|---|---|---|
| zlib | --enable-zlib | --use-port=zlib | PNG decode, FLAC, MKV compression |
| Dawn (WebGPU) | --enable-webgpu | --use-port=emdawnwebgpu | scale_webgpu (WebGPU target only) |
| libx264 / libx265 | --enable-libx264 / --enable-libx265 (GPL) | cross-compiled wasm libs | H.264/H.265 encode in wasmpeg-full |
The WebGPU and x264/x265 rows are added automatically when you select the webgpu target
or the gpl preset; you don’t list them in a preset’s extraFlags yourself for the
shipping builds.
avformat_find_stream_info opens the
stream but can’t determine frame dimensions and avcodec_receive_frame returns nothing —
with no compile-time error. It’s wired up in the shipped presets.Export a new C function
- Add
EMSCRIPTEN_KEEPALIVEto the function insrc/pipeline.c. - Add
_function_nameto the relevant*_EXPORTSlist inscripts/build.sh. - Relink — no FFmpeg rebuild needed (see the incremental relink).
- Call from JS:
mod.ccall('function_name', returnType, argTypes, args).
SIMD
-msimd128 (WebAssembly SIMD128) is on in CPU builds — it roughly doubles libswscale
inner-loop throughput with no size cost. Browser support: Chrome 91+, Firefox 89+,
Safari 16.4+. Remove it from extraCflags in configure.mjs and the CPU link in
scripts/build.sh for a legacy build.
Upgrading FFmpeg
Check out the new tag in vendor/ffmpeg, regenerate the configure script, then
emmake make distclean && emmake make install. After a major bump, re-run the
--list-* commands to catch renamed components and run the full test suite.