Repository navigation
Render desktop exports with a bundled native ffmpeg - #108
Merged
Merged
Conversation
Desktop exports kept failing with wasm memory errors (RESCRIPT-1W "memory access out of bounds", RESCRIPT-2A "media engine stopped responding", both stage=export-video on desktop 1.2.1). Even the growable ffmpeg.wasm core tops out at 2 GiB, and the whole encoded output is held in wasm memory and then copied into a Blob. The Electron app now extracts audio and renders exports with a static ffmpeg run from the main process. ffmpeg reads the source from disk and writes the render to a temp file; a native Save dialog then moves it where the user wants it, with Show in Finder/Explorer afterwards. The web build is unchanged. - lib/exportArgs.ts: filtergraph/codec builder shared by both engines, plus request validation and a -progress parser. Adds -pix_fmt yuv420p so 10-bit / 4:2:2 sources export playable files. - electron/ffmpegRunner.ts + electron/media.ts: spawn/supervise ffmpeg and the IPC surface. The renderer only handles opaque ids; main builds the argv itself from a validated request. Filtergraph goes through -filter_complex_script (Windows command-line limit), temp files are swept per session and per renderer. - lib/nativeMedia.ts + lib/mediaEngine.ts: renderer client and facade; falls back to ffmpeg.wasm (reported as native-media-fallback) only when the binary can't run, not when a job fails. - Projects remember the original file's path/size/mtime so a restored project reads it from disk instead of copying the IndexedDB blob back out. - scripts/fetch-ffmpeg.mjs downloads pinned ffmpeg-static b6.1.1 binaries (SHA-256 verified) per os/arch for extraResources; after-pack.cjs fails the build if one is missing. - Tests: argv parity with the old wasm commands, request validation, and an end-to-end native render of every preset (CI fetches the host binary). Fixes RESCRIPT-1W Fixes RESCRIPT-2A Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
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.
Why
Desktop users keep losing exports to wasm memory limits. Sentry has RESCRIPT-1W ("memory access out of bounds") and RESCRIPT-2A ("The media engine stopped responding…"), both tagged
stage=export-video,surface=desktop, on 1.2.1. Switching to the growable wasm core (#101) wasn't enough:Blob.What
The Electron app now runs a bundled static ffmpeg in the main process for import audio extraction and for video/audio export. ffmpeg reads the source straight from disk and writes the render to a temp file. A native Save… dialog then moves the file wherever the user picks, and Show in Finder/Explorer works after that. The web build is unchanged.
Fallback and errors
native-media-fallbackto Sentry.extra.detail, with paths scrubbed.Security
webContentsthat created them.parseExportRequest: enums, finite sorted ranges, a count cap, unknown keys dropped).{ok, code, detail}instead of throwing, so the renderer can tell "fall back" apart from "failed".Pieces
lib/exportArgs.ts-progressparser. Also adds-pix_fmt yuv420p, so 10-bit / 4:2:2 / 4:4:4 sources export playable files (this applies to wasm too).electron/ffmpegRunner.tselectronimport, so tests can drive it.electron/media.tslib/nativeMedia.ts,lib/mediaEngine.tsscripts/fetch-ffmpeg.mjsotool -Lon mac). Output goes tobuild/ffmpeg/<os>-<arch>/and ships throughextraResources.scripts/after-pack.cjslib/projects.ts/autosave.ts/store.tsexportUrlbecomesexportResult(blob or file), which is freed on reset or project switch. This also fixes a Blob-URL leak.ExportDialog/Editorelectron/media.tsdetails:-filter_complex_script. At about 130 characters per kept range, Windows' 32K command-line limit is hit at roughly 250 cuts.EXDEV/EPERM/EBUSY.Binaries and packaging
Upstream builds per platform:
All of them include libx264, libvpx-vp9, libopus, aac and libmp3lame.
Windows arm64 uses the x64 binary under emulation, because upstream has no arm64 Windows build.
Installer size grows by about 20–30 MB compressed per platform.
macOS signing: osx-sign already signs every Mach-O file in the bundle with
entitlementsInheritand hardened runtime, so no signing config changes are needed.Licensing: these are GPL builds. They ship alongside the app as a separate executable, with their LICENSE and README next to them. The wasm core was already GPL (x264).
Docs: RELEASING.md has a new "Native ffmpeg" section.
Testing
tsc(web + electron),eslint,next build,build:electron,test:i18n,test:timelineall pass.tests/export-args-test.ts: argv matches the old wasm commands exactly for every preset; request validation (including injection attempts and oversized range lists); progress parsing edge cases (N/A, negative values, chunks split mid-line).tests/native-ffmpeg-test.ts, run against the real bundled binary:failed, notunavailable.unavailable.build.*.targethas a pinned binary.fetch:ffmpeg --hostbeforetest:ffmpeg.-filter_complex_script.electron:dev, macOS arm64), driven over CDP:~/Downloads, and the result is a playable 1920×1080 H.264 High yuv420p MP4. The dialog showed "Saved as… / Show in Finder".File(the restored-project case) staged correctly. Junk input was classifiedno-audio, which matches wasm's behavior.sourcePath/sourceSize/sourceMtimeon the project record.Not yet verified, worth checking before release
npm run diston macOS (checkcodesign -dvonContents/Resources/ffmpeg/ffmpeg).RESCRIPT_FFMPEG_PATH=/nonexistent npm run electron:devshould export through wasm.Follow-ups (not in this PR)
export:cancelIPC already exists).h264_videotoolbox, etc.).select/aselectgraph, which scales better than one trim branch per cut.Fixes RESCRIPT-1W, RESCRIPT-2A
🤖 Generated with Claude Code