Merge pull request #8327 from prlanzarin/2.2-mdns-only

Account for mDNS candidates on gUM fallback for recvonly peers
This commit is contained in:
Anton Georgiev 2019-11-14 16:09:40 -05:00 committed by GitHub
commit 52d52cd0d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 78 additions and 38 deletions

View File

@ -1,6 +1,8 @@
import Screenshare from '/imports/api/screenshare';
import KurentoBridge from '/imports/api/screenshare/client/bridge';
import Settings from '/imports/ui/services/settings';
import logger from '/imports/startup/client/logger';
import { tryGenerateIceCandidates } from '/imports/utils/safari-webrtc';
// when the meeting information has been updated check to see if it was
// screensharing. If it has changed either trigger a call to receive video
@ -24,9 +26,24 @@ const presenterScreenshareHasEnded = () => {
// if remote screenshare has been started connect and display the video stream
const presenterScreenshareHasStarted = () => {
// references a function in the global namespace inside kurento-extension.js
// that we load dynamically
KurentoBridge.kurentoWatchVideo();
// KurentoBridge.kurentoWatchVideo: references a function in the global
// namespace inside kurento-extension.js that we load dynamically
// WebRTC restrictions may need a capture device permission to release
// useful ICE candidates on recvonly/no-gUM peers
tryGenerateIceCandidates().then(() => {
KurentoBridge.kurentoWatchVideo();
}).catch((error) => {
logger.error({
logCode: 'screenshare_no_valid_candidate_gum_failure',
extraInfo: {
errorName: error.name,
errorMessage: error.message,
},
}, `Forced gUM to release additional ICE candidates failed due to ${error.name}.`);
// The fallback gUM failed. Try it anyways and hope for the best.
KurentoBridge.kurentoWatchVideo();
});
};
const shareScreen = (onFail) => {

View File

@ -16,7 +16,7 @@ import {
updateWebcamStats,
} from '/imports/ui/services/network-information/index';
import { tryGenerateIceCandidates } from '../../../utils/safari-webrtc';
import { tryGenerateIceCandidates } from '/imports/utils/safari-webrtc';
import Auth from '/imports/ui/services/auth';
import VideoService from './service';
@ -209,7 +209,6 @@ class VideoProvider extends Component {
}
componentDidMount() {
this.checkIceConnectivity();
document.addEventListener('joinVideo', this.shareWebcam); // TODO find a better way to do this
document.addEventListener('exitVideo', this.unshareWebcam);
this.ws.onmessage = this.onWsMessage;
@ -374,17 +373,6 @@ class VideoProvider extends Component {
}
}
checkIceConnectivity() {
// Webkit ICE restrictions demand a capture device permission to release
// host candidates
if (browser().name === 'safari') {
const { intl } = this.props;
tryGenerateIceCandidates().catch(() => {
VideoProvider.notifyError(intl.formatMessage(intlSFUErrors[2021]));
});
}
}
_sendPauseStream(id, role, state) {
this.sendMessage({
cameraId: id,
@ -584,6 +572,22 @@ class VideoProvider extends Component {
this.webRtcPeers[id] = {};
// WebRTC restrictions may need a capture device permission to release
// useful ICE candidates on recvonly/no-gUM peers
if (!shareWebcam) {
try {
await tryGenerateIceCandidates();
} catch (error) {
logger.error({
logCode: 'video_provider_no_valid_candidate_gum_failure',
extraInfo: {
errorName: error.name,
errorMessage: error.message,
},
}, `Forced gUM to release additional ICE candidates failed due to ${error.name}.`);
}
}
try {
iceServers = await fetchWebRTCMappedStunTurnServers(sessionToken);
} catch (error) {

View File

@ -7,8 +7,8 @@ import logger from '/imports/startup/client/logger';
import { notify } from '/imports/ui/services/notification';
import browser from 'browser-detect';
import playAndRetry from '/imports/utils/mediaElementPlayRetry';
import iosWebviewAudioPolyfills from '../../../utils/ios-webview-audio-polyfills';
import { tryGenerateIceCandidates } from '../../../utils/safari-webrtc';
import iosWebviewAudioPolyfills from '/imports/utils/ios-webview-audio-polyfills';
import { tryGenerateIceCandidates } from '/imports/utils/safari-webrtc';
import AudioErrors from './error-codes';
const MEDIA = Meteor.settings.public.media;
@ -163,14 +163,18 @@ class AudioManager {
inputStream: this.createListenOnlyStream(),
};
// Webkit ICE restrictions demand a capture device permission to release
// host candidates
if (name === 'safari') {
try {
await tryGenerateIceCandidates();
} catch (e) {
this.notify(this.intl.formatMessage(this.messages.error.ICE_NEGOTIATION_FAILED));
}
// WebRTC restrictions may need a capture device permission to release
// useful ICE candidates on recvonly/no-gUM peers
try {
await tryGenerateIceCandidates();
} catch (error) {
logger.error({
logCode: 'listenonly_no_valid_candidate_gum_failure',
extraInfo: {
errorName: error.name,
errorMessage: error.message,
},
}, `Forced gUM to release additional ICE candidates failed due to ${error.name}.`);
}
// Call polyfills for webrtc client if navigator is "iOS Webview"

View File

@ -1,6 +1,7 @@
import { fetchWebRTCMappedStunTurnServers } from '/imports/utils/fetchStunTurnServers';
import Auth from '/imports/ui/services/auth';
import { Session } from 'meteor/session';
import logger from '/imports/startup/client/logger';
const defaultIceServersList = [
{ urls: 'stun:stun.l.google.com:19302' },
@ -39,16 +40,15 @@ export function canGenerateIceCandidates() {
}
getIceServersList().catch((e) => {
reject();
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) {
if (e.candidate && e.candidate.candidate.indexOf('.local') === -1) {
countIceCandidates++;
Session.set('canGenerateIceCandidates', true);
resolve();
@ -56,7 +56,10 @@ export function canGenerateIceCandidates() {
};
pc.onicegatheringstatechange = function (e) {
if (e.currentTarget.iceGatheringState == 'complete' && countIceCandidates == 0) reject();
if (e.currentTarget.iceGatheringState === 'complete' && countIceCandidates === 0) {
logger.warn({ logCode: 'no_valid_candidate' }, 'No useful ICE candidate found. Will request gUM permission.');
reject();
}
};
setTimeout(() => {
@ -70,19 +73,31 @@ export function canGenerateIceCandidates() {
});
}
/*
* 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((ok) => {
canGenerateIceCandidates().then(() => {
resolve();
}).catch((e) => {
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((stream) => {
canGenerateIceCandidates().then((ok) => {
}).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((e) => {
reject();
}).catch((error) => {
reject(error);
});
}).catch((e) => {
reject();
}).catch((error) => {
reject(error);
});
});
});