Commit Graph
2 Commits
Author SHA1 Message Date
4affad934e Add per-sink decoder preferences (hardwareAcceleration, optimizeForLatency) (#406)
* Add per-sink decoder preferences to VideoSampleSink and CanvasSink

Adds an optional VideoSinkDecoderOptions ({ hardwareAcceleration,
optimizeForLatency }) parameter to VideoSampleSink, exposed on CanvasSink
via options.decoderOptions, applied to the decoder config before the
VideoDecoderWrapper is constructed.

Motivation: applications that run many sinks concurrently (multi-track
video editors) need to manage hardware decode sessions deliberately -
the number of concurrent hardware sessions is OS-limited, undocumented,
and exceeding it fails silently on some platforms (macOS VideoToolbox
accepts configure() and decode() and simply never outputs). Such an
application places overflow sinks on 'prefer-software' explicitly.
optimizeForLatency is exposed alongside it since it is the other
WebCodecs decoder-config preference an application may want per sink.

The override composes with the existing interlaced-AVC Chromium
workaround, which runs later and can only strengthen the preference
toward software.

Validation mirrors decode.ts's validateVideoDecodingConfig.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* Modify docs

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>
Co-authored-by: Vanilagy <[email protected]>
2026-06-16 11:42:24 +00:00
08e3a85370 Fix orphaned queued reads when a freed worker slot is stolen concurrently (#404)
* Fix orphaned queued reads when a freed worker slot is stolen concurrently

ReadOrchestrator.runWorker's finally callback dequeues the oldest queued
read and asserts that createWorker succeeds ("we just freed up a worker").
That assumption races: the callback runs on a later microtask than the
worker's stop, and concurrent read() calls in that gap can LRU-evict the
freed worker and saturate every slot. The assert then throws as an
unhandled rejection after the read was removed from the queue but before
it was attached to any worker - its pending slices' promises never settle
and the awaiting reads hang forever.

Observed in production-like load (a 4-source composition player): 25
back-to-back occurrences saturating both workers, leaving clips
permanently undecodable.

Fix: create the worker first; only dequeue the read once a slot was
actually obtained. If every slot is busy, leave the read queued - each
running worker drains the queue from this same block when it stops, so
the read is picked up by whichever worker stops next.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* Update logic

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>
Co-authored-by: Vanilagy <[email protected]>
2026-06-16 09:26:40 +00:00