bigbluebutton-Github/bigbluebutton-html5/imports/api/2.0/audio/client/manager/index.js
2017-07-31 13:54:18 -07:00

234 lines
6.4 KiB
JavaScript
Executable File

import Auth from '/imports/ui/services/auth';
import BaseAudioBridge from '../bridge/base';
import VertoBridge from '../bridge/verto';
import SIPBridge from '../bridge/sip';
class CallStates {
static get init() {
return 'initialized state';
}
static get echo() {
return 'do echo test state';
}
static get callIntoEcho() {
return 'calling into echo test state';
}
static get inEchoTest() {
return 'in echo test state';
}
static get joinVoiceConference() {
return 'join voice conference state';
}
static get callIntoConference() {
return 'calling into conference state';
}
static get inConference() {
return 'in conference state';
}
static get transferToConference() {
return 'joining from echo into conference state';
}
static get echoTestFailed() {
return 'echo test failed state';
}
static get callToListenOnly() {
return 'call to listen only state';
}
static get connectToListenOnly() {
return 'connecting to listen only state';
}
static get inListenOnly() {
return 'in listen only state';
}
static get reconnecting() {
return 'reconecting';
}
}
const ErrorCodes = {
CODE_1001: '1001',
CODE_1002: '1002',
CODE_1003: '1003',
CODE_1004: '1004',
CODE_1005: '1005',
CODE_1006: '1006',
CODE_1007: '1007',
CODE_1008: '1008',
CODE_1009: '1009',
CODE_1010: '1010',
CODE_1011: '1011',
};
const AudioErrorCodes = Object.freeze(ErrorCodes);
// manages audio calls and audio bridges
class AudioManager {
init(userData) {
// this check ensures changing locales will not rerun init
if (this.currentState !== undefined) {
return;
}
const MEDIA_CONFIG = Meteor.settings.public.media;
const audioBridge = MEDIA_CONFIG.useSIPAudio
? new SIPBridge(userData)
: new VertoBridge(userData);
if (!(audioBridge instanceof BaseAudioBridge)) {
throw 'Audio Bridge not compatible';
}
this.bridge = audioBridge;
this.isListenOnly = false;
this.microphoneLockEnforced = userData.microphoneLockEnforced;
this.callStates = CallStates;
this.currentState = this.callStates.init;
callbackToAudioBridge = function (message) {
switch (message.status) {
case 'failed': {
this.currentState = this.callStates.init;
const audioFailed = new CustomEvent('bbb.webrtc.failed', {
detail: {
status: 'Failed',
errorCode: message.errorcode,
},
});
window.dispatchEvent(audioFailed);
break;
}
case 'mediafail': {
const mediaFailed = new CustomEvent('bbb.webrtc.mediaFailed', {
detail: {
status: 'MediaFailed',
},
});
window.dispatchEvent(mediaFailed);
break;
}
case 'mediasuccess':
case 'started': {
const connected = new CustomEvent('bbb.webrtc.connected', {
detail: {
status: 'started',
},
});
window.dispatchEvent(connected);
break;
}
}
};
}
getCurrentState() {
return this.currentState;
}
exitAudio() {
this.bridge.exitAudio(this.isListenOnly);
this.currentState = this.callStates.init;
}
joinAudio(listenOnly) {
AudioManager.fetchServers().then(({ error, stunServers, turnServers }) => {
if (error || error !== undefined) {
// We need to alert the user about this problem by some gui message.
console.error("Couldn't fetch the stuns/turns servers!");
AudioManager.stunTurnServerFail();
return;
}
if (listenOnly || this.microphoneLockEnforced) {
this.isListenOnly = true;
this.bridge.joinListenOnly(stunServers, turnServers, callbackToAudioBridge.bind(this));
// TODO: remove line below after echo test implemented, use webRTCCallStarted instead
this.currentState = this.callStates.inListenOnly;
} else {
this.bridge.joinMicrophone(stunServers, turnServers, callbackToAudioBridge.bind(this));
// TODO: remove line below after echo test implemented, use webRTCCallStarted instead
this.currentState = this.callStates.inConference;
}
});
}
transferToConference() {
// TODO: transfer from initialized state
// TODO: transfer from echo test to conference
// this.bridge.transferToConference();
}
webRTCCallStarted(inEchoTest) {
if (this.isListenOnly) {
this.currentState = this.callStates.inListenOnly;
}
this.currentState = this.callStates.inConference;
}
webRTCCallFailed(inEchoTest, errorcode, cause) {
if (this.currentState !== this.CallStates.reconecting) {
this.currentState = this.CallStates.reconecting;
}
}
getMicId() {
// Placeholder, will get the microphone ID for switching input device
// this.bridge.getMicId();
}
setMicId() {
// Placeholder, will set the microphone ID for switching input device
// this.bridge.setMicId();
}
getSpeakerId() {
// Placeholder, will get the speaker ID for switching output device
// this.bridge.getSpeakerId();
}
setSpeakerId() {
// Placeholder, will set the speaker ID for switching output device
// this.bridge.setSpeakerId();
}
getActiveMic() {
// Placeholder, will detect active input hardware
// this.bridge.getActiveMic();
}
stunTurnServerFail() {
const audioFailed = new CustomEvent('bbb.webrtc.failed', {
detail: {
status: 'Failed',
errorCode: AudioErrorCodes.CODE_1009,
},
});
window.dispatchEvent(audioFailed);
}
// We use on the SIP an String Array, while in the server, it comes as
// an Array of objects, we need to map from Array<Object> to Array<String>
static mapToArray({ response, stunServers, turnServers }) {
const promise = new Promise((resolve) => {
if (response) {
resolve({ error: 404, stunServers: [], turnServers: [] });
}
resolve({
stunServers: stunServers.map(server => server.url),
turnServers: turnServers.map(server => server.url),
});
});
return promise;
}
static fetchServers() {
const url = `/bigbluebutton/api/stuns?sessionToken=${Auth.sessionToken}`;
return fetch(url)
.then(response => response.json())
.then(json => AudioManager.mapToArray(json));
}
}
const AudioManagerSingleton = new AudioManager();
export default AudioManagerSingleton;
export { AudioErrorCodes };