Files
av-tools/docs/FFMPEG_BUILD.md

7.7 KiB

FFmpeg WebAssembly build record

av-tools ships the locally rebuilt, reviewed core profile 0.12.10-reviewed-stack5m.1 in single-thread and multithread forms. The browser wrapper is @ffmpeg/ffmpeg 0.12.15 and helpers are @ffmpeg/util 0.12.2. All versions and build inputs are exact, not ranges.

Underlying build

The base ffmpeg.wasm upstream release is v12.15 at 71aa99d37c02a7b4c435275ca9ef50e612f6efa1. Its Dockerfile selects:

  • FFmpeg n5.1.4 (libavcodec 59.37.100);
  • Emscripten SDK 3.1.40;
  • production -O3 -msimd128;
  • pthread flags for the MT build and explicit thread disabling for ST.

The reviewed profile then applies upstream ffmpeg.wasm commit b409e36475bc21f0451b5b1e1d126fa82871439a to select a 5 MiB Emscripten stack. That upstream fix addresses the libopus zero-byte/out-of-bounds failures reported in issues #591 and #775. The local driver additionally makes the upstream container-stage dependency isolation explicit, disables native x265 assembly for the wasm32 build, bounds parallel jobs and refuses output that contains the maintainer's repository or SDK paths.

The build configuration embedded in both copied WASM files is:

--target-os=none
--arch=x86_32
--enable-cross-compile
--disable-asm
--disable-stripping
--disable-programs
--disable-doc
--disable-debug
--disable-runtime-cpudetect
--disable-autodetect
--nm=emnm
--ar=emar
--ranlib=emranlib
--cc=emcc
--cxx=em++
--objcc=emcc
--dep-cc=emcc
--extra-cflags='-I../../../../install/include -O3 -msimd128'
--extra-cxxflags='-I../../../../install/include -O3 -msimd128'
--enable-gpl
--enable-libx264
--enable-libx265
--enable-libvpx
--enable-libmp3lame
--enable-libtheora
--enable-libvorbis
--enable-libopus
--enable-zlib
--enable-libwebp
--enable-libfreetype
--enable-libfribidi
--enable-libass
--enable-libzimg

The ST string also has --disable-pthreads --disable-w32threads --disable-os2threads; the MT build supplies Emscripten pthread flags instead. At runtime the application executes -version, -buildconf, -formats, -codecs, -encoders and -filters sequentially. Presets are enabled only when the active core reports every required muxer, encoder and filter. Build flags alone are not treated as a capability guarantee.

External libraries

The upstream build references x264 4-cores, x265 3.4, libvpx v1.13.1, LAME master, Ogg v1.3.4, Theora v1.1.1, Opus v1.3.1, Vorbis v1.3.3, zlib v1.2.11, libwebp v1.3.2, FreeType VER-2-10-4, FriBidi v1.0.9, HarfBuzz 5.2.0, libass 0.15.0 and zimg release-3.0.5. These resolve to x264 33cac6b77d5b9259c552156013a817ab23119612, x265 2bb5520e9596f361bf0ed81b3b8da0d7fd999069 and LAME 2badea1974ae36cb8312afe99cff1e6b3b5decee. All three commits predate the ffmpeg.wasm v12.15 release; the branch-based x264/LAME commits remain the respective branch tips. SOURCE.md records retrieval commands.

Shipped files

Mode File Bytes SHA-256
ST st/ffmpeg-core.js 111804 80c05d79d0e4e9434977b76cb851d10ddf2bfd378570178700d2c43b8afdcb24
ST st/ffmpeg-core.wasm 32232580 fe41ddc77220cef6c04f5d48eeffcaaaef179ad270642e098f9945013aa0c9dc
MT mt/ffmpeg-core.js 128953 4f2650099ab70cb2583951c0421147c62bea6c18dfbba5cfae4d7698b5d0ab62
MT mt/ffmpeg-core.wasm 32718455 abbead010cb0448b26f01a36a3c8c03a6b04aca07812ddd4719a7f4b6bf4b645
MT mt/ffmpeg-core.worker.js 2115 f77898d631dc010b45c29c23cb4379c611a7d7b131bf591d08a656bb729a4ca3

