Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
8d3766b
chore: establish 5.1.4 build baseline and fix emulated-build OOM
asoulliereHT Jul 4, 2026
bc80279
build: bump emsdk to 6.0.2 (native arm64) and drop x265
asoulliereHT Jul 4, 2026
8634391
wip: port fftools to FFmpeg 7.1.5 — MT core builds (runtime WIP)
asoulliereHT Jul 4, 2026
f77730f
wip: MT 7.1.5 core loads + transcodes (runtime fixes)
asoulliereHT Jul 4, 2026
9d490fb
fix: cap auto thread count + pool sizing (fixes re-entrancy race)
asoulliereHT Jul 4, 2026
3cb5bc9
test: reuse a single FFmpeg instance across suites
asoulliereHT Jul 4, 2026
3af4400
feat: re-add progress + timeout bridges for 7.x — MT suite 10/10
asoulliereHT Jul 4, 2026
5784308
build: drop theora/vorbis/ogg (lean codec set)
asoulliereHT Jul 4, 2026
3cbc59d
build: drop libass + freetype/fribidi/harfbuzz (no subtitles/text)
asoulliereHT Jul 4, 2026
837d02a
fix(test): replace broken serve script with a COI-capable server
asoulliereHT Jul 4, 2026
602a955
test: cover clip-editor ops (lossless cut + concat)
asoulliereHT Jul 4, 2026
bab9ab3
build: pin floating lib branches to SHAs + bump zlib to 1.3.1
asoulliereHT Jul 4, 2026
03d8ef0
ci: modernize workflow (MT-only) + tag-release artifacts for vendoring
asoulliereHT Jul 4, 2026
5550c76
docs: add FORK.md — fork overview, capabilities, headers, vendoring
asoulliereHT Jul 4, 2026
d1b80bc
ci: trigger workflow on v* tags so the release job runs
asoulliereHT Jul 4, 2026
ee0ff88
ci: run headless Chrome with --no-sandbox (required on CI as root)
asoulliereHT Jul 4, 2026
ba022b6
ci: use npm install (lockfile drift in apps/*) + Node 22
asoulliereHT Jul 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
242 changes: 73 additions & 169 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,186 +2,90 @@ name: CI

on:
push:
branches:
- main
branches: [main]
tags: ["v*"]
pull_request:
branches:
- main
branches: [main]

# Least-privilege default; the release job below opts into write explicitly.
permissions:
contents: read

jobs:
build-core:
runs-on: ubuntu-latest
steps:
- name: Checkout Source Code
uses: actions/checkout@v2
- name: Update pkg-config database
run: sudo ldconfig
- name: Setup Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
- name: Cache build
id: cache-build
uses: actions/cache@v4
with:
path: build-cache-st
key: build-cache-st-v1-${{ hashFiles('Dockerfile', 'Makefile', 'build/*') }}
restore-keys: |
build-cache-st-v1-
- name: Build ffmpeg-core
run: make prd EXTRA_ARGS="--cache-from=type=local,src=build-cache-st --cache-to=type=local,dest=build-cache-st,mode=max"
- name: Upload core
uses: actions/upload-artifact@v4
with:
name: ffmpeg-core
path: packages/core/dist/*
build-core-mt:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout Source Code
uses: actions/checkout@v2
- name: Setup Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
- name: Cache build
id: cache-build
uses: actions/cache@v4
- name: Checkout
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

# Build the multithreaded core (emsdk 6.0.2 is multi-arch, so this builds
# natively on the amd64 runner). MT-only: the 7.x fftools require threads,
# so there is no single-threaded core (see docs/handoff).
- name: Build ffmpeg-core-mt
run: |
make prd-mt EXTRA_ARGS="--cache-from=type=gha --cache-to=type=gha,mode=max"

- name: Set up Node.js
uses: actions/setup-node@v4
with:
path: build-cache-mt
key: build-cache-mt-v1-${{ hashFiles('Dockerfile', 'Makefile', 'build/*') }}
restore-keys: |
build-cache-v1-
- name: Build ffmpet-core-mt
run: make prd-mt EXTRA_ARGS="--cache-from=type=local,src=build-cache-mt --cache-to=type=local,dest=build-cache-mt,mode=max"
- name: Upload core-mt
node-version: 22

- name: Install dependencies
run: npm install --no-audit --no-fund

# Build the JS packages (@ffmpeg/ffmpeg, @ffmpeg/util). npm install --no-audit --no-fund also fetches
# the headless Chromium that mocha-headless-chrome (puppeteer) drives.
- name: Build JS packages
run: npm run build

- name: Run MT test suite
run: npx start-server-and-test "npm run serve" 3000 "npm run test:browser:ffmpeg:mt"

- name: Upload core-mt artifact
uses: actions/upload-artifact@v4
with:
name: ffmpeg-core-mt
path: packages/core-mt/dist/*
tests:

# On a version tag, publish the vendorable artifacts (core + wrapper) as a
# GitHub Release, so downstream apps can vendor from a targeted release.
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: build-and-test
runs-on: ubuntu-latest
needs:
- build-core
- build-core-mt
permissions:
contents: write
steps:
- name: Checkout Source Code
uses: actions/checkout@v2
- name: Download ffmpeg-core
uses: actions/download-artifact@v4
with:
name: ffmpeg-core
path: packages/core/dist
- name: Download ffmpeg-core-mt
uses: actions/download-artifact@v4
with:
name: ffmpeg-core-mt
path: packages/core-mt/dist
- name: Use Node.js 18
uses: actions/setup-node@v2
with:
node-version: 18.x
- name: Cache dependencies
id: cache-dependencies
uses: actions/cache@v4
with:
path: node_modules
key: node-modules-${{ hashFiles('package-lock.json') }}
restore-keys: |
node-modules-
- name: Install dependencies
run: npm install
- name: Install Chrome
uses: browser-actions/setup-chrome@latest
- name: Checkout
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build core + JS packages
run: |
make prd-mt EXTRA_ARGS="--cache-from=type=gha --cache-to=type=gha,mode=max"

- name: Set up Node.js
uses: actions/setup-node@v4
with:
chrome-version: stable
- name: Run tests
env:
CHROME_HEADLESS: 1
CHROME_PATH: chrome
CHROME_FLAGS: "--headless --disable-gpu --no-sandbox --enable-features=SharedArrayBuffer,CrossOriginIsolation"
HEADERS: '{"Cross-Origin-Opener-Policy": "same-origin", "Cross-Origin-Embedder-Policy": "require-corp"}'
node-version: 22

- name: Install + build JS packages
run: |
# Start test server with proper headers for all tests
npm run serve -- --headers "$HEADERS" &

# Increase wait time to ensure server is ready
sleep 15

# Verify headers and isolation status
echo "Checking security headers and isolation status..."
curl -v http://localhost:3000/tests/ffmpeg-core-st.test.html 2>&1 | grep -i "cross-origin"

# Run verification script first
echo "Verifying browser environment..."
cat << EOF > verify-browser.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Cross-Origin-Opener-Policy" content="same-origin">
<meta http-equiv="Cross-Origin-Embedder-Policy" content="require-corp">
</head>
<body>
<script>
console.log('SharedArrayBuffer available:', typeof SharedArrayBuffer !== 'undefined');
console.log('crossOriginIsolated:', window.crossOriginIsolated);
if (!window.crossOriginIsolated || typeof SharedArrayBuffer === 'undefined') {
throw new Error('Browser environment not properly configured for SharedArrayBuffer');
}
</script>
</body>
</html>
EOF

# Run single-threaded tests first
echo "Running single-threaded tests..."
npx mocha-headless-chrome \
--args="$CHROME_FLAGS" \
-a no-sandbox \
-f http://localhost:3000/tests/ffmpeg-core-st.test.html 2>&1 | tee st-core-test.log

npx mocha-headless-chrome \
--args="$CHROME_FLAGS" \
-a no-sandbox \
-f http://localhost:3000/tests/ffmpeg-st.test.html 2>&1 | tee st-test.log

# Run multi-threaded tests
echo "Running multi-threaded tests..."
# Create a test script to verify browser environment
cat << EOF > verify-browser.html
<!DOCTYPE html>
<html>
<head>
<title>Browser Environment Test</title>
</head>
<body>
<script>
console.log('SharedArrayBuffer available:', typeof SharedArrayBuffer !== 'undefined');
console.log('crossOriginIsolated:', window.crossOriginIsolated);
</script>
</body>
</html>
EOF

# Run the verification in Chrome
echo "Verifying browser environment..."
npx mocha-headless-chrome \
--args="$CHROME_FLAGS --enable-features=SharedArrayBuffer,CrossOriginIsolation" \
-a no-sandbox \
-f http://localhost:3000/verify-browser.html

# Run MT tests with verified configuration
npx mocha-headless-chrome \
--args="$CHROME_FLAGS --enable-features=SharedArrayBuffer,CrossOriginIsolation" \
-a no-sandbox \
-f http://localhost:3000/tests/ffmpeg-core-mt.test.html 2>&1 | tee mt-core-test.log

npx mocha-headless-chrome \
--args="$CHROME_FLAGS --enable-features=SharedArrayBuffer,CrossOriginIsolation" \
-a no-sandbox \
-f http://localhost:3000/tests/ffmpeg-mt.test.html 2>&1 | tee mt-test.log

# Display all logs for debugging
echo "=== Test Logs ==="
for log in *-test.log; do
echo "Contents of $log:"
cat $log
done
npm install --no-audit --no-fund
npm run build

- name: Package vendorable artifacts
run: |
mkdir -p dist-release
tar -C packages/core-mt/dist -czf "dist-release/ffmpeg-core-mt-${GITHUB_REF_NAME}.tgz" .
tar -C packages/ffmpeg/dist -czf "dist-release/ffmpeg-wasm-${GITHUB_REF_NAME}.tgz" .

- name: Attach artifacts to release
uses: softprops/action-gh-release@v2
with:
files: dist-release/*.tgz
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ dist
/.nyc_output
.DS_Store

# local buildx cache (see Makefile EXTRA_ARGS / CI cache steps)
build-cache-st/
build-cache-mt/

# ide
.idea/

Expand Down
87 changes: 87 additions & 0 deletions BASELINE-n5.1.4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Baseline snapshot — pre-upgrade rollback reference

This records the exact state of the **current (pre-upgrade) `n5.1.4` build** so the
FFmpeg 7.1 upgrade (and every intermediate phase) can be diffed against a known-good
reference. Captured on the `chore/phase-0-baseline` branch.

## Toolchain

| Component | Version / pin | Source |
|----------------------|----------------------------------------|--------|
| FFmpeg | `n5.1.4` | `Dockerfile` `FFMPEG_VERSION` |
| Emscripten (emsdk) | `3.1.40` | `Dockerfile` base image (amd64-only) |
| Host build note | Built under **x86 emulation** on Apple Silicon (arm64); `emsdk:3.1.40` has no arm64 image | — |

## External libraries (all from `github.com/ffmpegwasm/*` forks unless noted)

| Library | Pin (branch/tag) | Notes for upgrade |
|------------|--------------------|-------------------|
| x264 | `4-cores` (branch) | **floating branch** — pin to SHA in Phase 4 |
| x265 | `3.4` | **DROP** in Phase 3 (HEVC encode; decode is native) |
| libvpx | `v1.13.1` | keep, consider bump |
| lame | `master` (branch) | **floating branch** — pin to SHA in Phase 4 |
| ogg | `v1.3.4` | **DROP** with vorbis/theora |
| theora | `v1.1.1` (2009) | **DROP** in Phase 3 |
| opus | `v1.3.1` | keep |
| vorbis | `v1.3.3` | **DROP** in Phase 3 |
| zlib | `v1.2.11` | **BUMP → 1.3.1** (CVE-2018-25032, CVE-2022-37434) |
| libwebp | `v1.3.2` | keep |
| freetype2 | `VER-2-10-4` (2020)| **BUMP** (CVEs) |
| fribidi | `v1.0.9` (upstream)| **BUMP → 1.0.13+** (CVE-2022-2530x) |
| harfbuzz | `5.2.0` (upstream) | **BUMP → 6.0+** (CVE-2023-25193) |
| libass | `0.15.0` | keep |
| zimg | `release-3.0.5` | keep |

## FFmpeg configure (extracted from `packages/core/dist/umd/ffmpeg-core.wasm`)

```
--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/opt/include -O3 -msimd128'
--extra-cxxflags='-I/opt/include -O3 -msimd128'
--disable-pthreads --disable-w32threads --disable-os2threads # (ST variant)
--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
```

Note: prod build enables `-msimd128` (Emscripten wasm SIMD); FFmpeg's own x86 asm stays
`--disable-asm`. (Corrects an earlier plan note that said SIMD was off.)

## Artifacts

| Artifact | Size |
|-------------------------------------------|---------|
| `packages/core/dist/umd/ffmpeg-core.wasm` | ~32.2 MB |
| `packages/core/dist/esm/ffmpeg-core.wasm` | ~32.2 MB |
| `packages/core/dist/{umd,esm}/ffmpeg-core.js` | ~112 KB |

Target for the trimmed 7.1 build: smaller wasm after dropping x265/theora/vorbis.

## Test results (baseline)

Run via headless Chrome. MT suites require cross-origin isolation (COOP/COEP), served
here by a scratchpad COI server because the repo's `serve` script is broken (see gaps).

| Suite | Result | Notes |
|--------------------------------|--------------|-------|
| `test:browser:core:st` | **12 passing** | ST core, full green |
| `test:browser:ffmpeg:st` | **11 passing** | ST wrapper, full green |
| `test:browser:ffmpeg:mt` | **11 passing** | **MT real-world path** (core runs in a Worker) — transcodes correctly |
| `test:browser:core:mt` | 8 passing / **4 failing** | ⚠️ pre-existing test-design limit — loads MT core on the page **main thread**; the 4 threaded ops fail with `Atomics.wait cannot be called in this context` (forbidden on a Window main thread by spec, unfixable by headers/flags) |
| `test:node:core:*` | ⚠️ pre-existing gap — UMD core needs browser globals (`self`, `location`); Node harness does not shim them |

**Bottom line:** ST fully green; MT build functional and verified via the wrapper/Worker
path (the way it's actually used). The MT-core-direct and Node suites have pre-existing
harness limitations, not build defects.

## Known baseline gaps (inform Phase 1)

- **No transcode-correctness assertions** — tests only check exit code / non-empty / progress==1; a codec regression would pass silently. (Phase 1 adds PSNR/SSIM + stream-probe goldens.)
- **`serve` script is broken** — `package.json` `serve` passes `--headers '{...}'` to `http-server@14.1.1`, which has **no such option** (only `--cors`), so COOP/COEP headers are never sent and pages aren't cross-origin isolated. MT suites can't run against it. Phase 1 should replace it with a server that actually sets COOP/COEP (a minimal one is proven in `scratchpad/coi-server.js`).
- **MT core direct test unsupported by design** — `tests/ffmpeg-core-mt.test.html` runs the MT core on the page main thread; threaded ops hit `Atomics.wait` (illegal on main thread). Phase 1 fix: drive the core from a Worker in that test, or drop it in favor of the wrapper MT test.
- **Node test path broken** — needs `self`/`location`/`document` shims or an ESM-in-Node loader.
- **Build fragility on Apple Silicon** — amd64-only emsdk emulated; required 24 GiB Docker VM + `make -j4` cap (`build/ffmpeg.sh`) to avoid OOM. Phase 2 (native arm64 emsdk) retires this.
Loading
Loading