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

PresetForContents
minimalfast dev iterationH.264/VP8 decode, AAC/MJPEG/PNG encode, basic filters
standardbroad dev buildwide decode set, common encoders, all pipeline filters
lgplthe shipped wasmpegcomprehensive decode, native encoders — all FFmpeg built-ins, safe for commercial use
gplthe shipped wasmpeg-fulleverything 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:

FieldFFmpeg flag emittedWhat 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
extraFlagspassed verbatimLicense/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.

Tip
A decoder needs three things to read a real file end to end: the demuxer for its container, a parser if the stream is a raw/elementary bitstream, and the decoder itself. Enabling a decoder without its demuxer is a common reason a sample fails to open — the codec is present but nothing can hand it packets.

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
The image-pipe naming trap

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 pipeimage_png_pipepng_pipe
JPEG pipeimage_jpeg_pipejpeg_pipe
File imagesimage2image2

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.

LibraryFFmpeg flagemcc flagUsed by
zlib--enable-zlib--use-port=zlibPNG decode, FLAC, MKV compression
Dawn (WebGPU)--enable-webgpu--use-port=emdawnwebgpuscale_webgpu (WebGPU target only)
libx264 / libx265--enable-libx264 / --enable-libx265 (GPL)cross-compiled wasm libsH.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.

Caution
zlib is required for PNG decode. Without it, 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

  1. Add EMSCRIPTEN_KEEPALIVE to the function in src/pipeline.c.
  2. Add _function_name to the relevant *_EXPORTS list in scripts/build.sh.
  3. Relink — no FFmpeg rebuild needed (see the incremental relink).
  4. 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.