Repository navigation
@remotion/web-renderer: Preserve final video sample duration - #12177
Merged
JonnyBurger merged 2 commits intoOct 12, 2026
Merged
JonnyBurger merged 2 commits into
JonnyBurger merged 2 commits into
Conversation
Contributor
Contributor
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
add-sample.ts— explicit video sample duration:addVideoSampleAndCloseFrame()now acceptsdurationInSecondsand forwards it tonew VideoSample(frame, {duration}), so the muxer no longer defaults the sample duration to zero. The duration is applied to every frame, which is what lets the muxer derive the final sample's end time.render-media-on-web.tsx— per-frame duration: the encode loop passes1 / resolved.fps, matching the microsecond timestamp step already used atrender-media-on-web.tsx:628. This also coversonFramereplacement frames, sinceframeToEncodeis whatevervalidateVideoFrame()returns.frame-range.test.tsx— regressions: adds atest.formatrix asserting track duration, packet count, and final-packet duration for 1/2/61 frames at 30 fps plus 7 frames at 24/60 fps (one case exercising anonFramereplacement), and tightens the existing frame-range duration tolerance from 1 to 5 decimals.
Notes verified during review:
1 / resolved.fpstruncates below the rounded microsecondtimestampstep (e.g. 24 fps → 41666 vs 41667), so explicit durations cannot overlap the following sample.- Mediabunny's timestamp/duration reconstruction (
preciseTimingsinmedia-source.js) marks a duration invalid only when two samples share a timestamp with differing durations; constant per-frame durations keep every entry valid. - No transform
frameRateis set on theVideoSampleSource, so the retiming/padding path stays inactive (theframeRateatrender-media-on-web.tsx:542only feeds the track metadata). addVideoSampleAndCloseFrameis internal (single call site), so the new required positional argument is not a public API break.
deepseek-v4.1-flash | 𝕏
This branch was successfully deployed
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.

Browser MP4 exports contain the requested pictures but give the final video sample zero duration. A 61-frame export at 30 fps therefore has a 2-second video track instead of 61/30 seconds; a one-frame export has zero video duration.
The renderer constructs a
VideoFramewith a timestamp but no duration.VideoSampledefaults the missing duration to zero. The MP4 muxer can infer earlier sample durations from subsequent timestamps, but the final sample has no subsequent timestamp. Pass1 / resolved.fpsthrough the existing sample helper so the muxer receives the intended final-frame duration, including whenonFramereturns a replacement frame without a duration.Validation against upstream source and public Mediabunny 1.61.3 in headless Chrome:
frame-range.test.tsx,on-frame.test.tsx, andrender-media.test.tsxpass.onFrame.git diff --checkpass. Local browser validation used a temporary config to resolve upstream core/licensing bundles and select installed Chrome; no test setup or dependency changes are committed. Firefox, WebKit, other codecs, and the full monorepo build were not run locally.packages/web-renderer/src/add-sample.tspackages/web-renderer/src/render-media-on-web.tsxpackages/web-renderer/src/test/frame-range.test.tsxShared CI test repair
Includes the one-line caption-inspector track selector fix from #12179. Current main fails this test before it can exercise caption persistence. Both caption-inspector E2E cases pass in the actual Studio with this repair; restoring the old selector reproduces the failure.
packages/example/e2e/captions-inspector.test.mts