Repository navigation
fix(producer): exports no longer fail on a video clip outside the composition's length - #5431
Merged
Merged
Conversation
…sition's length A clip that starts at or after the composition's end (or ends before 0) is never on screen, so extraction skips it. Its timeline window stayed as authored, so the frame-coverage gate still expected its full length and aborted the render with "captured 0 of expected N frames". Such a clip now leaves extraction with an empty window, the same way a partly visible clip is already trimmed to its visible part, so it owes no frames.
Contributor
Edit accuracy: accurate 2061 (base branch 2061), smooth 1541 of thoseThe gate passes. Quarantined, measured but not gated (0) |
…ud renders included The previous commit marked such a clip with end = start, but the window resolver reads a zero-length slot as "use the source's natural length", and the distributed plan still required an extracted entry for every declared clip. Instead, the extract stage (shared by in-process and distributed renders) removes clips that lie entirely outside [0, duration) from the composition before probing and extraction, using one engine predicate that the extractor's own early skip also uses, so a clip ending before 0 is no longer downloaded or probed either.
…e timeline A clip with no authored end carries end 0 (script-created media, or a src set from a script), which the window resolver reads as "natural source length". The outside-the-timeline check treated it as ending at 0 and dropped it, so the clip froze on its first frame. Only an authored end (after the start) at or before 0 now counts as before the timeline.
…'s end A clip with no authored length carries end 0, the same value as an authored end at 0, so "ends at or before 0" cannot be told apart from "natural length" and a script-created clip with a negative start was dropped and froze. Clips entirely before 0 keep main's behaviour; the filter now only drops clips starting at or after the end, which never reads the end.
miguel-heygen
enabled auto-merge
October 10, 2026 23:55
somanshreddy
left a comment
Contributor
There was a problem hiding this comment.
Reviewed at 1da8db78. Clean, no findings.
- Both sides use the same rule. The stage filter and the extractor's existing skip both call
isVideoPastTimelineEnd(video, composition.duration)(start >= end). The extractor already receivedtimelineEnd: composition.duration(extractVideosStage.ts:508), so the composition's video list now matches exactly what extraction skipped. - A zero duration can't drop everything. The extract stage runs after the probe stage, which discovers a non-positive duration or aborts on it (
probeStage.ts:481,:751). Sostart >= 0can't empty the list. - Filtering before probe and extraction is right. The coverage gate, frame lookup, HDR planning and the distributed
meta/videos.jsonall read the filtered list. A clip starting at or after the end has no on-screen time, so its audio and frames are absent either way. - Leaving clips that lie entirely before 0 alone is correct. An
endof 0 can't be told apart from natural length. - Tests:
extractVideosStage.timelineBound.test.tspasses 11/11. Mutants killed:- no stage filter: 2 fail, in-process and distributed
>=changed to>: 2 fail
Review by Somu
somanshreddy
approved these changes
Oct 11, 2026
somanshreddy
left a comment
Contributor
There was a problem hiding this comment.
Approving at 1da8db78, unchanged since my COMMENT (5481247015). Required checks green, no CRs.
Review by Somu
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.
What
A render no longer aborts with
Video "<id>" captured 0 of expected N frameswhen a video clip starts at or after the composition's end (the rootdata-duration). That clip is never on screen, so the export now completes without it. Distributed renders of the same composition no longer fail planning withmeta/videos.json.extracted is missing declared video "<id>".Why
Extraction already skips such a clip, and lint already warns that it is cut off. But the clip stayed in the composition's video list, so the frame-coverage gate expected its full length, found no frames and aborted a correct render; the distributed plan rejected it one step later for the same reason.
Re-running fails the same way within a second: the visible clips hit the extraction cache and the same clip is skipped again, so it looks like a stale cached result even though nothing stale is involved.
Repro on current main (3 s composition, one clip at 0 s and one at 4 s):
hyperframes renderon main fails:Video "v-past" captured 0 of expected 60 frames (coverage 0.0%, threshold 95.0%). With this change it completes: a 3.000 s mp4, 90 frames, withv-invisible.Related work
None open. The extractor's early skip for these clips predates this change; this makes the rest of the render agree with it.
How
isVideoPastTimelineEnd(video, timelineEnd)(engine):start >= timelineEnd. The extractor's existing early skip now calls it.Deliberately not covered: clips that lie entirely before 0. A clip with no authored length carries
end0 (script-created media, or asrcset from a script), the same value as an authored end at 0, so "ends at or before 0" cannot be told apart from "natural length" here. Such clips keep main's behaviour (the coverage gate aborts loudly rather than shipping a frozen clip).Review history: a first version set
end = starton skipped clips (the window resolver reads that as natural length; distributed plan still broken). A second dropped clips withend <= 0, which froze natural-length clips; a third requiredend > start, which still froze a natural-length clip with a negative start. All three were caught by independent review and are replaced by the start-only rule.Test plan
extractVideosStage.timelineBound.test.ts, run with and without materialized symlinks (the stage options the distributed plan uses; the plan builder itself is not exercised by a unit test, it was checked on the repro below): a clip starting exactly at the end never reaches extraction and leaves the composition; a clip starting just before the end, and natural-length clips (end0, with start 0 and with start -1), stay. An over-eager drop (start + 0.5 >= end): 2 failed. Without the filter: 2 failed, 9 passed. With>instead of>=: 2 failed, 9 passed. 3 runs in a row: exit 0, 11 passed.videoFrameExtractor.test.tsunchanged: exit 0, 199 passed.hyperframes renderbuilt from this branch):v-invisible).srcis set from a script, no authored length: extracted, frames at 1 s and 2 s differ (it plays).data-start="-1": fails withcaptured 0 of expected 30 frames, as on main.plan()on the repro: main fails withmeta/videos.json.extracted is missing declared video "v-past"; this branch plans one chunk.Not exercised: distributed
renderChunkandassembleend to end; a full HDR render with an HDR clip past the end. A dropped clip's<video>element still loads in the page, so with--no-best-efforta missing file for it still blocks the render (as on main).