Repository navigation
feat(build): ST 5.1.10 cores (full + 2.6 MB copy) with non-COI vetting lane - #3
Conversation
Capture why this fork ships an MT-only @ffmpeg/core-mt and rejects the single-threaded @ffmpeg/core: FFmpeg 8.x's fftools frontend is thread-based, so "switch to ST" means downgrading the engine to upstream 5.1.4 (losing the zlib/x264/lame CVE patches and slim allowlist). ST would not fix the multi-input filtergraph deadlock (a wasm-scheduler limit) and is ~2x+ slower. Cross-origin isolation is handled at the serving layer instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fftools requires threads from FFmpeg 6.0 onward (ffmpeg_deps gains threads; pthread_create is unconditional in demux/mux), so 5.1 is the last single-thread-capable frontend. This adds ST 5.1.10 cores built from the fork's hardened dep pins, needing no COOP/COEP: - Resurrect the 5.1-era vendored fftools (pre-scheduler, 8 files) from the phase-0 baseline into src/fftools-5.1/. - Parameterize the build by FFMPEG_VERSION (Makefile, Dockerfile, ffmpeg-wasm.sh); the ARG is scoped to ffmpeg-base so the dep-builder cache stays shared between MT and ST builds. - New `copy` variant: --disable-everything + mov/mpegts/concat demuxers, mp4 muxer, h264/aac parsers+decoders, copy-path bsfs. No encoders, no external libs, no GPL. 2.6 MB wasm (full ST is 22.8 MB), sized to clipping-tool-ui's two `-c copy` commands. - Non-COI test lane: serve.js --no-coi mode on port 3001; the ST page asserts crossOriginIsolated === false (COOP/COEP <meta> tags were inert and are removed). - Vetting suites: clipping-tool-ui's runClipExport/runClipStitch argv verbatim against synthesized short-GOP H.264+AAC TS segments, with stream-presence assertions (a single-keyframe fixture + size-only asserts had let an audio-only export pass); multi-input probe showing overlay/xfade/concat-filter complete on ST 5.1 (the 8.x deadlock is scheduler-specific); perf probe (MT default 5.7s / MT -threads 1 20.9s / ST 22.9s on a 1080p60 2s x264 re-encode). Vetting record: ~/vault/docs/ffmpeg.wasm/plans/2026-08-13 st-5.1.10-core.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
asoulliereHT
left a comment
There was a problem hiding this comment.
Panel review (Danny/streaming, Keanu/build+CI, Bruce/QA, Jet/docs — Liam and Harrison excluded: no auth/input-surface or ABI/contract changes in this diff). 16 raw findings, deduplicated to 11 threads below; all verified against the working tree before posting.
Two findings could not anchor (lines outside the PR diff):
- [correctness] Jet (Docs): FORK.md's H1 (
# FloSports fork — FFmpeg 8.1.2 (MT-only)) and opening paragraph still assert MT-only while the body now documents two ST 5.1.10 cores. Anyone who reads the first two lines and stops walks away with the wrong model of the fork. Fix the title and lede. - [cleanup] Jet (Docs): The "Release variants" section still says "Each release ships two MT cores" and the vendoring section lists only MT tarballs — true, but a reader who just learned about the 2.6 MB copy core will go looking for a release asset that does not exist. State plainly that the ST cores are local-build/vetting-only until CI attaches them.
Convergence worth noting: three reviewers independently flagged the zero-CI-coverage gap, and two independently flagged the missing duration/start assertions — both are the load-bearing threads in this review.
- Dockerfile: move FFMPEG_VERSION ARG/ENV below the embuilder layer so a version switch invalidates only the FFmpeg source ADD (Keanu) - Makefile: fail loudly on empty FFMPEG_VERSION (would silently fetch FFmpeg's default branch); ffmpeg-wasm.sh now requires it via :? (Keanu) - cliptool suite: probe() returns Duration/start and the tests assert the clip window (duration within [0.9,1.6] with keyframe pre-roll, start~0) and stitch ratio ~2x member (Bruce, Danny) - add MT A/B lane (ffmpeg-cliptool-mt.test.html, test:cliptool:mt): the identical verbatim commands on the 8.1.2 MT core; reuse one instance when synth==DUT (two-instance reload trips the emsdk pthread-pool leak) and run synth encodes with -threads 1 — the unthrottled synth reproduced the production MT deadlock deterministically (Danny) - perf pages: assert crossOriginIsolated false (ST) / true (MT) (Bruce) - remove tests/ffmpeg-core-st.test.html + its script: instantiates the worker-built core in a document, which cannot work (sync-XHR abort); superseded by the ffmpeg-st suite (Keanu) - soften cliptool header overclaim: synthesized inputs, not real packager segments (Bruce) - FORK.md: title/lede no longer claim MT-only; release-variants notes ST is local-build only; vetting references now point in-repo (Jet) - ADR 0001: status amendment recording the two falsified bullets and the re-evaluation, pending a superseding ADR if ST ships (Jet) - build/ffmpeg.sh: WHY comment for the null muxer/bsf entries (Jet) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v0.15.0 shipped MT-only bytes under new names because the release job was never told about ST: the branch added build-st/prd/prd-st-copy but CI.yml still invoked only prd-mt/prd-mt-slim, so GHA layer caching replayed v0.14.0's layers and uploaded them under v0.15.0 asset names. - st-build-and-test job: builds the full ST core (the cliptool suite's input synthesizer) + the copy core, runs the ST suite and the verbatim clipping-tool-ui commands on the non-COI server, uploads core-copy - release job: needs st-build-and-test, builds prd-st-copy, and attaches ffmpeg-core-st-copy-<tag>.tgz (no "-mt-" in the name) - FORK.md: release-variants table gains the st-copy row (from v0.15.1); the "not built by CI" note is gone The bad tag stays: v0.15.0 is immutable per the consumer fetch-script contract (a re-pointed tag is indistinguishable from tampering and the SHA pin rejects it). Ship the ST asset as v0.15.1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The v0.15.0 rerun failed in st-build-and-test: testdata/ is a git submodule and actions/checkout does not fetch submodules by default, so the cliptool suite's fetch of audio-1s.wav 404'd and the 404 body fed to ffmpeg produced a cryptic "TS segment synthesis failed (ret=1)". The MT jobs never noticed because they use only embedded base64 fixtures. Also guard the fixture fetches (cliptool + perf) with res.ok so an unpopulated submodule fails with a named error instead of an ffmpeg exit code three steps downstream. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The st-copy artifact is LGPL: no --enable-gpl, no libx264, no libpostproc, no external libs at all. Assert it at runtime from the core's own -version banner in the copy lane (EXPECT_GPL_FREE) so a future allowlist change can't silently re-introduce a GPL component. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review (from the consuming app's side)Approve. The Findings:
🤖 Generated with Claude Code |
Summary
Adds single-threaded FFmpeg 5.1.10 cores to the fork, built from the same hardened dependency pins as the MT 8.1.2 cores. The driver: cross-origin isolation (COOP/COEP) required by the MT core breaks popup-based auth (Facebook login) in the consumer, and clipping-tool-ui already forces
-threads 1to dodge an emscripten pthread-layer deadlock, so MT parallelism was not being used.Why 5.1: fftools requires threads from FFmpeg 6.0 onward (
ffmpeg_depsgainsthreadsat n6.0 configure;pthread_createis unconditional inffmpeg_demux.c/ffmpeg_mux.c), so the 5.1 LTS branch (5.1.10, 2026-06-21, still receiving backports) is the last single-thread-capable frontend.What's in here
src/fftools-5.1/: the pre-scheduler 5.1-era vendored fftools, resurrected from thechore/phase-0-baselinetree.FFMPEG_VERSIONflows Makefile → Dockerfile →ffmpeg-wasm.sh, selecting the FFmpeg tag and the fftools generation. The ARG is scoped to theffmpeg-basestage so MT and ST builds share the dependency-builder cache.copyvariant (make prd-st-copy→packages/core-copy):--disable-everythingplus mov/mpegts/concat demuxers, mp4 muxer, h264/aac parsers+decoders, and the copy-path bsfs. No encoders, no external libs, no GPL. 2.6 MB wasm (full ST is 22.8 MB), sized to clipping-tool-ui's two-c copycommands.scripts/serve.js --no-coion port 3001; the ST pages assertcrossOriginIsolated === falseso the lane cannot silently run isolated. The inert COOP/COEP<meta>tags are removed.npm run test:cliptool/test:cliptool:copy: clipping-tool-ui'srunClipExportandrunClipStitchargv verbatim (includingffconcat+option ignore_editlist 1,-avoid_negative_ts make_zero,+negative_cts_offsets+faststart) against synthesized short-GOP H.264+AAC TS segments, with stream-presence assertions.npm run test:st:probe: multi-input filtergraphs (overlay,xfade, concat filter) complete on ST 5.1 — the 8.x deadlock is scheduler-specific, confirming FORK.md's hypothesis; capability map updated per engine.npm run test:perf:st/test:perf:mt: 1080p60 2s x264 re-encode: MT default 5.7s, MT-threads 120.9s, ST 22.9s.Test results
test:allunchanged on port 3000).Notes for review
-sspast the only keyframe drops all non-key packets under-c copy). The cliptool suite synthesizes keyint=15 segments and asserts stream presence.fetch-ffmpeg-core.mjs, drop COOP/COEP,thread: false).Vetting record:
~/vault/docs/ffmpeg.wasm/plans/2026-08-13 st-5.1.10-core.md🤖 Generated with Claude Code