Merge branch 'v2.2.x-release' of github.com:bigbluebutton/bigbluebutton into feb18-merge
This commit is contained in:
commit
55e8de4357
@ -43,6 +43,7 @@ trait SystemConfiguration {
|
||||
lazy val voiceConfRecordCodec = Try(config.getString("voiceConf.recordCodec")).getOrElse("wav")
|
||||
lazy val checkVoiceRecordingInterval = Try(config.getInt("voiceConf.checkRecordingInterval")).getOrElse(19)
|
||||
lazy val syncVoiceUsersStatusInterval = Try(config.getInt("voiceConf.syncUserStatusInterval")).getOrElse(43)
|
||||
lazy val ejectRogueVoiceUsers = Try(config.getBoolean("voiceConf.ejectRogueVoiceUsers")).getOrElse(false)
|
||||
|
||||
lazy val recordingChapterBreakLengthInMinutes = Try(config.getInt("recording.chapterBreakLengthInMinutes")).getOrElse(0)
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
package org.bigbluebutton.core.apps.voice
|
||||
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.LockSettingsUtil
|
||||
import org.bigbluebutton.common2.msgs.{ BbbClientMsgHeader, BbbCommonEnvCoreMsg, BbbCoreEnvelope, ConfVoiceUser, MessageTypes, Routing, UserJoinedVoiceConfToClientEvtMsg, UserJoinedVoiceConfToClientEvtMsgBody, UserLeftVoiceConfToClientEvtMsg, UserLeftVoiceConfToClientEvtMsgBody, UserMutedVoiceEvtMsg, UserMutedVoiceEvtMsgBody }
|
||||
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
|
||||
import org.bigbluebutton.core.bus.InternalEventBus
|
||||
import org.bigbluebutton.core.models.{ VoiceUserState, VoiceUsers }
|
||||
import org.bigbluebutton.core.models.{ Users2x, VoiceUserState, VoiceUsers }
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
|
||||
object VoiceApp {
|
||||
object VoiceApp extends SystemConfiguration {
|
||||
|
||||
def genRecordPath(
|
||||
recordDir: String,
|
||||
@ -136,6 +137,20 @@ object VoiceApp {
|
||||
// Update the user status to indicate they are still in the voice conference.
|
||||
VoiceUsers.setLastStatusUpdate(liveMeeting.voiceUsers, vu)
|
||||
}
|
||||
|
||||
// Purge voice users that don't have a matching user record
|
||||
// Avoid this if the meeting is a breakout room since might be real
|
||||
// voice users participating
|
||||
// Also avoid ejecting if the user is dial-in (v_*)
|
||||
if (ejectRogueVoiceUsers && !liveMeeting.props.meetingProp.isBreakout && !cvu.intId.startsWith("v_")) {
|
||||
Users2x.findWithIntId(liveMeeting.users2x, cvu.intId) match {
|
||||
case Some(_) =>
|
||||
case None =>
|
||||
println(s"Ejecting rogue voice user. meetingId=${liveMeeting.props.meetingProp.intId} userId=${cvu.intId}")
|
||||
val event = MsgBuilder.buildEjectUserFromVoiceConfSysMsg(liveMeeting.props.meetingProp.intId, liveMeeting.props.voiceProp.voiceConf, cvu.voiceUserId)
|
||||
outGW.send(event)
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
handleUserJoinedVoiceConfEvtMsg(
|
||||
liveMeeting,
|
||||
|
@ -85,6 +85,8 @@ voiceConf {
|
||||
checkRecordingInterval = 23
|
||||
# Internval seconds to sync voice users status.
|
||||
syncUserStatusInterval = 41
|
||||
# Voice users with no matching user record
|
||||
ejectRogueVoiceUsers = true
|
||||
}
|
||||
|
||||
recording {
|
||||
|
@ -85,10 +85,12 @@ public class GetUsersStatusCommand extends FreeswitchCommand {
|
||||
voiceUserId = callWithSess.group(1).trim();
|
||||
clientSession = callWithSess.group(2).trim();
|
||||
callerIdName = callWithSess.group(3).trim();
|
||||
} else
|
||||
if (matcher.matches()) {
|
||||
} else if (matcher.matches()) {
|
||||
voiceUserId = matcher.group(1).trim();
|
||||
callerIdName = matcher.group(2).trim();
|
||||
} else {
|
||||
// This is a caller using dial in or out
|
||||
voiceUserId = "v_" + member.getId().toString();
|
||||
}
|
||||
|
||||
log.info("Conf user. uuid=" + uuid
|
||||
|
@ -6,7 +6,7 @@ import VoiceUsers from '/imports/api/voice-users';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
export default function muteToggle(uId) {
|
||||
export default function muteToggle(uId, toggle) {
|
||||
const REDIS_CONFIG = Meteor.settings.private.redis;
|
||||
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
|
||||
const EVENT_NAME = 'MuteUserCmdMsg';
|
||||
@ -39,10 +39,18 @@ export default function muteToggle(uId) {
|
||||
}
|
||||
}
|
||||
|
||||
let _muted;
|
||||
|
||||
if ((toggle === undefined) || (toggle === null)) {
|
||||
_muted = !muted;
|
||||
} else {
|
||||
_muted = !!toggle;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
userId: userToMute,
|
||||
mutedBy: requesterUserId,
|
||||
mute: !muted,
|
||||
mute: _muted,
|
||||
};
|
||||
|
||||
RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
|
||||
|
@ -6,10 +6,41 @@ import Meetings from '/imports/api/meetings';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import VoiceUsers from '/imports/api/voice-users';
|
||||
import logger from '/imports/startup/client/logger';
|
||||
import { throttle } from 'lodash';
|
||||
import Storage from '../../services/storage/session';
|
||||
|
||||
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
||||
const TOGGLE_MUTE_THROTTLE_TIME = Meteor.settings.public.media.toggleMuteThrottleTime;
|
||||
|
||||
const MUTED_KEY = 'muted';
|
||||
|
||||
const recoverMicState = () => {
|
||||
const muted = Storage.getItem(MUTED_KEY);
|
||||
|
||||
if ((muted === undefined) || (muted === null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug({
|
||||
logCode: 'audio_recover_mic_state',
|
||||
}, `Audio recover previous mic state: muted = ${muted}`);
|
||||
makeCall('toggleVoice', null, muted);
|
||||
};
|
||||
|
||||
const audioEventHandler = (event) => {
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.name) {
|
||||
case 'started':
|
||||
recoverMicState();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const init = (messages, intl) => {
|
||||
AudioManager.setAudioMessages(messages, intl);
|
||||
if (AudioManager.initialized) return;
|
||||
@ -33,7 +64,7 @@ const init = (messages, intl) => {
|
||||
microphoneLockEnforced,
|
||||
};
|
||||
|
||||
AudioManager.init(userData);
|
||||
AudioManager.init(userData, audioEventHandler);
|
||||
};
|
||||
|
||||
const isVoiceUser = () => {
|
||||
@ -46,6 +77,9 @@ const toggleMuteMicrophone = throttle(() => {
|
||||
const user = VoiceUsers.findOne({
|
||||
meetingId: Auth.meetingID, intId: Auth.userID,
|
||||
}, { fields: { muted: 1 } });
|
||||
|
||||
Storage.setItem(MUTED_KEY, !user.muted);
|
||||
|
||||
if (user.muted) {
|
||||
logger.info({
|
||||
logCode: 'audiomanager_unmute_audio',
|
||||
|
@ -11,7 +11,7 @@ import ReactPlayer from 'react-player';
|
||||
import Panopto from './custom-players/panopto';
|
||||
|
||||
const isUrlValid = (url) => {
|
||||
return ReactPlayer.canPlay(url) || Panopto.canPlay(url);
|
||||
return /^https.*$/.test(url) && (ReactPlayer.canPlay(url) || Panopto.canPlay(url));
|
||||
}
|
||||
|
||||
const startWatching = (url) => {
|
||||
|
@ -58,13 +58,14 @@ class AudioManager {
|
||||
this.monitor = this.monitor.bind(this);
|
||||
}
|
||||
|
||||
init(userData) {
|
||||
init(userData, audioEventHandler) {
|
||||
this.bridge = new SIPBridge(userData); // no alternative as of 2019-03-08
|
||||
if (this.useKurento) {
|
||||
this.listenOnlyBridge = new KurentoBridge(userData);
|
||||
}
|
||||
this.userData = userData;
|
||||
this.initialized = true;
|
||||
this.audioEventHandler = audioEventHandler;
|
||||
}
|
||||
|
||||
setAudioMessages(messages, intl) {
|
||||
@ -338,6 +339,7 @@ class AudioManager {
|
||||
this.notify(this.intl.formatMessage(this.messages.info.JOINED_AUDIO));
|
||||
logger.info({ logCode: 'audio_joined' }, 'Audio Joined');
|
||||
if (STATS.enabled) this.monitor();
|
||||
this.audioEventHandler({ name: 'started' });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,7 +249,7 @@ public:
|
||||
# profile: a camera profile id from the cameraProfiles configuration array
|
||||
# that will be applied to all cameras when threshold is hit
|
||||
cameraQualityThresholds:
|
||||
enabled: false
|
||||
enabled: true
|
||||
thresholds:
|
||||
- threshold: 8
|
||||
profile: low-u8
|
||||
@ -265,7 +265,7 @@ public:
|
||||
profile: low-u30
|
||||
pagination:
|
||||
# whether to globally enable or disable pagination.
|
||||
enabled: false
|
||||
enabled: true
|
||||
# how long (in ms) the negotiation will be debounced after a page change.
|
||||
pageChangeDebounceTime: 2500
|
||||
# video page sizes for DESKTOP endpoints. It stands for the number of SUBSCRIBER streams.
|
||||
@ -273,11 +273,11 @@ public:
|
||||
# A page size of 0 (zero) means that the page size is unlimited (disabled).
|
||||
desktopPageSizes:
|
||||
moderator: 0
|
||||
viewer: 5
|
||||
viewer: 0
|
||||
# video page sizes for MOBILE endpoints
|
||||
mobilePageSizes:
|
||||
moderator: 2
|
||||
viewer: 2
|
||||
moderator: 6
|
||||
viewer: 4
|
||||
syncUsersWithConnectionManager:
|
||||
enabled: false
|
||||
syncInterval: 60000
|
||||
|
@ -212,7 +212,12 @@ class ApiController {
|
||||
|
||||
// BEGIN - backward compatibility
|
||||
if (StringUtils.isEmpty(params.checksum)) {
|
||||
invalid("checksumError", "You did not pass the checksum security check", REDIRECT_RESPONSE)
|
||||
invalid("checksumError", "You did not pass the checksum security check")
|
||||
return
|
||||
}
|
||||
|
||||
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
|
||||
invalid("checksumError", "You did not pass the checksum security check")
|
||||
return
|
||||
}
|
||||
|
||||
@ -244,11 +249,6 @@ class ApiController {
|
||||
return
|
||||
}
|
||||
|
||||
if (!paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) {
|
||||
invalid("checksumError", "You did not pass the checksum security check", REDIRECT_RESPONSE)
|
||||
return
|
||||
}
|
||||
|
||||
// END - backward compatibility
|
||||
|
||||
// Do we have a checksum? If none, complain.
|
||||
|
Loading…
Reference in New Issue
Block a user