2cba85e866
Refactored STUN/TURN fetch to be done only once, when successful, per session and cache it in mem to avoid too many reqs. Current way is a bit dumb, this should increase reliability a bit more. The caching is configurable so folks who want to use very short lived TURN credentials can disable it Add a fallback STUN config option to be used when the default STUN/TURN fetch fails Clean the safari/no candidate generation pre flight check from 3rd party STUNs Fix deadlock in audio join when STUN/TURN fetch failed
90 lines
3.0 KiB
JavaScript
90 lines
3.0 KiB
JavaScript
import {
|
|
fetchWebRTCMappedStunTurnServers,
|
|
getMappedFallbackStun,
|
|
} from '/imports/utils/fetchStunTurnServers';
|
|
import Auth from '/imports/ui/services/auth';
|
|
import { Session } from 'meteor/session';
|
|
import logger from '/imports/startup/client/logger';
|
|
|
|
const getSessionToken = () => Auth.sessionToken;
|
|
|
|
export async function getIceServersList() {
|
|
try {
|
|
const iceServers = await fetchWebRTCMappedStunTurnServers(getSessionToken());
|
|
return iceServers;
|
|
} catch (error) {
|
|
return getMappedFallbackStun();
|
|
}
|
|
}
|
|
|
|
export function canGenerateIceCandidates() {
|
|
return new Promise((resolve, reject) => {
|
|
if (Session.get('canGenerateIceCandidates')) {
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
getIceServersList().catch((e) => {
|
|
reject(e);
|
|
}).then((iceServersReceived) => {
|
|
const pc = new RTCPeerConnection({ iceServers: iceServersReceived });
|
|
let countIceCandidates = 0;
|
|
|
|
try { pc.addTransceiver('audio'); } catch (e) { }
|
|
pc.onicecandidate = function (e) {
|
|
if (countIceCandidates) return;
|
|
if (e.candidate && e.candidate.candidate.indexOf('.local') === -1) {
|
|
countIceCandidates++;
|
|
Session.set('canGenerateIceCandidates', true);
|
|
resolve();
|
|
}
|
|
};
|
|
|
|
pc.onicegatheringstatechange = function (e) {
|
|
if (e.currentTarget.iceGatheringState === 'complete' && countIceCandidates === 0) {
|
|
logger.warn({ logCode: 'no_valid_candidate' }, 'No useful ICE candidate found. Will request gUM permission.');
|
|
reject();
|
|
}
|
|
};
|
|
|
|
setTimeout(() => {
|
|
pc.close();
|
|
if (!countIceCandidates) reject();
|
|
}, 5000);
|
|
|
|
const p = pc.createOffer({ offerToReceiveVideo: true });
|
|
p.then((answer) => { pc.setLocalDescription(answer); });
|
|
});
|
|
});
|
|
}
|
|
|
|
/*
|
|
* Try to generate candidates for a recvonly RTCPeerConnection without
|
|
* a gUM permission and check if there are any candidates generated other than
|
|
* a mDNS host candidate. If there aren't, forcefully request gUM permission
|
|
* for mic (best chance of a gUM working is mic) to try and make the browser
|
|
* generate at least srflx candidates.
|
|
* This is a workaround due to a behaviour some browsers display (mainly Safari)
|
|
* where they won't generate srflx or relay candidates if no gUM permission is
|
|
* given. Since our media servers aren't able to make it work by prflx
|
|
* candidates, we need to do this.
|
|
*/
|
|
export function tryGenerateIceCandidates() {
|
|
return new Promise((resolve, reject) => {
|
|
canGenerateIceCandidates().then(() => {
|
|
resolve();
|
|
}).catch(() => {
|
|
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(() => {
|
|
logger.info({ logCode: 'no_valid_candidate_gum_success' }, 'Forced gUM to release additional ICE candidates succeeded.');
|
|
canGenerateIceCandidates().then(() => {
|
|
resolve();
|
|
}).catch((error) => {
|
|
reject(error);
|
|
});
|
|
}).catch((error) => {
|
|
reject(error);
|
|
});
|
|
});
|
|
});
|
|
}
|