The deterministic five-file repository artifact is 21,338,974 bytes with SHA-256 ebc068c6d096de55ae3335b04b7121130665f5b0ffa7a828067122733f55185f.

npm run vendor:ffmpeg verifies and unpacks the deterministic tracked core artifact to public/vendor/ffmpeg/0.12.10-reviewed-stack5m.1/; npm run vendor:verify-runtime checks the build profile, source/toolchain/patch identity, tracked artifact, generated paths, sizes and digests without requiring a release-directory download. The stricter npm run vendor:verify release gate also hashes the build-qualified Corresponding Source archive. Vite then copies the verified bytes into dist. A normal production build performs no network download and cannot fall back to the older published npm core bytes.

Multithread Chromium guardrail

The pinned multithread core can stall in Chromium when FFmpeg lets codecs use the browser's full reported logical-core count. This is tracked upstream in ffmpeg.wasm issue 654. av-tools keeps the pthread core but adds -threads 2 as an input and output codec option for MT jobs. ST arguments remain unchanged. A regular browser test performs an actual MT H.264/AAC conversion; loading the core alone is not accepted as functional evidence.

Every execution also has a watchdog: an explicit shorter plan timeout wins; otherwise the timeout is at least two minutes, scales to thirty times the expected media duration, and is capped at the displayed four-hour product limit. Timeout is still a failure, not a successful partial export.

Long-session heap guardrail

The pinned Emscripten core was observed to end a later heterogeneous browser workflow with a bare Aborted() even though the same command passed in a fresh core and all per-job virtual files had been removed. Its tile filter also aborted deterministically on a fourth consecutive contact sheet in local stress runs. av-tools therefore refreshes an idle core before its seventeenth media execution and before a fourth contact-sheet execution. Application queue state, sources and completed results remain outside the worker and are not discarded. A bare core abort also triggers recreation after cleanup so a manual or queued retry starts on a fresh instance.

This is a conservative browser-memory guardrail, not a guarantee that sixteen large jobs or three large contact sheets fit in memory. Cancellation and failed filesystem-cleanup recovery continue to recreate the core immediately.

Opus regression evidence

Published core 0.12.10 predates the upstream stack-size fix and stalled in the real Opus preset path even though it advertised the opus muxer and libopus encoder. Capability discovery alone was therefore not accepted as enablement evidence.

On 2026-07-26 the Chromium harness used the checked tests/fixtures/generated/tone.wav fixture (32,102 bytes, SHA-256 0fd1f54d6be2f1cc7fd778775dd29cf184770d3716cf3394bf03ed54ac264c3c) and the real built-in audio-opus preset/command planner. After rebuilding with the reviewed stack profile, the isolated ST and MT runs each:

  • loaded the requested mode rather than a fallback;
  • mapped PCM audio to opus (libopus);
  • opened the declared Opus output and reported its libopus encoder;
  • returned non-empty bytes within its independent containment limit;
  • re-probed the result as positive-duration Opus audio.
# Record JSON evidence.
npm run evidence:opus

# Positive Opus enablement/release gate.
npm run test:opus-regression

The Opus audio and VP9/Opus presets are enabled only when the reviewed-core lock records both passing modes and runtime discovery finds every preset requirement. This is exact Chromium fixture evidence, not a claim about every browser, input or possible Opus build.

Licence consequence

The actual core build enables GPL and links external codecs. The wrapper/util are MIT. av-tools is GPL-3.0-or-later, and the distributed application must not be described as MIT-only. Publish the reviewed Corresponding Source and external notices with the binary release. See LICENSE, LICENSES/, THIRD_PARTY_NOTICES.md and SOURCE.md.