Merge pull request #20752 from prlanzarin/u27/fix/tlo-br-ralam-sfu2140

fix(audio): transparent listen only state inconsistencies, build(bbb-webrtc-sfu): v2.14.0
This commit is contained in:
Anton Georgiev 2024-08-01 10:13:41 -04:00 committed by GitHub
commit 20d4944ce6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 77 additions and 7 deletions

View File

@ -12,11 +12,34 @@ trait ListenOnlyModeToggledInSfuEvtMsgHdlr {
def handleListenOnlyModeToggledInSfuEvtMsg(msg: ListenOnlyModeToggledInSfuEvtMsg): Unit = { def handleListenOnlyModeToggledInSfuEvtMsg(msg: ListenOnlyModeToggledInSfuEvtMsg): Unit = {
for { for {
vu <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId) vu <- VoiceUsers.findWithIntIdAndCallerNum(
liveMeeting.voiceUsers,
msg.body.userId,
msg.body.callerNum
)
} yield { } yield {
VoiceApp.holdChannelInVoiceConf( // 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 <toggle> 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) || !msg.body.enabled) {
VoiceApp.holdChannelInVoiceConf(
liveMeeting,
outGW,
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, liveMeeting,
outGW, outGW,
msg.body.userId,
vu.uuid, vu.uuid,
msg.body.enabled msg.body.enabled
) )

View File

@ -131,6 +131,7 @@ object VoiceApp extends SystemConfiguration {
liveMeeting, liveMeeting,
outGW, outGW,
mutedUser.intId, mutedUser.intId,
mutedUser.callerNum,
muted, muted,
toggleListenOnlyAfterMuteTimer toggleListenOnlyAfterMuteTimer
) )
@ -476,6 +477,7 @@ object VoiceApp extends SystemConfiguration {
liveMeeting: LiveMeeting, liveMeeting: LiveMeeting,
outGW: OutMsgRouter, outGW: OutMsgRouter,
userId: String, userId: String,
callerNum: String,
enabled: Boolean, enabled: Boolean,
delay: Int = 0 delay: Int = 0
)(implicit context: ActorContext): Unit = { )(implicit context: ActorContext): Unit = {
@ -485,6 +487,7 @@ object VoiceApp extends SystemConfiguration {
liveMeeting.props.meetingProp.intId, liveMeeting.props.meetingProp.intId,
liveMeeting.props.voiceProp.voiceConf, liveMeeting.props.voiceProp.voiceConf,
userId, userId,
callerNum,
enabled enabled
) )
outGW.send(event) outGW.send(event)
@ -543,13 +546,15 @@ object VoiceApp extends SystemConfiguration {
hold hold
) match { ) match {
case Some(vu) => case Some(vu) =>
// Mute vs hold state mismatch, enforce hold state again. // Mute vs hold state mismatch. Enforce it if the user is unmuted,
// Mute state is the predominant one here. // but hold is active, to avoid the user being unable to talk when
if (vu.muted != hold) { // the channel is active again.
if (!vu.muted && vu.hold) {
toggleListenOnlyMode( toggleListenOnlyMode(
liveMeeting, liveMeeting,
outGW, outGW,
intId, intId,
vu.callerNum,
vu.muted vu.muted
) )
} }

View File

@ -15,6 +15,18 @@ object VoiceUsers {
users.toVector.find(u => u.uuid == uuid && u.intId == intId) users.toVector.find(u => u.uuid == uuid && u.intId == intId)
} }
def findWithIntIdAndCallerNum(users: VoiceUsers, intId: String, callerNum: String): Option[VoiceUserState] = {
// 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 def findAll(users: VoiceUsers): Vector[VoiceUserState] = users.toVector
def findAllNonListenOnlyVoiceUsers(users: VoiceUsers): Vector[VoiceUserState] = users.toVector.filter(u => u.listenOnly == false) def findAllNonListenOnlyVoiceUsers(users: VoiceUsers): Vector[VoiceUserState] = users.toVector.filter(u => u.listenOnly == false)

View File

@ -637,11 +637,12 @@ object MsgBuilder {
meetingId: String, meetingId: String,
voiceConf: String, voiceConf: String,
userId: String, userId: String,
callerNum: String,
enabled: Boolean enabled: Boolean
): BbbCommonEnvCoreMsg = { ): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(ToggleListenOnlyModeSysMsg.NAME, routing) 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 header = BbbCoreHeaderWithMeetingId(ToggleListenOnlyModeSysMsg.NAME, meetingId)
val event = ToggleListenOnlyModeSysMsg(header, body) val event = ToggleListenOnlyModeSysMsg(header, body)

View File

@ -109,6 +109,7 @@ public class FreeswitchConferenceEventListener implements ConferenceEventListene
evt.callSession, evt.callSession,
evt.clientSession, evt.clientSession,
evt.userId, evt.userId,
evt.getVoiceUserId(),
evt.callerName, evt.callerName,
evt.callState, evt.callState,
evt.origCallerIdName, evt.origCallerIdName,

View File

@ -59,6 +59,7 @@ public interface IVoiceConferenceService {
String callSession, String callSession,
String clientSession, String clientSession,
String userId, String userId,
String voiceUserId,
String callerName, String callerName,
String callState, String callState,
String origCallerIdName, String origCallerIdName,

View File

@ -4,6 +4,8 @@ public class VoiceCallStateEvent extends VoiceConferenceEvent {
public final String callSession; public final String callSession;
public final String clientSession; public final String clientSession;
public final String userId; public final String userId;
// AKA mod_conference memberId
public final String voiceUserId;
public final String callerName; public final String callerName;
public final String callState; public final String callState;
public final String origCallerIdName; public final String origCallerIdName;
@ -14,6 +16,7 @@ public class VoiceCallStateEvent extends VoiceConferenceEvent {
String callSession, String callSession,
String clientSession, String clientSession,
String userId, String userId,
String voiceUserId,
String callerName, String callerName,
String callState, String callState,
String origCallerIdName, String origCallerIdName,
@ -22,9 +25,14 @@ public class VoiceCallStateEvent extends VoiceConferenceEvent {
this.callSession = callSession; this.callSession = callSession;
this.clientSession = clientSession; this.clientSession = clientSession;
this.userId = userId; this.userId = userId;
this.voiceUserId = voiceUserId;
this.callerName = callerName; this.callerName = callerName;
this.callState = callState; this.callState = callState;
this.origCallerIdName = origCallerIdName; this.origCallerIdName = origCallerIdName;
this.origCalledDest = origCalledDest; this.origCalledDest = origCalledDest;
} }
public String getVoiceUserId() {
return voiceUserId;
}
} }

View File

@ -84,6 +84,7 @@ public class ESLEventListener implements IEslEventListener {
String origCallerIdName = headers.get("Caller-Caller-ID-Name"); String origCallerIdName = headers.get("Caller-Caller-ID-Name");
String origCallerDestNumber = headers.get("Caller-Destination-Number"); String origCallerDestNumber = headers.get("Caller-Destination-Number");
String clientSession = "0"; String clientSession = "0";
String memberIdStr = memberId != null ? memberId.toString() : "";
Matcher matcher = CALLERNAME_PATTERN.matcher(callerIdName); Matcher matcher = CALLERNAME_PATTERN.matcher(callerIdName);
Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(callerIdName); Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(callerIdName);
@ -106,6 +107,7 @@ public class ESLEventListener implements IEslEventListener {
coreuuid, coreuuid,
clientSession, clientSession,
voiceUserId, voiceUserId,
memberIdStr,
callerIdName, callerIdName,
callState, callState,
origCallerIdName, origCallerIdName,
@ -281,6 +283,7 @@ public class ESLEventListener implements IEslEventListener {
String varvBridge = (eventHeaders.get("variable_vbridge") == null) ? "" : eventHeaders.get("variable_vbridge"); String varvBridge = (eventHeaders.get("variable_vbridge") == null) ? "" : eventHeaders.get("variable_vbridge");
if ("echo".equalsIgnoreCase(application) && !varvBridge.isEmpty()) { if ("echo".equalsIgnoreCase(application) && !varvBridge.isEmpty()) {
Integer memberId = this.getMemberId(eventHeaders);
String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name"); String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name");
String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number"); String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number");
String coreuuid = eventHeaders.get("Core-UUID"); String coreuuid = eventHeaders.get("Core-UUID");
@ -291,6 +294,7 @@ public class ESLEventListener implements IEslEventListener {
String callerName = origCallerIdName; String callerName = origCallerIdName;
String clientSession = "0"; String clientSession = "0";
String callState = "IN_ECHO_TEST"; String callState = "IN_ECHO_TEST";
String memberIdStr = memberId != null ? memberId.toString() : "";
Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName); Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName);
Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName); Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName);
@ -314,6 +318,7 @@ public class ESLEventListener implements IEslEventListener {
coreuuid, coreuuid,
clientSession, clientSession,
voiceUserId, voiceUserId,
memberIdStr,
callerName, callerName,
callState, callState,
origCallerIdName, origCallerIdName,
@ -321,6 +326,7 @@ public class ESLEventListener implements IEslEventListener {
conferenceEventListener.handleConferenceEvent(csEvent); conferenceEventListener.handleConferenceEvent(csEvent);
} else if ("RINGING".equalsIgnoreCase(channelCallState) && !varvBridge.isEmpty()) { } else if ("RINGING".equalsIgnoreCase(channelCallState) && !varvBridge.isEmpty()) {
Integer memberId = this.getMemberId(eventHeaders);
String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name"); String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name");
String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number"); String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number");
String coreuuid = eventHeaders.get("Core-UUID"); String coreuuid = eventHeaders.get("Core-UUID");
@ -330,6 +336,7 @@ public class ESLEventListener implements IEslEventListener {
String callerName = origCallerIdName; String callerName = origCallerIdName;
String clientSession = "0"; String clientSession = "0";
String callState = "CALL_STARTED"; String callState = "CALL_STARTED";
String memberIdStr = memberId != null ? memberId.toString() : "";
Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName); Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName);
Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName); Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName);
@ -353,6 +360,7 @@ public class ESLEventListener implements IEslEventListener {
coreuuid, coreuuid,
clientSession, clientSession,
voiceUserId, voiceUserId,
memberIdStr,
callerName, callerName,
callState, callState,
origCallerIdName, origCallerIdName,
@ -365,6 +373,7 @@ public class ESLEventListener implements IEslEventListener {
String channelState = (eventHeaders.get("Channel-State") == null) ? "" : eventHeaders.get("Channel-State"); String channelState = (eventHeaders.get("Channel-State") == null) ? "" : eventHeaders.get("Channel-State");
if ("HANGUP".equalsIgnoreCase(channelCallState) && "CS_DESTROY".equalsIgnoreCase(channelState)) { if ("HANGUP".equalsIgnoreCase(channelCallState) && "CS_DESTROY".equalsIgnoreCase(channelState)) {
Integer memberId = this.getMemberId(eventHeaders);
String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name"); String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name");
String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number"); String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number");
String coreuuid = eventHeaders.get("Core-UUID"); String coreuuid = eventHeaders.get("Core-UUID");
@ -374,6 +383,7 @@ public class ESLEventListener implements IEslEventListener {
String callerName = origCallerIdName; String callerName = origCallerIdName;
String clientSession = "0"; String clientSession = "0";
String callState = "CALL_ENDED"; String callState = "CALL_ENDED";
String memberIdStr = memberId != null ? memberId.toString() : "";
Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName); Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName);
Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName); Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName);
@ -397,6 +407,7 @@ public class ESLEventListener implements IEslEventListener {
coreuuid, coreuuid,
clientSession, clientSession,
voiceUserId, voiceUserId,
memberIdStr,
callerName, callerName,
callState, callState,
origCallerIdName, origCallerIdName,
@ -405,6 +416,7 @@ public class ESLEventListener implements IEslEventListener {
conferenceEventListener.handleConferenceEvent(csEvent); conferenceEventListener.handleConferenceEvent(csEvent);
} else if ("RINGING".equalsIgnoreCase(channelCallState) && "CS_EXECUTE".equalsIgnoreCase(channelState)) { } else if ("RINGING".equalsIgnoreCase(channelCallState) && "CS_EXECUTE".equalsIgnoreCase(channelState)) {
Integer memberId = this.getMemberId(eventHeaders);
String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name"); String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name");
String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number"); String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number");
String coreuuid = eventHeaders.get("Core-UUID"); String coreuuid = eventHeaders.get("Core-UUID");
@ -414,6 +426,7 @@ public class ESLEventListener implements IEslEventListener {
String callerName = origCallerIdName; String callerName = origCallerIdName;
String clientSession = "0"; String clientSession = "0";
String callState = "CALL_STARTED"; String callState = "CALL_STARTED";
String memberIdStr = memberId != null ? memberId.toString() : "";
Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName); Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName);
Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName); Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName);
@ -437,6 +450,7 @@ public class ESLEventListener implements IEslEventListener {
coreuuid, coreuuid,
clientSession, clientSession,
voiceUserId, voiceUserId,
memberIdStr,
callerName, callerName,
callState, callState,
origCallerIdName, origCallerIdName,

View File

@ -229,6 +229,7 @@ class VoiceConferenceService(healthz: HealthzService,
callSession: String, callSession: String,
clientSession: String, clientSession: String,
userId: String, userId: String,
voiceUserId: String,
callerName: String, callerName: String,
callState: String, callState: String,
origCallerIdName: String, origCallerIdName: String,
@ -240,6 +241,7 @@ class VoiceConferenceService(healthz: HealthzService,
callSession = callSession, callSession = callSession,
clientSession = clientSession, clientSession = clientSession,
userId = userId, userId = userId,
voiceUserId = voiceUserId,
callerName = callerName, callerName = callerName,
callState = callState, callState = callState,
origCallerIdName = origCallerIdName, origCallerIdName = origCallerIdName,

View File

@ -555,6 +555,7 @@ case class VoiceConfCallStateEvtMsgBody(
callSession: String, callSession: String,
clientSession: String, clientSession: String,
userId: String, userId: String,
voiceUserId: String,
callerName: String, callerName: String,
callState: String, callState: String,
origCallerIdName: String, origCallerIdName: String,
@ -685,6 +686,7 @@ case class ToggleListenOnlyModeSysMsg(
case class ToggleListenOnlyModeSysMsgBody( case class ToggleListenOnlyModeSysMsgBody(
voiceConf: String, voiceConf: String,
userId: String, userId: String,
callerNum: String,
enabled: Boolean enabled: Boolean
) )
@ -701,5 +703,6 @@ case class ListenOnlyModeToggledInSfuEvtMsgBody(
meetingId: String, meetingId: String,
voiceConf: String, voiceConf: String,
userId: String, userId: String,
callerNum: String,
enabled: Boolean enabled: Boolean
) )

View File

@ -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