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
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.core2.{ MeetingStatus2x }
import org.bigbluebutton.core.apps.webcam.CameraHdlrHelpers
import org.bigbluebutton.core.apps.voice.VoiceApp
import org.bigbluebutton.core.models.{
Roles,
Users2x,
@ -16,19 +17,19 @@ import org.bigbluebutton.core.models.{
object LockSettingsUtil {
private def muteUserInVoiceConf(liveMeeting: LiveMeeting, outGW: OutMsgRouter, vu: VoiceUserState, mute: Boolean): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, vu.intId)
val envelope = BbbCoreEnvelope(MuteUserInVoiceConfSysMsg.NAME, routing)
val header = BbbCoreHeaderWithMeetingId(MuteUserInVoiceConfSysMsg.NAME, liveMeeting.props.meetingProp.intId)
val body = MuteUserInVoiceConfSysMsgBody(liveMeeting.props.voiceProp.voiceConf, vu.voiceUserId, mute)
val event = MuteUserInVoiceConfSysMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
private def muteUserInVoiceConf(
liveMeeting: LiveMeeting,
outGW: OutMsgRouter,
vu: VoiceUserState, mute: Boolean
)(implicit context: akka.actor.ActorContext): Unit = {
VoiceApp.muteUserInVoiceConf(liveMeeting, outGW, vu.intId, mute)
}
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 =>
Users2x.findWithIntId(liveMeeting.users2x, vu.intId).foreach { user =>
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)
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)
if (permissions.disableMic) {
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)
voiceUser.foreach { vu =>
// 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.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.apps.voice.VoiceApp
import org.bigbluebutton.core.models.{ Roles, Users2x, VoiceUsers }
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core2.MeetingStatus2x
@ -51,13 +52,12 @@ trait MuteUserCmdMsgHdlr extends RightsManagementTrait {
} else {
if (u.muted != msg.body.mute) {
log.info("Send mute user request. meetingId=" + meetingId + " userId=" + u.intId + " user=" + u)
val event = MsgBuilder.buildMuteUserInVoiceConfSysMsg(
meetingId,
voiceConf,
u.voiceUserId,
VoiceApp.muteUserInVoiceConf(
liveMeeting,
outGW,
u.intId,
msg.body.mute
)
outGW.send(event)
}
}
}

View File

@ -138,7 +138,7 @@ object VoiceApp extends SystemConfiguration {
// If the user is muted or unmuted with an unheld channel, broadcast
// the event right away.
// If the user is unmuted, but channel is held, we need to wait for the
// If the user is unmuted, but channel is held, we need to wait for the
// channel to be active again to broadcast the event. See
// VoiceApp.handleChannelHoldChanged for this second case.
if (muted || (!muted && !mutedUser.hold)) {
@ -149,7 +149,6 @@ object VoiceApp extends SystemConfiguration {
outGW
)
}
}
}
@ -614,4 +613,48 @@ object VoiceApp extends SystemConfiguration {
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.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
import org.bigbluebutton.core.apps.voice.VoiceApp
trait MuteAllExceptPresentersCmdMsgHdlr extends RightsManagementTrait {
this: MeetingActor =>
@ -57,8 +58,8 @@ trait MuteAllExceptPresentersCmdMsgHdlr extends RightsManagementTrait {
VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu =>
if (!vu.listenOnly) {
Users2x.findWithIntId(liveMeeting.users2x, vu.intId) match {
case Some(u) => if (!u.presenter) muteUserInVoiceConf(vu, muted)
case None => muteUserInVoiceConf(vu, muted)
case Some(u) => if (!u.presenter) VoiceApp.muteUserInVoiceConf(liveMeeting, outGW, vu.intId, muted)
case None => VoiceApp.muteUserInVoiceConf(liveMeeting, outGW, vu.intId, muted)
}
}
}
@ -82,17 +83,4 @@ trait MuteAllExceptPresentersCmdMsgHdlr extends RightsManagementTrait {
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.core2.MeetingStatus2x
import org.bigbluebutton.core2.message.senders.{ MsgBuilder }
import org.bigbluebutton.core.apps.voice.VoiceApp
trait MuteMeetingCmdMsgHdlr extends RightsManagementTrait {
this: MeetingActor =>
@ -30,19 +31,6 @@ trait MuteMeetingCmdMsgHdlr extends RightsManagementTrait {
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) {
val notifyEvent = MsgBuilder.buildNotifyAllInMeetingEvtMsg(
@ -79,7 +67,7 @@ trait MuteMeetingCmdMsgHdlr extends RightsManagementTrait {
if (muted) {
VoiceUsers.findAll(liveMeeting.voiceUsers) foreach { vu =>
if (!vu.listenOnly) {
muteUserInVoiceConf(vu, muted)
VoiceApp.muteUserInVoiceConf(liveMeeting, outGW, vu.intId, muted)
}
}
}