Upgrade to FFmpeg 7.1.5 + harden (MT-only, lean, vendorable) - #1
Open
asoulliereHT wants to merge 17 commits into
Open
asoulliereHT wants to merge 17 commits into
asoulliereHT wants to merge 17 commits into
Conversation
Reproduce the current (pre-upgrade) FFmpeg n5.1.4 WASM build as a rollback reference before the 7.1 upgrade effort. - build/ffmpeg.sh: cap FFmpeg `make` parallelism (FFMPEG_JOBS, default 4) so individual clang processes aren't OOM-killed when the amd64-only emsdk 3.1.40 toolchain runs under x86 emulation on Apple Silicon - .gitignore: ignore local buildx cache dirs (build-cache-st/-mt) - BASELINE-n5.1.4.md: record toolchain, external-lib pins, FFmpeg configure flags, artifact sizes, test results (ST 23 pass, MT wrapper 11 pass), and pre-existing harness gaps (broken serve script, MT-core main-thread test, Node test path) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the toolchain off the amd64-only emsdk 3.1.40, which forced slow, OOM-prone x86 emulation on Apple Silicon. Only emscripten/emsdk:latest is multi-arch, so pin it by manifest-list digest (= Emscripten 6.0.2): reproducible builds that run native on both arm64 (local) and amd64 (CI). Drop x265: its x86-centric CMake passes `-march=`, which emsdk 6.0.2's newer clang rejects for the wasm target. x265 was already slated for removal (HEVC *encode* only; HEVC decode remains via FFmpeg's native decoder), so this both unblocks the toolchain bump and advances the trim. All other vendored libs build cleanly under 6.0.2. FFmpeg itself is not built here yet; the current n5.1.4 is superseded by the 7.1 upgrade (the pre-upgrade baseline is captured on chore/phase-0-baseline). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Toolchain + build now produce a statically-linked MT ffmpeg-core.wasm (~27.5 MB) reporting "version 7.1.5" (libavcodec 61). NOT yet functional at runtime — MT scheduler hangs during the test suite; re-entrancy, progress, and timeout bridges are not reimplemented for 7.x yet. Build/toolchain: - Dockerfile: FFMPEG_VERSION n5.1.4 -> n7.1.5 - build/ffmpeg.sh: scope -fPIC to FFmpeg configure (emsdk 6.0.2 probe fix) and strip *.so from the dep prefix so -l<lib> links statically (zlib's CMake emits libz.so despite BUILD_SHARED_LIBS=OFF, which emsdk 6.0.2 turned into a runtime dlopen) - build/ffmpeg-wasm.sh: add 7.x fftools (dec/demux/enc/mux_init/sched + objpool/sync_queue/thread_queue); add -I./compat/stdbit (C23 header) - build/fribidi.sh: serial install (c2man man-page race under -j) fftools (pristine 7.1.5 + minimal wasm patches): - vendored all 7.1.5 fftools/*.c/*.h (scheduler-based frontend) - ffmpeg.c: main() -> ffmpeg(); ffprobe.c: main() -> ffprobe() - ffprobe.c: rename program_name / program_birth_year / show_help_default (+_ffprobe) to avoid link collisions with ffmpeg.c - bind.js: capture _ffmpeg()/_ffprobe() return value into Module.ret (7.x removed exit_program(); main() returns the code directly) Still TODO: MT runtime hang; re-entrancy (init_globals equivalent); progress bridge; timeout; ST-core threading decision; lib version bumps + theora/vorbis trim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The MT core now loads, remuxes, and transcodes (single-instance, incl.
auto codec-threading and re-entrant execs when logging paces them).
- ffmpeg-wasm.sh: bake pthread-worker-URL fix — emsdk 6.0.2 spawns
pthread workers from _scriptName (the wrapper worker's URL, not the
core's), hanging load(); post-build sed restores the
mainScriptUrlOrBlob override so pthreads load the core. Fixes the
silent load() hang.
- ffmpeg-wasm.sh: PTHREAD_POOL_SIZE 32 -> 64 — FFmpeg 7.x scheduler
(per-component threads) + codec frame-threading overflowed a 32-pool
and deadlocked mid-transcode (overflow pthread_create can't spawn
while the worker main thread is blocked).
- ffmpeg.c: add init_globals() reset at the top of ffmpeg() — 7.x
ffmpeg_cleanup() frees the input/output/filtergraph/decoder arrays but
leaves nb_* counts and transcode_init_done/nb_output_dumped set, so a
2nd exec() ran against stale state and hung.
Known remaining (runtime): (1) rapid successive transcodes have a
timing-sensitive scheduler race (masked by log pacing); (2) the test
suite creates a fresh 64-worker instance per describe block and exhausts
browser workers ("Maximum call stack size"); pool sizing vs multi-
instance needs tuning (likely cap default threads + smaller pool).
Also still TODO: progress bridge, timeout, ST-core decision, lib trim.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Single-instance now transcodes repeatedly with no deadlock: -h -> N
sequential transcodes all succeed, without needing log pacing.
- PTHREAD_POOL_SIZE 64 -> 32 (matches the historical footprint; keeps
per-instance worker count sane).
- Post-build: cap navigator.hardwareConcurrency to 8 so FFmpeg's
av_cpu_count (sysconf -> hardwareConcurrency) requests at most ~8
threads/codec. Uncapped, the 7.x scheduler + frame-threading requested
~2*cores threads, overflowed the pool, and deadlocked mid-transcode
(the "race" that log pacing masked). Explicit -threads still overrides.
- Fix -o output extraction in the post-build patch step (printf '%s'
dropped arg spaces so neither patch applied).
Known remaining: terminate() after a transcode does not reap the MT
core's pthread workers, so creating a fresh instance afterwards fails
("Maximum call stack size"). Single long-lived instance (the common
case) is unaffected. Still TODO: progress bridge, timeout, ST decision.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adopt the single-long-lived-instance pattern (create/load once, reuse for every operation, terminate once) instead of one instance per describe block. This mirrors real-world usage and sidesteps the emsdk 6.0.2 pthread-pool leak that surfaces when many instances are created and terminated in one page (see Phase 3 handoff). The MT suite now runs cleanly; remaining failures are the not-yet-ported progress and timeout bridges, not instance lifecycle. Dropped the brittle "should list root dir" exact-count assertion (a shared instance pre-writes video.mp4, changing the count); listDir stays covered by the directory-API tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Re-implement the two wasm JS bridges against FFmpeg 7.x's scheduler frontend (bind.js already wired them): - send_progress (EM_JS -> Module.receiveProgress): computed in print_report() from pts / longest input duration, before the -stats early return so it always fires; emits 1.0 on the final report. - is_timeout / get_timeout (EM_JS -> Module.timeout): checked in the transcode() scheduler loop. 7.x has no exit_program(), so a timeout breaks the loop and forces ret=1 (bind.js captures ffmpeg()'s return). The loop's poll period is tightened to the timeout when one is set (it otherwise wakes only every stats_period ~0.5s, too coarse for short timeouts); print_report self-rate-limits so stats are unaffected. The MT wrapper suite (tests/ffmpeg.test.js) now passes 10/10: FS/dir APIs, help, transcode+progress, timeout, abort. The MT-core *direct* suite still fails — it runs the MT core on the page main thread where Atomics.wait is illegal; pre-existing, needs a worker-based harness. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove the theora, vorbis, and ogg builder stages, their COPYs into ffmpeg-base, the --enable-libtheora/--enable-libvorbis configure flags, and the -logg/-ltheora/-lvorbis* link flags. These are dead formats for a web clip/stitch/H.264 workload; x264 (re-encode), vpx, opus, mp3, aac (native), and webp remain. MT core: 27.5 MB -> 24.9 MB; wrapper suite still 10/10. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The clip/stitch workload burns in no subtitles or text overlays, so the
entire subtitle/text-shaping stack is removed: libass, freetype, fribidi,
harfbuzz builder stages, their COPY, the --enable-lib{freetype,fribidi,ass}
configure flags, and the -l{freetype,fribidi,harfbuzz,ass} link flags.
This removes three CVE-carrying libraries outright (freetype 2.10.4,
fribidi 1.0.9, harfbuzz 5.2.0) rather than bumping them, and shrinks the
MT core 24.9 MB -> 23.6 MB. Also disables the drawtext filter (shares
freetype) — re-add freetype if in-browser text overlays are needed later.
Wrapper suite still 10/10. (build/{freetype2,fribidi,harfbuzz,libass,
ogg,theora,vorbis}.sh are now unused; left in place, harmless.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `serve` script piped COOP/COEP/CORP headers to `http-server --headers`, which is a silent no-op (http-server has no such flag) — so pages were never cross-origin isolated and the MT (SharedArrayBuffer) tests could not run against it. Add scripts/serve.js, a dependency-free Node static server that actually sends the isolation headers and correct MIME types (esp. application/wasm), and point `serve` at it. MT wrapper suite now runs 10/10 against `npm run serve`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add correctness tests for the browser-side clipping workload: lossless cut (-ss/-t -c copy) and lossless concat (-f concat -c copy) — both verified working on the MT 7.1.5 core. Suite now 12/12. Documents (in a comment) that multi-input filtergraphs (xfade transitions, concat filter, overlay) deadlock on the MT 7.x core in wasm and must run server-side; only stream-copy stitching is browser-side. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Supply-chain hardening: - x264 (was branch `4-cores`) and lame (was branch `master`) are now pinned to their branch-HEAD commit SHAs — those floating branches made the build non-reproducible. - zlib 1.2.11 -> 1.3.1 from upstream madler/zlib (pinned by the v1.3.1 commit SHA; the ffmpegwasm fork has no 1.3.1 tag). Fixes CVE-2018-25032 and CVE-2022-37434. Confirmed: buildkit resolves the commit-SHA git refs, zlib 1.3.1 is embedded in the core, wrapper suite still 12/12. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite .github/workflows/CI.yml for the 7.1.5/emsdk-6.0.2 toolchain: - checkout@v4, setup-node@v4 (Node 20), buildx v3; add least-privilege permissions block (was none; deprecated checkout@v2/setup-node@v2 before). - Build the MT core via `make prd-mt` with GHA buildx cache (emsdk 6.0.2 is multi-arch -> native amd64 on the runner). MT-only: no ST core exists on 7.x. Chromium comes from the puppeteer bundled by mocha-headless-chrome (npm ci), so no separate setup-chrome action. - Run the MT suite via start-server-and-test against the fixed serve script. - release job (on v* tags): build + tar core-mt/dist and ffmpeg/dist and attach to the GitHub Release, so a Go app can vendor from a targeted release. Also: - test:all -> MT-only (npm test now passes 12/12; ST/core-direct targets can't run on 7.x). - scripts/serve.js returns 200 at "/" so start-server-and-test/wait-on's health check passes. Note: CI itself is verified only once pushed to GitHub Actions; the flow it runs is validated locally (npm test = 12/12). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Team-facing doc: what changed vs upstream (7.1.5, lean codec set, MT-only), the browser capability map (cut/transcode/concat work; multi-input filtergraphs run server-side), the required COOP/COEP headers, single-instance usage, build steps, and how to vendor from a tagged release. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The committed package-lock.json is out of sync with the apps/* example workspaces, so strict `npm ci` fails. Those apps aren't needed to build or test the core; use `npm install` (lenient). Bump Node 20 -> 22 to clear the EBADENGINE warning from an apps/* devDep. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upgrades this fork from FFmpeg 5.1.4 → 7.1.5 and hardens it for an internal
video-clipping use case. Built and verified natively on Apple Silicon; 12/12
MT browser suite passing locally.
Highlights
stdbit, return-code capture,
init_globalsre-entrancy, progress + timeout bridges).(libass/freetype/fribidi/harfbuzz). Core 32.7 MB → 23.6 MB.
v*-tagrelease job that packages vendorable artifacts (
ffmpeg-core-mt,ffmpeg-wasm).servescript (http-server --headerswas a silent no-op → no COOP/COEP).Browser capability map
-c copy)Decisions
MT-only · single long-lived instance · transitions/re-encode server-side · vendored (not npm).
See
FORK.mdfor build/vendor/usage and the required headers.🤖 Generated with Claude Code