fix(stream): keep recovery notifications and stop on the dispatcher
The FFmpeg ProcessFailed event arrives off-thread. Start recovery on the UI dispatcher, publish retry status there, and route StopStream back to the dispatcher before changing bound state or alert collections. This prevents a recovery exception from becoming an unhandled WPF CollectionView crash. AttentionAlertStackTests covers worker-thread recovery reporting and stop. 616 tests green, clean build with 0 warnings.
This commit is contained in:
@@ -152,6 +152,13 @@ public partial class MainViewModel
|
||||
|
||||
private void OnEncoderFailed(object? sender, string message)
|
||||
{
|
||||
var dispatcher = System.Windows.Application.Current?.Dispatcher;
|
||||
if (dispatcher != null && !dispatcher.CheckAccess())
|
||||
{
|
||||
dispatcher.BeginInvoke(new Action(() => OnEncoderFailed(sender, message)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsLive && !IsRecording) return;
|
||||
if (_recoveryCts != null) return; // one recovery at a time
|
||||
if (!IsLive) return; // a recording-only session has nothing to relive
|
||||
@@ -203,22 +210,7 @@ public partial class MainViewModel
|
||||
_ => BroadcastLiveness.Live,
|
||||
};
|
||||
},
|
||||
Report = text =>
|
||||
{
|
||||
var progress = text.StartsWith("Stream dropped", StringComparison.Ordinal)
|
||||
? ParseAttempt(text)
|
||||
: 0;
|
||||
if (progress > 0) ReconnectAttempt = progress;
|
||||
|
||||
var to = AppLog.SnapshotCurrentLaunch() is { Count: > 0 } entries
|
||||
? entries[^1].Sequence
|
||||
: logFrom;
|
||||
PushAttentionAlert(
|
||||
"stream-recovery",
|
||||
text,
|
||||
logFrom,
|
||||
Math.Max(logFrom, to));
|
||||
},
|
||||
Report = text => ReportRecoveryStatus(text, logFrom),
|
||||
Log = line => AppLog.Write(line, AppLogMessageType.Error, AppLogErrorClass.Class2),
|
||||
});
|
||||
|
||||
@@ -267,6 +259,26 @@ public partial class MainViewModel
|
||||
}
|
||||
}
|
||||
|
||||
internal void ReportRecoveryStatus(string text, long logFrom)
|
||||
{
|
||||
var dispatcher = System.Windows.Application.Current?.Dispatcher;
|
||||
if (dispatcher != null && !dispatcher.CheckAccess())
|
||||
{
|
||||
dispatcher.Invoke(() => ReportRecoveryStatus(text, logFrom));
|
||||
return;
|
||||
}
|
||||
|
||||
var progress = text.StartsWith("Stream dropped", StringComparison.Ordinal)
|
||||
? ParseAttempt(text)
|
||||
: 0;
|
||||
if (progress > 0) ReconnectAttempt = progress;
|
||||
|
||||
var to = AppLog.SnapshotCurrentLaunch() is { Count: > 0 } entries
|
||||
? entries[^1].Sequence
|
||||
: logFrom;
|
||||
PushAttentionAlert("stream-recovery", text, logFrom, Math.Max(logFrom, to));
|
||||
}
|
||||
|
||||
private static int ParseAttempt(string text)
|
||||
{
|
||||
// "Stream dropped — reconnecting (attempt 2 of 4)"
|
||||
|
||||
Reference in New Issue
Block a user