Merge pull request #4117 from Klauswk/html5-2x-fix-audio

[HTML5 2.0] Fix join audio on the HTML5 client 2.0
This commit is contained in:
Anton Georgiev 2017-07-25 10:30:43 -04:00 committed by GitHub
commit 1bdeb248c1
8 changed files with 83 additions and 43 deletions

View File

@ -13,18 +13,18 @@ export default class SIPBridge extends BaseAudioBridge {
this.userData = userData;
}
joinListenOnly() {
joinListenOnly(stunServers, turnServers) {
makeCall('listenOnlyToggle', true);
this._joinVoiceCallSIP({ isListenOnly: true });
this._joinVoiceCallSIP({ isListenOnly: true, stunServers, turnServers });
}
joinMicrophone() {
this._joinVoiceCallSIP({ isListenOnly: false });
joinMicrophone(stunServers, turnServers) {
this._joinVoiceCallSIP({ isListenOnly: false, stunServers, turnServers });
}
// 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 = () => {}) {
exitAudio(isListenOnly, afterExitCall = () => { }) {
// To be called when the hangup is confirmed
const hangupCallback = function () {
console.log(`Exited Voice Conference, listenOnly=${isListenOnly}`);
@ -40,7 +40,7 @@ export default class SIPBridge extends BaseAudioBridge {
triedHangup = false;
// function to initiate call
const checkToHangupCall = ((context, afterExitCall = () => {}) => {
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) {
@ -96,8 +96,8 @@ export default class SIPBridge extends BaseAudioBridge {
};
const stunsAndTurns = {
stun: this.userData.stuns,
turn: this.userData.turns,
stun: options.stunServers,
turn: options.turnServers,
};
callIntoConference(extension, (audio) => {

View File

@ -1,3 +1,4 @@
import Auth from '/imports/ui/services/auth';
import BaseAudioBridge from '../bridge/base';
import VertoBridge from '../bridge/verto';
import SIPBridge from '../bridge/sip';
@ -24,12 +25,42 @@ export default class AudioManager {
}
joinAudio(listenOnly) {
if (listenOnly || this.microphoneLockEnforced) {
this.isListenOnly = true;
this.bridge.joinListenOnly();
} else {
this.bridge.joinMicrophone();
}
AudioManager.fetchServers().then(({ error, stunServers, turnServers }) => {
if (error) {
//We need to alert the user about this problem by some gui message.
console.error("Couldn't fetch the stuns/turns servers!");
return;
}
if (listenOnly || this.microphoneLockEnforced) {
this.isListenOnly = true;
this.bridge.joinListenOnly(stunServers, turnServers);
} else {
this.bridge.joinMicrophone(stunServers, turnServers);
}
});
}
// 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));
}
}

View File

@ -3,11 +3,13 @@ import mapToAcl from '/imports/startup/mapToAcl';
import userLogout from './methods/userLogout';
import validateAuthToken from './methods/validateAuthToken';
import setEmojiStatus from './methods/setEmojiStatus';
import listenOnlyToggle from './methods/listenOnlyToggle';
Meteor.methods(mapToAcl(['methods.userLogout', 'methods.setEmojiStatus',
Meteor.methods(mapToAcl(['methods.userLogout', 'methods.setEmojiStatus', 'methods.listenOnlyToggle',
], {
userLogout,
setEmojiStatus,
listenOnlyToggle,
}));
Meteor.methods({ validateAuthToken2x: validateAuthToken });

View File

@ -7,7 +7,7 @@ import Users from '/imports/api/2.0/users';
export default function listenOnlyToggle(credentials, isJoining = true) {
const REDIS_CONFIG = Meteor.settings.redis;
const CHANNEL = REDIS_CONFIG.channels.toBBBApps.meeting;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const { meetingId, requesterUserId } = credentials;
@ -18,40 +18,36 @@ export default function listenOnlyToggle(credentials, isJoining = true) {
let EVENT_NAME;
if (isJoining) {
EVENT_NAME = 'user_connected_to_global_audio';
EVENT_NAME = 'UserConnectedToGlobalAudioMsg';
} else {
EVENT_NAME = 'user_disconnected_from_global_audio';
EVENT_NAME = 'UserDisconnectedFromGlobalAudioMsg';
}
const Meeting = Meetings.findOne({ meetingId });
if (!Meeting) {
throw new Meteor.Error(
'meeting-not-found', 'You need a valid meeting to be able to toggle audio');
}
check(Meeting.voiceConf, String);
const User = Users.findOne({
meetingId,
userId: requesterUserId,
});
const Meeting = Meetings.findOne({ meetingId });
if (!User) {
throw new Meteor.Error(
'user-not-found', 'You need a valid user to be able to toggle audio');
}
check(User.user.name, String);
// check(User.user.name, String);
const header = {
name: EVENT_NAME,
voiceConf: Meeting.voiceProp.voiceConf,
};
const payload = {
userid: requesterUserId,
meeting_id: meetingId,
voice_conf: Meeting.voiceConf,
userId: requesterUserId,
name: User.user.name,
};
Logger.verbose(`User '${requesterUserId}' ${isJoining
? 'joined' : 'left'} global audio from meeting '${meetingId}'`);
return RedisPubSub.publish(CHANNEL, EVENT_NAME, payload);
return RedisPubSub.publish(CHANNEL, EVENT_NAME, meetingId, payload, header);
}

View File

@ -21,7 +21,7 @@ export function joinRouteHandler(nextState, replace, callback) {
.then((data) => {
const { meetingID, internalUserID, authToken, logoutUrl } = data.response;
Auth.set(meetingID, internalUserID, authToken, logoutUrl);
Auth.set(meetingID, internalUserID, authToken, logoutUrl, sessionToken);
replace({ pathname: '/' });
callback();
});

View File

@ -2,25 +2,23 @@ import Users from '/imports/api/2.0/users';
import Auth from '/imports/ui/services/auth';
import AudioManager from '/imports/api/1.1/audio/client/manager';
import Meetings from '/imports/api/2.0/meetings';
let audioManager;
const init = () => {
const userId = Auth.userID;
const User = Users.findOne({ userId });
const username = User.user.name;
const Meeting = Meetings.findOne({ meetingId: User.meetingId });
const voiceBridge = Meeting.voiceProp.voiceConf;
const turns = [];
const stuns = [];
// FIX ME
const voiceBridge = 'Meeting.voiceConf';
// FIX ME
const microphoneLockEnforced = 'Meeting.roomLockSettings.disableMic';
const microphoneLockEnforced = false;
const userData = {
userId,
username,
turns,
stuns,
voiceBridge,
microphoneLockEnforced,
};

View File

@ -13,6 +13,7 @@ class Auth {
this._meetingID = Storage.getItem('meetingID');
this._userID = Storage.getItem('userID');
this._authToken = Storage.getItem('authToken');
this._sessionToken = Storage.getItem('sessionToken');
this._logoutURL = Storage.getItem('logoutURL');
this._loggedIn = {
value: false,
@ -29,6 +30,15 @@ class Auth {
Storage.setItem('meetingID', this._meetingID);
}
set sessionToken(sessionToken) {
this._sessionToken = sessionToken;
Storage.setItem('sessionToken', this._sessionToken);
}
get sessionToken() {
return this._sessionToken;
}
get userID() {
return this._userID;
}
@ -72,14 +82,16 @@ class Auth {
requesterUserId: this.userID,
requesterToken: this.token,
logoutURL: this.logoutURL,
sessionToken: this.sessionToken,
};
}
set(meetingId, requesterUserId, requesterToken, logoutURL) {
set(meetingId, requesterUserId, requesterToken, logoutURL, sessionToken) {
this.meetingID = meetingId;
this.userID = requesterUserId;
this.token = requesterToken;
this.logoutURL = logoutURL;
this.sessionToken = sessionToken;
}
clearCredentials(...args) {
@ -88,6 +100,7 @@ class Auth {
this.token = null;
this.loggedIn = false;
this.logoutURL = null;
this.sessionToken = null;
return Promise.resolve(...args);
}