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
+15
View File
@@ -908,3 +908,18 @@ frame/chunk end-state AND the exception it swallowed. Rules for every future gam
(2) any producer that yields zero output inside a grace becomes a fallback trigger, not a wait;
(3) one integration test per rescue: `SilentDecoder_FallsBackToTheAnimationAfterTheNoFrameGrace`
pins transparent→fell-back→renders→drains.
The pacing half that this entry flagged came true the same day: the 12:35 session showed a FULLY
HEALTHY decoder (`frames=240 audioChunks=156 failed=False`, alert audio in the mix) and the creator
still saw "no video / you lost the video". The clip played at ~240× for a second then froze on the
last frame: `AlertClipDecoderFor` passed no `frameRateProbe`, so `RunVideoAsync`'s pace
(`if (frameDuration > 0)`) was dead code while the audio chunk-pacer ran real-time. LESSON, sharper
than the whole fallback saga: **a decoder that drops data faster than wall-clock looks identical to a
dead decoder on screen — "plays but you don't see it" is a PACING bug before it's a decode bug.**
A healthy pipeline still needs an explicit pace per stream; never ship a pipe whose `frameDuration`
can be `TimeSpan.Zero`. Fix: the alert factory passes `FfmpegFrameRateProbe` exactly like the media
path does (MainViewModel.cs:308). Verify-by-seam once, everywhere: the same fake-process +
fake-probe delay-recorder test now pins real-time video pacing
(`AlertClipDecoderTests.AlertClipDecoder_PacesVideoFramesToTheProbedFrameRate`). Rule added to the
list above: (4) if output arrives but the USER still reports no/motionless picture, check pacing
before decode — count the bytes/s against wall-clock, don't re-audit the codec.