bigbluebutton-Github/bigbluebutton-html5/imports/api/2.0/audio/client/bridge/sip.js

221 lines
6.7 KiB
JavaScript
Raw Normal View History

2017-08-01 04:54:18 +08:00
import BaseAudioBridge from './base';
import { Tracker } from 'meteor/tracker';
import VoiceUsers from '/imports/api/2.0/voice-users';
2017-08-01 04:54:18 +08:00
2017-10-12 05:04:10 +08:00
const STUN_TURN_FETCH_URL = Meteor.settings.public.media.stunTurnServersFetchAddress;
const MEDIA_TAG = Meteor.settings.public.media.mediaTag;
const handleStunTurnResponse = ({ result, stunServers, turnServers }) =>
new Promise((resolve) => {
if (result) {
resolve({ error: 404, stun: [], turn: [] });
}
resolve({
stun: stunServers.map(server => server.url),
turn: turnServers.map(server => server.url),
});
});
const fetchStunTurnServers = sessionToken =>
new Promise(async (resolve, reject) => {
console.log('FETCHSTUNTURN');
const url = `${STUN_TURN_FETCH_URL}?sessionToken=${sessionToken}`;
const response = await fetch(url)
.then(res => res.json())
.then(json => handleStunTurnResponse(json));
if (response.error) return reject('Could not fetch the stuns/turns servers!');
return resolve(response);
});
const inviteUserAgent = (voiceBridge, server, userAgent, inputStream) => {
console.log('INVITEUSERAGENT');
const options = {
media: {
stream: inputStream,
constraints: {
audio: true,
video: false,
},
render: {
remote: document.querySelector(MEDIA_TAG),
},
},
RTCConstraints: {
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: false,
},
},
};
console.log(voiceBridge, server, userAgent);
return userAgent.invite(`sip:${voiceBridge}@${server}`, options);
};
2017-07-24 22:15:46 +08:00
export default class SIPBridge extends BaseAudioBridge {
constructor(userData) {
2017-10-12 05:04:10 +08:00
super(userData);
2017-09-30 04:42:34 +08:00
this.isConnected = false;
2017-10-12 05:04:10 +08:00
this.errorCodes = {
'Request Timeout': this.baseErrorCodes.REQUEST_TIMEOUT,
'Invalid Target': this.baseErrorCodes.INVALID_TARGET,
'Connection Error': this.baseErrorCodes.CONNECTION_ERROR,
};
2017-09-29 21:38:10 +08:00
}
2017-07-24 22:15:46 +08:00
2017-09-30 04:42:34 +08:00
joinAudio({ isListenOnly, extension, inputStream }, managerCallback) {
2017-10-05 04:49:11 +08:00
return new Promise((resolve, reject) => {
const callExtension = extension + this.userData.voiceBridge || this.userData.voiceBridge;
2017-07-24 22:15:46 +08:00
2017-09-29 21:38:10 +08:00
const callback = (message) => {
2017-10-12 05:04:10 +08:00
managerCallback(message).then(resolve);
2017-09-30 04:42:34 +08:00
};
2017-07-24 22:15:46 +08:00
2017-10-12 05:30:38 +08:00
this.callback = callback;
return this.doCall({ callExtension, isListenOnly, inputStream }, callback)
.catch((reason) => {
2017-10-12 05:04:10 +08:00
callback({ status: this.baseCallStates.failed, error: reason });
2017-10-05 04:49:11 +08:00
reject(reason);
});
2017-09-30 04:42:34 +08:00
});
2017-07-24 22:15:46 +08:00
}
transferCall(onTransferStart, onTransferSuccess) {
return new Promise((resolve) => {
onTransferStart();
this.currentSession.dtmf(1);
Tracker.autorun((c) => {
const selector = { meetingId: this.userData.meetingId, intId: this.userData.userId };
const query = VoiceUsers.find(selector);
console.log(selector);
window.Kappa = query;
query.observeChanges({
changed: (id, fields) => {
console.log('changed', fields);
if (fields.joined) {
console.log('LUL', fields.joined);
onTransferSuccess();
c.stop();
resolve();
}
},
});
});
})
}
2017-09-29 21:38:10 +08:00
exitAudio() {
return new Promise((resolve) => {
2017-10-05 04:49:11 +08:00
this.currentSession.on('bye', () => {
this.hangup = true;
resolve();
});
this.currentSession.bye();
2017-09-30 04:42:34 +08:00
});
}
doCall({ isListenOnly, callExtension, inputStream }, callback) {
2017-09-30 04:42:34 +08:00
const {
userId,
username,
sessionToken,
} = this.userData;
const server = window.document.location.hostname;
const callerIdName = `${userId}-bbbID-${username}`;
2017-10-12 05:04:10 +08:00
return fetchStunTurnServers(sessionToken)
.then(stunTurnServers =>
this.createUserAgent(server, callerIdName, stunTurnServers))
.then(userAgent =>
2017-10-12 05:04:10 +08:00
inviteUserAgent(callExtension, server, userAgent, inputStream))
.then(currentSession =>
this.setupEventHandlers(currentSession, callback));
2017-09-30 04:42:34 +08:00
}
createUserAgent(server, username, { stun, turn }) {
console.log('CREATEUSERAGENT');
return new Promise((resolve, reject) => {
const protocol = document.location.protocol;
console.log('username', username);
2017-09-30 04:42:34 +08:00
this.userAgent = new window.SIP.UA({
uri: `sip:${encodeURIComponent(username)}@${server}`,
2017-10-12 05:04:10 +08:00
wsServers: `${(protocol === 'https:' ? 'wss://' : 'ws://')}${server}/ws`,
log: {
builtinEnabled: false,
},
2017-09-30 04:42:34 +08:00
displayName: username,
register: false,
traceSip: true,
autostart: false,
userAgentString: 'BigBlueButton',
stunServers: stun,
turnServers: turn,
});
this.userAgent.removeAllListeners('connected');
this.userAgent.removeAllListeners('disconnected');
2017-10-12 05:04:10 +08:00
this.userAgent.on('connected', () => this.handleUserAgentConnection(resolve));
this.userAgent.on('disconnected', () => this.handleUserAgentDisconnection(reject));
2017-09-30 04:42:34 +08:00
this.userAgent.start();
});
}
2017-10-12 05:04:10 +08:00
handleUserAgentConnection(resolve) {
console.log('CONNECTED');
this.isConnected = true;
resolve(this.userAgent);
}
2017-10-12 05:04:10 +08:00
handleUserAgentDisconnection(reject) {
this.userAgent.stop();
this.userAgent = null;
this.callback({ status: this.baseCallStates.failed,
error: this.baseErrorCodes.GENERIC_ERROR,
bridgeError: 'User Agent' });
2017-10-12 05:04:10 +08:00
console.log('DISCONNECTED');
reject('CONNECTION_ERROR');
2017-09-30 04:42:34 +08:00
}
setupEventHandlers(currentSession, callback) {
2017-10-12 05:04:10 +08:00
return new Promise(() => {
2017-09-30 04:42:34 +08:00
console.log('SETUPEVENTHANDLERS');
2017-10-12 05:04:10 +08:00
currentSession.on('terminated', (message, cause) => this.handleSessionTerminated(message, cause, callback));
2017-10-05 04:49:11 +08:00
2017-10-12 05:04:10 +08:00
currentSession.mediaHandler.on('iceConnectionCompleted', () => this.handleConnectionCompleted(callback));
currentSession.mediaHandler.on('iceConnectionConnected', () => this.handleConnectionCompleted(callback));
2017-10-05 04:49:11 +08:00
this.currentSession = currentSession;
2017-10-12 05:04:10 +08:00
});
}
handleConnectionCompleted(callback) {
console.log('handleConnectionCompleted');
this.hangup = false;
callback({ status: this.baseCallStates.started });
Promise.resolve();
}
handleSessionTerminated(message, cause, callback) {
console.log('TERMINATED', message, cause);
if (!message && !cause) {
return callback({ status: this.baseCallStates.ended });
}
const mappedCause = cause in this.errorCodes ?
this.errorCodes[cause] :
this.baseErrorCodes.GENERIC_ERROR;
return callback({ status: this.baseCallStates.failed, error: mappedCause, bridgeError: cause });
2017-07-24 22:15:46 +08:00
}
}