diff --git a/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js b/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js index 129e02f5244342f310b46fa1e5433ba9168effc7..4889aad28ad3dfa23a0f9dc8c8c3cb5e4d6bf910 100644 --- a/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js +++ b/bigbluebutton-html5/imports/api/audio/client/bridge/sip.js @@ -56,7 +56,9 @@ export default class SIPBridge extends BaseAudioBridge { this.protocol = window.document.location.protocol; this.hostname = window.document.location.hostname; - const causes = window.SIP.C.causes; + const { + causes, + } = window.SIP.C; this.errorCodes = { [causes.REQUEST_TIMEOUT]: this.baseErrorCodes.REQUEST_TIMEOUT, @@ -76,14 +78,14 @@ export default class SIPBridge extends BaseAudioBridge { this.callback = callback; return this.doCall({ callExtension, isListenOnly, inputStream }) - .catch((reason) => { - callback({ - status: this.baseCallStates.failed, - error: this.baseErrorCodes.GENERIC_ERROR, - bridgeError: reason, - }); - reject(reason); - }); + .catch((reason) => { + callback({ + status: this.baseCallStates.failed, + error: this.baseErrorCodes.GENERIC_ERROR, + bridgeError: reason, + }); + reject(reason); + }); }); } @@ -108,9 +110,9 @@ export default class SIPBridge extends BaseAudioBridge { this.callOptions = options; return fetchStunTurnServers(sessionToken) - .then(this.createUserAgent.bind(this)) - .then(this.inviteUserAgent.bind(this)) - .then(this.setupEventHandlers.bind(this)); + .then(this.createUserAgent.bind(this)) + .then(this.inviteUserAgent.bind(this)) + .then(this.setupEventHandlers.bind(this)); } transferCall(onTransferSuccess) { @@ -123,7 +125,8 @@ export default class SIPBridge extends BaseAudioBridge { this.callback({ status: this.baseCallStates.failed, error: this.baseErrorCodes.REQUEST_TIMEOUT, - bridgeError: 'Timeout on call transfer' }); + bridgeError: 'Timeout on call transfer', + }); reject(this.baseErrorCodes.REQUEST_TIMEOUT); }, CALL_TRANSFER_TIMEOUT); @@ -207,8 +210,6 @@ export default class SIPBridge extends BaseAudioBridge { turnServers: turn, }); - console.log('USERAGENT', userAgent); - userAgent.removeAllListeners('connected'); userAgent.removeAllListeners('disconnected'); @@ -222,7 +223,8 @@ export default class SIPBridge extends BaseAudioBridge { this.callback({ status: this.baseCallStates.failed, error: this.baseErrorCodes.CONNECTION_ERROR, - bridgeError: 'User Agent Disconnected' }); + bridgeError: 'User Agent Disconnected', + }); reject(this.baseErrorCodes.CONNECTION_ERROR); }; @@ -262,8 +264,6 @@ export default class SIPBridge extends BaseAudioBridge { }, }; - console.log('LOLLL', options, `sip:${callExtension}@${hostname}`); - return userAgent.invite(`sip:${callExtension}@${hostname}`, options); } @@ -287,8 +287,8 @@ export default class SIPBridge extends BaseAudioBridge { } const mappedCause = cause in this.errorCodes ? - this.errorCodes[cause] : - this.baseErrorCodes.GENERIC_ERROR; + this.errorCodes[cause] : + this.baseErrorCodes.GENERIC_ERROR; return this.callback({ status: this.baseCallStates.failed, @@ -312,7 +312,7 @@ export default class SIPBridge extends BaseAudioBridge { const device = mediaDevices.find(d => d.label === deviceLabel); return this.changeInputDevice(device.deviceId); }); - } + }; return navigator.mediaDevices.getUserMedia({ audio: true }).then(handleMediaSuccess); } @@ -328,7 +328,7 @@ export default class SIPBridge extends BaseAudioBridge { media.inputDevice.scriptProcessor = null; media.inputDevice.source = null; return this.changeInputDevice(value); - } + }; return media.inputDevice.audioContext.close().then(handleAudioContextCloseSuccess); } @@ -341,7 +341,7 @@ export default class SIPBridge extends BaseAudioBridge { media.inputDevice.id = value; media.inputDevice.scriptProcessor = media.inputDevice.audioContext - .createScriptProcessor(2048, 1, 1); + .createScriptProcessor(2048, 1, 1); media.inputDevice.source = null; const constraints = { @@ -352,12 +352,13 @@ export default class SIPBridge extends BaseAudioBridge { const handleMediaSuccess = (mediaStream) => { media.inputDevice.stream = mediaStream; - media.inputDevice.source = media.inputDevice.audioContext.createMediaStreamSource(mediaStream); + media.inputDevice.source = media.inputDevice.audioContext + .createMediaStreamSource(mediaStream); media.inputDevice.source.connect(media.inputDevice.scriptProcessor); media.inputDevice.scriptProcessor.connect(media.inputDevice.audioContext.destination); return this.media.inputDevice; - } + }; return navigator.mediaDevices.getUserMedia(constraints).then(handleMediaSuccess); } @@ -365,8 +366,6 @@ export default class SIPBridge extends BaseAudioBridge { async changeOutputDevice(value) { const audioContext = document.querySelector(MEDIA_TAG); - console.log(audioContext); - if (audioContext.setSinkId) { try { await audioContext.setSinkId(value); diff --git a/bigbluebutton-html5/imports/ui/components/audio/permissions-overlay/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/permissions-overlay/component.jsx index 6fa31d32581b06b01f6b3d349d13e9467996ff0c..a086055ea7f25db818f29bf96fc51941c4bc0e6e 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/permissions-overlay/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/permissions-overlay/component.jsx @@ -1,43 +1,65 @@ import React, { Component } from 'react'; +import { injectIntl, intlShape, defineMessages } from 'react-intl'; import styles from './styles'; -export default class PermissionsOverlay extends Component { +const propTypes = { + intl: intlShape.isRequired, +}; + +const intlMessages = defineMessages({ + title: { + id: 'app.audio.permissionsOverlay.title', + description: 'Title for the overlay', + }, + hint: { + id: 'app.audio.permissionsOverlay.hint', + description: 'Hint for the overlay', + }, +}); + +class PermissionsOverlay extends Component { constructor(props) { super(props); - const styles = { - 'Chrome': { + const broswerStyles = { + Chrome: { top: '145px', left: '380px', }, - 'Firefox': { + Firefox: { top: '210px', - left: '385px', + left: '605px', }, - } + }; const browser = window.bowser.name; this.state = { styles: { - top: styles[browser].top, - left: styles[browser].left, - } - } - - console.log(this.state); + top: broswerStyles[browser].top, + left: broswerStyles[browser].left, + }, + }; } render() { + const { + intl, + } = this.props; + return ( <div className={styles.overlay}> <div style={this.state.styles} className={styles.hint}> - Allow BigBlueButton to use your Microphone + { intl.formatMessage(intlMessages.title) } <small> - We need you to allow us to use your microphone in order to join you to the voice conference :) + { intl.formatMessage(intlMessages.hint) } </small> </div> </div> - ) + ); } } + +PermissionsOverlay.propTypes = propTypes; + +export default injectIntl(PermissionsOverlay); diff --git a/bigbluebutton-html5/imports/ui/components/audio/service.js b/bigbluebutton-html5/imports/ui/components/audio/service.js index be4ed225cebd5eff27f260193968c207d4f3b4c3..81fc11b73f6d7bfa975765acfe7f99bf2853b8eb 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/service.js +++ b/bigbluebutton-html5/imports/ui/components/audio/service.js @@ -5,6 +5,7 @@ import Meetings from '/imports/api/meetings'; import VoiceUsers from '/imports/api/voice-users'; const init = (messages) => { + if (AudioManager.initialized) return; const meetingId = Auth.meetingID; const userId = Auth.userID; const sessionToken = Auth.sessionToken; diff --git a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js index da1a14946d37ea1c00fda2576daca109ff49f2f2..6bab22deee57027321f94b3e51a063af57b471a7 100644 --- a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js +++ b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js @@ -2,7 +2,7 @@ import { Tracker } from 'meteor/tracker'; import { makeCall } from '/imports/ui/services/api'; import VertoBridge from '/imports/api/audio/client/bridge/verto'; import Auth from '/imports/ui/services/auth'; -import Users from '/imports/api/users'; +import VoiceUsers from '/imports/api/voice-users'; import SIPBridge from '/imports/api/audio/client/bridge/sip'; import { notify } from '/imports/ui/services/notification'; @@ -33,12 +33,22 @@ class AudioManager { error: null, outputDeviceId: null, }); + + const query = VoiceUsers.find({ intId: Auth.userID }); + + query.observeChanges({ + changed: (id, fields) => { + if (fields.muted === this.isMuted) return; + this.isMuted = fields.muted; + }, + }); } init(userData, messages) { this.bridge = USE_SIP ? new SIPBridge(userData) : new VertoBridge(userData); this.userData = userData; this.messages = messages; + this.initialized = true; } defineProperties(obj) { @@ -87,18 +97,16 @@ class AudioManager { extension: isEchoTest ? ECHO_TEST_NUMBER : null, inputStream: this.isListenOnly ? this.createListenOnlyStream() : this.inputStream, }; - - console.log('LISTENONLY', callOptions.inputStream); return this.bridge.joinAudio(callOptions, this.callStateCallback.bind(this)); - } + }; if (this.devicesInitialized) return doCall(); return Promise.all([ - // this.setDefaultInputDevice(), - // this.setDefaultOutputDevice(), + this.setDefaultInputDevice(), + this.setDefaultOutputDevice(), ]).then(doCall) - .catch(err => { + .catch((err) => { clearTimeout(permissionsTimeout); this.isWaitingPermissions = false; this.error = err; @@ -120,9 +128,7 @@ class AudioManager { } toggleMuteMicrophone() { - makeCall('toggleSelfVoice').then(() => { - this.onToggleMicrophoneMute(); - }); + makeCall('toggleSelfVoice'); } onAudioJoin() { @@ -151,10 +157,6 @@ class AudioManager { this.isEchoTest = false; } - onToggleMicrophoneMute() { - this.isMuted = !this.isMuted; - } - callStateCallback(response) { return new Promise((resolve) => { const { @@ -189,8 +191,8 @@ class AudioManager { } this.listenOnlyAudioContext = window.AudioContext ? - new window.AudioContext : - new window.webkitAudioContext(); + new window.AudioContext() : + new window.webkitAudioContext(); return this.listenOnlyAudioContext.createMediaStreamDestination().stream; } @@ -209,19 +211,20 @@ class AudioManager { return Promise.resolve(inputDevice); }; - const handleChangeInputDeviceError = () => { - return Promise.reject({ + const handleChangeInputDeviceError = () => + Promise.reject({ type: 'MEDIA_ERROR', message: this.messages.error.MEDIA_ERROR, }); - }; if (!deviceId) { - return this.bridge.setDefaultInputDevice().then(handleChangeInputDeviceSuccess) - .catch(handleChangeInputDeviceError); + return this.bridge.setDefaultInputDevice() + .then(handleChangeInputDeviceSuccess) + .catch(handleChangeInputDeviceError); } - return this.bridge.changeInputDevice(deviceId).then(handleChangeInputDeviceSuccess) - .catch(handleChangeInputDeviceError); + return this.bridge.changeInputDevice(deviceId) + .then(handleChangeInputDeviceSuccess) + .catch(handleChangeInputDeviceError); } async changeOutputDevice(deviceId) { @@ -251,9 +254,11 @@ class AudioManager { } notify(message) { - notify(message, - this.error ? 'error' : 'info', - this.isListenOnly ? 'audio_on' : 'unmute'); + notify( + message, + this.error ? 'error' : 'info', + this.isListenOnly ? 'audio_on' : 'unmute', + ); } } diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/private/locales/en.json index 301fef54a47d32794f436b64299110f2125cfa5e..e44339cf07cc4e3cca5e8a847820390bea4c97b0 100644 --- a/bigbluebutton-html5/private/locales/en.json +++ b/bigbluebutton-html5/private/locales/en.json @@ -247,6 +247,8 @@ "app.audio.audioSettings.retryLabel": "Retry", "app.audio.listenOnly.backLabel": "Back", "app.audio.listenOnly.closeLabel": "Close", + "app.audio.permissionsOverlay.title": "Allow BigBlueButton to use your Media Devices", + "app.audio.permissionsOverlay.hint": "We need you to allow us to use your Media Devices in order to join you to the voice conference :)", "app.error.kicked": "You have been kicked out of the meeting", "app.error.meeting.ended": "You have logged out of the conference", "app.dropdown.close": "Close",