fix(call): volume>100% crash, picker UI freeze, native-path diagnostics

Volume crash:
- setParticipantVolume / setScreenShareVolume propagated values up to 2.0
(200%) to the per-track GainNode, but also called applyToAttachedElements
which set the raw HTMLAudioElement.volume — that property is hard-clamped
to [0, 1] and throws IndexSizeError above 1. Clip the element-path apply
at 1.0. WebAudio GainNode keeps doing the actual amplification.

Picker freeze:
- Firing ~20 captureScreenSourceThumbnail invokes in parallel caused
perceptible input freezes while each ~100KB base64 result arrived and
triggered a setState. Bounded the worker pool to 4 concurrent captures
with a queue — overall wall-clock is nearly identical and the grid stays
scrollable / clickable throughout the load.

Native-path diagnostics:
- Previous logs only fired on non-NativeCaptureUnavailable errors, so
users couldn't tell whether the native path was skipped (audio toggle
on, no sourceId) or attempted-and-failed. Added explicit info logs for
each skip reason plus an always-on warn with the underlying error when
the try block throws. Makes the next debug pass on screenshare much
quicker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
byGalax
2026-04-22 21:50:20 +02:00
parent c3e0c47d32
commit a5e930ac17
4 changed files with 53 additions and 24 deletions
+7 -1
View File
@@ -78,12 +78,18 @@ export function subscribeParticipantVolumes(fn: Listener): () => void {
// Apply a volume to any audio elements already attached for this user.
// Attached elements are tagged with `data-participant` in attachTrack.
// HTMLMediaElement.volume is hard-clamped to [0, 1] — anything above 1
// throws IndexSizeError. Values above 1 are only meaningful on the
// WebAudio path (remoteAudioPipelines' GainNode handles them); on the
// plain-element fallback path we clip at 1.0 so the user just hears the
// loudest level the element supports rather than an exception.
function applyToAttachedElements(userId: string, volume: number): void {
const elVolume = Math.min(1, Math.max(0, volume));
const nodes = document.querySelectorAll<HTMLAudioElement>(
'audio[data-participant="' + cssEscape(userId) + '"]',
);
nodes.forEach((el) => {
el.volume = volume;
el.volume = elVolume;
});
}