Repository navigation
#538 Stamp stream video by delivery slot, not render frame number - #723
Merged
Merged
Conversation
Compositor frame numbers count RENDERED frames; the program buffer delivers on a fixed slot grid. A render stall (a take opening media at stream start) leaves slots without a new frame, and every later frame is delivered that many slots later than its number says. Frame-number video PTS absorbed the stall, so the stream sat 4-6 frames early against sample-counted audio for the rest of the session: RTMP-record -63 to -99 ms in about half of all sessions started at core launch, including the first real-YouTube #703 run. The recording, stamped by delivery time, was unaffected. Proven with a temporary frame-number label drawn into Program pixels (pixels matched their PTS, so the encoder was right) and a per-frame trace of K = timeline - frame/fps, which stepped by exactly the error. ProgramSlotClock tracks K from the buffered frames each sender receives; the writer shifts each encoded frame by the whole slots K moved since the audio anchor (never decreasing), so a stall becomes a video PTS gap instead of a permanent A/V offset. Evidence: streamClock.videoSlotShift/videoSlotShifts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Found by the first real-YouTube #703 run on
beta-2026-09-30-ec70a7b: the exact packets sent to YouTube had video about 66 ms early against audio, while a local stand-in run of the same build was clean.Root cause
Compositor frame numbers count rendered frames, but the program buffer delivers on a fixed slot grid. A render stall leaves slots without a new frame. In these runs it was a take opening the media clip at stream start, a stall of about 100 ms. Every later frame is then delivered that many slots later than its number says.
Stream video PTS was
frameNumber / fps, so it silently absorbed the stall. Audio is sample-counted, so for the rest of the session video sat N frames early. The Program recording stamps each frame by delivery time, so it was unaffected. The clap gates anchor after startup and nothing stalled later, so they never saw it.This existed before Slice 8, because video always used frame numbers. A mid-show render stall would cause the same offset.
How it was proven. A temporary frame-number label was drawn into Program pixels and a per-frame trace of
K = timeline − frame/fpswas logged. Neither is in this PR.About half of all sessions started at core launch were bad.
Fix
ProgramSlotClock: each sender tracks K from the buffered frames it receives. Their timeline is the exact delivery deadline; unbuffered frames are ignored.streamClock.videoSlotShift/videoSlotShifts, plus the log line[stream-clock] delivery grid moved N slot(s) at frame F.Evidence (Windows Release, final build)
--local-standin×2: RTMP−recording +1.5 and +2.2 ms.The real-YouTube leg of #703 still needs a live event; the first attempt's playback capture failed, most likely because the event ended when the encoder stopped.
🤖 Generated with Claude Code