fix: await playout callbacks before refresh
This commit is contained in:
@@ -250,6 +250,7 @@ public sealed partial class MainWindow
|
||||
message = result.Message,
|
||||
preparedCode = status.PreparedSceneName,
|
||||
onAirCode = status.OnAirSceneName,
|
||||
playCompletionPending = status.IsPlayCompletionPending,
|
||||
currentCueIndex = workflowState.CurrentCueIndexZeroBased,
|
||||
currentEntryId = workflowState.CurrentEntryId,
|
||||
builderKey = workflowState.BuilderKey,
|
||||
@@ -414,6 +415,7 @@ public sealed partial class MainWindow
|
||||
networkMonitoringCheckRequired = false,
|
||||
commandAvailable = false,
|
||||
liveTakeInAllowed = false,
|
||||
playCompletionPending = false,
|
||||
outcomeUnknown = browserCorrelationQuarantined,
|
||||
browserCorrelationQuarantined,
|
||||
operationTimeoutMilliseconds = 5000,
|
||||
@@ -460,6 +462,7 @@ public sealed partial class MainWindow
|
||||
networkMonitoringCheckRequired = false,
|
||||
commandAvailable = false,
|
||||
liveTakeInAllowed = false,
|
||||
playCompletionPending = false,
|
||||
outcomeUnknown = browserCorrelationQuarantined,
|
||||
browserCorrelationQuarantined,
|
||||
operationTimeoutMilliseconds = 5000,
|
||||
@@ -515,6 +518,7 @@ public sealed partial class MainWindow
|
||||
networkMonitoringCheckRequired = status.NetworkMonitoringCheckRequired,
|
||||
commandAvailable = status.IsCommandAvailable && !browserCorrelationQuarantined,
|
||||
liveTakeInAllowed = status.LiveTakeInAllowed && !browserCorrelationQuarantined,
|
||||
playCompletionPending = status.IsPlayCompletionPending,
|
||||
outcomeUnknown = browserCorrelationQuarantined ||
|
||||
status.State == PlayoutConnectionState.OutcomeUnknown,
|
||||
browserCorrelationQuarantined,
|
||||
@@ -569,6 +573,7 @@ public sealed partial class MainWindow
|
||||
networkMonitoringCheckRequired = status.NetworkMonitoringCheckRequired,
|
||||
commandAvailable = status.IsCommandAvailable && !browserCorrelationQuarantined,
|
||||
liveTakeInAllowed = status.LiveTakeInAllowed && !browserCorrelationQuarantined,
|
||||
playCompletionPending = status.IsPlayCompletionPending,
|
||||
outcomeUnknown = browserCorrelationQuarantined ||
|
||||
status.State == PlayoutConnectionState.OutcomeUnknown,
|
||||
browserCorrelationQuarantined,
|
||||
@@ -768,6 +773,28 @@ public sealed partial class MainWindow
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(nextDelay, token);
|
||||
|
||||
var engine = _playoutEngine;
|
||||
if (engine is null ||
|
||||
!ReferenceEquals(_playoutWorkflow, workflow) ||
|
||||
!ReferenceEquals(_legacyRefreshCancellation, cancellation) ||
|
||||
Volatile.Read(ref _playoutShutdownStarted) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Play returns before Tornado necessarily delivers OnScenePlayed. Waiting
|
||||
// outside the app command gate keeps emergency TAKE OUT available and does
|
||||
// not dispatch, repeat, or probe any SDK mutation.
|
||||
if (!await WaitForPlayCompletionAsync(engine, token))
|
||||
{
|
||||
SetLegacyRefreshFault(
|
||||
"PLAY_CALLBACK_TIMEOUT",
|
||||
"Tornado 재생 완료 이벤트를 제한 시간 안에 확인하지 못해 자동 장면 갱신을 중단했습니다. TAKE OUT 후 실제 화면을 확인하세요.");
|
||||
QueuePlayoutStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
await _playoutCommandGate.WaitAsync(token);
|
||||
var databaseActivityEntered = false;
|
||||
try
|
||||
@@ -856,6 +883,64 @@ public sealed partial class MainWindow
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> WaitForPlayCompletionAsync(
|
||||
IPlayoutEngine engine,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!engine.Status.IsPlayCompletionPending)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var timeoutMilliseconds = Math.Clamp(
|
||||
engine.Status.OperationTimeoutMilliseconds,
|
||||
100,
|
||||
300_000);
|
||||
using var timeoutCancellation = CancellationTokenSource.CreateLinkedTokenSource(
|
||||
cancellationToken);
|
||||
timeoutCancellation.CancelAfter(timeoutMilliseconds);
|
||||
|
||||
while (engine.Status.IsPlayCompletionPending)
|
||||
{
|
||||
var statusChanged = new TaskCompletionSource(
|
||||
TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
void OnStatusChanged(object? sender, PlayoutStatusChangedEventArgs args)
|
||||
{
|
||||
if (!args.Current.IsPlayCompletionPending)
|
||||
{
|
||||
statusChanged.TrySetResult();
|
||||
}
|
||||
}
|
||||
|
||||
engine.StatusChanged += OnStatusChanged;
|
||||
try
|
||||
{
|
||||
// Close the subscribe-after-read race without causing an engine call.
|
||||
if (!engine.Status.IsPlayCompletionPending)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await statusChanged.Task.WaitAsync(timeoutCancellation.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
when (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
engine.StatusChanged -= OnStatusChanged;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private LegacyRefreshRuntimeStatus GetLegacyRefreshStatus()
|
||||
{
|
||||
lock (_legacyRefreshStatusGate)
|
||||
|
||||
Reference in New Issue
Block a user