Auto-throttle visualizer under sustained Opus decode pressure; strip streaming investigation instrumentation

This commit is contained in:
daniel-c-harvey
2026-06-26 06:00:05 -04:00
parent 76f7f389a3
commit 374f09150f
7 changed files with 313 additions and 159 deletions
@@ -29,6 +29,7 @@
*/
import { AudioContextManager } from './AudioContextManager.js';
import { decodePressure } from './decodePressure.js';
import { IStreamingDecoder } from './IStreamingDecoder.js';
import { OggDemuxer, OpusPacket, extractOpusHead, opusHeadChannelCount } from './OggDemuxer.js';
import { OpusSeekData, OPUS_SAMPLE_RATE } from './OpusSidecar.js';
@@ -163,11 +164,6 @@ export class OpusStreamDecoder implements IStreamingDecoder {
// order-sensitive). configure() is deferred too — no need to spin up the decoder while
// throttled. The C# loop also stops reading above high-water, so the stash stays small.
if (this.isSchedulerFull?.()) {
// [BP-DIAG] First stash since last drain — production is now throttled and decode is parked.
// Trivially removable.
if (this.pendingBytes.length === 0) {
console.log('[BP-DIAG] Opus stash START (scheduler full, decode parked)');
}
this.pendingBytes.push(chunk);
return [];
}
@@ -178,8 +174,6 @@ export class OpusStreamDecoder implements IStreamingDecoder {
// the new chunk, through the demuxer as one contiguous feed.
const out: AudioBuffer[] = [];
if (this.pendingBytes.length > 0) {
// [BP-DIAG] Scheduler drained below low-water — replaying the stash. Trivially removable.
console.log(`[BP-DIAG] Opus stash DRAIN ${this.pendingBytes.length} chunks`);
const stashed = this.pendingBytes;
this.pendingBytes = [];
for (const bytes of stashed) {
@@ -382,12 +376,12 @@ export class OpusStreamDecoder implements IStreamingDecoder {
let iters = 0;
const poll = () => {
if (!this.decoder || this.decoder.decodeQueueSize === 0 || iters >= MAX_YIELD_ITERS) {
// [BP-DIAG] If we hit the 200 ms ceiling with the decode queue still non-empty, the
// WebCodecs decoder is falling behind realtime (the throughput suspect for sustained
// underrun — worse with HW accel off). Frequent CAP lines pin decode, not back-pressure,
// as the block. Trivially removable.
// Hitting the 200 ms ceiling with the decode queue still non-empty means the WebCodecs
// decoder is falling behind realtime the decode-starvation symptom that worsens with
// HW accel off (software WebGL render contending for the main thread). Report it as
// decode pressure so the visualizer throttles and yields the main thread back to decode.
if (this.decoder && iters >= MAX_YIELD_ITERS && this.decoder.decodeQueueSize > 0) {
console.log(`[BP-DIAG] Opus yield CAP hit, decodeQueueSize=${this.decoder.decodeQueueSize} (decoder behind realtime)`);
decodePressure.report();
}
resolve();
return;