From c21bb693b4da07e27881d84733518230d2fa0122 Mon Sep 17 00:00:00 2001 From: gustavotrott Date: Mon, 14 Jan 2019 22:45:32 -0200 Subject: [PATCH] Store in session if can generate ice candidates, change timeout to 5secs, display error msgs --- .../components/video-provider/component.jsx | 23 +-- .../ui/services/audio-manager/index.js | 8 +- .../imports/utils/safari-webrtc.js | 136 ++++++++++-------- 3 files changed, 100 insertions(+), 67 deletions(-) diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx index 80aae0fd16..c0cc7b83b4 100755 --- a/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/video-provider/component.jsx @@ -101,14 +101,7 @@ const MAX_CAMERA_SHARE_FAILED_WAIT_TIME = 60000; const PING_INTERVAL = 15000; class VideoProvider extends Component { - static checkIceConnectivity() { - // Webkit ICE restrictions demand a capture device permission to release - // host candidates - if (browser().name === 'safari') { - tryGenerateIceCandidates(); - } - } - + constructor(props) { super(props); @@ -146,6 +139,7 @@ class VideoProvider extends Component { this.customGetStats = this.customGetStats.bind(this); } + componentWillMount() { this.ws.onopen = this.onWsOpen; @@ -156,7 +150,7 @@ class VideoProvider extends Component { } componentDidMount() { - VideoProvider.checkIceConnectivity(); + 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; @@ -284,6 +278,17 @@ 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((e) => { + this.notifyError(intl.formatMessage(intlSFUErrors[2021])); + }); + } + } + logger(type, message, options = {}) { const { userId, userName } = this.props; const topic = options.topic || 'video'; diff --git a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js index 691450b179..d311901fb1 100755 --- a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js +++ b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js @@ -89,7 +89,7 @@ class AudioManager { this.isWaitingPermissions = false; this.devicesInitialized = false; - + return Promise.all([ this.setDefaultInputDevice(), this.setDefaultOutputDevice(), @@ -150,7 +150,11 @@ class AudioManager { // Webkit ICE restrictions demand a capture device permission to release // host candidates if (name === 'safari') { - await tryGenerateIceCandidates(); + try { + await tryGenerateIceCandidates(); + } catch (e) { + this.notify(this.messages.error.ICE_NEGOTIATION_FAILED); + } } // Call polyfills for webrtc client if navigator is "iOS Webview" diff --git a/bigbluebutton-html5/imports/utils/safari-webrtc.js b/bigbluebutton-html5/imports/utils/safari-webrtc.js index 4d569dc6df..524ceb737b 100644 --- a/bigbluebutton-html5/imports/utils/safari-webrtc.js +++ b/bigbluebutton-html5/imports/utils/safari-webrtc.js @@ -1,65 +1,89 @@ -const iceServersList = [ - {urls:"stun:stun.l.google.com:19302"}, - {urls:"stun:stun1.l.google.com:19302"}, - {urls:"stun:stun2.l.google.com:19302"}, - {urls:"stun:stun3.l.google.com:19302"}, - {urls:"stun:stun4.l.google.com:19302"}, - {urls:"stun:stun.ekiga.net"}, - {urls:"stun:stun.ideasip.com"}, - {urls:"stun:stun.schlund.de"}, - {urls:"stun:stun.stunprotocol.org:3478"}, - {urls:"stun:stun.voiparound.com"}, - {urls:"stun:stun.voipbuster.com"}, - {urls:"stun:stun.voipstunt.com"}, - {urls:"stun:stun.voxgratia.org"}, - {urls:"stun:stun.services.mozilla.com"} - ]; +import { fetchWebRTCMappedStunTurnServers } from '/imports/utils/fetchStunTurnServers'; +import Auth from '/imports/ui/services/auth'; +import { Session } from 'meteor/session'; +const defaultIceServersList = [ + { urls: 'stun:stun.l.google.com:19302' }, + { urls: 'stun:stun1.l.google.com:19302' }, + { urls: 'stun:stun2.l.google.com:19302' }, + { urls: 'stun:stun3.l.google.com:19302' }, + { urls: 'stun:stun4.l.google.com:19302' }, + { urls: 'stun:stun.ekiga.net' }, + { urls: 'stun:stun.ideasip.com' }, + { urls: 'stun:stun.schlund.de' }, + { urls: 'stun:stun.stunprotocol.org:3478' }, + { urls: 'stun:stun.voiparound.com' }, + { urls: 'stun:stun.voipbuster.com' }, + { urls: 'stun:stun.voipstunt.com' }, + { urls: 'stun:stun.voxgratia.org' }, + { urls: 'stun:stun.services.mozilla.com' }, +]; + +const getSessionToken = () => Auth.sessionToken; + +export async function getIceServersList() { + try { + const iceServers = await fetchWebRTCMappedStunTurnServers(getSessionToken()); + + return iceServers || defaultIceServersList; + } catch (error) { + return defaultIceServersList; + } +} export function canGenerateIceCandidates() { + return new Promise((resolve, reject) => { + if (Session.get('canGenerateIceCandidates')) { + resolve(); + return; + } - return new Promise((resolve, reject) => { - pc = new RTCPeerConnection({iceServers: iceServersList}); - countIceCandidates = 0; + getIceServersList().catch((e) => { + reject(); + }).then((iceServersReceived) => { + const pc = new RTCPeerConnection({ iceServers: iceServersReceived }); + let countIceCandidates = 0; - try{ pc.addTransceiver('audio'); } catch (e) {} + try { pc.addTransceiver('audio'); } catch (e) { } - pc.onicecandidate = function (e) { - if(countIceCandidates) return; - if (e.candidate) { - countIceCandidates++; - resolve(); - } - } - - pc.onicegatheringstatechange = function(e) { - if(e.currentTarget.iceGatheringState == 'complete' && countIceCandidates == 0) reject(); - } - - setTimeout(function(){ - pc.close(); - if(!countIceCandidates) reject(); - }, 3000); + pc.onicecandidate = function (e) { + if (countIceCandidates) return; + if (e.candidate) { + countIceCandidates++; + Session.set('canGenerateIceCandidates', true); + resolve(); + } + } - p = pc.createOffer({offerToReceiveVideo: true}); - p.then( answer => {pc.setLocalDescription(answer) ; } ) - }); -}; + pc.onicegatheringstatechange = function (e) { + if (e.currentTarget.iceGatheringState == 'complete' && countIceCandidates == 0) reject(); + } + + setTimeout(function () { + pc.close(); + if (!countIceCandidates) reject(); + }, 5000); + + const p = pc.createOffer({ offerToReceiveVideo: true }); + p.then((answer) => { pc.setLocalDescription(answer); }); + }); + }); +} export function tryGenerateIceCandidates() { - return new Promise((resolve, reject) => { - canGenerateIceCandidates().then(ok => { - resolve(); - }).catch(e => { - navigator.mediaDevices.getUserMedia({audio: true, video: false}).then(function (stream) { - canGenerateIceCandidates().then(ok => { - resolve(); - }).catch(e => { - reject(); - }); - }).catch(e => { - reject(); - }); - }); - }); -}; \ No newline at end of file + return new Promise((resolve, reject) => { + canGenerateIceCandidates().then((ok) => { + resolve(); + }).catch((e) => { + navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(function (stream) { + canGenerateIceCandidates().then((ok) => { + resolve(); + }).catch((e) => { + reject(); + }); + }).catch((e) => { + reject(); + }); + }); + }); +} \ No newline at end of file