Repository navigation
fix(core): release decoded preview audio once no clip uses its source - #6178
miguel-heygen wants to merge 2 commits into
Conversation
The preview transport cached every decoded audio buffer by source until the player was destroyed. Each scheduling pass now drops cached buffers whose source no clip in the document still uses (scene edits, swapped sources), and keeps the rest so playback never re-decodes a clip still in the film.
Edit accuracy: accurate 271 (base branch 271), smooth 153 of thoseThe gate passes. Quarantined, measured but not gated (0) Unstable (1)
|
There was a problem hiding this comment.
Verdict: APPROVE · 0 blocker · 0 important · 1 nit · head bb4b8df9
- nit: a decode still in flight from an older pass can land its buffer after
releaseDecodedExceptran (webAudioTransport.ts:306), so a just-removed source can sit cached until the next pass. Bounded to one pass, not worth gating.
Scope
Read the full diff plus decodeAudioElement, scheduleWebAudioForActiveClips and webAudioMediaIn. decodeAudioElement has exactly one caller and it iterates the same clips list passed to the release, so a dropped key can only come back if the source reappears. Hidden and skipped clips stay in the live set, so release is conservative. Both sides read the key through decodeKey, so they cannot disagree. Playing AudioBufferSourceNodes keep their own buffer reference. All Actions runs on this head are green. No prior reviews.
What
The preview audio transport now lets go of decoded audio once no clip in the film still uses that source.
Why
When an audio clip falls back to the decoded path (for example cross-origin audio without a
crossoriginattribute), the transport caches the decodedAudioBufferby source URL. Nothing ever removed an entry exceptdestroy(), so in a long editing session every source the film ever used stayed decoded in memory: each scene swap or replaced clip added buffers that were never released.Repro: a composition with decoded-route audio clips, play, pause, change the clips' sources (or swap scenes in place), play again. Before this change the cache holds the old and new sources; after it, only the sources still in the document.
Related work
None.
How
WebAudioTransport.releaseDecodedExcept(els)drops cached buffers whose source none of the given elements points at. The cache key is read through one helper (decodeKey) so the release and the lookup cannot disagree.scheduleWebAudioForActiveClipscalls it with the same element list it is about to schedule. That pass is the only caller ofdecodeAudioElement, so a source it no longer visits can never be asked for again until it reappears.Why this rule and not a playhead window or an LRU cap: each pass schedules every future clip at once, and each scheduled
AudioBufferSourceNodealready holds its own buffer. While playing, the decoded set for clips in the film is the working set, so evicting by playhead distance or by a byte cap would only add re-fetch and re-decode on the next resume or seek back (audio entering late). Dropping a cache entry never touches a playing source, because the node keeps its own reference.Test plan
init.test.ts("decoded audio across edits"): 8 decoded clips, then an edit swaps 4 of them to new sources and playback resumes.init.test.ts,webAudioTransport.test.ts,webAudioTransportIdle.test.ts,init.swapScenes.test.ts: 510 passed. New test passed 3 runs in a row.tsc --noEmit(core), oxfmt and oxlint on the changed files.