`@remotion/media`: `<Video>` still stalls at t=0.2s on Windows after #11018 — expose decoder `hardwareAcceleration`
Bug Report
Client-side rendering (@remotion/web-renderer) with <Video> from @remotion/media still hangs on Windows with the fix from #11018 (shipped in 4.0.521) applied:
A delayRender() "Extracting frame at time 0.2 from blob:https://…" was called but not cleared after 58000ms. See https://remotion.dev/docs/timeout for help. Retries left: 1 - Rendering the frame will be retried.
Environment
remotion,@remotion/media,@remotion/web-renderer: 4.0.525 (mediabunny 1.56.1) — verified thedeleteFramesBeforeTimestampchange from #11018 is present in the installeddist/esm/index.mjs- Windows 10, Chrome 152 and Brave (Chromium 152), Intel integrated GPU, 4–8 GB RAM, 8–10 logical cores
- Sources: H.264 MP4 from phones, 17–51 s; output 1080×1920 and 2160×2160; source is a
blob:URL (IndexedDB) - The same projects export fine on macOS and Android; a Windows 11 machine with a different Intel driver also exports fine
This is a production SaaS (browser-only rendering), so we see it across several users' machines rather than one laptop; every failure lands at exactly time 0.2.
Why #11018 does not cover this case
ensureEnoughFramesForTimestamp now calls deleteFramesBeforeTimestamp({ timestampInSeconds: t - getSafeWindowOfMonotonicity(fps) }) before each sampleIterator.next(). getSafeWindowOfMonotonicity(fps) is 0.2 * 30 / fps — i.e. 0.2 s at any fps — so the threshold is ≤ 0 for every request up to t = 0.2 s and nothing is freed until after the point where the stall happens.
At the t = 0.2 request the keyframe bank holds every frame from 0 to 0.2 s (7 frames at 30 fps, 13 at 60 fps), on top of mediabunny's own decoded-sample queue (up to 8, computeMaxQueueSize) and the decoder's in-flight frames. Windows D3D11 hardware decoders have a small, driver-dependent picture-buffer pool; once every buffer is held by an open VideoFrame, the decoder silently stops producing output — no error, so the delayRender is never cleared. This is the same failure mode described in Vanilagy/mediabunny#406 ("the excess decoder accepts configure() and decode() and simply never outputs a frame"). The regression test added in #11018 models only the bank at 30 fps, not the mediabunny queue or a smaller pool, which is why it passes while real machines still stall.
Reproduction
The NewVideo composition from #11020 on a Windows/Intel laptop. If that machine's driver happens to allocate a large enough pool, a 60 fps 4K H.264 source reproduces more reliably (13 frames must be open before the safe window ever frees one).
What fixes it in our app
Decoding in software, which has no fixed picture pool. mediabunny ≥ 1.48 accepts new VideoSampleSink(track, { hardwareAcceleration: 'prefer-software' }), but packages/media/src/get-sink.ts constructs the sink without options and <Video> exposes no prop for it. We are shipping a 3-line pnpm patch that passes a decoder option there, gated by VideoDecoder.isConfigSupported({ ...decoderConfig, hardwareAcceleration: 'prefer-software' }) so that HEVC (no software decoder in Chrome on Windows) keeps the browser default. That removes the stall for AVC/VP8/VP9/AV1 sources.
Possible Solution
Either of:
- A
<Video>prop, e.g.decoderHardwareAcceleration?: 'no-preference' | 'prefer-hardware' | 'prefer-software', forwarded toVideoSampleSink(and included in the sink cache key); or - An automatic fallback: on Windows, when
VideoDecoder.isConfigSupported(...)reports software support for the track, prefer software decoding for client-side renders.
Happy to open a PR following the "Implementing a new option" guide once you indicate which shape you'd prefer.
Related: #10701, #11020, Vanilagy/mediabunny#406.
Source: remotion-dev/remotion