feat(core): frame-range capture, pinned encoder params, and an upload sink for capture-video templates - #43
Merged
Conversation
…ess for capture-video templates
- capture.range { startFrame, endFrame }: render a sub-range of the
composition as an independent segment — frames are evaluated at their
global timeline time but captured with segment-local timestamps, so a
host can render disjoint ranges (distributed or resumable rendering) and
concatenate the files externally. Validated against ceil(duration*fps).
- capture.encoder { codec, bitrate }: pin encoder settings explicitly so
every segment of one render shares a single configuration; defaults
unchanged (avc/vp9 by format, QUALITY_HIGH).
- capture.uploadUrl: PUT the finished bytes instead of embedding base64 in
__renderComplete; fail-open to embedding with an uploadError field so a
render is never lost. Avoids marshalling large outputs through strings.
- window.__renderProgress = { framesDone, totalFrames } during the loop.
- deterministic video handling in the capture loop: __vos__.isPaused = true
and the two-phase waitForVideosReady settle (matching the client
exporter), so compositions with video sources capture frame-accurately.
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
Four additive capabilities for
generateRenderTemplate's capture-video mode, plus a determinism fix in the capture loop:capture.range?: { startFrame, endFrame }— render only a sub-range of the composition as an independent segment: frames are evaluated at their global timeline time, but captured with segment-local timestamps (the file starts at t=0 and begins on a keyframe by construction). Because vos evaluation is a pure function of time, disjoint ranges render correctly in isolation — hosts can distribute a long render across workers, or resume a partial one, and concatenate the segments externally. Validated againstceil(duration × fps).capture.encoder?: { codec?, bitrate? }— pin encoder settings explicitly instead of relying on format defaults, so every segment of one render is guaranteed to share a single encoder configuration (the precondition for lossless stream-copy concatenation). Defaults are unchanged:avcfor mp4,vp9for webm,QUALITY_HIGH.capture.uploadUrl?—PUTthe finished bytes to a URL instead of embedding them as base64 in__renderComplete.data. Large outputs no longer have to marshal through a giant string. Fail-open: if the upload fails, the bytes are embedded as before with anuploadErrorfield, so a completed render is never lost.window.__renderProgress = { framesDone, totalFrames }— structured progress during the capture loop for host polling.__vos__.isPaused = trueand uses the same two-phasewaitForVideosReadysettle as the client-side exporter (seek → wait decodes → rAF → re-check decodes →gl.finish()), so compositions with video sources capture frame-accurately server-side.Compatibility
Fully backward compatible: with no new options the emitted loop is behaviorally identical (
startFrame = 0,endFrame = ceil(duration × fps), local timestamps equal global ones, same default codecs/bitrate, base64 embedding unchanged apart from the added video settle + progress writes).Tests
13 new template assertions (range emission + local-timestamp expression, range validation matrix, default vs pinned encoder settings, upload vs embed paths, progress/settle/isPaused) — 29 pass in
renderTemplate.test.ts, 123 pass across the core suite; typecheck, lint, and build clean. Changeset: minor.🤖 Generated with Claude Code