Skip to content

Fix OOM crash when stacking large 4K videos (streamed GPU extraction) - #6

Open
jsladerman wants to merge 1 commit into
timing:mainfrom
jsladerman:fix/large-video-oom-streamed-gpu
Open

Fix OOM crash when stacking large 4K videos (streamed GPU extraction)#6
jsladerman wants to merge 1 commit into
timing:mainfrom
jsladerman:fix/large-video-oom-streamed-gpu

Conversation

@jsladerman

@jsladerman jsladerman commented Aug 5, 2026

Copy link
Copy Markdown

claude spat out this solution and it seemed to work well for the videos i was trying to stack (4K iPhone ProRes clips that previously OOM'd with RuntimeError: abort(OOM), both on eise.app and running locally).

the problem: the normal video path runs ffmpeg -i video out%d.png with no frame limit, so every frame is decoded into ffmpeg.wasm's 32-bit heap/MEMFS before any get consumed — ~150 frames of 4K 16-bit PNG blows way past the heap cap. and even one-frame-at-a-time extraction doesn't survive on its own, since the 0.10.x ffmpeg.wasm core doesn't release decode memory between run() calls (dies after ~70 4K frames).

the fix:

  • before bulk extraction with no max-frames cap, probe resolution/duration/fps and estimate the extraction footprint; if it would blow the wasm heap budget, route through a new streamed path instead
  • streamed path extracts one frame at a time and feeds the existing WebGPU analyze/stack pipeline (not the CPU lite path)
  • recycles the ffmpeg.wasm instance every 25 frames via a new $recycleFFmpeg plugin helper to reclaim the decode heap
  • fps is parsed only from the Stream metadata line — progress lines like frame= 1 fps=0.0 were matching the old regex via the frame counter and corrupting the estimate

tested with 5s and 15s 4K ProRes 422 HQ clips (3840x2160@30) that both crashed before; both stack fine now.

decoding every frame into FFmpeg-WASM's wasm32 heap/MEMFS before any frame
was consumed. Any 4K clip longer than a few seconds (~150+ ProRes frames)
reliably aborted with RuntimeError: abort(OOM) - reproducible on the hosted
version with stock iPhone ProRes footage.

Changes:

- FileUploader.vue: before bulk extraction with no max-frames cap, probe
  resolution/duration/fps and estimate the extraction footprint. If it
  exceeds a safe budget, route through a new streamed extraction path
  instead of bulk-extracting. fps is parsed only from the Stream metadata
  line, since FFmpeg progress lines ("frame=  1 fps=...") match looser
  patterns via the frame counter and corrupt the estimate.

- useFFmpegReader.js: new processVideoStreamedGpu() - extracts one frame at
  a time (bounded MEMFS usage) and feeds the existing WebGPU batch
  analysis/stacking pipeline, unlike the Lite-mode fallback which is
  CPU/OpenCV-only. Shared crop-region math extracted into
  computeCropRegionFromDetections() for reuse by both paths.

- plugins/ffmpeg.js: new $recycleFFmpeg() helper. The 0.10.x FFmpeg-WASM
  core does not fully release decode memory between run() calls, so long
  per-frame extraction loops still OOM after ~70 4K frames; the streamed
  path recycles the instance every 25 frames to reclaim the heap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jsladerman
jsladerman force-pushed the fix/large-video-oom-streamed-gpu branch from 5d74710 to e0463ac Compare August 5, 2026 23:07
@timing

timing commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Ah thanks! I will have a look after the eclipse :-). I think I've been fiddling with this issue before. How fast/slow is the cropping/aligning (and stacking?) to your taste with your fix?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants