Skip to content
Snippets Groups Projects
Commit 6a4ba7a3 authored by Chad Pilkey's avatar Chad Pilkey
Browse files

watch for voice call state updates instead of DTMFs in the client

parent 4b5c6c65
No related branches found
No related tags found
No related merge requests found
Showing
with 154 additions and 31 deletions
......@@ -10,6 +10,10 @@ import {
analyzeSdp,
logSelectedCandidate,
} from '/imports/utils/sdpUtils';
import { Tracker } from 'meteor/tracker';
import VoiceCallStates from '/imports/api/voice-call-states';
import CallStateOptions from '/imports/api/voice-call-states/utils/callStates';
import Auth from '/imports/ui/services/auth';
const MEDIA = Meteor.settings.public.media;
const MEDIA_TAG = MEDIA.mediaTag;
......@@ -47,14 +51,6 @@ class SIPSession {
this.reconnectAttempt = reconnectAttempt;
}
static parseDTMF(message) {
const parse = message.match(/Signal=(.)/);
if (parse && parse.length === 2) {
return parse[1];
}
return '';
}
joinAudio({ isListenOnly, extension, inputStream }, managerCallback) {
return new Promise((resolve, reject) => {
const callExtension = extension ? `${extension}${this.userData.voiceBridge}` : this.userData.voiceBridge;
......@@ -119,8 +115,10 @@ class SIPSession {
return new Promise((resolve, reject) => {
this.inEchoTest = false;
const timeout = setInterval(() => {
clearInterval(timeout);
let trackerControl = null;
const timeout = setTimeout(() => {
trackerControl.stop();
logger.error({ logCode: 'sip_js_transfer_timed_out' }, 'Timeout on transferring from echo test to conference');
this.callback({
status: this.baseCallStates.failed,
......@@ -136,15 +134,22 @@ class SIPSession {
// This is is the call transfer code ask @chadpilkey
this.currentSession.dtmf(1);
this.currentSession.on('dtmf', (event) => {
if (event.body && (typeof event.body === 'string')) {
const key = SIPSession.parseDTMF(event.body);
if (key === '7') {
clearInterval(timeout);
onTransferSuccess();
resolve();
}
}
Tracker.autorun((c) => {
trackerControl = c;
const selector = { meetingId: Auth.meetingID, userId: Auth.userID };
const query = VoiceCallStates.find(selector);
query.observeChanges({
changed: (id, fields) => {
if (fields.callState === CallStateOptions.IN_CONFERENCE) {
clearTimeout(timeout);
onTransferSuccess();
c.stop();
resolve();
}
},
});
});
});
}
......@@ -491,17 +496,21 @@ class SIPSession {
};
['iceConnectionClosed'].forEach(e => mediaHandler.on(e, handleIceConnectionTerminated));
const inEchoDTMF = (event) => {
if (event.body && typeof event.body === 'string') {
const dtmf = SIPSession.parseDTMF(event.body);
if (dtmf === '0') {
fsReady = true;
checkIfCallReady();
}
}
currentSession.off('dtmf', inEchoDTMF);
};
currentSession.on('dtmf', inEchoDTMF);
Tracker.autorun((c) => {
const selector = { meetingId: Auth.meetingID, userId: Auth.userID };
const query = VoiceCallStates.find(selector);
query.observeChanges({
changed: (id, fields) => {
if (fields.callState === CallStateOptions.IN_ECHO_TEST) {
fsReady = true;
checkIfCallReady();
c.stop();
}
},
});
});
});
}
}
......
......@@ -19,6 +19,7 @@ import clearNote from '/imports/api/note/server/modifiers/clearNote';
import clearNetworkInformation from '/imports/api/network-information/server/modifiers/clearNetworkInformation';
import clearLocalSettings from '/imports/api/local-settings/server/modifiers/clearLocalSettings';
import clearRecordMeeting from './clearRecordMeeting';
import clearVoiceCallStates from '/imports/api/voice-call-states/server/modifiers/clearVoiceCallStates';
export default function meetingHasEnded(meetingId) {
removeAnnotationsStreamer(meetingId);
......@@ -40,6 +41,7 @@ export default function meetingHasEnded(meetingId) {
clearNetworkInformation(meetingId);
clearLocalSettings(meetingId);
clearRecordMeeting(meetingId);
clearVoiceCallStates(meetingId);
return Logger.info(`Cleared Meetings with id ${meetingId}`);
});
......
import { Meteor } from 'meteor/meteor';
const VoiceCallStates = new Mongo.Collection('voiceCallStates');
if (Meteor.isServer) {
// types of queries for the voice users:
// 1. intId
// 2. meetingId, intId
VoiceCallStates._ensureIndex({ meetingId: 1, userId: 1 });
}
export default VoiceCallStates;
import RedisPubSub from '/imports/startup/server/redis';
import handleVoiceCallStateEvent from './handlers/voiceCallStateEvent';
RedisPubSub.on('VoiceCallStateEvtMsg', handleVoiceCallStateEvent);
import { check } from 'meteor/check';
import VoiceCallState from '/imports/api/voice-call-states';
import Logger from '/imports/startup/server/logger';
// "CALL_STARTED", "IN_ECHO_TEST", "IN_CONFERENCE", "CALL_ENDED"
export default function handleVoiceCallStateEvent({ body }, meetingId) {
const {
voiceConf,
clientSession,
userId,
callerName,
callState,
} = body;
check(meetingId, String);
check(voiceConf, String);
check(clientSession, String);
check(userId, String);
check(callerName, String);
check(callState, String);
const selector = {
meetingId,
userId,
clientSession,
};
const modifier = {
$set: {
meetingId,
userId,
voiceConf,
clientSession,
callState,
},
};
const cb = (err) => {
if (err) {
return Logger.error(`Update voice call state=${userId}: ${err}`);
}
return Logger.debug(`Update voice call state=${userId} meeting=${meetingId} clientSession=${clientSession}`);
};
return VoiceCallState.upsert(selector, modifier, cb);
}
import './eventHandlers';
import './publishers';
import Logger from '/imports/startup/server/logger';
import VoiceCallStates from '/imports/api/voice-users';
export default function clearVoiceCallStates(meetingId) {
if (meetingId) {
return VoiceCallStates.remove({ meetingId }, () => {
Logger.info(`Cleared VoiceCallStates in (${meetingId})`);
});
}
return VoiceCallStates.remove({}, () => {
Logger.info('Cleared VoiceCallStates in all meetings');
});
}
import VoiceCallStates from '/imports/api/voice-call-states';
import { Meteor } from 'meteor/meteor';
import Logger from '/imports/startup/server/logger';
import { extractCredentials } from '/imports/api/common/server/helpers';
function voiceCallStates() {
if (!this.userId) {
return VoiceCallStates.find({ meetingId: '' });
}
const { meetingId, requesterUserId } = extractCredentials(this.userId);
Logger.debug(`Publishing Voice Call States for ${meetingId} ${requesterUserId}`);
return VoiceCallStates.find({ meetingId, userId: requesterUserId });
}
function publish(...args) {
const boundVoiceCallStates = voiceCallStates.bind(this);
return boundVoiceCallStates(...args);
}
Meteor.publish('voice-call-states', publish);
const CallStateOptions = {
CALL_STARTED: 'CALL_STARTED',
IN_ECHO_TEST: 'IN_ECHO_TEST',
IN_CONFERENCE: 'IN_CONFERENCE',
CALL_ENDED: 'CALL_ENDED',
};
export default CallStateOptions;
......@@ -20,6 +20,7 @@ const SUBSCRIPTIONS = [
'voiceUsers', 'whiteboard-multi-user', 'screenshare', 'group-chat',
'presentation-pods', 'users-settings', 'guestUser', 'users-infos', 'note', 'meeting-time-remaining',
'network-information', 'ping-pong', 'local-settings', 'users-typing', 'record-meetings', 'video-streams',
'voice-call-states',
];
class Subscriptions extends Component {
......
......@@ -26,7 +26,7 @@ import '/imports/api/external-videos/server';
import '/imports/api/guest-users/server';
import '/imports/api/ping-pong/server';
import '/imports/api/local-settings/server';
import '/imports/api/voice-call-states/server';
// Commons
import '/imports/api/log-client/server';
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment