fix(alerts): silent decoder falls back to the six animations after a 1s no-frame grace
Routing and ruling both good: 'procs in the chat windows but nothing in the alert box'
with zero exceptions — every failure stage was silent by design (Debug.WriteLine-only
preview catch, bare catch{} in RunAsync firing Completed regardless). A decode run that
starts but yields neither a frame nor an audio chunk left _clip != null with
_latestClipFrame == null, so RenderClipFrame returned null forever: the box stayed
transparent for the whole alert and the six-animation fallback (which only ran on
setup-time throws) never fired.
Fix: AlertOverlayLayer.Advance gives a started clip a NoFrameFallbackSeconds (1.0s)
grace — produce no frame AND no audio inside it and the clip is torn down, _elapsed
resets, and the SAME alert continues as the animation branch (never blank, never drained
early). Diagnostics stop the lying silence: BeginClip logs box id/path/size + setup
failures; AlertClipDecoder.RunAsync logs frame/chunk end-state and the exception it used
to swallow.
Good Dog test: SilentDecoder_FallsBackToTheAnimationAfterTheNoFrameGrace (transparent
inside grace, disposed past it, animation renders, drains to idle).
Reference: alert playback must degrade to its fallback on dead-air, never vanish —
same contract as every player/booth failure budget (e.g. OBS source fallbacks).
https://obsproject.com/forum/threads/source-visibility-fallback.187383/
This commit is contained in:
@@ -148,6 +148,35 @@ on the UI thread in lockstep. Good Dog test
|
||||
`OnMessageReceived_FromPollerThread_MarshalsPreviewWritesToTheUiThread` (red on the old seam: the
|
||||
WriteableBitmap's `Dispatcher` was the poller's) — committed with the fix.
|
||||
|
||||
## Silent-decoder fallback + decode diagnostics (2026-09-26)
|
||||
|
||||
Creator repro after the crash fix: On-Air → test → remove and re-add the Stream Alerts
|
||||
layer → TEST tab event → procs in the chat windows but NOTHING in the alert box.
|
||||
Startup.log 11:44 (the re-add) shows `Test chat simulation 3 injected` but `peakMix 0.105`
|
||||
(no alert audio), vs 11:21's 0.375–0.891 (alert audio in the mix) — the re-added box's
|
||||
alert never reached the mix. No exception anywhere: every failure stage in the clip path
|
||||
was silent by design. Hunted facts: `LiveScene` is never assigned (the encoder always
|
||||
renders `StagedScene`, MainViewModel.cs:323) so the re-added box IS in the render graph;
|
||||
the bake is invalidated per-element on add; the box's DB row (`c85b46ac-…`, X=602 Y=26
|
||||
W=680 H=200) is healthy; ffmpeg tools incl. the pinned binary decode the default clip to
|
||||
`scale=680:200` rawvideo correctly to a file. The silent void: a decoder whose `Start()`
|
||||
succeeds but whose task yields no frames AND no audio leaves `_clip != null` with
|
||||
`_latestClipFrame == null` → `RenderClipFrame` returns null forever → permanently blank
|
||||
box, no animation fallback (the fallback only ran on setup-time throws) and no log trail.
|
||||
|
||||
Fix (commit `…)`: a no-frame grace in `AlertOverlayLayer.Advance`
|
||||
(`NoFrameFallbackSeconds = 1.0`) — a started clip that has produced neither a frame nor
|
||||
an audio chunk inside the grace is torn down (`StopClip`), `_elapsed` resets, and the SAME
|
||||
alert continues as the six-animation render (never a blank box, never drained early).
|
||||
Diagnostics stop the lying silence: `BeginClip` logs the box id + resolved path + size and
|
||||
the setup failure that used to only `Debug.WriteLine`; `AlertClipDecoder.RunAsync` now logs
|
||||
frame/chunk end-state and the exception it used to swallow. Good Dog test
|
||||
`SilentDecoder_FallsBackToTheAnimationAfterTheNoFrameGrace` pins the whole contract —
|
||||
transparent inside the grace, decoder disposed past it, animation rendering, drains to
|
||||
idle. This lands the corpus at 321, with the one known-env flake (RealMouseDrag reorder,
|
||||
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.**
|
||||
|
||||
## Open follow-ups (NOT this unit)
|
||||
|
||||
- TASK 3 item 20 (RewardEvent SQLite persistence half) and item 16 (Text source) remain
|
||||
|
||||
Reference in New Issue
Block a user