Repository navigation
fix(engine): exports place sound and picture right when a source's audio and video start apart - #5455
Open
miguel-heygen wants to merge 3 commits into
Open
miguel-heygen wants to merge 3 commits into
miguel-heygen wants to merge 3 commits into
Conversation
…dio and video start apart
Contributor
Edit accuracy: accurate 2061 (base branch 2061), smooth 1572 of thoseThe gate passes. Quarantined, measured but not gated (0) |
… HDR renders hide its lead
This branch has not been 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.
What
Exports of a source whose audio and video streams start at different times (MP4, MOV, MPEG-TS) now place picture and sound where the browser preview shows them. That covers the whole clip, every render path (in-process, distributed, HDR layers) and loops.
Scope: MP4, MOV and MPEG-TS sources. WebM and MKV are a known gap that a follow-up fixes: the Matroska demuxer reports
start_time0 for every stream even when the first video packet is 7 s in, so this change cannot see the offset there and those files export exactly as on main.A user reported an export that lost its first ~7 s of picture and whose sound was wrong. One source property reproduces both symptoms: per-stream
start_timevalues that differ, e.g. a video stream that starts 7 s after its audio. Keyframe-cut screen recordings have this shape, and so do recorders that start audio before the first video frame.Why
On main, for a 12 s clip with the source played from 0:
The picture of the video-late source has two problems on main:
Root cause, in
packages/engine:audioMixer.ts, both the<video>and the<audio>path):-ss <mediaStart> -t <dur>went before-i. Even-ss 0makes the mov demuxer seek the default stream (video) and move every other stream to the keyframe it found, so audio before the video's first keyframe was cut.fps=...:start_time=0, which fills the slots before the first frame with that frame.resolvePlayableVideoDurationreturned the video stream's own length. Its callers treat it as an end time on the media timeline (the extraction window, the held tail, the loop cycle), so a late stream was cut, or wrapped,leadseconds early.Related work
None open. The sibling fix for a split scene's in-point lives in the producer's media collector and does not touch these files.
How
ffprobe.ts: one helper measures how far after the file's media time 0 a stream starts. Media time 0 is the earliest audio/video stream start, which is the browser's origin. It feedsAudioMetadata.latestStreamLeadSecondsandVideoMetadata.videoStreamLeadSeconds, and both come from the probe each path already makes.audioMixer.ts: oneextractAudioSegmentserves both audio paths.mediaStartis at or past every stream's start, it keeps today's fast input seek, with the same argv and output.atrim=start=S:end=S+D,asetpts=PTS-S/TB,aresample=async=1:first_pts=0. This trims by timestamp first and only then pads the leading gap, so memory stays bounded by the clip, not by the stream offset. The trim also goes in front of the rate-lane graph.videoFrameExtractor.ts:resolvePlayableVideoDurationnow returns the video's end on the media timeline (lead + stream length). The extraction window, the held tail, the loop cycle and frame coverage all read it from there.isVideoHiddenBeforeStreamStartis the one rule for "nothing on screen yet". BothgetFrameIndexAtTime(the SDR and distributed lookup) and the HDR layer blit use it.distributed/shared.ts) carryvideoStreamLeadSecondsthe same way they carryvideoStreamStartSeconds. Older plans read it as 0.Decisions (reversible):
hyperframes snapshoton sources with a 0.1, 0.5, 1, 2, 3 and 7 s video lead. Chrome paints the upcoming first frame when it is under 1 s away and nothing when it is 1 s or more away (3 s lead: black at 2.00 s, first frame at 2.03 s).Test plan
New
packages/engine/src/services/streamStartOffset.test.ts(real FFmpeg, skipped without it). It builds the sources from lavfi; a tone of 200 + 100·s Hz names each source second.Sound, checked as a per-second source map:
<video>and<audio>elements, from media start 0 and 3;Picture:
Other tests:
hdrCompositor.test.ts: the HDR blit paints nothing for a late stream before it starts.videoMetadata.test.ts: plans round-trip the lead.Red without each fix (each mutant applied alone, then restored):
videoStreamLeadSeconds<video>from 3 s (rate lane)"mediaStartRuns:
audioMixer*,videoFrameExtractor*,audioFxRender,audioVolumeEnvelope,videoFrameInjector: green.ffprobe.test.ts: the only failures are 4 tests that need LFS fixtures my checkout skipped, the same as on main.hdrCompositor,captureHdr*andvideoFrameCoveragegreen (93);distributed/green (232, bun).Memory, reading a 12 s window from a file whose audio starts 1 h late:
End to end,
hyperframes render --format mp4 --fps 30 --quality standard --workers autoon Linux:Not verified: