Phase 21.2: back-pressure to bound the unplayed decoded region

Shared scheduler fill signal (forward water-marks + hard byte cap) pauses
the C# read loop above high-water and, for Opus, stops the demux/decode
feed so WebCodecs queues stay near-empty. Routes through the existing
cancellation discipline; releases the latch on clear/seek.
This commit is contained in:
daniel-c-harvey
2026-06-23 23:16:08 -04:00
parent a2becf45d6
commit 518479e7ae
8 changed files with 436 additions and 8 deletions
@@ -159,6 +159,25 @@ public class AudioInteropService : IAsyncDisposable
}
}
/// <summary>
/// Phase 21.2a back-pressure poll: ask whether the scheduler is still over its forward
/// high/low-water band. The read loop calls this only WHILE already throttled, to learn when it
/// may resume reading — the steady-state loop reads the piggybacked <c>ProductionPaused</c> flag
/// off each chunk result instead. Defaults to false on any interop failure so a torn-down player
/// never wedges a loop that is exiting anyway.
/// </summary>
public async Task<bool> IsProductionPaused(string playerId)
{
try
{
return await _jsRuntime.InvokeAsync<bool>("DeepDrftAudio.isProductionPaused", playerId);
}
catch
{
return false;
}
}
public async Task<AudioOperationResult> ReinitializeFromOffset(string playerId, long totalStreamLength, double seekPosition)
{
return await InvokeJsAsync<AudioOperationResult>("DeepDrftAudio.reinitializeFromOffset", playerId, totalStreamLength, seekPosition);
@@ -419,6 +438,11 @@ public class StreamingResult : AudioOperationResult
public bool HeaderParsed { get; set; }
public int BufferCount { get; set; }
public double? Duration { get; set; } // Duration in seconds calculated from WAV header
// Phase 21.2a back-pressure: true when the scheduler's forward decoded fill is over the
// high-water mark and the C# read loop should stop calling ReadAsync until it drains. Read off
// the chunk result the loop already awaits — no extra interop hop in the unthrottled steady state.
public bool ProductionPaused { get; set; }
}
public class AudioPlayerState