fix(audio): change unmute/unhold flow to work around FS unmute stutter

FS has an intermittent issue where unmuting a HELD channel sometimes
takes significantly (seconds) longer than usual.
conference <XYZ> unmute <WVU> simply gets stuck with no FS_API response,
which delays the unmute action whenever transparent listen only is
active.

Apparently, unholding the channel PRIOR TO unmuting works around the
issue - at least it could not be reproduced with the scenario at hand.
The unmute API already triggered an unhold in FS internally, which is
the reason why this was not done beforehand. The aforementioned issue is
way worse than an extra "redudant" API call, though.

Always unhold audio channels manually _before_ unmuting.
This commit is contained in:
prlanzarin 2024-07-01 21:04:55 -03:00
parent e6e1f28036
commit cf6202c572
5 changed files with 83 additions and 51 deletions

View File

@ -1,9 +1,10 @@
package org.bigbluebutton package org.bigbluebutton
import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, MessageTypes, MuteUserInVoiceConfSysMsg, MuteUserInVoiceConfSysMsgBody, Routing } import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, BbbCoreEnvelope, BbbCoreHeaderWithMeetingId, MessageTypes, Routing }
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter } import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core2.{ MeetingStatus2x } import org.bigbluebutton.core2.{ MeetingStatus2x }
import org.bigbluebutton.core.apps.webcam.CameraHdlrHelpers import org.bigbluebutton.core.apps.webcam.CameraHdlrHelpers
import org.bigbluebutton.core.apps.voice.VoiceApp
import org.bigbluebutton.core.models.{ import org.bigbluebutton.core.models.{
Roles, Roles,
Users2x, Users2x,
@ -16,19 +17,19 @@ import org.bigbluebutton.core.models.{
object LockSettingsUtil { object LockSettingsUtil {
private def muteUserInVoiceConf(liveMeeting: LiveMeeting, outGW: OutMsgRouter, vu: VoiceUserState, mute: Boolean): Unit = { private def muteUserInVoiceConf(
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, vu.intId) liveMeeting: LiveMeeting,
val envelope = BbbCoreEnvelope(MuteUserInVoiceConfSysMsg.NAME, routing) outGW: OutMsgRouter,
val header = BbbCoreHeaderWithMeetingId(MuteUserInVoiceConfSysMsg.NAME, liveMeeting.props.meetingProp.intId) vu: VoiceUserState, mute: Boolean
)(implicit context: akka.actor.ActorContext): Unit = {
val body = MuteUserInVoiceConfSysMsgBody(liveMeeting.props.voiceProp.voiceConf, vu.voiceUserId, mute) VoiceApp.muteUserInVoiceConf(liveMeeting, outGW, vu.intId, mute)
val event = MuteUserInVoiceConfSysMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
} }
private def applyMutingOfUsers(disableMic: Boolean, liveMeeting: LiveMeeting, outGW: OutMsgRouter): Unit = { private def applyMutingOfUsers(
disableMic: Boolean,
liveMeeting: LiveMeeting,
outGW: OutMsgRouter
)(implicit context: akka.actor.ActorContext): Unit = {
VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu => VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu =>
Users2x.findWithIntId(liveMeeting.users2x, vu.intId).foreach { user => Users2x.findWithIntId(liveMeeting.users2x, vu.intId).foreach { user =>
if (user.role == Roles.VIEWER_ROLE && !vu.listenOnly && user.locked) { if (user.role == Roles.VIEWER_ROLE && !vu.listenOnly && user.locked) {
@ -44,12 +45,20 @@ object LockSettingsUtil {
} }
} }
def enforceLockSettingsForAllVoiceUsers(liveMeeting: LiveMeeting, outGW: OutMsgRouter): Unit = { def enforceLockSettingsForAllVoiceUsers(
liveMeeting: LiveMeeting,
outGW: OutMsgRouter
)(implicit context: akka.actor.ActorContext): Unit = {
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status) val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
applyMutingOfUsers(permissions.disableMic, liveMeeting, outGW) applyMutingOfUsers(permissions.disableMic, liveMeeting, outGW)
} }
def enforceLockSettingsForVoiceUser(voiceUser: VoiceUserState, liveMeeting: LiveMeeting, outGW: OutMsgRouter): Unit = { def enforceLockSettingsForVoiceUser(
voiceUser: VoiceUserState,
liveMeeting: LiveMeeting,
outGW: OutMsgRouter
)(implicit context: akka.actor.ActorContext): Unit = {
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status) val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
if (permissions.disableMic) { if (permissions.disableMic) {
Users2x.findWithIntId(liveMeeting.users2x, voiceUser.intId).foreach { user => Users2x.findWithIntId(liveMeeting.users2x, voiceUser.intId).foreach { user =>
@ -65,7 +74,11 @@ object LockSettingsUtil {
} }
} }
private def enforceListenOnlyUserIsMuted(intUserId: String, liveMeeting: LiveMeeting, outGW: OutMsgRouter): Unit = { private def enforceListenOnlyUserIsMuted(
intUserId: String,
liveMeeting: LiveMeeting,
outGW: OutMsgRouter
)(implicit context: akka.actor.ActorContext): Unit = {
val voiceUser = VoiceUsers.findWithIntId(liveMeeting.voiceUsers, intUserId) val voiceUser = VoiceUsers.findWithIntId(liveMeeting.voiceUsers, intUserId)
voiceUser.foreach { vu => voiceUser.foreach { vu =>
// Make sure that listen only user is muted. (ralam dec 6, 2019 // Make sure that listen only user is muted. (ralam dec 6, 2019

View File

@ -2,6 +2,7 @@ package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs.MuteUserCmdMsg import org.bigbluebutton.common2.msgs.MuteUserCmdMsg
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.apps.voice.VoiceApp
import org.bigbluebutton.core.models.{ Roles, Users2x, VoiceUsers } import org.bigbluebutton.core.models.{ Roles, Users2x, VoiceUsers }
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter } import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.MeetingStatus2x
@ -51,13 +52,12 @@ trait MuteUserCmdMsgHdlr extends RightsManagementTrait {
} else { } else {
if (u.muted != msg.body.mute) { if (u.muted != msg.body.mute) {
log.info("Send mute user request. meetingId=" + meetingId + " userId=" + u.intId + " user=" + u) log.info("Send mute user request. meetingId=" + meetingId + " userId=" + u.intId + " user=" + u)
val event = MsgBuilder.buildMuteUserInVoiceConfSysMsg( VoiceApp.muteUserInVoiceConf(
meetingId, liveMeeting,
voiceConf, outGW,
u.voiceUserId, u.intId,
msg.body.mute msg.body.mute
) )
outGW.send(event)
} }
} }
} }

View File

@ -149,7 +149,6 @@ object VoiceApp extends SystemConfiguration {
outGW outGW
) )
} }
} }
} }
@ -614,4 +613,48 @@ object VoiceApp extends SystemConfiguration {
case _ => case _ =>
} }
} }
def muteUserInVoiceConf(
liveMeeting: LiveMeeting,
outGW: OutMsgRouter,
userId: String,
muted: Boolean
)(implicit context: akka.actor.ActorContext): Unit = {
for {
u <- VoiceUsers.findWithIntId(
liveMeeting.voiceUsers,
userId
)
} yield {
if (u.muted != muted) {
val muteEvent = MsgBuilder.buildMuteUserInVoiceConfSysMsg(
liveMeeting.props.meetingProp.intId,
liveMeeting.props.voiceProp.voiceConf,
u.voiceUserId,
muted
)
// If we're unmuting, trigger a channel unhold -> toggle listen only
// mode -> unmute
if (!muted) {
holdChannelInVoiceConf(
liveMeeting,
outGW,
u.uuid,
muted
)
toggleListenOnlyMode(
liveMeeting,
outGW,
u.intId,
u.callerNum,
muted,
0
)
}
outGW.send(muteEvent)
}
}
}
} }

