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
+8 -2
View File
@@ -109,9 +109,15 @@ public partial class MainViewModel : ViewModelBase
/// <summary>One decoder per play (the refcounted MediaVideoSourceManager sessions
/// can't restart per replay). The clip is full-box in video + 48k stereo audio;
/// both steams pace themselves to real time inside the decoder.</summary>
/// both steams pace themselves to real time inside the decoder — the frame-rate
/// probe is what makes the VIDEO pace (without it every frame dumps in ~1-2s and
/// the box freezes on the last frame for the rest of the audio).</summary>
private IAlertClipDecoder AlertClipDecoderFor(string path, int width, int height) =>
new AlertClipDecoder(path, width, height, new FfmpegLocator(), () => new FfmpegDecodeProcess());
new AlertClipDecoder(
path, width, height,
new FfmpegLocator(),
() => new FfmpegDecodeProcess(),
frameRateProbe: new FfmpegFrameRateProbe(new FfmpegLocator(), () => new FfmpegDecodeProcess()));
/// <summary>Stamp the shipped llama placeholder into the Asset table at startup
/// (idempotent — hash-keyed UpsertAsset dedupes). Best-effort: if it fails the