Repository navigation
[T5.3] A clip entering Program cold-starts with a placeholder flash (hand the warmed cue decoder to Program) #449
Description
Activity
Owner observation, live, 2026-09-10 (build e26a0e9, test meeting): "Media playout of a video is not smooth from preview to program. It takes a moment to display and it displays a color until the video loads. That is not ideal."
This confirms the gap on air. The solid colour is the compositor's placeholder slab for a
media:layer with no decoded frame yet. It goes out on Program, so the virtual camera, recordings and streams all carry it. Cause, as recorded in CLAUDE.md "Media is a persistent source": the Preview cue poster (preview:media:<id>) and the rolling Program source (media:<id>:live:<n>) are different decoders, so going live opens a fresh one.Competitor bar: vMix, Ecamm and mimoLive preroll the clip, so the first on-air frame is the clip's first frame, with no gap or colour.
Two layers of fix:
- Never put a placeholder on Program for a media layer. Hold the outgoing Program picture until the clip's first real frame arrives (the take completes when the frame exists). This is small and independent of the rearch.
- Hand the warmed cue decoder to Program (the original T5.3 scope), so there is no wait at all.
- added a commit that references this issue
on Sep 12, 2026 - added a commit that references this issue
on Sep 12, 2026 Reopening: only step 2 shipped.
PR #492 said "Closes #449 (step 2)" and GitHub closed the whole issue on merge. That was my bookkeeping error — the parenthetical doesn't scope the keyword.
Done (#492, merged
aac2d981, live-checked onbeta-2026-09-12-aac2d98): step 2, handing the warmed cue decoder to Program. A clip cued in Preview no longer flashes a placeholder when taken.Still open — step 1: a clip taken to Program that was never cued in Preview has no warm decoder to adopt and still cold-starts. The fix is to hold the outgoing picture until its first real frame, bounded by a timeout.
docs/BACKLOG.mdlists this as T1.11, still at the top of the open Tier 1 order, sizedS.Status 2026-09-12. Step 2 is shipped; step 1 is the only work left and it needs an owner ruling.
Step 2 (hand the warmed cue decoder to Program) — DONE in #492, live-checked
onbeta-2026-09-12-aac2d98. A clip CUED in Preview no longer flashes:
OwnedMediaFrameSource::adoptCuedDecodersre-keys the cue's entry onto the live
request instead of retiring it, gated by the puremodules/MediaCueHandoff.h
(same asset id AND path, same loop flag,preview:prefix match, generation
exactly +1, and — the load-bearing one — the cue NEVER ROLLED).(For the record: this issue auto-closed on that merge because the commit said
"Closes #449 (step 2)" — a parenthetical does not scope the keyword. Reopened.)Step 1 (hold the outgoing picture until the first real frame) is NOT done, and
deliberately so. A clip cut to Program that was never cued in Preview still has
no warm decoder to adopt, and for the ticks before its first frameresolveLayers
paintscolorFromParticipantIdover PROGRAM.The reason it was not shipped unreviewed: a plain cut goes through
load-scene-graphand never captures the outgoing scene, so "hold the outgoing
picture" is a change to the Take path itself, beside the take record and
SourceContinuityLedger(which already carries a documented race). That is not a
change to make on air without a ruling.The fork, owner's call:
- Defer the cut until the clip's first frame, bounded (adds latency to the Take).
- Render a cold clip's layer transparent instead of a coloured slab (no latency,
but a beat of nothing rather than a beat of wrong). - Leave [T5.3] A clip entering Program cold-starts with a placeholder flash (hand the warmed cue decoder to Program) #449 at step 2 and accept the cold-start flash for un-cued clips.
Owner ruling 2026-09-19 (step 1, Take semantics): on a cut into a scene that contains a clip, the clip restarts from 0 on the cut (vMix/OBS default; the cued Preview shows the poster at frame 0 and the cut rolls it; a clip already on Program keeps rolling). Source dropout mid-show is a per-source operator choice on the Sources page: hold last frame or go black (default: hold last frame, broadcast norm). This unblocks #535 slice 3b.
- added a commit that references this issue
on Sep 21, 2026 Backlog reconciliation corrects the closed status: step 2/core-owned cue transport is shipped (#492, #567), but I could not substantiate completion of step 1 (hold the outgoing Program picture until a never-cued clip has its first frame, bounded by timeout). On main 793c452, MediaCore::loadSceneGraph immediately replaces sceneId_/sceneRoutes_; D3D11CompositorAdapter resolves absent content to the warming slate. The late-first-frame test checks eventual playback and avoids a false ended state; it does not assert retention of the outgoing picture. PR #567's recorded oracle tests the cued path. The issue was closed on 2026-09-12, before #567, rather than by demonstrated step-1 acceptance. Reopening for the outstanding cold-Take acceptance; the 2026-09-19 owner ruling already resolves restart-from-zero semantics. No fresh live reproduction is claimed by this code audit.
Backlog item T5.3 (tier 5) in
docs/BACKLOG.md.A clip entering Program cold-starts with a placeholder flash (hand the warmed cue decoder to Program)
Clause / size / why: 3 | M
Order lives in BACKLOG.md; status lives here.