View File

@ -6,6 +6,7 @@ import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core2.message.senders.{ MsgBuilder } import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
import org.bigbluebutton.core.apps.voice.VoiceApp
trait MuteAllExceptPresentersCmdMsgHdlr extends RightsManagementTrait { trait MuteAllExceptPresentersCmdMsgHdlr extends RightsManagementTrait {
this: MeetingActor => this: MeetingActor =>
@ -57,8 +58,8 @@ trait MuteAllExceptPresentersCmdMsgHdlr extends RightsManagementTrait {
VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu => VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu =>
if (!vu.listenOnly) { if (!vu.listenOnly) {
Users2x.findWithIntId(liveMeeting.users2x, vu.intId) match { Users2x.findWithIntId(liveMeeting.users2x, vu.intId) match {
case Some(u) => if (!u.presenter) muteUserInVoiceConf(vu, muted) case Some(u) => if (!u.presenter) VoiceApp.muteUserInVoiceConf(liveMeeting, outGW, vu.intId, muted)
case None => muteUserInVoiceConf(vu, muted) case None => VoiceApp.muteUserInVoiceConf(liveMeeting, outGW, vu.intId, muted)
} }
} }
} }
@ -82,17 +83,4 @@ trait MuteAllExceptPresentersCmdMsgHdlr extends RightsManagementTrait {
BbbCommonEnvCoreMsg(envelope, event) BbbCommonEnvCoreMsg(envelope, event)
} }
def muteUserInVoiceConf(vu: VoiceUserState, mute: Boolean): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, props.meetingProp.intId, vu.intId)
val envelope = BbbCoreEnvelope(MuteUserInVoiceConfSysMsg.NAME, routing)
val header = BbbCoreHeaderWithMeetingId(MuteUserInVoiceConfSysMsg.NAME, props.meetingProp.intId)
val body = MuteUserInVoiceConfSysMsgBody(props.voiceProp.voiceConf, vu.voiceUserId, mute)
val event = MuteUserInVoiceConfSysMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
} }

