# Browser media engine

The GIF tools use the single-threaded `@ffmpeg/core` 0.12.10 build and
`@ffmpeg/ffmpeg` 0.12.15 wrapper. The existing compressor continues to use
Gifsicle. Media is processed in a dedicated browser Worker and is never sent
to a server.

## Assets and integrity

Run `pnpm media-engine:prepare` after installing dependencies. Production
builds run this automatically. The script rejects unexpected package versions
or changed upstream JS/WASM hashes, copies the wrapper's complete ESM module
tree, and divides the WASM into four pieces of at most 8 MiB. The generated
manifest records byte lengths and SHA-256 hashes for each piece and the
reassembled WASM. Preparation reopens and verifies all emitted pieces.

Only processing loads these assets. The runtime verifies every piece and the
combined WASM before loading its Blob URL; no third-party CDN is contacted.
This single-threaded build works without COOP/COEP or SharedArrayBuffer.
Core and wrapper updates require reviewing the pinned hashes, licenses,
corresponding sources, browser compatibility, and timing tests together.

## Licenses and corresponding sources

The FFmpeg core is GPL-2.0-or-later, including its linked x264/x265 libraries.
The JavaScript wrapper and ffmpeg.wasm binding code are MIT licensed.
License texts, original source archives, component checksums, and upstream
build information are publicly downloadable under `/media-engine/`.
`/media-engine/source/index.html` lists the corresponding source downloads;
large archives are split into numbered pieces to satisfy the static asset
size limit. Concatenate their pieces in manifest order before extraction.
The source downloads are independent of runtime engine loading.

The upstream core release is commit
`71aa99d37c02a7b4c435275ca9ef50e612f6efa1` of
<https://github.com/ffmpegwasm/ffmpeg.wasm>. Its Dockerfile and build scripts
are included in the archived tree. Note that upstream tag `v0.12.10` refers
to an older wrapper release and does **not** identify core 0.12.10.

To build the upstream single-thread production core, extract the ffmpeg.wasm
archive and run `make prd` with Docker Buildx installed. Its build uses
`emscripten/emsdk:3.1.40`, FFmpeg 5.1.4, `-O3 -msimd128`, and the library
versions recorded in the source manifest. `Dockerfile`, `Makefile`,
`build/*.sh`, `src/bind`, and `src/fftools` contain the full configuration.
The original Dockerfile fetches library sources from upstream; the bundled
archives provide those sources separately for inspection or local rebuilds.
The published npm artifacts are pinned by their hashes; a bit-for-bit rebuild
has not been claimed or verified.

## Compatibility and frame timing

The native build exposes GIF, libx264 H.264, animated libwebp, H.264, VP8,
and VP9 decoders. The compatibility gate reassembled and hashed the actual
public pieces, loaded them without isolation, and processed real GIF and
video fixtures in Chromium, Firefox, and WebKit. WebKit is a browser-engine
check; physical iPhone Safari memory limits still require device testing.

FFmpeg's GIF demuxer defaults can replace short delays. The tools explicitly
set `min_delay=0`, `default_delay=0`, and `ignore_loop=1`, then restore GIF
centisecond delays after encoding. FFmpeg 5.1.4's animated WebP encoder
guesses the final frame duration; the runtime must restore the final ANMF
duration against the source total, allowing identical-frame coalescing.
H.264 variable timing requires an explicit centisecond encoder timebase and
a nominal output rate matching the final frame delay. These behaviors need
regression verification when upgrading the engine.
