mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
124 lines
3.6 KiB
JavaScript
124 lines
3.6 KiB
JavaScript
// WHIP/WebRTC transport helpers for microphone forwarding.
|
|
export function waitForIceGatheringComplete(pc, timeoutMs = 1500) {
|
|
return new Promise((resolve) => {
|
|
if (!pc || pc.iceGatheringState === 'complete') {
|
|
resolve();
|
|
return;
|
|
}
|
|
const timer = setTimeout(() => {
|
|
pc.removeEventListener('icegatheringstatechange', onChange);
|
|
resolve();
|
|
}, timeoutMs);
|
|
function onChange() {
|
|
if (pc.iceGatheringState === 'complete') {
|
|
clearTimeout(timer);
|
|
pc.removeEventListener('icegatheringstatechange', onChange);
|
|
resolve();
|
|
}
|
|
}
|
|
pc.addEventListener('icegatheringstatechange', onChange);
|
|
});
|
|
}
|
|
|
|
function isPeerTransportReady(pc) {
|
|
if (!pc) return false;
|
|
const conn = pc.connectionState;
|
|
const ice = pc.iceConnectionState;
|
|
if (conn === 'connected') return true;
|
|
if (ice === 'connected' || ice === 'completed') return true;
|
|
return false;
|
|
}
|
|
|
|
export function waitForPeerConnected(pc, timeoutMs = 10000) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!pc) {
|
|
reject(new Error('Peer connection missing'));
|
|
return;
|
|
}
|
|
if (isPeerTransportReady(pc)) {
|
|
resolve();
|
|
return;
|
|
}
|
|
const timer = setTimeout(() => {
|
|
cleanup();
|
|
reject(new Error('Peer connection timeout'));
|
|
}, timeoutMs);
|
|
const onState = () => {
|
|
if (isPeerTransportReady(pc)) {
|
|
cleanup();
|
|
resolve();
|
|
} else if (
|
|
pc.connectionState === 'failed' ||
|
|
pc.connectionState === 'closed' ||
|
|
pc.iceConnectionState === 'failed'
|
|
) {
|
|
cleanup();
|
|
reject(new Error(`Peer connection ${pc.connectionState || pc.iceConnectionState}`));
|
|
}
|
|
};
|
|
function cleanup() {
|
|
clearTimeout(timer);
|
|
pc.removeEventListener('connectionstatechange', onState);
|
|
pc.removeEventListener('iceconnectionstatechange', onState);
|
|
}
|
|
pc.addEventListener('connectionstatechange', onState);
|
|
pc.addEventListener('iceconnectionstatechange', onState);
|
|
});
|
|
}
|
|
|
|
export async function configureSenderForLowLatency(sender) {
|
|
if (!sender?.getParameters || !sender?.setParameters) return;
|
|
const params = sender.getParameters() || {};
|
|
const first = (params.encodings && params.encodings[0]) || {};
|
|
params.encodings = [
|
|
{
|
|
...first,
|
|
maxBitrate: 64000,
|
|
dtx: 'disabled',
|
|
},
|
|
];
|
|
try {
|
|
await sender.setParameters(params);
|
|
} catch {
|
|
// Browser support varies; keep defaults if rejected.
|
|
}
|
|
}
|
|
|
|
export function waitForOutboundAudioFlow(pc, timeoutMs = 6000) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!pc) {
|
|
reject(new Error('Peer connection missing'));
|
|
return;
|
|
}
|
|
const start = Date.now();
|
|
let baseline = -1;
|
|
const timer = setInterval(async () => {
|
|
if (Date.now() - start > timeoutMs) {
|
|
clearInterval(timer);
|
|
reject(new Error('WHIP connected but no outbound audio flow'));
|
|
return;
|
|
}
|
|
try {
|
|
const senders = pc.getSenders().filter((s) => s.track?.kind === 'audio');
|
|
for (const sender of senders) {
|
|
const stats = await sender.getStats();
|
|
for (const report of stats.values()) {
|
|
if (report.type !== 'outbound-rtp' || report.kind !== 'audio') continue;
|
|
const sent = Number(report.bytesSent || 0);
|
|
const packets = Number(report.packetsSent || 0);
|
|
if (baseline < 0) {
|
|
baseline = sent;
|
|
} else if (sent > baseline + 200 || packets > 5) {
|
|
clearInterval(timer);
|
|
resolve();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} catch {
|
|
// Keep polling until timeout.
|
|
}
|
|
}, 250);
|
|
});
|
|
}
|