View File

@ -6,6 +6,7 @@ import org.bigbluebutton.core.models.{ VoiceUserState, VoiceUsers }
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter } import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core2.message.senders.{ MsgBuilder } import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
import org.bigbluebutton.core.apps.voice.VoiceApp
trait MuteMeetingCmdMsgHdlr extends RightsManagementTrait { trait MuteMeetingCmdMsgHdlr extends RightsManagementTrait {
this: MeetingActor => this: MeetingActor =>
@ -30,19 +31,6 @@ trait MuteMeetingCmdMsgHdlr extends RightsManagementTrait {
BbbCommonEnvCoreMsg(envelope, event) BbbCommonEnvCoreMsg(envelope, event)
} }
def muteUserInVoiceConf(vu: VoiceUserState, mute: Boolean): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, props.meetingProp.intId, vu.intId)
val envelope = BbbCoreEnvelope(MuteUserInVoiceConfSysMsg.NAME, routing)
val header = BbbCoreHeaderWithMeetingId(MuteUserInVoiceConfSysMsg.NAME, props.meetingProp.intId)
val body = MuteUserInVoiceConfSysMsgBody(props.voiceProp.voiceConf, vu.voiceUserId, mute)
val event = MuteUserInVoiceConfSysMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
if (msg.body.mute != MeetingStatus2x.isMeetingMuted(liveMeeting.status)) { if (msg.body.mute != MeetingStatus2x.isMeetingMuted(liveMeeting.status)) {
if (msg.body.mute) { if (msg.body.mute) {
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg( val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
@ -79,7 +67,7 @@ trait MuteMeetingCmdMsgHdlr extends RightsManagementTrait {
if (muted) { if (muted) {
VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu => VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu =>
if (!vu.listenOnly) { if (!vu.listenOnly) {
muteUserInVoiceConf(vu, muted) VoiceApp.muteUserInVoiceConf(liveMeeting, outGW, vu.intId, muted)
} }
} }
} }