From 623c90b19f9da7eabe099422afecc557f944b544 Mon Sep 17 00:00:00 2001 From: prlanzarin <4529051+prlanzarin@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:22:45 -0300 Subject: [PATCH 1/7] fix(audio): add callerNum to transparent listen only RPCs There are some scenarios where transparent listen only toggle RPCs are directed to multiple different sessions in the SFU, which might cause a hold-unhold loop because they're all anchored to the userId. Append the callerNum to transparent listen only toggle RPCs so that they are directed to the appropriate audio sessions in all cases. --- .../scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala | 4 ++++ .../org/bigbluebutton/core2/message/senders/MsgBuilder.scala | 3 ++- .../scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala index 96a2b121c9..afa2c68663 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala @@ -131,6 +131,7 @@ object VoiceApp extends SystemConfiguration { liveMeeting, outGW, mutedUser.intId, + mutedUser.callerNum, muted, toggleListenOnlyAfterMuteTimer ) @@ -476,6 +477,7 @@ object VoiceApp extends SystemConfiguration { liveMeeting: LiveMeeting, outGW: OutMsgRouter, userId: String, + callerNum: String, enabled: Boolean, delay: Int = 0 )(implicit context: ActorContext): Unit = { @@ -485,6 +487,7 @@ object VoiceApp extends SystemConfiguration { liveMeeting.props.meetingProp.intId, liveMeeting.props.voiceProp.voiceConf, userId, + callerNum, enabled ) outGW.send(event) @@ -550,6 +553,7 @@ object VoiceApp extends SystemConfiguration { liveMeeting, outGW, intId, + vu.callerNum, vu.muted ) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala index de5e3e7389..f74badeb48 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala @@ -637,11 +637,12 @@ object MsgBuilder { meetingId: String, voiceConf: String, userId: String, + callerNum: String, enabled: Boolean ): BbbCommonEnvCoreMsg = { val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") val envelope = BbbCoreEnvelope(ToggleListenOnlyModeSysMsg.NAME, routing) - val body = ToggleListenOnlyModeSysMsgBody(voiceConf, userId, enabled) + val body = ToggleListenOnlyModeSysMsgBody(voiceConf, userId, callerNum, enabled) val header = BbbCoreHeaderWithMeetingId(ToggleListenOnlyModeSysMsg.NAME, meetingId) val event = ToggleListenOnlyModeSysMsg(header, body) diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala index 86d878e6c1..5fedb8792c 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala @@ -685,6 +685,7 @@ case class ToggleListenOnlyModeSysMsg( case class ToggleListenOnlyModeSysMsgBody( voiceConf: String, userId: String, + callerNum: String, enabled: Boolean ) @@ -701,5 +702,6 @@ case class ListenOnlyModeToggledInSfuEvtMsgBody( meetingId: String, voiceConf: String, userId: String, + callerNum: String, enabled: Boolean ) From 025942de5ba95f02b8fee7ad4f4f6153fe0c1470 Mon Sep 17 00:00:00 2001 From: prlanzarin <4529051+prlanzarin@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:17:44 -0300 Subject: [PATCH 2/7] fix(audio): only enforce hold on mute state mismatch if !muted There's a routine that's supposed to enforce the channel hold state if mute and hold states are mismatched. This should only happen in case the user is unmuted and the channel is held to avoid leaving an user without inbound or outbound audio by accident. The routine currently checks for the oppositve scenario as well (muted=true,hold=false), which should not be enforced because of special scenarios (e.g.: audio-only breakout room transfers, which have their transparent LO mechanism disabled). Only enforce hold on muted state mismatch IF muted == false. --- .../apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala | 6 +++++- .../scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala | 7 ++++--- .../scala/org/bigbluebutton/core/models/VoiceUsers.scala | 4 ++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala index 0b16a39a61..1111793b3c 100644 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala @@ -12,7 +12,11 @@ trait ListenOnlyModeToggledInSfuEvtMsgHdlr { def handleListenOnlyModeToggledInSfuEvtMsg(msg: ListenOnlyModeToggledInSfuEvtMsg): Unit = { for { - vu <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId) + vu <- VoiceUsers.findWithIntIdAndCallerNum( + liveMeeting.voiceUsers, + msg.body.userId, + msg.body.callerNum + ) } yield { VoiceApp.holdChannelInVoiceConf( liveMeeting, diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala index afa2c68663..efb6218611 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala @@ -546,9 +546,10 @@ object VoiceApp extends SystemConfiguration { hold ) match { case Some(vu) => - // Mute vs hold state mismatch, enforce hold state again. - // Mute state is the predominant one here. - if (vu.muted != hold) { + // Mute vs hold state mismatch. Enforce it if the user is unmuted, + // but hold is active, to avoid the user being unable to talk when + // the channel is active again. + if (!vu.muted && vu.hold) { toggleListenOnlyMode( liveMeeting, outGW, diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/VoiceUsers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/VoiceUsers.scala index 8b9adbb15f..47ca576a4e 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/VoiceUsers.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/VoiceUsers.scala @@ -15,6 +15,10 @@ object VoiceUsers { users.toVector.find(u => u.uuid == uuid && u.intId == intId) } + def findWithIntIdAndCallerNum(users: VoiceUsers, intId: String, callerNum: String): Option[VoiceUserState] = { + users.toVector.find(u => u.callerNum == callerNum && u.intId == intId) + } + def findAll(users: VoiceUsers): Vector[VoiceUserState] = users.toVector def findAllNonListenOnlyVoiceUsers(users: VoiceUsers): Vector[VoiceUserState] = users.toVector.filter(u => u.listenOnly == false) From d3e2d3965cefa31cc29a1e6395d88156d240b918 Mon Sep 17 00:00:00 2001 From: prlanzarin <4529051+prlanzarin@users.noreply.github.com> Date: Mon, 24 Jun 2024 18:36:31 -0300 Subject: [PATCH 3/7] fix(audio): do not send FSESL hold cmd if already on hold This is an edge case with the uuid_hold command being used through FSESL or fsapi where holding only works via the uuid_hold subcommand, which may cause the channel to be the opposite of what we want. Do not execute if the command is asking for the channel to be HELD and the channel is already HELD. --- ...ListenOnlyModeToggledInSfuEvtMsgHdlr.scala | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala index 1111793b3c..b8eef0bc75 100644 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala @@ -18,12 +18,31 @@ trait ListenOnlyModeToggledInSfuEvtMsgHdlr { msg.body.callerNum ) } yield { - VoiceApp.holdChannelInVoiceConf( - liveMeeting, - outGW, - vu.uuid, - msg.body.enabled - ) + // Do not execute if the command is asking for the channel to be HELD + // and the channel is already HELD. This is an edge case with the uuid_hold + // command being used through FSESL or fsapi where holding only works via + // the uuid_hold subcommand, which may cause the channel to be the + // opposite of what we want. + // The unhold (uuid_hold off) command is not affected by this, but we don't + // want to send it if the channel is already unheld. + if (msg.body.enabled != vu.hold) { + VoiceApp.holdChannelInVoiceConf( + liveMeeting, + outGW, + vu.uuid, + msg.body.enabled + ) + } else { + // If the channel is already in the desired state, just make sure + // any pending mute or unmute commands are sent. + VoiceApp.handleChannelHoldChanged( + liveMeeting, + outGW, + msg.body.userId, + vu.uuid, + msg.body.enabled + ) + } } } } From 631d5186e5e22386e932f9584f9a5ed4498bd779 Mon Sep 17 00:00:00 2001 From: prlanzarin <4529051+prlanzarin@users.noreply.github.com> Date: Mon, 24 Jun 2024 18:41:15 -0300 Subject: [PATCH 4/7] feat(audio): add memberId to VoiceStateEvent when applicable We need the voiceUserId (memberId) in IN_CONFERENCE callState events so that the SFU can bypass dialplan-level mute states when transferring between breakout rooms. Add memberId to VoiceCallStateEvent when applicable. For CHANNEL_STATE events that do not carry it (RINGING, HANGUP), memberId is an empty string. --- .../voice/FreeswitchConferenceEventListener.java | 1 + .../freeswitch/voice/IVoiceConferenceService.java | 1 + .../voice/events/VoiceCallStateEvent.java | 8 ++++++++ .../voice/freeswitch/ESLEventListener.java | 14 ++++++++++++++ .../freeswitch/VoiceConferenceService.scala | 2 ++ .../bigbluebutton/common2/msgs/VoiceConfMsgs.scala | 1 + 6 files changed, 27 insertions(+) diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/FreeswitchConferenceEventListener.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/FreeswitchConferenceEventListener.java index 076d82cc54..b136b0fd57 100755 --- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/FreeswitchConferenceEventListener.java +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/FreeswitchConferenceEventListener.java @@ -109,6 +109,7 @@ public class FreeswitchConferenceEventListener implements ConferenceEventListene evt.callSession, evt.clientSession, evt.userId, + evt.getVoiceUserId(), evt.callerName, evt.callState, evt.origCallerIdName, diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/IVoiceConferenceService.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/IVoiceConferenceService.java index 5ca5f7b4c6..72080f8732 100755 --- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/IVoiceConferenceService.java +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/IVoiceConferenceService.java @@ -59,6 +59,7 @@ public interface IVoiceConferenceService { String callSession, String clientSession, String userId, + String voiceUserId, String callerName, String callState, String origCallerIdName, diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/VoiceCallStateEvent.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/VoiceCallStateEvent.java index 9d0a9277e6..f48b3576fd 100755 --- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/VoiceCallStateEvent.java +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/VoiceCallStateEvent.java @@ -4,6 +4,8 @@ public class VoiceCallStateEvent extends VoiceConferenceEvent { public final String callSession; public final String clientSession; public final String userId; + // AKA mod_conference memberId + public final String voiceUserId; public final String callerName; public final String callState; public final String origCallerIdName; @@ -14,6 +16,7 @@ public class VoiceCallStateEvent extends VoiceConferenceEvent { String callSession, String clientSession, String userId, + String voiceUserId, String callerName, String callState, String origCallerIdName, @@ -22,9 +25,14 @@ public class VoiceCallStateEvent extends VoiceConferenceEvent { this.callSession = callSession; this.clientSession = clientSession; this.userId = userId; + this.voiceUserId = voiceUserId; this.callerName = callerName; this.callState = callState; this.origCallerIdName = origCallerIdName; this.origCalledDest = origCalledDest; } + + public String getVoiceUserId() { + return voiceUserId; + } } diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ESLEventListener.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ESLEventListener.java index a67e6e5768..0549d6babf 100755 --- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ESLEventListener.java +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ESLEventListener.java @@ -84,6 +84,7 @@ public class ESLEventListener implements IEslEventListener { String origCallerIdName = headers.get("Caller-Caller-ID-Name"); String origCallerDestNumber = headers.get("Caller-Destination-Number"); String clientSession = "0"; + String memberIdStr = memberId != null ? memberId.toString() : ""; Matcher matcher = CALLERNAME_PATTERN.matcher(callerIdName); Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(callerIdName); @@ -106,6 +107,7 @@ public class ESLEventListener implements IEslEventListener { coreuuid, clientSession, voiceUserId, + memberIdStr, callerIdName, callState, origCallerIdName, @@ -281,6 +283,7 @@ public class ESLEventListener implements IEslEventListener { String varvBridge = (eventHeaders.get("variable_vbridge") == null) ? "" : eventHeaders.get("variable_vbridge"); if ("echo".equalsIgnoreCase(application) && !varvBridge.isEmpty()) { + Integer memberId = this.getMemberId(eventHeaders); String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name"); String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number"); String coreuuid = eventHeaders.get("Core-UUID"); @@ -291,6 +294,7 @@ public class ESLEventListener implements IEslEventListener { String callerName = origCallerIdName; String clientSession = "0"; String callState = "IN_ECHO_TEST"; + String memberIdStr = memberId != null ? memberId.toString() : ""; Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName); Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName); @@ -314,6 +318,7 @@ public class ESLEventListener implements IEslEventListener { coreuuid, clientSession, voiceUserId, + memberIdStr, callerName, callState, origCallerIdName, @@ -321,6 +326,7 @@ public class ESLEventListener implements IEslEventListener { conferenceEventListener.handleConferenceEvent(csEvent); } else if ("RINGING".equalsIgnoreCase(channelCallState) && !varvBridge.isEmpty()) { + Integer memberId = this.getMemberId(eventHeaders); String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name"); String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number"); String coreuuid = eventHeaders.get("Core-UUID"); @@ -330,6 +336,7 @@ public class ESLEventListener implements IEslEventListener { String callerName = origCallerIdName; String clientSession = "0"; String callState = "CALL_STARTED"; + String memberIdStr = memberId != null ? memberId.toString() : ""; Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName); Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName); @@ -353,6 +360,7 @@ public class ESLEventListener implements IEslEventListener { coreuuid, clientSession, voiceUserId, + memberIdStr, callerName, callState, origCallerIdName, @@ -365,6 +373,7 @@ public class ESLEventListener implements IEslEventListener { String channelState = (eventHeaders.get("Channel-State") == null) ? "" : eventHeaders.get("Channel-State"); if ("HANGUP".equalsIgnoreCase(channelCallState) && "CS_DESTROY".equalsIgnoreCase(channelState)) { + Integer memberId = this.getMemberId(eventHeaders); String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name"); String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number"); String coreuuid = eventHeaders.get("Core-UUID"); @@ -374,6 +383,7 @@ public class ESLEventListener implements IEslEventListener { String callerName = origCallerIdName; String clientSession = "0"; String callState = "CALL_ENDED"; + String memberIdStr = memberId != null ? memberId.toString() : ""; Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName); Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName); @@ -397,6 +407,7 @@ public class ESLEventListener implements IEslEventListener { coreuuid, clientSession, voiceUserId, + memberIdStr, callerName, callState, origCallerIdName, @@ -405,6 +416,7 @@ public class ESLEventListener implements IEslEventListener { conferenceEventListener.handleConferenceEvent(csEvent); } else if ("RINGING".equalsIgnoreCase(channelCallState) && "CS_EXECUTE".equalsIgnoreCase(channelState)) { + Integer memberId = this.getMemberId(eventHeaders); String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name"); String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number"); String coreuuid = eventHeaders.get("Core-UUID"); @@ -414,6 +426,7 @@ public class ESLEventListener implements IEslEventListener { String callerName = origCallerIdName; String clientSession = "0"; String callState = "CALL_STARTED"; + String memberIdStr = memberId != null ? memberId.toString() : ""; Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName); Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName); @@ -437,6 +450,7 @@ public class ESLEventListener implements IEslEventListener { coreuuid, clientSession, voiceUserId, + memberIdStr, callerName, callState, origCallerIdName, diff --git a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala index 4e520a82dc..4c73bedcf5 100755 --- a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala +++ b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala @@ -229,6 +229,7 @@ class VoiceConferenceService(healthz: HealthzService, callSession: String, clientSession: String, userId: String, + voiceUserId: String, callerName: String, callState: String, origCallerIdName: String, @@ -240,6 +241,7 @@ class VoiceConferenceService(healthz: HealthzService, callSession = callSession, clientSession = clientSession, userId = userId, + voiceUserId = voiceUserId, callerName = callerName, callState = callState, origCallerIdName = origCallerIdName, diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala index 5fedb8792c..4bea316d11 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala @@ -555,6 +555,7 @@ case class VoiceConfCallStateEvtMsgBody( callSession: String, clientSession: String, userId: String, + voiceUserId: String, callerName: String, callState: String, origCallerIdName: String, From 1c57db94bb15d07dfb26c6f15e80ef7b42c42893 Mon Sep 17 00:00:00 2001 From: prlanzarin <4529051+prlanzarin@users.noreply.github.com> Date: Thu, 27 Jun 2024 21:22:32 -0300 Subject: [PATCH 5/7] fix(audio): always send unhold Alway send the unhold command since it doesn't change flip the state (contrary to the uuid_hold toggle command). It's not idempotent, though - so always update the internal hold state to prevent state mismatches preventing channels from being unheld. --- ...ListenOnlyModeToggledInSfuEvtMsgHdlr.scala | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala index b8eef0bc75..b2e137c1e6 100644 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/ListenOnlyModeToggledInSfuEvtMsgHdlr.scala @@ -25,24 +25,24 @@ trait ListenOnlyModeToggledInSfuEvtMsgHdlr { // opposite of what we want. // The unhold (uuid_hold off) command is not affected by this, but we don't // want to send it if the channel is already unheld. - if (msg.body.enabled != vu.hold) { + if ((msg.body.enabled && !vu.hold) || !msg.body.enabled) { VoiceApp.holdChannelInVoiceConf( liveMeeting, outGW, vu.uuid, msg.body.enabled ) - } else { - // If the channel is already in the desired state, just make sure - // any pending mute or unmute commands are sent. - VoiceApp.handleChannelHoldChanged( - liveMeeting, - outGW, - msg.body.userId, - vu.uuid, - msg.body.enabled - ) } + + // If the channel is already in the desired state, just make sure + // any pending mute or unmute commands are sent. + VoiceApp.handleChannelHoldChanged( + liveMeeting, + outGW, + msg.body.userId, + vu.uuid, + msg.body.enabled + ) } } } From 6918d4e0900529d5fe10d53ccc4843f2b13f5ab2 Mon Sep 17 00:00:00 2001 From: prlanzarin <4529051+prlanzarin@users.noreply.github.com> Date: Tue, 23 Jul 2024 09:23:50 -0300 Subject: [PATCH 6/7] build(bbb-webrtc-sfu): v2.14.0 v2.14.0 --- * feat(mediasoup): add least-loaded worker balancing strategy * feat(mediasoup): worker transposition (off by default) * feat(audio): dynamic global audio bridge mechanism * feat: livekit module, initial implementation * feat(audio): add signaling support for passive-sendrecv role * feat(freeswitch): overridable UA string * feat(audio): muteOnStart detection for conditional dialplans * feat(audio): mute passive-sendrecv clients on start * feat(audio): support for mute-and-hold on start * fix(audio): ignore TLO-incapable clients in hold/unhold metrics * fix(audio): mute/unmute stuck due to inconsistent hold status * fix(audio): hold/unhold loop when there are multiple sessions per user * fix(audio): muteOnStart sessions incorrectly muted on breakout transfers * fix(audio): header-provided userName incorrectly decoded * fix(audio): stuck unmute due to borked callerIdNum * fix(audio): correctly decode user name space chars * !build(npm): set min Node.js version to >=18.0.0 * build: nodemon@3.1.3 * build: ws@8.17.1 * build(mediasoup): 3.14.8 --- bbb-webrtc-sfu.placeholder.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bbb-webrtc-sfu.placeholder.sh b/bbb-webrtc-sfu.placeholder.sh index e94cdce28c..ed899aacbe 100755 --- a/bbb-webrtc-sfu.placeholder.sh +++ b/bbb-webrtc-sfu.placeholder.sh @@ -1 +1 @@ -git clone --branch v2.13.3 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu +git clone --branch v2.14.0 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu From 437c6844239716fdf39e5c72cea86263abdc830d Mon Sep 17 00:00:00 2001 From: prlanzarin <4529051+prlanzarin@users.noreply.github.com> Date: Wed, 3 Jul 2024 00:10:14 -0300 Subject: [PATCH 7/7] fix(audio): stuck unmute due to borked callerId(Num) FreeSWITCH incorrectly generates callerNum headers in its ESL events when specific, special characters are in place. e.g.: w_etc_0-bbbID-User;Semi (notice the semicolon) will be generated by FS as w_etc_0-bbbID-User (everything after the semicolon is ignored). This breaks callerId comparision for session matching in a few places - one of the is the unmute/unhold toggle control. Compare callerNum as prefixes instead of exact strings to match those scenarios. This is a temporary fix; we should review callerNum generation in the future (use Caller-Id-Name in FSESL), as well as stop relying on it for session matching (use UUIDs and/or client session numbers instead). Both of these are more involved changes, though. --- .../org/bigbluebutton/core/models/VoiceUsers.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/VoiceUsers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/VoiceUsers.scala index 47ca576a4e..abac75842b 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/VoiceUsers.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/VoiceUsers.scala @@ -16,7 +16,15 @@ object VoiceUsers { } def findWithIntIdAndCallerNum(users: VoiceUsers, intId: String, callerNum: String): Option[VoiceUserState] = { - users.toVector.find(u => u.callerNum == callerNum && u.intId == intId) + // prlanzarin: This is a hack to allow for partial matching of callerNums. + // This is needed because the callerNums are incorrectly generated by + // FREESWITCH's ESL events when special characters are in place. + // e.g.: w_etc_0-bbbID-User;Semi (notice the semicolon) will be generated by + // FS as w_etc_0-bbbID-User (everything after the semicolon is ignored). + // We should review callerNum generation in the future as well as stop + // relying on it for session matching (use UUIDs or client session numbers instead). + users.toVector.find(u => u.intId == intId && + (u.callerNum.startsWith(callerNum) || callerNum.startsWith(u.callerNum))) } def findAll(users: VoiceUsers): Vector[VoiceUserState] = users.toVector