diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala index c76b11a0f7..82f81a56d7 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala @@ -26,11 +26,13 @@ trait UsersApp { val user = usersModel.getUser(msg.userid) user foreach { u => - val vu = u.voiceUser.copy(joined = false, talking = false) - val uvo = u.copy(listenOnly = true, voiceUser = vu) - usersModel.addUser(uvo) - log.info("UserConnectedToGlobalAudio: meetingId=" + mProps.meetingID + " userId=" + uvo.userID + " user=" + uvo) - outGW.send(new UserListeningOnly(mProps.meetingID, mProps.recorded, uvo.userID, uvo.listenOnly)) + if (usersModel.addGlobalAudioConnection(msg.userid)) { + val vu = u.voiceUser.copy(joined = false, talking = false) + val uvo = u.copy(listenOnly = true, voiceUser = vu) + usersModel.addUser(uvo) + log.info("UserConnectedToGlobalAudio: meetingId=" + mProps.meetingID + " userId=" + uvo.userID + " user=" + uvo) + outGW.send(new UserListeningOnly(mProps.meetingID, mProps.recorded, uvo.userID, uvo.listenOnly)) + } } } @@ -39,16 +41,18 @@ trait UsersApp { val user = usersModel.getUser(msg.userid) user foreach { u => - if (!u.joinedWeb) { - val userLeaving = usersModel.removeUser(u.userID) - log.info("Not web user. Send user left message. meetingId=" + mProps.meetingID + " userId=" + u.userID + " user=" + u) - userLeaving foreach (u => outGW.send(new UserLeft(mProps.meetingID, mProps.recorded, u))) - } else { - val vu = u.voiceUser.copy(joined = false) - val uvo = u.copy(listenOnly = false, voiceUser = vu) - usersModel.addUser(uvo) - log.info("UserDisconnectedToGlobalAudio: meetingId=" + mProps.meetingID + " userId=" + uvo.userID + " user=" + uvo) - outGW.send(new UserListeningOnly(mProps.meetingID, mProps.recorded, uvo.userID, uvo.listenOnly)) + if (usersModel.removeGlobalAudioConnection(msg.userid)) { + if (!u.joinedWeb) { + val userLeaving = usersModel.removeUser(u.userID) + log.info("Not web user. Send user left message. meetingId=" + mProps.meetingID + " userId=" + u.userID + " user=" + u) + userLeaving foreach (u => outGW.send(new UserLeft(mProps.meetingID, mProps.recorded, u))) + } else { + val vu = u.voiceUser.copy(joined = false) + val uvo = u.copy(listenOnly = false, voiceUser = vu) + usersModel.addUser(uvo) + log.info("UserDisconnectedToGlobalAudio: meetingId=" + mProps.meetingID + " userId=" + uvo.userID + " user=" + uvo) + outGW.send(new UserListeningOnly(mProps.meetingID, mProps.recorded, uvo.userID, uvo.listenOnly)) + } } } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersModel.scala index 9fe3bdd34d..9c2f264840 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersModel.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersModel.scala @@ -14,6 +14,12 @@ class UsersModel { private var regUsers = new collection.immutable.HashMap[String, RegisteredUser] + /* When reconnecting SIP global audio, users may receive the connection message + * before the disconnection message. + * This variable is a connection counter that should control this scenario. + */ + private var globalAudioConnectionCounter = new collection.immutable.HashMap[String, Integer] + private var locked = false private var meetingMuted = false @@ -144,4 +150,34 @@ class UsersModel { case None => } } + + def addGlobalAudioConnection(userID: String): Boolean = { + globalAudioConnectionCounter.get(userID) match { + case Some(vc) => { + globalAudioConnectionCounter += userID -> (vc + 1) + false + } + case None => { + globalAudioConnectionCounter += userID -> 1 + true + } + } + } + + def removeGlobalAudioConnection(userID: String): Boolean = { + globalAudioConnectionCounter.get(userID) match { + case Some(vc) => { + if (vc == 1) { + globalAudioConnectionCounter -= userID + true + } else { + globalAudioConnectionCounter += userID -> (vc - 1) + false + } + } + case None => { + false + } + } + } } \ No newline at end of file diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java index 0a3e699e19..ada2122d5d 100755 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java @@ -133,6 +133,7 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter { connection.setAttribute(Constants.SESSION, bbbSession); connection.setAttribute("INTERNAL_USER_ID", internalUserID); connection.setAttribute("USER_SESSION_ID", sessionId); + connection.setAttribute("TIMESTAMP", System.currentTimeMillis()); red5InGW.initLockSettings(room, lsMap); diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java index fcd5d386d9..501209469d 100755 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java @@ -21,6 +21,7 @@ package org.bigbluebutton.red5.client.messaging; import java.util.Set; import java.util.ArrayList; import java.util.List; +import java.util.HashSet; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -274,15 +275,31 @@ public class ConnectionInvokerService { } private IConnection getConnection(IScope scope, String userID) { - Set conns = scope.getClientConnections(); - for (IConnection conn : conns) { + Set conns = new HashSet(); + for (IConnection conn : scope.getClientConnections()) { String connID = (String) conn.getAttribute("USER_SESSION_ID"); if (connID != null && connID.equals(userID)) { - return conn; + conns.add(conn); } } - log.warn("Failed to get connection for userId = " + userID); - return null; + if (!conns.isEmpty()) { + return getLastConnection(conns); + } else { + log.warn("Failed to get connection for userId = " + userID); + return null; + } + } + + private IConnection getLastConnection(Set conns) { + IConnection conn = null; + for (IConnection c : conns) { + if (conn == null) { + conn = c; + } else if ((long) conn.getAttribute("TIMESTAMP") < (long) c.getAttribute("TIMESTAMP")) { + conn = c; + } + } + return conn; } public IScope getScope(String meetingID) { diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/ConnectionManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/ConnectionManager.as index e7d03d7437..a3eaa9085b 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/ConnectionManager.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/ConnectionManager.as @@ -53,6 +53,7 @@ package org.bigbluebutton.modules.phone.managers { private var closedByUser:Boolean = false; private var reconnecting:Boolean = false; + private var amIListenOnly:Boolean = false; private var dispatcher:Dispatcher; @@ -83,19 +84,24 @@ package org.bigbluebutton.modules.phone.managers { } public function connect():void { - closedByUser = false; - var isTunnelling:Boolean = BBB.initConnectionManager().isTunnelling; - if (isTunnelling) { - uri = uri.replace(/rtmp:/gi, "rtmpt:"); - } - LOGGER.debug("Connecting to uri=[{0}]", [uri]); - NetConnection.defaultObjectEncoding = flash.net.ObjectEncoding.AMF0; - netConnection = new NetConnection(); - netConnection.proxyType = "best"; - netConnection.client = this; - netConnection.addEventListener( NetStatusEvent.NET_STATUS , netStatus ); - netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); - netConnection.connect(uri, meetingId, externUserId, username); + if (!reconnecting || amIListenOnly) { + closedByUser = false; + var isTunnelling:Boolean = BBB.initConnectionManager().isTunnelling; + if (isTunnelling) { + uri = uri.replace(/rtmp:/gi, "rtmpt:"); + } + LOGGER.debug("Connecting to uri=[{0}]", [uri]); + NetConnection.defaultObjectEncoding = flash.net.ObjectEncoding.AMF0; + netConnection = new NetConnection(); + netConnection.proxyType = "best"; + netConnection.client = this; + netConnection.addEventListener( NetStatusEvent.NET_STATUS , netStatus ); + netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); + netConnection.connect(uri, meetingId, externUserId, username); + } + if (reconnecting && !amIListenOnly) { + handleConnectionSuccess(); + } } public function disconnect(requestByUser:Boolean):void { @@ -134,7 +140,7 @@ package org.bigbluebutton.modules.phone.managers { disconnectedEvent.payload.callbackParameters = []; dispatcher.dispatchEvent(disconnectedEvent); - dispatcher.dispatchEvent(new FlashVoiceConnectionStatusEvent(FlashVoiceConnectionStatusEvent.DISCONNECTED)); + dispatcher.dispatchEvent(new FlashVoiceConnectionStatusEvent(FlashVoiceConnectionStatusEvent.DISCONNECTED, reconnecting)); } } @@ -212,12 +218,14 @@ package org.bigbluebutton.modules.phone.managers { // //******************************************************************************************** public function doCall(dialStr:String, listenOnly:Boolean = false):void { + amIListenOnly = listenOnly; LOGGER.debug("in doCall - Calling {0} {1}", [dialStr, listenOnly? "*listen only*": ""]); netConnection.call("voiceconf.call", null, "default", username, dialStr, listenOnly.toString()); } public function doHangUp():void { if (isConnected()) { + amIListenOnly = false; LOGGER.debug("hanging up call"); netConnection.call("voiceconf.hangup", null, "default"); } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/FlashCallManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/FlashCallManager.as index 8cc7ad818f..3d5ba4ee63 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/FlashCallManager.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/FlashCallManager.as @@ -12,6 +12,7 @@ import org.as3commons.logging.util.jsonXify; import org.bigbluebutton.common.Media; import org.bigbluebutton.core.UsersUtil; + import org.bigbluebutton.core.events.VoiceConfEvent; import org.bigbluebutton.main.api.JSLog; import org.bigbluebutton.modules.phone.PhoneOptions; import org.bigbluebutton.modules.phone.events.FlashCallConnectedEvent; @@ -453,5 +454,13 @@ connectionManager.disconnect(true); } } + + public function handleReconnectSIPSucceededEvent():void { + if (state != ON_LISTEN_ONLY_STREAM) { + var e:VoiceConfEvent = new VoiceConfEvent(VoiceConfEvent.EJECT_USER); + e.userid = UsersUtil.getMyUserID(); + dispatcher.dispatchEvent(e); + } + } } } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/FlashCallEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/FlashCallEventMap.mxml index 77f3c9a13a..2aadf3376b 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/FlashCallEventMap.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/FlashCallEventMap.mxml @@ -28,6 +28,7 @@ with BigBlueButton; if not, see . import mx.events.FlexEvent; import org.bigbluebutton.main.events.MadePresenterEvent; + import org.bigbluebutton.main.events.BBBEvent; import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent; import org.bigbluebutton.modules.phone.events.FlashCallConnectedEvent; import org.bigbluebutton.modules.phone.events.FlashCallDisconnectedEvent; @@ -119,4 +120,8 @@ with BigBlueButton; if not, see . + + + +