From cbb897b23fc2fcd470c218d95dc0aaaf33ad4a62 Mon Sep 17 00:00:00 2001 From: gcampes <gabrieldecampes@gmail.com> Date: Fri, 29 Sep 2017 10:38:10 -0300 Subject: [PATCH] audio connects. WIP --- .../api/2.0/audio/client/bridge/base.js | 6 +- .../api/2.0/audio/client/bridge/sip.js | 104 +++--------- .../api/2.0/audio/client/bridge/verto.js | 4 +- .../imports/api/2.0/bbb/index.js | 34 ++-- .../audio/audio-controls/container.jsx | 8 +- .../audio/audio-modal/component.jsx | 58 ++++--- .../audio/audio-modal/container.jsx | 47 +++++ .../components/audio/audio-modal/styles.scss | 2 +- .../audio/audio-settings/component.jsx | 52 +++++- .../audio/audio-settings/styles.scss | 29 +++- .../components/audio/audio-test/styles.scss | 2 - .../imports/ui/components/audio/container.jsx | 4 +- .../imports/ui/components/audio/service.js | 6 +- .../ui/components/modal/simple/styles.scss | 1 + .../ui/services/audio-manager/index.js | 160 +++++++++++++----- 15 files changed, 339 insertions(+), 178 deletions(-) create mode 100644 bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx diff --git a/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/base.js b/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/base.js index 58037d1e53..62e4a6d311 100644 --- a/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/base.js +++ b/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/base.js @@ -5,10 +5,6 @@ export default class BaseAudioBridge { exitAudio() { } - joinListenOnly() { + joinAudio() { } - - joinMicrophone() { - } - } diff --git a/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/sip.js b/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/sip.js index 3401d72dba..8067a3fc75 100644 --- a/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/sip.js +++ b/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/sip.js @@ -1,95 +1,47 @@ -import { makeCall } from '/imports/ui/services/api'; - import BaseAudioBridge from './base'; -const APP_CONFIG = Meteor.settings.public.app; -const MEDIA_CONFIG = Meteor.settings.public.media; - -let triedHangup = false; +const RETRY_INTERVAL = Meteor.settings.public.media.WebRTCHangupRetryInterval; export default class SIPBridge extends BaseAudioBridge { constructor(userData) { super(); this.userData = userData; - } - - joinListenOnly(stunServers, turnServers, callbackFromManager) { - console.log('Join listen only from sip'); - makeCall('listenOnlyToggle', true); - return this._joinVoiceCallSIP({ isListenOnly: true }, stunServers, turnServers, callbackFromManager); - } - - joinMicrophone(stunServers, turnServers, callbackFromManager) { - console.log('Join microphone from sip'); - return this._joinVoiceCallSIP({ isListenOnly: false }, stunServers, turnServers, callbackFromManager); - } - - // Periodically check the status of the WebRTC call, when a call has been established attempt to - // hangup, retry if a call is in progress, send the leave voice conference message to BBB - exitAudio(isListenOnly, afterExitCall = () => { }) { - // To be called when the hangup is confirmed - const hangupCallback = function () { - console.log(`Exited Voice Conference, listenOnly=${isListenOnly}`); - - // notify BBB-apps we are leaving the call if we are in listen only mode - if (isListenOnly) { - makeCall('listenOnlyToggle', false); - } + this.callStates = { + callStarted: 'started', + callEnded: 'ended', + callDisconnected: 'failed', }; + } - // Checks periodically until a call is established so we can successfully - // end the call clean state - triedHangup = false; - - // function to initiate call - const checkToHangupCall = ((context, afterExitCall = () => { }) => { - // if an attempt to hang up the call is made when the current session is not yet finished, - // the request has no effect keep track in the session if we haven't tried a hangup - if (window.getCallStatus() != null && !triedHangup) { - console.log('Attempting to hangup on WebRTC call'); - window.webrtc_hangup(hangupCallback); - - // we have hung up, prevent retries - triedHangup = true; - - if (afterExitCall) { - afterExitCall(this, APP_CONFIG.listenOnly); - } - } else { - console.log('RETRYING hangup on WebRTC call in ' + - `${MEDIA_CONFIG.WebRTCHangupRetryInterval} ms`); + joinAudio({ isListenOnly, dialplan }, stunTurnServers, managerCallback) { + return new Promise((resolve) => { + const extension = dialplan || this.userData.voiceBridge; - // try again periodically - setTimeout(checkToHangupCall, MEDIA_CONFIG.WebRTCHangupRetryInterval); + const callback = (message) => { + managerCallback(message).then(() => resolve()); } - })(this, afterExitCall); - return false; + this.callIntoConference(extension, callback, isListenOnly, stunTurnServers); + }) } - // join the conference. If listen only send the request to the server - _joinVoiceCallSIP(options, stunServers, turnServers, callbackFromManager) { - return new Promise((resolve, reject) => { - const extension = this.userData.voiceBridge; - console.log(options); - - // create voice call params - const joinCallback = function (message) { - console.log('Beginning WebRTC Conference Call', this.userData); - }; - - const stunsAndTurns = { - stun: stunServers, - turn: turnServers, + exitAudio() { + return new Promise((resolve) => { + let didTryHangup = false; + + const attemptHangup = () => { + if (window.getCallStatus()) { + console.log('Attempting to hangup on WebRTC call'); + didTryHangup = true; + window.webrtc_hangup(() => resolve()); + } else { + console.log('RETRYING hangup on WebRTC call in ' + + `${RETRY_INTERVAL} ms`); + setTimeout(attemptHangup, RETRY_INTERVAL); + } }; - const callback = (message) => { - console.log('CALLBACK FROM SIP BRIDGE'); - return callbackFromManager(message).then(response => resolve(response)) - .catch(reason => reject(reason)); - } - - callIntoConference(extension, callback, options.isListenOnly, stunsAndTurns); + return attemptHangup(); }) } } diff --git a/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/verto.js b/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/verto.js index df5a59fd37..b3637c7494 100644 --- a/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/verto.js +++ b/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/verto.js @@ -17,8 +17,8 @@ export default class VertoBridge extends BaseAudioBridge { window.vertoExitAudio(); } - joinListenOnly() { - window.vertoJoinListenOnly( + joinAudio() { + window.vertoJoinMicrophone( 'remote-media', this.voiceBridge, this.vertoUsername, diff --git a/bigbluebutton-html5/imports/api/2.0/bbb/index.js b/bigbluebutton-html5/imports/api/2.0/bbb/index.js index d8092d2188..2240365b03 100644 --- a/bigbluebutton-html5/imports/api/2.0/bbb/index.js +++ b/bigbluebutton-html5/imports/api/2.0/bbb/index.js @@ -4,10 +4,8 @@ import Users from '/imports/api/2.0/users'; import Meetings from '/imports/api/2.0/meetings'; class BBB { - getUserId() { - const userID = Auth.userID; - return userID; + return Auth.userID; } getUsername() { @@ -15,22 +13,32 @@ class BBB { } getExtension() { - const extension = Meetings.findOne().voiceProp.voiceConf; - return extension; + return Meetings.findOne().voiceProp.voiceConf; + } + + getMyRole() { + return Users.findOne({ userId: this.getUserId() }).role + } + + amIPresenter() { + return Users.findOne({ userId: this.getUserId() }).presenter + } + + getMyAvatarURL() { + return Users.findOne({ userId: this.getUserId() }).avatar; } getMyUserInfo(callback) { - const result = { + return callback({ myUserID: this.getUserId(), myUsername: this.getUsername(), - myInternalUserID: this.getUserId(), - myAvatarURL: null, - myRole: 'getMyRole', - amIPresenter: 'false', + myExternalUserID: this.getUserId(), + myAvatarURL: this.getMyAvatarURL(), + myRole: this.getMyRole(), + amIPresenter: this.amIPresenter(), voiceBridge: this.getExtension(), dialNumber: null, - }; - return callback(result); + }); } webRTCCallFailed(inEchoTest, errorcode, cause) { @@ -47,7 +55,7 @@ class BBB { } export const initBBB = () => { - if (window.BBB == undefined) { + if (!window.BBB) { window.BBB = new BBB(); } }; diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx index 8990df798b..e647adf894 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/container.jsx @@ -1,9 +1,9 @@ import React from 'react'; import { createContainer } from 'meteor/react-meteor-data'; -import PropTypes from 'prop-types'; +// import PropTypes from 'prop-types'; import { withModalMounter } from '/imports/ui/components/modal/service'; import AudioControls from './component'; -import AudioModal from '../audio-modal/component'; +import AudioModalContainer from '../audio-modal/container'; import Service from '../service'; // const propTypes = { @@ -16,8 +16,6 @@ import Service from '../service'; const AudioControlsContainer = props => <AudioControls {...props} />; -let didMountAutoJoin = false; - export default withModalMounter(createContainer(({ mountModal }) => { // const APP_CONFIG = Meteor.settings.public.app; // @@ -33,7 +31,7 @@ export default withModalMounter(createContainer(({ mountModal }) => { join: Service.isConnected(), handleToggleMuteMicrophone: () => Service.toggleMuteMicrophone(), - handleJoinAudio: () => mountModal(<AudioModal handleJoinListenOnly={() => {}} />), + handleJoinAudio: () => mountModal(<AudioModalContainer />), handleLeaveAudio: () => Service.exitAudio(), }; }, AudioControlsContainer)); diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx index c7c6d8d840..ddf71059fa 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/component.jsx @@ -3,7 +3,6 @@ import ModalBase from '/imports/ui/components/modal/base/component'; import Button from '/imports/ui/components/button/component'; import { defineMessages, injectIntl } from 'react-intl'; import styles from './styles'; -import JoinAudio from '../join-audio/component'; import AudioSettings from '../audio-settings/component'; const intlMessages = defineMessages({ @@ -59,8 +58,12 @@ class AudioModal extends Component { }; this.handleBack = this.handleBack.bind(this); - this.handleMicrophone = this.handleMicrophone.bind(this); - this.handleClose = this.handleClose.bind(this); + this.handleGoToAudioSettings = this.handleGoToAudioSettings.bind(this); + this.handleClose = props.closeModal; + this.handleJoinMicrophone = props.joinMicrophone; + this.handleJoinListenOnly = props.joinListenOnly; + this.joinEchoTest = props.joinEchoTest; + this.exitAudio = props.exitAudio; } handleBack() { @@ -69,16 +72,12 @@ class AudioModal extends Component { }); } - handleMicrophone() { + handleGoToAudioSettings() { this.setState({ settings: true, }); } - handleClose() { - console.log('handleClose'); - } - renderAudioOptions() { const { intl, @@ -92,7 +91,7 @@ class AudioModal extends Component { icon={'unmute'} circle size={'jumbo'} - onClick={this.handleMicrophone} + onClick={this.handleGoToAudioSettings} /> <Button className={styles.audioBtn} @@ -100,14 +99,30 @@ class AudioModal extends Component { icon={'listen'} circle size={'jumbo'} - onClick={() => console.log('click listen only')} + onClick={this.handleJoinListenOnly} /> </div> ); } renderAudioSettings() { - return <AudioSettings handleBack={this.handleBack} />; + const { + isConnecting, + isConnected, + isEchoTest, + } = this.props; + + return ( + <AudioSettings + handleBack={this.handleBack} + handleJoin={this.handleJoinMicrophone} + joinEchoTest={this.joinEchoTest} + exitAudio={this.exitAudio} + isConnecting={isConnecting} + isConnected={isConnected} + isEchoTest={isEchoTest} + /> + ); } render() { @@ -117,23 +132,24 @@ class AudioModal extends Component { const { intl, + isConnecting } = this.props; return ( <ModalBase overlayClassName={styles.overlay} className={styles.modal}> <header className={styles.header}> <h3 className={styles.title}>{intl.formatMessage(intlMessages.audioChoiceLabel)}</h3> - <Button - className={styles.closeBtn} - label={intl.formatMessage(intlMessages.closeLabel)} - icon={'close'} - size={'md'} - hideLabel - onClick={this.handleClose} - /> + { !settings ? + <Button + className={styles.closeBtn} + label={intl.formatMessage(intlMessages.closeLabel)} + icon={'close'} + size={'md'} + hideLabel + onClick={this.handleClose} + /> : null } </header> - - { settings ? this.renderAudioSettings() : this.renderAudioOptions() } + { settings ? this.renderAudioSettings() : this.renderAudioOptions() } </ModalBase> ); } diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx new file mode 100644 index 0000000000..8e3cc88df6 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { createContainer } from 'meteor/react-meteor-data'; +import PropTypes from 'prop-types'; +import { withModalMounter } from '/imports/ui/components/modal/service'; +// import AudioControls from './component'; +// import AudioModal from '../audio-modal/component'; +import AudioModal from './component'; +import Service from '../service'; + +// const propTypes = { +// children: PropTypes.element, +// }; +// +// const defaultProps = { +// children: null, +// }; + +const AudioModalContainer = props => <AudioModal {...props} />; + +export default withModalMounter(createContainer(({ mountModal }) => { + // const APP_CONFIG = Meteor.settings.public.app; + // + // const { autoJoinAudio } = APP_CONFIG; + // const { isConnected, isConnecting, isListenOnly } = Service.getStats(); + // let shouldShowMute = isConnected && !isListenOnly; + // let shouldShowUnmute = isConnected && !isListenOnly && isMuted; + // let shouldShowJoin = !isConnected; + + return { + closeModal: () => mountModal(null), + joinMicrophone: () => { + Service.exitAudio().then(() => Service.joinMicrophone()) + .then(() => mountModal(null)); + }, + joinListenOnly: () => { + Service.joinMicrophone().then(a => mountModal(null)) + }, + joinEchoTest: () => Service.joinEchoTest(), + exitAudio: () => Service.exitAudio(), + isConnecting: Service.isConnecting(), + isConnected: Service.isConnected(), + isEchoTest: Service.isEchoTest(), + }; +}, AudioModalContainer)); + +// AudioControlsContainer.propTypes = propTypes; +// AudioControlsContainer.defaultProps = defaultProps; diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/styles.scss b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/styles.scss index 4c9734b572..041b138d98 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/styles.scss @@ -51,7 +51,7 @@ &:hover{ background-color: $color-white; i{ - color: $color-danger; + color: $color-primary; } } } diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-settings/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-settings/component.jsx index 39be540dca..9c80ab849e 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-settings/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-settings/component.jsx @@ -34,6 +34,10 @@ const intlMessages = defineMessages({ id: 'app.audio.audioSettings.microphoneStreamLabel', description: 'Label for stream volume', }, + enterSessionLabel: { + id: 'app.audio.enterSessionLabel', + description: 'enter session button label', + }, }); class AudioSettings extends React.Component { @@ -43,8 +47,10 @@ class AudioSettings extends React.Component { this.chooseAudio = this.chooseAudio.bind(this); this.handleInputChange = this.handleInputChange.bind(this); this.handleOutputChange = this.handleOutputChange.bind(this); - this.handleClose = this.handleClose.bind(this); this.handleBack = props.handleBack; + this.handleJoin = props.handleJoin; + this.joinEchoTest = props.joinEchoTest; + this.exitAudio = props.exitAudio; this.state = { inputDeviceId: undefined, @@ -52,6 +58,20 @@ class AudioSettings extends React.Component { }; } + componentDidMount() { + this.joinEchoTest(); + } + + componentWillUnmount() { + const { + isEchoTest, + } = this.props; + + if (isEchoTest) { + this.exitAudio(); + } + } + chooseAudio() { this.props.changeMenu(this.props.JOIN_AUDIO); } @@ -63,23 +83,26 @@ class AudioSettings extends React.Component { }); } - handleOutputChange(deviceId) { + handleOutputChange(deviceId, device) { + console.log(device); console.log(`OUTPUT DEVICE CHANGED: ${deviceId}`); this.setState({ outputDeviceId: deviceId, }); } - handleClose() { - this.setState({ isOpen: false }); - this.props.mountModal(null); - } + // handleClose() { + // this.setState({ isOpen: false }); + // this.props.mountModal(null); + // } render() { const { + isConnecting, intl, } = this.props; + // return this.renderCalling(); return ( <div> <div className={styles.form}> @@ -143,15 +166,26 @@ class AudioSettings extends React.Component { className={styles.backBtn} label={intl.formatMessage(intlMessages.backLabel)} size={'md'} - ghost={true} color={'primary'} onClick={this.handleBack} + disabled={isConnecting} + ghost /> - <EnterAudioContainer isFullAudio /> + <Button + size={'md'} + color={'primary'} + onClick={this.handleJoin} + disabled={isConnecting} + > + <span className={ isConnecting ? styles.calling : null}> + { isConnecting ? 'Calling echo test' : intl.formatMessage(intlMessages.enterSessionLabel)} + </span> + </Button> </div> </div> - ); + ) } + } export default withModalMounter(injectIntl(AudioSettings)); diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-settings/styles.scss b/bigbluebutton-html5/imports/ui/components/audio/audio-settings/styles.scss index 1687effc2d..c223a09435 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-settings/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-settings/styles.scss @@ -34,9 +34,9 @@ .labelSmall { color: black; - font-size: 0.7rem; + font-size: 0.85rem; font-weight: 600; - margin-bottom: 0.3rem; + margin-bottom: 0.5rem; } .formElement { @@ -92,3 +92,28 @@ left:50%; transform: translate(-50%, 0); } + +.calling:after { + overflow: hidden; + display: inline-block; + vertical-align: bottom; + -webkit-animation: ellipsis steps(4,end) 900ms infinite; + animation: ellipsis steps(4,end) 900ms infinite; + content: "\2026"; /* ascii code for the ellipsis character */ + width: 0; + margin-right: 1.25em; +} + +@keyframes ellipsis { + to { + width: 1.25em; + margin-right: 0; + } +} + +@-webkit-keyframes ellipsis { + to { + width: 1.25em; + margin-right: 0; + } +} diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-test/styles.scss b/bigbluebutton-html5/imports/ui/components/audio/audio-test/styles.scss index 038a84ab9e..d2800c86f1 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/audio-test/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/audio/audio-test/styles.scss @@ -1,8 +1,6 @@ @import "/imports/ui/stylesheets/variables/_all"; .testAudioBtn { - border: none; - padding-left: 0; background-color: transparent; color: $color-primary; font-weight: normal; diff --git a/bigbluebutton-html5/imports/ui/components/audio/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/container.jsx index 845ac98ee0..e6fc03db8f 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/audio/container.jsx @@ -4,7 +4,7 @@ import { withModalMounter } from '/imports/ui/components/modal/service'; import PropTypes from 'prop-types'; import Service from './service'; import Audio from './component'; -import AudioModal from './audio-modal/component'; +import AudioModalContainer from './audio-modal/container'; const propTypes = { children: PropTypes.element, @@ -31,7 +31,7 @@ export default withModalMounter(createContainer(({ mountModal }) => { init: () => { Service.init(); if (!autoJoinAudio || didMountAutoJoin) return; - mountModal(<AudioModal handleJoinListenOnly={Service.joinListenOnly} />); + mountModal(<AudioModalContainer />); didMountAutoJoin = true; }, }; diff --git a/bigbluebutton-html5/imports/ui/components/audio/service.js b/bigbluebutton-html5/imports/ui/components/audio/service.js index a4144bc7e7..e657d961ef 100644 --- a/bigbluebutton-html5/imports/ui/components/audio/service.js +++ b/bigbluebutton-html5/imports/ui/components/audio/service.js @@ -6,6 +6,7 @@ import Meetings from '/imports/api/2.0/meetings'; const init = () => { console.log('Running audio service init.'); const userId = Auth.userID; + const sessionToken = Auth.sessionToken; const User = Users.findOne({ userId }); const username = User.name; const Meeting = Meetings.findOne({ meetingId: User.meetingId }); @@ -16,6 +17,7 @@ const init = () => { const userData = { userId, + sessionToken, username, voiceBridge, microphoneLockEnforced, @@ -27,8 +29,9 @@ const init = () => { export default { init, exitAudio: () => AudioManager.exitAudio(), - joinListenOnly: () => AudioManager.joinAudio(true), + joinListenOnly: () => AudioManager.joinAudio({ isListenOnly: true }), joinMicrophone: () => AudioManager.joinAudio(), + joinEchoTest: () => AudioManager.joinAudio({ isEchoTest: true }), toggleMuteMicrophone: () => AudioManager.toggleMuteMicrophone(), isConnected: () => AudioManager.isConnected, isMuted: () => AudioManager.isMuted, @@ -36,4 +39,5 @@ export default { isListenOnly: () => AudioManager.isListenOnly, inputDeviceId: () => AudioManager.inputDeviceId, outputDeviceId: () => AudioManager.outputDeviceId, + isEchoTest: () => AudioManager.isEchoTest, } diff --git a/bigbluebutton-html5/imports/ui/components/modal/simple/styles.scss b/bigbluebutton-html5/imports/ui/components/modal/simple/styles.scss index baa112c889..b235b549c3 100644 --- a/bigbluebutton-html5/imports/ui/components/modal/simple/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/modal/simple/styles.scss @@ -5,6 +5,7 @@ display: flex; flex-direction: column; padding: ($line-height-computed / 2) $line-height-computed; + box-shadow : 0px 0px 15px rgba(0, 0, 0, 0.5); } .content { diff --git a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js index 69eeb03288..1388fa582b 100644 --- a/bigbluebutton-html5/imports/ui/services/audio-manager/index.js +++ b/bigbluebutton-html5/imports/ui/services/audio-manager/index.js @@ -1,22 +1,13 @@ import { Tracker } from 'meteor/tracker'; +import { makeCall } from '/imports/ui/services/api'; +import VertoBridge from '/imports/api/2.0/audio/client/bridge/verto'; +import SIPBridge from '/imports/api/2.0/audio/client/bridge/sip'; -const joinAudioMicrophone = (cb) => { - this.setTimeout(() => { - console.log('joining microphone mock...'); - cb(); - }, 1000) -} - -const exitAudio = (cb) => { - setTimeout(() => { - console.log('exit audio mock...'); - cb(); - }, 1000); -} +const USE_SIP = Meteor.settings.public.media.useSIPAudio; const toggleMuteMicrophone = (cb) => { cb(); -} +}; // const collection = new Mongo.Collection(null); @@ -26,7 +17,8 @@ class AudioManager { isMuted: false, isConnected: false, isConnecting: false, - isListenOnly: null, + isListenOnly: false, + isEchoTest: false, error: null, inputDeviceId: null, outputDeviceId: null, @@ -34,43 +26,61 @@ class AudioManager { } defineProperties(obj) { - Object.keys(obj).forEach(key => { - let originalKey = `_${key}`; - this[originalKey] = { + Object.keys(obj).forEach((key) => { + const privateKey = `_${key}`; + this[privateKey] = { value: obj[key], - tracker: new Tracker.Dependency - } + tracker: new Tracker.Dependency, + }; Object.defineProperty(this, key, { set: (value) => { - this[originalKey].value = value; - this[originalKey].tracker.changed(); - // console.log('set', originalKey, value); - // this.update(originalKey, value); + this[privateKey].value = value; + this[privateKey].tracker.changed(); + // console.log('set', privateKey, value); + // this.update(privateKey, value); }, get: () => { - this[originalKey].tracker.depend(); - return this[originalKey].value; - // console.log('get', originalKey, collection.findOne({})[originalKey]); - // return collection.findOne({})[originalKey]; - } - }) - }) + this[privateKey].tracker.depend(); + return this[privateKey].value; + // console.log('get', privateKey, collection.findOne({})[privateKey]); + // return collection.findOne({})[privateKey]; + }, + }); + }); // return collection.insert(obj); } - joinAudio(isListenOnly) { + joinAudio(options = {}, callbacks = {}) { + const { + isListenOnly, + isEchoTest, + } = options; + console.log('joinAudio', this, isListenOnly); this.isConnecting = true; - this.isListenOnly = isListenOnly - - joinAudioMicrophone(this.onAudioJoin.bind(this)); + this.isListenOnly = isListenOnly; + this.isEchoTest = isEchoTest; + this.callbacks = callbacks; + + const callOptions = { + isListenOnly, + dialplan: isEchoTest ? '9196' : null, + } + + return this.fetchStunTurn().then((stunTurnServers) => + this.bridge.joinAudio(callOptions, + stunTurnServers, + this.callStateCallback.bind(this)) + ).catch((error) => { + console.error('error', error) + }) } exitAudio() { console.log('exitAudio', this); - exitAudio(this.onAudioExit.bind(this)); + return this.bridge.exitAudio() } toggleMuteMicrophone() { @@ -78,18 +88,34 @@ class AudioManager { toggleMuteMicrophone(this.onToggleMicrophoneMute.bind(this)); } + callbackToAudioBridge(message) { + console.log('This is the Manager Callback', message); + } + //---------------------------- onAudioJoin() { - this.isConnected = true; + if (!this.isEchoTest) { + this.isConnected = true; + } this.isConnecting = false; + + if (this.isListenOnly) { + makeCall('listenOnlyToggle', true); + } + console.log('onAudioJoin', this); } onAudioExit() { this.isConnected = false; - this.isListenOnly = null; - this.isMuted = false; + + if (this.isListenOnly) { + makeCall('listenOnlyToggle', false); + } else if (this.isEchoTest) { + this.isEchoTest = false; + } + console.log('onAudioExit', this); } @@ -104,6 +130,62 @@ class AudioManager { // const modifier = { $set: { [key]: value }}; // collection.update(query, modifier); // } + + callStateCallback({ status }) { + console.log('CALLSTATECALLBACK =====================', status); + return new Promise((resolve) => { + const { + callStarted, + callEnded, + callDisconnected, + } = this.bridge.callStates; + + if (status === callStarted) { + this.onAudioJoin(); + resolve(callStarted); + } else if (status === callEnded) { + this.onAudioExit(); + } else if (status === callDisconnected) { + this.onAudioExit(); + } + }) + } + + fetchStunTurn() { + return new Promise(async (resolve, reject) => { + const url = `/bigbluebutton/api/stuns?sessionToken=${this.userData.sessionToken}`; + + let response = await fetch(url) + .then(response => response.json()) + .then(({ response, stunServers, turnServers}) => { + console.log(response, stunServers, turnServers); + return new Promise((resolve) => { + if (response) { + resolve({ error: 404, stun: [], turn: [] }); + } + console.log('krappa'); + resolve({ + stun: stunServers.map(server => server.url), + turn: turnServers.map(server => server.url), + }); + }); + }); + + console.log(response); + if(response.error) return reject(`Could not fetch the stuns/turns servers!`); + resolve(response); + }) + } + + set userData(value) { + console.log('set user data'); + this._userData = value; + this.bridge = USE_SIP ? new SIPBridge(value) : new VertoBridge(value); + } + + get userData() { + return this._userData; + } } const audioManager = new AudioManager(); -- GitLab