fix(alerts): wire the frame-rate probe so alert-clip VIDEO paces to real time

The 12:35 live session proved the decoder healthy (frames=240 audioChunks=156
failed=False both clips, alert audio in the mix at peakMix 0.277→0.733) yet the
creator still saw 'no video plays / you lost the video'. Root cause: pacing,
not decode. AlertClipDecoderFor built the decoder with no frameRateProbe, so
RunAsync computed frameDuration = TimeSpan.Zero and RunVideoAsync's pace step
was dead code — all 240 frames of the 10s clip dumped through the pipe in the
first ~1-2s (130MB as fast as ffmpeg read), then the box froze on the LAST
frame while audio chunk-paced its real-time ~7.8s. Reads exactly like a dead
decoder on screen. The media path already wired this seam
(MainViewModel.cs:308); the alert factory never supplied one.

Derivative fix (media/decoder plumbing, cited in broad consensus of players):
pass FfmpegFrameRateProbe into the per-play alert decoder exactly like
MediaVideoSource does. Good Dog:
AlertClipDecoderTests.AlertClipDecoder_PacesVideoFramesToTheProbedFrameRate —
real AlertClipDecoder via the fake-process/fake-probe/delay-recorder seam;
red on the old factory (zero pacing delays), green with the probe (one ~10ms
delay per frame at 100fps). Full suite 321/322, one known-env flake
(RealMouseDrag reorder). Docs in this commit: task-47 addendum,
MyMistakes.md (a decoder that drops data faster than wall-clock looks
identical to a dead one), HANDOFF rewrite.
This commit is contained in:
2026-09-26 12:45:40 -07:00
parent 80038ff152
commit aea0670723
5 changed files with 182 additions and 43 deletions
+28
View File
@@ -177,6 +177,34 @@ idle. This lands the corpus at 321, with the one known-env flake (RealMouseDrag
passes with no fullscreen windows up) — unrelated to this change. Unit was the direct
lineage of bug surface: **a decoder booth production failure surfaces as dead air.**
## Video-frame pacing fix (2026-09-26)
Second creator repro after the fallback commit (12:35 session): decoder HEALTHY —
`Alert clip start: box …` → `Alert clip end: … frames=240 audioChunks=156 failed=False`
twice, alert audio in the live mix (`peakMix 0.277 → 0.733`, mic/loop 0.000) — yet
"no video plays in the web-alert box", "like you lost the video". The hole was never
decode: **the video pipe was unpaced.** `AlertClipDecoderFor` (MainViewModel.Chat.cs:113)
built the decoder with no `frameRateProbe`, so `RunAsync` computed
`frameDuration = TimeSpan.Zero` and `RunVideoAsync`'s pace (`if (frameDuration > 0)`)
was skipped — all 240 frames (a 10s, 680×200 clip, ~130MB) dumped through the pipe in
the first ~1-2s as fast as ffmpeg read them, then the box sat on the LAST frame frozen
for the remaining ~6-8s while the audio chunk-pacer (50ms/chunk × 156) ran out its
real-time cadence. To the eye: a blur then a dead frame = "no video / you lost the
video". The media path already wired exactly this seam (`MediaVideoSource` gets
`frameRateProbe: new FfmpegFrameRateProbe(…)`, MainViewModel.cs:308) — the alert
factory just never supplied one.
Fix (commit `…`): `AlertClipDecoderFor` now passes
`frameRateProbe: new FfmpegFrameRateProbe(new FfmpegLocator(), () => new FfmpegDecodeProcess())`
so the 240 frames pace at the clip's native ~24fps (10s real-time), matching the audio
cadence. Good Dog test `AlertClipDecoderTests.AlertClipDecoder_PacesVideoFramesToTheProbedFrameRate`
drives the REAL `AlertClipDecoder` through the same fake-process/fake-probe seam the
media test uses (3 frames, probe 100fps → one ~10ms pacing delay per frame) — red on
the old factory (no delay), green with a probe. Full suite 321/322, one known-env flake
(RealMouseDrag reorder). **Derivative lesson → `MyMistakes.md`: a decoder that reads
faster than wall-clock needs an explicit pace; "plays but you don't see it" is pacing,
not decode.**
## Open follow-ups (NOT this unit)
- TASK 3 item 20 (RewardEvent SQLite persistence half) and item 16 (Text source) remain