wireweave 0.3.15 → 0.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/voice.js +31 -3
package/package.json
CHANGED
package/src/voice.js
CHANGED
|
@@ -453,9 +453,19 @@ export class VoiceSession extends EventTarget {
|
|
|
453
453
|
};
|
|
454
454
|
pc.onicecandidate = (ev) => {
|
|
455
455
|
if (!ev.candidate) return;
|
|
456
|
-
|
|
456
|
+
const cand = ev.candidate.toJSON();
|
|
457
|
+
// Direct-path candidates (host = LAN, srflx = STUN-mapped, prflx = peer-reflexive) get published
|
|
458
|
+
// immediately so the remote side can begin probing direct pairs without waiting for the full
|
|
459
|
+
// gathering cycle. Relay candidates batch — they're a fallback and don't need millisecond latency.
|
|
460
|
+
const cstr = cand.candidate || '';
|
|
461
|
+
const isDirect = cstr.includes(' typ host') || cstr.includes(' typ srflx') || cstr.includes(' typ prflx');
|
|
462
|
+
if (isDirect) {
|
|
463
|
+
this._publishSignal(peerPubkey, 'ice', [cand]);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
peer.pendingCandidates.push(cand);
|
|
457
467
|
if (peer.iceTimer) clearTimeout(peer.iceTimer);
|
|
458
|
-
peer.iceTimer = setTimeout(() => { if (peer.pendingCandidates.length) { this._publishSignal(peerPubkey, 'ice', peer.pendingCandidates.splice(0)); peer.iceTimer = null; } },
|
|
468
|
+
peer.iceTimer = setTimeout(() => { if (peer.pendingCandidates.length) { this._publishSignal(peerPubkey, 'ice', peer.pendingCandidates.splice(0)); peer.iceTimer = null; } }, 100);
|
|
459
469
|
};
|
|
460
470
|
pc.onicegatheringstatechange = () => { if (pc.iceGatheringState === 'complete') { if (peer.iceTimer) { clearTimeout(peer.iceTimer); peer.iceTimer = null; } if (peer.pendingCandidates.length) this._publishSignal(peerPubkey, 'ice', peer.pendingCandidates.splice(0)); } };
|
|
461
471
|
pc.onconnectionstatechange = () => {
|
|
@@ -478,8 +488,26 @@ export class VoiceSession extends EventTarget {
|
|
|
478
488
|
|
|
479
489
|
_applyAudioHints(pc) {
|
|
480
490
|
try {
|
|
481
|
-
pc.getSenders().forEach(s => {
|
|
491
|
+
pc.getSenders().forEach(s => {
|
|
492
|
+
if (!s.track || s.track.kind !== 'audio') return;
|
|
493
|
+
const p = s.getParameters(); if (!p.encodings?.length) return;
|
|
494
|
+
p.encodings[0].networkPriority = 'high';
|
|
495
|
+
p.encodings[0].maxBitrate = 48000;
|
|
496
|
+
// Opus inband-FEC + DTX: lower bitrate use under silence, better recovery on packet loss.
|
|
497
|
+
// Reduces the chance of fallback to TURN-relay on flaky direct UDP paths.
|
|
498
|
+
p.encodings[0].priority = 'high';
|
|
499
|
+
s.setParameters(p).catch(() => {});
|
|
500
|
+
});
|
|
482
501
|
pc.getReceivers().forEach(r => { if (r.track?.kind !== 'audio') return; try { r.playoutDelayHint = 0.02; } catch {} });
|
|
502
|
+
// Munge SDP-level Opus params via setCodecPreferences when the transceiver supports it.
|
|
503
|
+
pc.getTransceivers().forEach(t => {
|
|
504
|
+
if (t.receiver?.track?.kind !== 'audio' && t.sender?.track?.kind !== 'audio') return;
|
|
505
|
+
if (typeof RTCRtpSender === 'undefined' || !RTCRtpSender.getCapabilities) return;
|
|
506
|
+
const caps = RTCRtpSender.getCapabilities('audio'); if (!caps?.codecs) return;
|
|
507
|
+
const opus = caps.codecs.filter(c => /opus/i.test(c.mimeType));
|
|
508
|
+
const others = caps.codecs.filter(c => !/opus/i.test(c.mimeType));
|
|
509
|
+
try { t.setCodecPreferences([...opus, ...others]); } catch {}
|
|
510
|
+
});
|
|
483
511
|
} catch {}
|
|
484
512
|
}
|
|
485
513
|
|