diff --git a/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java b/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java index d5562595647c8e667561507bed7284bc40f206ea..ce1c25bb2329733c64c0dad0034ac1260f29e624 100755 --- a/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java +++ b/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java @@ -79,6 +79,9 @@ public class VideoApplication extends MultiThreadedApplicationAdapter { for (IConnection conn : conns) { String connUserId = (String) conn.getAttribute("USERID"); String connSessionId = conn.getSessionId(); + String clientId = conn.getClient().getId(); + String remoteHost = conn.getRemoteAddress(); + int remotePort = conn.getRemotePort(); if (connUserId != null && connUserId.equals(userId) && !connSessionId.equals(sessionId)) { conn.removeAttribute("USERID"); Map<String, Object> logData = new HashMap<String, Object>(); @@ -86,6 +89,8 @@ public class VideoApplication extends MultiThreadedApplicationAdapter { logData.put("userId", userId); logData.put("oldConnId", connSessionId); logData.put("newConnId", sessionId); + logData.put("clientId", clientId); + logData.put("remoteAddress", remoteHost + ":" + remotePort); logData.put("event", "removing_defunct_connection"); logData.put("description", "Removing defunct connection BBB Video."); @@ -96,11 +101,17 @@ public class VideoApplication extends MultiThreadedApplicationAdapter { } } + String remoteHost = Red5.getConnectionLocal().getRemoteAddress(); + int remotePort = Red5.getConnectionLocal().getRemotePort(); + String clientId = Red5.getConnectionLocal().getClient().getId(); + Map<String, Object> logData = new HashMap<String, Object>(); logData.put("meetingId", meetingId); logData.put("userId", userId); logData.put("connType", connType); logData.put("connId", sessionId); + logData.put("clientId", clientId); + logData.put("remoteAddress", remoteHost + ":" + remotePort); logData.put("event", "user_joining_bbb_video"); logData.put("description", "User joining BBB Video."); 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 ee600aab123798fba46d0dfd44e3963eba47a1ec..d508fa1f8f88316f7d5977935295fffc5e1d1673 100755 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java @@ -138,6 +138,9 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter { for (IConnection conn : conns) { String connUserId = (String) conn.getAttribute("INTERNAL_USER_ID"); String connSessionId = conn.getSessionId(); + String clientId = conn.getClient().getId(); + String remoteHost = connection.getRemoteAddress(); + int remotePort = connection.getRemotePort(); if (connUserId != null && connUserId.equals(userId) && !connSessionId.equals(sessionId)) { conn.removeAttribute("INTERNAL_USER_ID"); Map<String, Object> logData = new HashMap<String, Object>(); @@ -145,6 +148,8 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter { logData.put("userId", userId); logData.put("oldConnId", connSessionId); logData.put("newConnId", sessionId); + logData.put("clientId", clientId); + logData.put("remoteAddress", remoteHost + ":" + remotePort); logData.put("event", "removing_defunct_connection"); logData.put("description", "Removing defunct connection BBB Apps."); @@ -173,14 +178,15 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter { String connId = Red5.getConnectionLocal().getSessionId(); String remoteHost = Red5.getConnectionLocal().getRemoteAddress(); - int remotePort = Red5.getConnectionLocal().getRemotePort(); - + int remotePort = Red5.getConnectionLocal().getRemotePort(); + String clientId = Red5.getConnectionLocal().getClient().getId(); Map<String, Object> logData = new HashMap<String, Object>(); logData.put("meetingId", meetingId); logData.put("connType", connType); logData.put("connId", connId); - logData.put("conn", remoteHost + ":" + remotePort); + logData.put("clientId", clientId); + logData.put("remoteAddress", remoteHost + ":" + remotePort); logData.put("userId", userId); logData.put("externalUserId", externalUserID); logData.put("sessionId", sessionId); @@ -222,14 +228,15 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter { String connType = getConnectionType(Red5.getConnectionLocal().getType()); String userFullname = bbbSession.getUsername(); String connId = Red5.getConnectionLocal().getSessionId(); - + String clientId = Red5.getConnectionLocal().getClient().getId(); String sessionId = CONN + userId; Map<String, Object> logData = new HashMap<String, Object>(); logData.put("meetingId", meetingId); logData.put("connType", connType); logData.put("connId", connId); - logData.put("conn", remoteHost + ":" + remotePort); + logData.put("clientId", clientId); + logData.put("remoteAddress", remoteHost + ":" + remotePort); logData.put("sessionId", sessionId); logData.put("userId", userId); logData.put("username", userFullname); 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 0ce096841767dd6d51ebc08023cdf18277e9f904..2d23b2118c7bbf49c41e6262b7571179ec8610e0 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 @@ -145,8 +145,8 @@ public class ConnectionInvokerService { private void handlDisconnectClientMessage(DisconnectClientMessage msg) { IScope meetingScope = getScope(msg.getMeetingId()); if (meetingScope != null) { - String sessionId = CONN + msg.getUserId(); - IConnection conn = getConnection(meetingScope, sessionId); + String userId = msg.getUserId(); + IConnection conn = getConnection(meetingScope, userId); if (conn != null) { if (conn.isConnected()) { log.info("Disconnecting user=[{}] from meeting=[{}]", msg.getUserId(), msg.getMeetingId()); diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/PortTest.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/PortTest.as index 1c4f27e3a64fd24ad8cc41d782fa9c997af2453d..0ab10e7dd69c7bb9a315d0c64506df1de94039fd 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/PortTest.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/PortTest.as @@ -144,7 +144,7 @@ package org.bigbluebutton.main.model try { var logData:Object = UsersUtil.initLogData(); logData.connection = this.baseURI; - logData.tags = ["connection"]; + logData.tags = ["initialization", "port-test", "connection"]; logData.message = "Port testing connection."; LOGGER.info(JSON.stringify(logData)); @@ -168,7 +168,7 @@ package org.bigbluebutton.main.model public function connectionTimeout (e:TimerEvent) : void { var logData:Object = UsersUtil.initLogData(); logData.connection = this.baseURI; - logData.tags = ["connection"]; + logData.tags = ["initialization", "port-test", "connection"]; logData.message = "Port testing connection timedout."; LOGGER.info(JSON.stringify(logData)); @@ -200,7 +200,7 @@ package org.bigbluebutton.main.model private function closeConnectionTimerHandler (e:TimerEvent) : void { var logData:Object = UsersUtil.initLogData(); logData.connection = this.baseURI; - logData.tags = ["connection"]; + logData.tags = ["initialization", "port-test", "connection"]; logData.message = "Closing port testing connection."; LOGGER.info(JSON.stringify(logData)); @@ -222,7 +222,7 @@ package org.bigbluebutton.main.model var logData:Object = UsersUtil.initLogData(); logData.connection = this.baseURI; - logData.tags = ["connection"]; + logData.tags = ["initialization", "port-test", "connection"]; if ( statusCode == "NetConnection.Connect.Success" ) { diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/EnterApiService.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/EnterApiService.as index a5cac6a9fd97a0b856f979517fbf51c55f2f45c7..ce92a8f875ecc6cbc3694b8dc4088c27cf00356c 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/EnterApiService.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/modules/EnterApiService.as @@ -70,15 +70,18 @@ package org.bigbluebutton.main.model.modules var dispatcher:Dispatcher = new Dispatcher(); dispatcher.dispatchEvent(new MeetingNotFoundEvent(result.response.logoutURL)); } else if (returncode == 'SUCCESS') { - logData.tags = ["initialization"]; - logData.message = "Enter API call succeeded."; - LOGGER.info(JSON.stringify(logData)); + var response:Object = new Object(); response.username = result.response.fullname; response.userId = result.response.internalUserID; response.meetingName = result.response.confname; response.meetingId = result.response.meetingID; + + logData.response = response; + logData.tags = ["initialization"]; + logData.message = "Enter API call succeeded."; + LOGGER.info(JSON.stringify(logData)); if (_resultListener != null) _resultListener(true, response); } diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as index 7b5cad45d88fa94b13458a3bf211cfa1104c1c06..ba6043e0d05df3c7c619d928bed8829031f91b86 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as @@ -26,7 +26,7 @@ package org.bigbluebutton.main.model.users import flash.events.TimerEvent; import flash.net.NetConnection; import flash.net.Responder; - import flash.utils.Timer; + import flash.utils.Timer; import org.as3commons.logging.api.ILogger; import org.as3commons.logging.api.getClassLogger; import org.bigbluebutton.core.BBB; @@ -40,358 +40,397 @@ package org.bigbluebutton.main.model.users import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent; import org.bigbluebutton.main.model.users.events.UsersConnectionEvent; - public class NetConnectionDelegate - { - private static const LOGGER:ILogger = getClassLogger(NetConnectionDelegate); - - private var _netConnection:NetConnection; - private var connectionId:Number; - private var connected:Boolean = false; - - private var _userid:Number = -1; - private var _role:String = "unknown"; - - private var logoutOnUserCommand:Boolean = false; - private var dispatcher:Dispatcher; - private var _messageListeners:Array = new Array(); - - private var authenticated: Boolean = false; - private var reconnecting:Boolean = false; - private var numNetworkChangeCount:int = 0; - - private var _validateTokenTimer:Timer = null; - - public function NetConnectionDelegate():void { - dispatcher = new Dispatcher(); - } - - public function get connection():NetConnection { - return _netConnection; - } + public class NetConnectionDelegate { + private static const LOGGER:ILogger = getClassLogger(NetConnectionDelegate); + + private var _netConnection:NetConnection; + private var connectionId:Number; + private var connected:Boolean = false; + private var _userid:Number = -1; + private var _role:String = "unknown"; + private var logoutOnUserCommand:Boolean = false; + private var dispatcher:Dispatcher; + private var _messageListeners:Array = new Array(); + private var authenticated: Boolean = false; + private var reconnecting:Boolean = false; - public function addMessageListener(listener:IMessageListener):void { - _messageListeners.push(listener); - } + private var maxConnectAttempt:int = 2; + private var connectAttemptCount:int = 0; + private var connectAttemptTimeout:Number = 5000; + private var connectionTimer:Timer; + + private var numNetworkChangeCount:int = 0; + private var _validateTokenTimer:Timer = null; + + private var bbbAppsUrl: String = null; - public function removeMessageListener(listener:IMessageListener):void { - for (var ob:int=0; ob<_messageListeners.length; ob++) { - if (_messageListeners[ob] == listener) { - _messageListeners.splice (ob,1); - break; + public function NetConnectionDelegate():void { + dispatcher = new Dispatcher(); + _netConnection = new NetConnection(); + _netConnection.proxyType = "best"; + _netConnection.client = this; + _netConnection.addEventListener( NetStatusEvent.NET_STATUS, netStatus ); + _netConnection.addEventListener( AsyncErrorEvent.ASYNC_ERROR, netASyncError ); + _netConnection.addEventListener( SecurityErrorEvent.SECURITY_ERROR, netSecurityError ); + _netConnection.addEventListener( IOErrorEvent.IO_ERROR, netIOError ); } - } - } + - private function notifyListeners(messageName:String, message:Object):void { - if (messageName != null && messageName != "") { - for (var notify:String in _messageListeners) { - _messageListeners[notify].onMessage(messageName, message); - } - } else { - LOGGER.debug("Message name is undefined"); - } - } + public function get connection():NetConnection { + return _netConnection; + } + + public function addMessageListener(listener:IMessageListener):void { + _messageListeners.push(listener); + } + + public function removeMessageListener(listener:IMessageListener):void { + for (var ob:int=0; ob<_messageListeners.length; ob++) { + if (_messageListeners[ob] == listener) { + _messageListeners.splice (ob,1); + break; + } + } + } + + private function notifyListeners(messageName:String, message:Object):void { + if (messageName != null && messageName != "") { + for (var notify:String in _messageListeners) { + _messageListeners[notify].onMessage(messageName, message); + } + } else { + LOGGER.debug("Message name is undefined"); + } + } + + public function onMessageFromServer(messageName:String, msg:Object):void { + if (!authenticated && (messageName == "validateAuthTokenReply")) { + handleValidateAuthTokenReply(msg) + } else if (messageName == "validateAuthTokenTimedOut") { + handleValidateAuthTokenTimedOut(msg) + } else if (authenticated) { + notifyListeners(messageName, msg); + } else { + LOGGER.debug("Ignoring message=[{0}] as our token hasn't been validated yet.", [messageName]); + } + } + + private function validataTokenTimerHandler(event:TimerEvent):void { + var logData:Object = UsersUtil.initLogData(); + logData.tags = ["apps"]; + logData.key = "validate_token_request_timedout"; + logData.message = "No response for validate token request."; + LOGGER.info(JSON.stringify(logData)); + } + + private function validateToken():void { + var confParams:ConferenceParameters = BBB.initUserConfigManager().getConfParams(); + + var message:Object = new Object(); + message["userId"] = confParams.internalUserID; + message["authToken"] = confParams.authToken; + + sendMessage( + "validateToken",// Remote function name + // result - On successful result + function(result:Object):void { + + }, + // status - On error occurred + function(status:Object):void { + LOGGER.error("Error occurred:"); + for (var x:Object in status) { + LOGGER.error(x + " : " + status[x]); + } + }, + message + ); //_netConnection.call + + _validateTokenTimer = new Timer(10000, 1); + _validateTokenTimer.addEventListener(TimerEvent.TIMER, validataTokenTimerHandler); + _validateTokenTimer.start(); + } - public function onMessageFromServer(messageName:String, msg:Object):void { - if (!authenticated && (messageName == "validateAuthTokenReply")) { - handleValidateAuthTokenReply(msg) - } else if (messageName == "validateAuthTokenTimedOut") { - handleValidateAuthTokenTimedOut(msg) - } else if (authenticated) { - notifyListeners(messageName, msg); - } else { - LOGGER.debug("Ignoring message=[{0}] as our token hasn't been validated yet.", [messageName]); - } - } - - private function validataTokenTimerHandler(event:TimerEvent):void { - var logData:Object = UsersUtil.initLogData(); - logData.tags = ["apps"]; - logData.message = "No response for validate token request."; - LOGGER.info(JSON.stringify(logData)); - } - - private function validateToken():void { - var confParams:ConferenceParameters = BBB.initUserConfigManager().getConfParams(); - - var message:Object = new Object(); - message["userId"] = confParams.internalUserID; - message["authToken"] = confParams.authToken; + private function stopValidateTokenTimer():void { + if (_validateTokenTimer != null && _validateTokenTimer.running) { + _validateTokenTimer.stop(); + _validateTokenTimer = null; + } + } + + private function handleValidateAuthTokenTimedOut(msg: Object):void { + stopValidateTokenTimer(); - _validateTokenTimer = new Timer(7000, 1); - _validateTokenTimer.addEventListener(TimerEvent.TIMER, validataTokenTimerHandler); - _validateTokenTimer.start(); - - sendMessage( - "validateToken",// Remote function name - // result - On successful result - function(result:Object):void { - - }, - // status - On error occurred - function(status:Object):void { - LOGGER.error("Error occurred:"); - for (var x:Object in status) { - LOGGER.error(x + " : " + status[x]); - } - }, - message - ); //_netConnection.call - } - - private function stopValidateTokenTimer():void { - if (_validateTokenTimer != null && _validateTokenTimer.running) { - _validateTokenTimer.stop(); - _validateTokenTimer = null; - } - } - - private function handleValidateAuthTokenTimedOut(msg: Object):void { - stopValidateTokenTimer(); - - var map:Object = JSON.parse(msg.msg); - var tokenValid: Boolean = map.valid as Boolean; - var userId: String = map.userId as String; - - var logData:Object = UsersUtil.initLogData(); - logData.tags = ["apps"]; - logData.message = "Validate auth token timed out."; - LOGGER.info(JSON.stringify(logData)); - - if (tokenValid) { - authenticated = true; - } else { - dispatcher.dispatchEvent(new InvalidAuthTokenEvent()); - } - if (reconnecting) { - onReconnect(); - reconnecting = false; - } - } - - private function handleValidateAuthTokenReply(msg: Object):void { - stopValidateTokenTimer(); - - var map:Object = JSON.parse(msg.msg); - var tokenValid: Boolean = map.valid as Boolean; - var userId: String = map.userId as String; + var map:Object = JSON.parse(msg.msg); + var tokenValid: Boolean = map.valid as Boolean; + var userId: String = map.userId as String; + + var logData:Object = UsersUtil.initLogData(); + logData.tags = ["apps", "connected"]; + logData.tokenValid = tokenValid; + logData.key = "validate_token_response_received"; + logData.message = "Validate auth token timed out."; + LOGGER.info(JSON.stringify(logData)); - if (tokenValid) { - authenticated = true; - } else { - dispatcher.dispatchEvent(new InvalidAuthTokenEvent()); - } - if (reconnecting) { - onReconnect(); - reconnecting = false; - } - } + if (tokenValid) { + authenticated = true; + } else { + dispatcher.dispatchEvent(new InvalidAuthTokenEvent()); + } - private function onReconnect():void { - if (authenticated) { - onReconnectSuccess(); - } else { - onReconnectFailed(); - } - } - - private function onReconnectSuccess():void { - var attemptSucceeded:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT); - attemptSucceeded.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION; - dispatcher.dispatchEvent(attemptSucceeded); - } + if (reconnecting) { + onReconnect(); + reconnecting = false; + } + } + + private function handleValidateAuthTokenReply(msg: Object):void { + stopValidateTokenTimer(); - private function onReconnectFailed():void { - sendUserLoggedOutEvent(); - } - - private function sendConnectionSuccessEvent(userid:String):void{ - var e:UsersConnectionEvent = new UsersConnectionEvent(UsersConnectionEvent.CONNECTION_SUCCESS); - e.userid = userid; - dispatcher.dispatchEvent(e); + var map:Object = JSON.parse(msg.msg); + var tokenValid: Boolean = map.valid as Boolean; + var userId: String = map.userId as String; + + var logData:Object = UsersUtil.initLogData(); + logData.tags = ["apps", "connected"]; + logData.tokenValid = tokenValid; + logData.status = "validate_token_response_received"; + logData.message = "Received validate token response from server."; + LOGGER.info(JSON.stringify(logData)); + + if (tokenValid) { + authenticated = true; + } else { + dispatcher.dispatchEvent(new InvalidAuthTokenEvent()); + } - } - - public function sendMessage(service:String, onSuccess:Function, onFailure:Function, message:Object=null):void { - var responder:Responder = new Responder( - function(result:Object):void { // On successful result - onSuccess("Successfully sent [" + service + "]."); - }, - function(status:Object):void { // status - On error occurred - var errorReason:String = "Failed to send [" + service + "]:\n"; - for (var x:Object in status) { - errorReason += "\t" + x + " : " + status[x]; - } - } - ); - - if (message == null) { - _netConnection.call(service, responder); - } else { - _netConnection.call(service, responder, message); - } - } - - /** - * Connect to the server. - * uri: The uri to the conference application. - * username: Fullname of the participant. - * role: MODERATOR/VIEWER - * conference: The conference room - * mode: LIVE/PLAYBACK - Live:when used to collaborate, Playback:when being used to playback a recorded conference. - * room: Need the room number when playing back a recorded conference. When LIVE, the room is taken from the URI. - */ - public function connect():void { - var confParams:ConferenceParameters = BBB.initUserConfigManager().getConfParams(); - - _netConnection = new NetConnection(); - _netConnection.proxyType = "best"; - _netConnection.client = this; - _netConnection.addEventListener( NetStatusEvent.NET_STATUS, netStatus ); - _netConnection.addEventListener( AsyncErrorEvent.ASYNC_ERROR, netASyncError ); - _netConnection.addEventListener( SecurityErrorEvent.SECURITY_ERROR, netSecurityError ); - _netConnection.addEventListener( IOErrorEvent.IO_ERROR, netIOError ); + if (reconnecting) { + onReconnect(); + reconnecting = false; + } + } + + private function onReconnect():void { + if (authenticated) { + onReconnectSuccess(); + } else { + onReconnectFailed(); + } + } + + private function onReconnectSuccess():void { + var attemptSucceeded:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT); + attemptSucceeded.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION; + dispatcher.dispatchEvent(attemptSucceeded); + } + + private function onReconnectFailed():void { + sendUserLoggedOutEvent(); + } + + private function sendConnectionSuccessEvent(userid:String):void{ + var e:UsersConnectionEvent = new UsersConnectionEvent(UsersConnectionEvent.CONNECTION_SUCCESS); + e.userid = userid; + dispatcher.dispatchEvent(e); + } + + public function sendMessage(service:String, onSuccess:Function, onFailure:Function, message:Object=null):void { + var responder:Responder = new Responder( + function(result:Object):void { // On successful result + onSuccess("Successfully sent [" + service + "]."); + }, + function(status:Object):void { // status - On error occurred + var errorReason:String = "Failed to send [" + service + "]:\n"; + for (var x:Object in status) { + errorReason += "\t" + x + " : " + status[x]; + } + } + ); + + if (message == null) { + _netConnection.call(service, responder); + } else { + _netConnection.call(service, responder, message); + } + } + + public function connect():void { + var confParams:ConferenceParameters = BBB.initUserConfigManager().getConfParams(); + + + try { + var appURL:String = BBB.getConfigManager().config.application.uri; + var pattern:RegExp = /(?P<protocol>.+):\/\/(?P<server>.+)\/(?P<app>.+)/; + var result:Array = pattern.exec(appURL); + + var protocol:String = "rtmp"; + var uri:String = appURL + "/" + confParams.room; + + if (BBB.initConnectionManager().isTunnelling) { + bbbAppsUrl = "rtmpt://" + result.server + "/" + result.app + "/" + confParams.room; + } else { + bbbAppsUrl = "rtmp://" + result.server + ":1935/" + result.app + "/" + confParams.room; + } + + var logData:Object = UsersUtil.initLogData(); + logData.connection = bbbAppsUrl; + logData.tags = ["apps", "connection"]; + logData.message = "Connecting to bbb-apps."; + LOGGER.info(JSON.stringify(logData)); + + connectAttemptCount++; + + connectionTimer = new Timer(connectAttemptTimeout, 1); + connectionTimer.addEventListener(TimerEvent.TIMER, connectionTimeout); + connectionTimer.start(); + + _netConnection.connect(bbbAppsUrl, confParams.username, confParams.role, + confParams.room, confParams.voicebridge, + confParams.record, confParams.externUserID, + confParams.internalUserID, confParams.muteOnStart, confParams.lockSettings); + + } catch(e:ArgumentError) { + // Invalid parameters. + switch (e.errorID) { + case 2004 : + LOGGER.debug("Error! Invalid server location: {0}", [uri]); + break; + default : + LOGGER.debug("UNKNOWN Error! Invalid server location: {0}", [uri]); + break; + } + } + } + + public function connectionTimeout (e:TimerEvent) : void { + var logData:Object = UsersUtil.initLogData(); + logData.connection = bbbAppsUrl; + logData.tags = ["apps", "connection"]; + logData.connectAttemptCount = connectAttemptCount; + logData.message = "Connecting attempt to bbb-apps timedout. Retrying."; + LOGGER.info(JSON.stringify(logData)); + + if (connectAttemptCount <= maxConnectAttempt) { + connect(); + } else { + sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_ATTEMPT_TIMEDOUT); + } + + } - try { - var appURL:String = BBB.getConfigManager().config.application.uri; - var pattern:RegExp = /(?P<protocol>.+):\/\/(?P<server>.+)\/(?P<app>.+)/; - var result:Array = pattern.exec(appURL); + + public function disconnect(logoutOnUserCommand:Boolean):void { + this.logoutOnUserCommand = logoutOnUserCommand; + _netConnection.close(); + } - var protocol:String = "rtmp"; - var uri:String = appURL + "/" + confParams.room; + public function forceClose():void { + _netConnection.close(); + } - if (BBB.initConnectionManager().isTunnelling) { - uri = "rtmpt://" + result.server + "/" + result.app + "/" + confParams.room; - } else { - uri = "rtmp://" + result.server + ":1935/" + result.app + "/" + confParams.room; + protected function netStatus(event:NetStatusEvent):void { + handleResult( event ); } - - - LOGGER.debug("BBB Apps URI=" + uri); - - _netConnection.connect(uri, confParams.username, confParams.role, - confParams.room, confParams.voicebridge, - confParams.record, confParams.externUserID, - confParams.internalUserID, confParams.muteOnStart, confParams.lockSettings); - - } catch(e:ArgumentError) { - // Invalid parameters. - switch (e.errorID) { - case 2004 : - LOGGER.debug("Error! Invalid server location: {0}", [uri]); - break; - default : - LOGGER.debug("UNKNOWN Error! Invalid server location: {0}", [uri]); - break; - } - } - } - - public function disconnect(logoutOnUserCommand:Boolean):void { - this.logoutOnUserCommand = logoutOnUserCommand; - _netConnection.close(); - } - - - public function forceClose():void { - _netConnection.close(); - } - - protected function netStatus(event:NetStatusEvent):void { - handleResult( event ); - } - - public function handleResult(event:Object):void { - var info : Object = event.info; - var statusCode : String = info.code; + + public function handleResult(event:Object):void { + var info : Object = event.info; + var statusCode : String = info.code; + + //Stop timeout timer when connected/rejected + if (connectionTimer != null && connectionTimer.running) { + connectionTimer.stop(); + connectionTimer = null; + } + var logData:Object = UsersUtil.initLogData(); - logData.tags = ["apps"]; - - switch (statusCode) { - case "NetConnection.Connect.Success": - numNetworkChangeCount = 0; - logData.message = "Successfully connected to BBB App."; + logData.tags = ["apps", "connection"]; + + switch (statusCode) { + case "NetConnection.Connect.Success": + numNetworkChangeCount = 0; + connectAttemptCount = 0; + logData.message = "Successfully connected to bbb-apps."; LOGGER.info(JSON.stringify(logData)); validateToken(); - break; - - case "NetConnection.Connect.Failed": - logData.message = "Connection to bbb-apps failed, even when tunneling."; + break; + + case "NetConnection.Connect.Failed": + logData.message = "Connection to bbb-apps failed."; + LOGGER.info(JSON.stringify(logData)); + sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_FAILED); + break; + + case "NetConnection.Connect.Closed": + logData.message = "NetConnection.Connect.Closed on bbb-apps"; LOGGER.info(JSON.stringify(logData)); - sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_FAILED); - break; - - case "NetConnection.Connect.Closed": - logData.message = "NetConnection.Connect.Closed on bbb-apps"; - LOGGER.info(JSON.stringify(logData)); - sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_CLOSED); - break; - - case "NetConnection.Connect.InvalidApp": + sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_CLOSED); + break; + + case "NetConnection.Connect.InvalidApp": logData.message = "bbb-app not found."; LOGGER.info(JSON.stringify(logData)); - sendConnectionFailedEvent(ConnectionFailedEvent.INVALID_APP); - break; - - case "NetConnection.Connect.AppShutDown": + sendConnectionFailedEvent(ConnectionFailedEvent.INVALID_APP); + break; + + case "NetConnection.Connect.AppShutDown": LOGGER.debug(":viewers application has been shutdown"); - sendConnectionFailedEvent(ConnectionFailedEvent.APP_SHUTDOWN); - break; - - case "NetConnection.Connect.Rejected": - var appURL:String = BBB.getConfigManager().config.application.uri - LOGGER.debug(":Connection to the server rejected. Uri: {0}. Check if the red5 specified in the uri exists and is running", [appURL]); - sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_REJECTED); - break; - - case "NetConnection.Connect.NetworkChange": - numNetworkChangeCount++; - if (numNetworkChangeCount % 20 == 0) { - logData.message = "Detected network change on bbb-apps"; - logData.numNetworkChangeCount = numNetworkChangeCount; - LOGGER.info(JSON.stringify(logData)); - } - break; - - default : - LOGGER.debug(":Default status to the viewers application" ); - sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON); - break; - } - } - - protected function netSecurityError(event: SecurityErrorEvent):void { + sendConnectionFailedEvent(ConnectionFailedEvent.APP_SHUTDOWN); + break; + + case "NetConnection.Connect.Rejected": + var appURL:String = BBB.getConfigManager().config.application.uri + LOGGER.debug(":Connection to the server rejected. Uri: {0}. Check if the red5 specified in the uri exists and is running", [appURL]); + sendConnectionFailedEvent(ConnectionFailedEvent.CONNECTION_REJECTED); + break; + + case "NetConnection.Connect.NetworkChange": + numNetworkChangeCount++; + if (numNetworkChangeCount % 20 == 0) { + logData.message = "Detected network change on bbb-apps"; + logData.numNetworkChangeCount = numNetworkChangeCount; + LOGGER.info(JSON.stringify(logData)); + } + break; + + default : + LOGGER.debug(":Default status to the viewers application" ); + sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON); + break; + } + } + + protected function netSecurityError(event: SecurityErrorEvent):void { var logData:Object = UsersUtil.initLogData(); - logData.tags = ["apps"]; + logData.tags = ["apps", "connection"]; logData.message = "Security error - " + event.text; LOGGER.info(JSON.stringify(logData)); - sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON); - } - - protected function netIOError(event: IOErrorEvent):void { + sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON); + } + + protected function netIOError(event: IOErrorEvent):void { var logData:Object = UsersUtil.initLogData(); - logData.tags = ["apps"]; + logData.tags = ["apps", "connection"]; logData.message = "Input/output error - " + event.text; LOGGER.info(JSON.stringify(logData)); - sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON); - } - - protected function netASyncError(event: AsyncErrorEvent):void { - LOGGER.debug("Asynchronous code error - {0}", [event.toString()]); - sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON); - } - + sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON); + } + + protected function netASyncError(event: AsyncErrorEvent):void { + LOGGER.debug("Asynchronous code error - {0}", [event.toString()]); + sendConnectionFailedEvent(ConnectionFailedEvent.UNKNOWN_REASON); + } + private function sendConnectionFailedEvent(reason:String):void{ var logData:Object = UsersUtil.initLogData(); - logData.tags = ["apps"]; + logData.tags = ["apps", "connection"]; if (this.logoutOnUserCommand) { logData.reason = "User requested."; logData.message = "User logged out from BBB App."; LOGGER.info(JSON.stringify(logData)); - + sendUserLoggedOutEvent(); } else if (reason == ConnectionFailedEvent.CONNECTION_CLOSED && !UsersUtil.isUserEjected()) { // do not try to reconnect if the connection failed is different than CONNECTION_CLOSED @@ -400,25 +439,26 @@ package org.bigbluebutton.main.model.users LOGGER.info(JSON.stringify(logData)); if (reconnecting) { - var attemptFailedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT); - attemptFailedEvent.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION; - dispatcher.dispatchEvent(attemptFailedEvent); + var attemptFailedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT); + attemptFailedEvent.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION; + dispatcher.dispatchEvent(attemptFailedEvent); } else { - reconnecting = true; - authenticated = false; - - var disconnectedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_DISCONNECTED_EVENT); - disconnectedEvent.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION; - disconnectedEvent.payload.callback = connect; - disconnectedEvent.payload.callbackParameters = new Array(); - dispatcher.dispatchEvent(disconnectedEvent); - } + reconnecting = true; + authenticated = false; + var disconnectedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_DISCONNECTED_EVENT); + disconnectedEvent.payload.type = ReconnectionManager.BIGBLUEBUTTON_CONNECTION; + disconnectedEvent.payload.callback = connect; + disconnectedEvent.payload.callbackParameters = new Array(); + dispatcher.dispatchEvent(disconnectedEvent); + } } else { if (UsersUtil.isUserEjected()) { logData.message = "User has been ejected from meeting."; LOGGER.info(JSON.stringify(logData)); reason = ConnectionFailedEvent.USER_EJECTED_FROM_MEETING; + var cfe:ConnectionFailedEvent = new ConnectionFailedEvent(reason); + dispatcher.dispatchEvent(cfe); } else { logData.message = "Connection failed event - " + reason; LOGGER.info(JSON.stringify(logData)); @@ -428,22 +468,22 @@ package org.bigbluebutton.main.model.users } } - - private function sendUserLoggedOutEvent():void{ - var e:ConnectionFailedEvent = new ConnectionFailedEvent(ConnectionFailedEvent.USER_LOGGED_OUT); - dispatcher.dispatchEvent(e); - } - - public function onBWCheck(... rest):Number { - return 0; - } - - public function onBWDone(... rest):void { - var p_bw:Number; - if (rest.length > 0) p_bw = rest[0]; - // your application should do something here - // when the bandwidth check is complete - LOGGER.debug("bandwidth = {0} Kbps.", [p_bw]); - } - } + + private function sendUserLoggedOutEvent():void{ + var e:ConnectionFailedEvent = new ConnectionFailedEvent(ConnectionFailedEvent.USER_LOGGED_OUT); + dispatcher.dispatchEvent(e); + } + + public function onBWCheck(... rest):Number { + return 0; + } + + public function onBWDone(... rest):void { + var p_bw:Number; + if (rest.length > 0) p_bw = rest[0]; + // your application should do something here + // when the bandwidth check is complete + LOGGER.debug("bandwidth = {0} Kbps.", [p_bw]); + } + } } diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/events/ConnectionFailedEvent.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/events/ConnectionFailedEvent.as index 6ceb8b8efd29258a32fa63ddcc7f523a8df3af4e..f6b8530e5a99d5138daeff5fc225127aadf836be 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/events/ConnectionFailedEvent.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/events/ConnectionFailedEvent.as @@ -18,23 +18,22 @@ */ package org.bigbluebutton.main.model.users.events { - import flash.events.Event; + import flash.events.Event; - public class ConnectionFailedEvent extends Event - { - public static const UNKNOWN_REASON:String = "unknownReason"; - public static const CONNECTION_FAILED:String = "connectionFailed"; - public static const CONNECTION_CLOSED:String = "connectionClosed"; - public static const INVALID_APP:String = "invalidApp"; - public static const APP_SHUTDOWN:String = "appShutdown"; - public static const CONNECTION_REJECTED:String = "connectionRejected"; - public static const ASYNC_ERROR:String = "asyncError"; - public static const USER_LOGGED_OUT:String = "userHasLoggedOut"; - public static const USER_EJECTED_FROM_MEETING:String = "userHasBeenEjectFromMeeting"; - - public function ConnectionFailedEvent(type:String) - { - super(type, true, false); - } - } + public class ConnectionFailedEvent extends Event { + public static const UNKNOWN_REASON:String = "unknownReason"; + public static const CONNECTION_FAILED:String = "connectionFailed"; + public static const CONNECTION_CLOSED:String = "connectionClosed"; + public static const CONNECTION_ATTEMPT_TIMEDOUT:String = "connectionAttemptTimedout"; + public static const INVALID_APP:String = "invalidApp"; + public static const APP_SHUTDOWN:String = "appShutdown"; + public static const CONNECTION_REJECTED:String = "connectionRejected"; + public static const ASYNC_ERROR:String = "asyncError"; + public static const USER_LOGGED_OUT:String = "userHasLoggedOut"; + public static const USER_EJECTED_FROM_MEETING:String = "userHasBeenEjectFromMeeting"; + + public function ConnectionFailedEvent(type:String) { + super(type, true, false); + } + } } \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as index 81a554556abb2bf31dea39a2ffcb36abcb4a7d42..282b3920d35020c194a2e4700152e57dd9c5b4e6 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as @@ -180,7 +180,14 @@ package org.bigbluebutton.modules.users.services } private function handleUserEjectedFromMeeting(msg: Object):void { - UsersUtil.setUserEjected(); + UsersUtil.setUserEjected(); + var logData:Object = UsersUtil.initLogData(); + logData.tags = ["users"]; + logData.status = "user_ejected"; + logData.message = "User ejected from meeting."; + + LOGGER.info(JSON.stringify(logData)); + } private function handleUserLocked(msg:Object):void { diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/clearMeetings.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/clearMeetings.js index 65e2e45fdc65e7986534990e60cb998e17e54d44..7cd4111d0aba543ff55ed5d5fa45a10583c5a9bf 100755 --- a/bigbluebutton-html5/imports/api/meetings/server/modifiers/clearMeetings.js +++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/clearMeetings.js @@ -6,7 +6,7 @@ import { clearUsersCollection } from '/imports/api/users/server/modifiers/clearU import clearChats from '/imports/api/chat/server/modifiers/clearChats'; import { clearShapesCollection } from '/imports/api/shapes/server/modifiers/clearShapesCollection'; import clearSlides from '/imports/api/slides/server/modifiers/clearSlides'; -import { clearPollCollection } from '/imports/api/polls/server/modifiers/clearPollCollection'; +import clearPoll from '/imports/api/polls/server/modifiers/clearPoll'; import { clearCursorCollection } from '/imports/api/cursor/server/modifiers/clearCursorCollection'; import { clearCaptionsCollection } from '/imports/api/captions/server/modifiers/clearCaptionsCollection'; @@ -17,8 +17,8 @@ export default function clearMeetings() { clearCaptionsCollection(); clearChats(); clearCursorCollection(); - clearPollCollection(); clearPresentations(); + clearPoll(); clearShapesCollection(); clearSlides(); clearUsersCollection(); diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/removeMeeting.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/removeMeeting.js index d3379a15f9bf197316763e50adf5245b9ee169ee..8ae41ea02b0c52eabb6ac2fdb95d3c1b3b85bd12 100755 --- a/bigbluebutton-html5/imports/api/meetings/server/modifiers/removeMeeting.js +++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/removeMeeting.js @@ -6,7 +6,7 @@ import { clearUsersCollection } from '/imports/api/users/server/modifiers/clearU import clearChats from '/imports/api/chat/server/modifiers/clearChats'; import { clearShapesCollection } from '/imports/api/shapes/server/modifiers/clearShapesCollection'; import clearSlides from '/imports/api/slides/server/modifiers/clearSlides'; -import { clearPollCollection } from '/imports/api/polls/server/modifiers/clearPollCollection'; +import clearPoll from '/imports/api/polls/server/modifiers/clearPoll'; import { clearCursorCollection } from '/imports/api/cursor/server/modifiers/clearCursorCollection'; import { clearCaptionsCollection } from '/imports/api/captions/server/modifiers/clearCaptionsCollection'; @@ -28,8 +28,8 @@ export default function removeMeeting(meetingId) { clearCaptionsCollection(meetingId); clearChats(meetingId); clearCursorCollection(meetingId); - clearPollCollection(meetingId); clearPresentations(meetingId); + clearPoll(meetingId); clearShapesCollection(meetingId); clearSlides(meetingId); clearUsersCollection(meetingId); diff --git a/bigbluebutton-html5/imports/api/polls/index.js b/bigbluebutton-html5/imports/api/polls/index.js old mode 100755 new mode 100644 diff --git a/bigbluebutton-html5/imports/api/polls/server/eventHandlers.js b/bigbluebutton-html5/imports/api/polls/server/eventHandlers.js new file mode 100644 index 0000000000000000000000000000000000000000..c20b8f971f4bfe306867ae254cc03f4923184ec8 --- /dev/null +++ b/bigbluebutton-html5/imports/api/polls/server/eventHandlers.js @@ -0,0 +1,9 @@ +import RedisPubSub from '/imports/startup/server/redis'; +import handlePollStopped from './handlers/pollStopped'; +import handlePollStarted from './handlers/pollStarted'; +import handleUserVoted from './handlers/userVoted'; + +RedisPubSub.on('poll_show_result_message', handlePollStopped); +RedisPubSub.on('poll_started_message', handlePollStarted); +RedisPubSub.on('poll_stopped_message', handlePollStopped); +RedisPubSub.on('user_voted_poll_message', handleUserVoted); diff --git a/bigbluebutton-html5/imports/api/polls/server/handlers/pollStarted.js b/bigbluebutton-html5/imports/api/polls/server/handlers/pollStarted.js new file mode 100644 index 0000000000000000000000000000000000000000..68d312d641ba9248d274cf66975c974bd7d4c288 --- /dev/null +++ b/bigbluebutton-html5/imports/api/polls/server/handlers/pollStarted.js @@ -0,0 +1,16 @@ +import { check } from 'meteor/check'; +import addPoll from '../modifiers/addPoll'; + +export default function pollStarted({ payload }) { + check(payload, Object); + + const meetingId = payload.meeting_id; + const requesterId = payload.requester_id; + const poll = payload.poll; + + check(meetingId, String); + check(requesterId, String); + check(poll, Object); + + return addPoll(meetingId, requesterId, poll); +} diff --git a/bigbluebutton-html5/imports/api/polls/server/handlers/pollStopped.js b/bigbluebutton-html5/imports/api/polls/server/handlers/pollStopped.js new file mode 100644 index 0000000000000000000000000000000000000000..8705a6b22b54072093b886e9d367001af6621c45 --- /dev/null +++ b/bigbluebutton-html5/imports/api/polls/server/handlers/pollStopped.js @@ -0,0 +1,22 @@ +import { check } from 'meteor/check'; +import removePoll from '../modifiers/removePoll'; +import clearPolls from '../modifiers/clearPolls'; + +export default function pollStopped({ payload }) { + check(payload, Object); + + const meetingId = payload.meeting_id; + const poll = payload.poll; + + check(meetingId, String); + + if (poll) { + const pollId = poll.id; + + check(pollId, String); + + return removePoll(meetingId, pollId); + } + + return clearPolls(meetingId); +} diff --git a/bigbluebutton-html5/imports/api/polls/server/handlers/userVoted.js b/bigbluebutton-html5/imports/api/polls/server/handlers/userVoted.js new file mode 100644 index 0000000000000000000000000000000000000000..29261be9082d48f8738c033bb9178e59e2a6f675 --- /dev/null +++ b/bigbluebutton-html5/imports/api/polls/server/handlers/userVoted.js @@ -0,0 +1,16 @@ +import { check } from 'meteor/check'; +import updateVotes from '../modifiers/updateVotes'; + +export default function userVoted({ payload }) { + check(payload, Object); + + const meetingId = payload.meeting_id; + const poll = payload.poll; + const requesterId = payload.presenter_id; + + check(meetingId, String); + check(poll, Object); + check(requesterId, String); + + return updateVotes(poll, meetingId, requesterId); +}; diff --git a/bigbluebutton-html5/imports/api/polls/server/index.js b/bigbluebutton-html5/imports/api/polls/server/index.js new file mode 100644 index 0000000000000000000000000000000000000000..92451ac76bf27410726e8f3cd2eebac46cd7b83e --- /dev/null +++ b/bigbluebutton-html5/imports/api/polls/server/index.js @@ -0,0 +1,3 @@ +import './eventHandlers'; +import './methods'; +import './publishers'; diff --git a/bigbluebutton-html5/imports/api/polls/server/methods.js b/bigbluebutton-html5/imports/api/polls/server/methods.js new file mode 100644 index 0000000000000000000000000000000000000000..4136d79f73d2d95adb61d39aa729744b17a882f4 --- /dev/null +++ b/bigbluebutton-html5/imports/api/polls/server/methods.js @@ -0,0 +1,6 @@ +import { Meteor } from 'meteor/meteor'; +import publishVote from './methods/publishVote'; + +Meteor.methods({ + publishVote, +}); diff --git a/bigbluebutton-html5/imports/api/polls/server/methods/publishVote.js b/bigbluebutton-html5/imports/api/polls/server/methods/publishVote.js new file mode 100644 index 0000000000000000000000000000000000000000..5f7f545e4f1bd5fffc6a79bd0fc8db8450db55cc --- /dev/null +++ b/bigbluebutton-html5/imports/api/polls/server/methods/publishVote.js @@ -0,0 +1,61 @@ +import { isAllowedTo } from '/imports/startup/server/userPermissions'; +import RedisPubSub from '/imports/startup/server/redis'; +import { check } from 'meteor/check'; +import Polls from '/imports/api/polls'; +import Logger from '/imports/startup/server/logger'; + +export default function publishVote(credentials, pollId, pollAnswerId) { //TODO discuss location + const REDIS_CONFIG = Meteor.settings.redis; + const CHANNEL = REDIS_CONFIG.channels.toBBBApps.polling; + const EVENT_NAME = 'vote_poll_user_request_message'; + + if (!isAllowedTo('subscribePoll', credentials)) { + throw new Meteor.Error('not-allowed', `You are not allowed to publishVote`); + } + + const { meetingId, requesterUserId, requesterToken } = credentials; + + const currentPoll = Polls.findOne({ + users: requesterUserId, + meetingId: meetingId, + 'poll.answers.id': pollAnswerId, + 'poll.id': pollId, + }); + + check(meetingId, String); + check(requesterUserId, String); + check(pollAnswerId, Number); + check(currentPoll.meetingId, String); + + let payload = { + meeting_id: currentPoll.meetingId, + user_id: requesterUserId, + poll_id: currentPoll.poll.id, + question_id: 0, + answer_id: pollAnswerId, + }; + + const selector = { + users: requesterUserId, + meetingId: meetingId, + 'poll.answers.id': pollAnswerId, + }; + + const modifier = { + $pull: { + users: requesterUserId, + }, + }; + + const cb = (err, numChanged) => { + if (err) { + return Logger.error(`Updating Polls collection: ${err}`); + } + + Logger.info(`Updating Polls collection (meetingId: ${meetingId}, + pollId: ${currentPoll.poll.id}!)`); + }; + + Polls.update(selector, modifier, cb); + return RedisPubSub.publish(CHANNEL, EVENT_NAME, payload); +} diff --git a/bigbluebutton-html5/imports/api/polls/server/methods/publishVoteMessage.js b/bigbluebutton-html5/imports/api/polls/server/methods/publishVoteMessage.js deleted file mode 100755 index 9e88c0505d51b19cfc068bf130f191eba5381aff..0000000000000000000000000000000000000000 --- a/bigbluebutton-html5/imports/api/polls/server/methods/publishVoteMessage.js +++ /dev/null @@ -1,49 +0,0 @@ -import { publish } from '/imports/api/common/server/helpers'; -import { isAllowedTo } from '/imports/startup/server/userPermissions'; -import { appendMessageHeader } from '/imports/api/common/server/helpers'; -import Polls from '/imports/api/polls'; -import { logger } from '/imports/startup/server/logger'; - -Meteor.methods({ - publishVoteMessage(credentials, pollId, pollAnswerId) { //TODO discuss location - const REDIS_CONFIG = Meteor.settings.redis; - if (isAllowedTo('subscribePoll', credentials)) { - const { meetingId, requesterUserId, requesterToken } = credentials; - const eventName = 'vote_poll_user_request_message'; - - const result = Polls.findOne({ - users: requesterUserId, - meetingId: meetingId, - 'poll.answers.id': pollAnswerId, - 'poll.id': pollId, - }); - - if ((meetingId != null) && - (result.meetingId != null) && - (requesterUserId != null) && - (pollAnswerId != null)) { - let message = { - payload: { - meeting_id: result.meetingId, - user_id: requesterUserId, - poll_id: result.poll.id, - question_id: 0, - answer_id: pollAnswerId, - }, - }; - Polls.update({ - users: requesterUserId, - meetingId: meetingId, - 'poll.answers.id': pollAnswerId, - }, { - $pull: { - users: requesterUserId, - }, - }); - message = appendMessageHeader(eventName, message); - logger.info('publishing Poll response to redis'); - return publish(REDIS_CONFIG.channels.toBBBApps.polling, message); - } - } - }, -}); diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/addPoll.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/addPoll.js new file mode 100644 index 0000000000000000000000000000000000000000..ea091c7b82dfcce584eb2bd7689125e098c84458 --- /dev/null +++ b/bigbluebutton-html5/imports/api/polls/server/modifiers/addPoll.js @@ -0,0 +1,54 @@ +import Meetings from '/imports/api/meetings'; +import Users from '/imports/api/users'; +import Polls from '/imports/api/polls'; +import Logger from '/imports/startup/server/logger'; +import { check } from 'meteor/check'; + +export default function addPoll(meetingId, requesterId, poll) { + check(poll, Object); + check(requesterId, String); + check(meetingId, String); + + let selector = { + meetingId: meetingId, + }; + + const options = { + fields: { + 'user.userid': 1, + _id: 0, + }, + }; + + const userIds = Users.find(selector, options) + .fetch() + .map(user => user.user.userid); + + selector = { + meetingId, + requester: requesterId, + 'poll.id': poll.id, + }; + + const modifier = { + meetingId, + poll, + requester: requesterId, + users: userIds, + }; + + const cb = (err, numChanged) => { + if (err != null) { + return Logger.error(`Adding Poll to collection: ${poll.id}`); + } + + const { insertedId } = numChanged; + if (insertedId) { + return Logger.info(`Added Poll id=${poll.id}`); + } + + return Logger.info(`Upserted Poll id=${poll.id}`); + }; + + return Polls.upsert(selector, modifier, cb); +}; diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/addPollToCollection.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/addPollToCollection.js deleted file mode 100755 index 6ac5fb87e1749c476687afd3c26d89038e42b8d9..0000000000000000000000000000000000000000 --- a/bigbluebutton-html5/imports/api/polls/server/modifiers/addPollToCollection.js +++ /dev/null @@ -1,37 +0,0 @@ -import Polls from '/imports/api/polls'; -import { logger } from '/imports/startup/server/logger'; - -export function addPollToCollection(poll, requesterId, users, meetingId) { - // copying all the userids into an array - let newUsers = []; - newUsersLength = users.length; - for (let i = 0; i < newUsersLength; i++) { - const user = users[i]; - newUsers.push(user.user.userid); - } - - // adding the initial number of votes for each answer - // _answers = poll.answers; - // _answers_length = _answers.length; - // for (j = 0; j < _answers_length; j++) { - // answer = _answers[j]; - // answer.num_votes = 0; - // } - - // adding the initial number of responders and respondents to the poll, which will be displayed - // for presenter (in HTML5 client) when they start the poll - numResponders = -1; - numRespondents = -1; - - // adding all together and inserting into the Polls collection - const entry = { - meetingId: meetingId, - poll: poll, - requester: requesterId, - users: newUsers, - num_responders: -1, - num_respondents: -1, - }; - logger.info(`added poll _id=[${poll.id}]:meetingId=[${meetingId}].`); - return Polls.insert(entry); -}; diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/clearPollCollection.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/clearPollCollection.js deleted file mode 100755 index 5070cb700f616c9bc14b3f1475cf6b00cfe7a044..0000000000000000000000000000000000000000 --- a/bigbluebutton-html5/imports/api/polls/server/modifiers/clearPollCollection.js +++ /dev/null @@ -1,17 +0,0 @@ -import Polls from '/imports/api/polls'; -import { logger } from '/imports/startup/server/logger'; - -export function clearPollCollection() { - const meetingId = arguments[0]; - const pollId = arguments[1]; - - //TODO make it so you can delete the polls based only on meetingId - if (meetingId != null && pollId != null) { - return Polls.remove({ - meetingId: meetingId, - 'poll.id': pollId, - }, logger.info(`cleared Polls Collection (meetingId: ${meetingId}, pollId: ${pollId}!)`)); - } else { - return Polls.remove({}, logger.info('cleared Polls Collection (all meetings)!')); - } -}; diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/clearPolls.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/clearPolls.js new file mode 100644 index 0000000000000000000000000000000000000000..7e4ea4a1ea7b2ecfa23788f817f0345522a07fcc --- /dev/null +++ b/bigbluebutton-html5/imports/api/polls/server/modifiers/clearPolls.js @@ -0,0 +1,10 @@ +import Polls from '/imports/api/polls'; +import Logger from '/imports/startup/server/logger'; + +export default function clearPolls(meetingId) { + if (meetingId) { + return Polls.remove({ meetingId, }, Logger.info(`Cleared Polls (${meetingId})`)); + } + + return Polls.remove({}, Logger.info('Cleared Polls (all)')); +}; diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/eventHandlers.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/eventHandlers.js deleted file mode 100644 index 20bbc4c92ca13b4e8fd9997c2b5b20ac2fcd12bb..0000000000000000000000000000000000000000 --- a/bigbluebutton-html5/imports/api/polls/server/modifiers/eventHandlers.js +++ /dev/null @@ -1,70 +0,0 @@ -import { eventEmitter } from '/imports/startup/server'; -import { clearPollCollection } from './clearPollCollection'; -import { updatePollCollection } from './updatePollCollection'; -import { addPollToCollection } from './addPollToCollection'; -import Meetings from '/imports/api/meetings'; -import Users from '/imports/api/users'; - -eventEmitter.on('poll_show_result_message', function (arg) { - const payload = arg.payload; - const meetingId = payload.meeting_id; - if (payload != null && payload.poll != null && payload.poll.id != null && meetingId != null) { - const pollId = payload.poll.id; - clearPollCollection(meetingId, pollId); - } - - return arg.callback(); -}); - -eventEmitter.on('poll_started_message', function (arg) { - const payload = arg.payload; - const meetingId = payload.meeting_id; - - if (payload != null && meetingId != null && - payload.requester_id != null && payload.poll != null) { - if (Meetings.findOne({ - meetingId: meetingId, - }) != null) { - const users = Users.find({ - meetingId: meetingId, - }, { - fields: { - 'user.userid': 1, - _id: 0, - }, - }).fetch(); - addPollToCollection( - payload.poll, - payload.requester_id, - users, - meetingId - ); - } - } - - return arg.callback(); -}); - -eventEmitter.on('poll_stopped_message', function (arg) { - const payload = arg.payload; - const meetingId = payload.meeting_id; - - if (meetingId != null && payload != null && payload.poll_id != null) { - const pollId = payload.poll_id; - clearPollCollection(meetingId, pollId); - } - - return arg.callback(); -}); - -eventEmitter.on('user_voted_poll_message', function (arg) { - const payload = arg.payload; - const meetingId = payload.meeting_id; - if (payload != null && payload.poll != null && meetingId != null && - payload.presenter_id != null) { - const pollObj = payload.poll; - const requesterId = payload.presenter_id; - updatePollCollection(pollObj, meetingId, requesterId); - return arg.callback(); - } -}); diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/removePoll.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/removePoll.js new file mode 100644 index 0000000000000000000000000000000000000000..f5af42d8febab5cf649878a77f0cbde1ad8ff16c --- /dev/null +++ b/bigbluebutton-html5/imports/api/polls/server/modifiers/removePoll.js @@ -0,0 +1,25 @@ +import Polls from '/imports/api/polls'; +import { check } from 'meteor/check'; +import Logger from '/imports/startup/server/logger'; + +export default function removePoll(meetingId, pollId) { + check(meetingId, String); + check(pollId, String); + + const selector = { + meetingId, + 'poll.id': pollId, + }; + + const cb = (err, numChanged) => { + if (err) { + return Logger.error(`Removing Poll from collection: ${err}`); + } + + if (numChanged) { + return Logger.info(`Removed Poll id=${pollId}`); + } + }; + + return Polls.remove(selector, cb); +}; diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/updatePollCollection.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/updatePollCollection.js deleted file mode 100755 index e224c5932f9a99d645c42fa441ade80459e9e183..0000000000000000000000000000000000000000 --- a/bigbluebutton-html5/imports/api/polls/server/modifiers/updatePollCollection.js +++ /dev/null @@ -1,19 +0,0 @@ -import Polls from '/imports/api/polls'; -import { logger } from '/imports/startup/server/logger'; - -export function updatePollCollection(poll, meetingId, requesterId) { - if ((poll.answers != null) && (poll.numResponders != null) && (poll.numRespondents != null) && - (poll.id != null) && (meetingId != null) && (requesterId != null)) { - return Polls.update({ - meetingId: meetingId, - requester: requesterId, - poll: { id: poll.id }, - }, { - $set: { - poll: { answers: poll.answers }, - poll: { num_responders: poll.numResponders }, - poll: { num_respondents: poll.numRespondents }, - }, - }, logger.info(`updating Polls Collection (meetingId: ${meetingId}, pollId: ${poll.id}!)`)); - } -}; diff --git a/bigbluebutton-html5/imports/api/polls/server/modifiers/updateVotes.js b/bigbluebutton-html5/imports/api/polls/server/modifiers/updateVotes.js new file mode 100644 index 0000000000000000000000000000000000000000..540f58373eb43d75efe2109541e18811ddd3e9d0 --- /dev/null +++ b/bigbluebutton-html5/imports/api/polls/server/modifiers/updateVotes.js @@ -0,0 +1,49 @@ +import Polls from '/imports/api/polls'; +import { check } from 'meteor/check'; +import Logger from '/imports/startup/server/logger'; + +export default function updateVotes(poll, meetingId, requesterId) { + check(meetingId, String); + check(requesterId, String); + check(poll, Object); + + const { + id, + answers, + } = poll; + + const numResponders = poll.num_responders; + const numRespondents = poll.num_respondents; + + check(id, String); + check(answers, Array); + + check(numResponders, Number); + check(numRespondents, Number); + + const selector = { + meetingId, + requester: requesterId, + 'poll.id': id, + }; + + const modifier = { + $set: { + poll: { + answers: answers, + num_responders: numResponders, + num_respondents: numRespondents, + }, + }, + }; + + const cb = (err, numChanged) => { + if (err) { + return Logger.error(`Updating Polls collection: ${err}`); + } + + Logger.info(`Updating Polls collection (meetingId: ${meetingId}, pollId: ${id}!)`); + }; + + return Polls.update(selector, modifier, cb); +}; diff --git a/bigbluebutton-html5/imports/api/polls/server/publications.js b/bigbluebutton-html5/imports/api/polls/server/publications.js deleted file mode 100755 index 7092ebe1beca24c6f2eaaaf468609ec6ae15f5c8..0000000000000000000000000000000000000000 --- a/bigbluebutton-html5/imports/api/polls/server/publications.js +++ /dev/null @@ -1,33 +0,0 @@ -import { isAllowedTo } from '/imports/startup/server/userPermissions'; -import Polls from '/imports/api/polls'; -import { logger } from '/imports/startup/server/logger'; - -Meteor.publish('polls', function (credentials) { - const { meetingId, requesterUserId, requesterToken } = credentials; - - //checking if it is allowed to see Poll Collection in general - if (isAllowedTo('subscribePoll', credentials)) { - //checking if it is allowed to see a number of votes (presenter only) - if (isAllowedTo('subscribeAnswers', credentials)) { - logger.info('publishing Poll for presenter: ' + meetingId + ' ' + requesterUserId + ' ' + - requesterToken); - return Polls.find({ - meetingId: meetingId, - users: requesterUserId, - }); - } else { - logger.info('publishing Poll for viewer: ' + meetingId + ' ' + requesterUserId + ' ' + - requesterToken); - return Polls.find({ - meetingId: meetingId, - users: requesterUserId, - }, { - fields: { - 'poll.answers.num_votes': 0, - }, - }); - } - } else { - this.error(new Meteor.Error(402, "The user was not authorized to subscribe for 'polls'")); - } -}); diff --git a/bigbluebutton-html5/imports/api/polls/server/publishers.js b/bigbluebutton-html5/imports/api/polls/server/publishers.js new file mode 100644 index 0000000000000000000000000000000000000000..930e15edcb4994a75c53e9a041fe2d1ced67d21d --- /dev/null +++ b/bigbluebutton-html5/imports/api/polls/server/publishers.js @@ -0,0 +1,35 @@ +import { Meteor } from 'meteor/meteor'; +import { isAllowedTo } from '/imports/startup/server/userPermissions'; +import Polls from '/imports/api/polls'; +import { check } from 'meteor/check'; +import { logger } from '/imports/startup/server/logger'; + +Meteor.publish('polls', (credentials) => { + //checking if it is allowed to see Poll Collection in general + if (!isAllowedTo('subscribePoll', credentials)) { + this.error(new Meteor.Error(402, "The user was not authorized to subscribe for 'polls'")); + } + + const { meetingId, requesterUserId, requesterToken } = credentials; + + check(meetingId, String); + check(requesterUserId, String); + check(requesterToken, String); + + const selector = { + meetingId: meetingId, + users: requesterUserId, + }; + + let options = null; + + if (!isAllowedTo('subscribeAnswers', credentials)) { + options = { + fields: { + 'poll.answers.num_votes': 0, + }, + }; + } + + return Polls.find(selector, options); +}); diff --git a/bigbluebutton-html5/imports/ui/components/polling/service.js b/bigbluebutton-html5/imports/ui/components/polling/service.js index 6741436dd6d4a199a28340e4f9787dad51645b8f..a2fc2746f2ac81b5a8b46a3cbfc3b6f461c056b8 100755 --- a/bigbluebutton-html5/imports/ui/components/polling/service.js +++ b/bigbluebutton-html5/imports/ui/components/polling/service.js @@ -17,7 +17,7 @@ let mapPolls = function () { pollExists: true, amIRequester: amIRequester, handleVote: function (pollId, answerId) { - callServer('publishVoteMessage', pollId, answerId.id); + callServer('publishVote', pollId, answerId.id); }, }; }; diff --git a/bigbluebutton-html5/server/main.js b/bigbluebutton-html5/server/main.js index 6d0a06ca788dcd2ab505c32a22fe2cf2b6971d67..d1256192fe2f115b164ba056648bc8d26f0c61ab 100755 --- a/bigbluebutton-html5/server/main.js +++ b/bigbluebutton-html5/server/main.js @@ -17,12 +17,7 @@ import '/imports/api/meetings/server'; import '/imports/api/phone/server/modifiers/eventHandlers'; -import '/imports/api/polls/server/publications'; -import '/imports/api/polls/server/methods/publishVoteMessage'; -import '/imports/api/polls/server/modifiers/addPollToCollection'; -import '/imports/api/polls/server/modifiers/clearPollCollection'; -import '/imports/api/polls/server/modifiers/updatePollCollection'; -import '/imports/api/polls/server/modifiers/eventHandlers'; +import '/imports/api/polls/server'; import '/imports/api/presentations/server'; diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy index cb9a9ffbfb21087aa9285e888ad4fcad7c672d51..01e641cd98585163e011b320640b1f20eb256f61 100755 --- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy +++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy @@ -18,6 +18,8 @@ */ package org.bigbluebutton.web.controllers +import com.google.gson.Gson + import javax.servlet.ServletRequest; import java.net.URI; @@ -1455,7 +1457,7 @@ class ApiController { reject = true } else { sessionToken = StringUtils.strip(params.sessionToken) - log.info("SessionToken = " + sessionToken) + log.info("Getting ConfigXml for SessionToken = " + sessionToken) if (!session[sessionToken]) { reject = true } else { @@ -1478,6 +1480,20 @@ class ApiController { } } } else { + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", us.meetingID); + logData.put("externalMeetingId", us.externMeetingID); + logData.put("name", us.fullname); + logData.put("userId", us.internalUserId); + logData.put("sessionToken", sessionToken); + logData.put("message", "handle_configxml_api"); + logData.put("description", "Handling ConfigXml API."); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.info(logStr); + response.addHeader("Cache-Control", "no-cache") render text: us.configXML, contentType: 'text/xml' } @@ -1544,7 +1560,20 @@ class ApiController { // how many times a user reconnects or refresh the browser. String newInternalUserID = us.internalUserId + "_" + us.incrementConnectionNum() - log.info("Found conference for " + us.fullname) + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", us.meetingID); + logData.put("externalMeetingId", us.externMeetingID); + logData.put("name", us.fullname); + logData.put("userId", newInternalUserID); + logData.put("sessionToken", sessionToken); + logData.put("message", "handle_enter_api"); + logData.put("description", "Handling ENTER API."); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.info(logStr); + response.addHeader("Cache-Control", "no-cache") withFormat { json { diff --git a/record-and-playback/core/Gemfile.lock b/record-and-playback/core/Gemfile.lock index 16e319e4073c5e1eae440674a67af97b779b217e..c708d7d2f809817332301df0c40978d97a9fbb57 100644 --- a/record-and-playback/core/Gemfile.lock +++ b/record-and-playback/core/Gemfile.lock @@ -2,37 +2,41 @@ GEM remote: http://rubygems.org/ specs: absolute_time (1.0.0) - addressable (2.3.6) + addressable (2.4.0) builder (3.2.2) - curb (0.8.6) - fastimage (1.6.4) - addressable (~> 2.3, >= 2.3.5) - mime-types (2.4.3) - mini_portile (0.6.1) + curb (0.9.3) + fastimage (2.0.0) + addressable (~> 2) + mime-types (2.6.2) + mini_portile2 (2.1.0) mono_logger (1.1.0) - multi_json (1.10.1) - nokogiri (1.6.4.1) - mini_portile (~> 0.6.0) + multi_json (1.12.1) + nokogiri (1.6.8) + mini_portile2 (~> 2.1.0) + pkg-config (~> 1.1.7) open4 (1.3.4) - rack (1.5.2) + pkg-config (1.1.7) + rack (1.6.4) rack-protection (1.5.3) rack - redis (3.1.0) - redis-namespace (1.5.1) + redis (3.3.1) + redis-namespace (1.5.2) redis (~> 3.0, >= 3.0.4) - resque (1.25.2) + resque (1.26.0) mono_logger (~> 1.0) multi_json (~> 1.0) redis-namespace (~> 1.3) sinatra (>= 0.9.2) vegas (~> 0.1.2) - rubyzip (1.1.6) - sinatra (1.4.5) - rack (~> 1.4) + rubyzip (1.2.0) + sinatra (1.4.7) + rack (~> 1.5) rack-protection (~> 1.4) - tilt (~> 1.3, >= 1.3.4) - tilt (1.4.1) - trollop (2.0) + tilt (>= 1.3, < 3) + streamio-ffmpeg (2.1.0) + multi_json (~> 1.8) + tilt (2.0.5) + trollop (2.1.2) vegas (0.1.11) rack (>= 1.0.0) @@ -44,13 +48,15 @@ DEPENDENCIES builder curb fastimage - mime-types + mime-types (= 2.6.2) nokogiri open4 redis resque rubyzip + streamio-ffmpeg trollop BUNDLED WITH - 1.10.6 + 1.12.5 + diff --git a/record-and-playback/core/lib/recordandplayback/edl/audio.rb b/record-and-playback/core/lib/recordandplayback/edl/audio.rb index 943200b6b823fb866af1cb7e6509489560db8f6d..316a7d07954555b58af2751637baa7d4545a6a89 100644 --- a/record-and-playback/core/lib/recordandplayback/edl/audio.rb +++ b/record-and-playback/core/lib/recordandplayback/edl/audio.rb @@ -172,15 +172,22 @@ module BigBlueButton def self.audio_info(filename) IO.popen([*FFPROBE, filename]) do |probe| - info = JSON.parse(probe.read, :symbolize_names => true) - if !info[:streams] - return {} + info = nil + begin + info = JSON.parse(probe.read, :symbolize_names => true) + rescue StandardError => e + BigBlueButton.logger.warn("Couldn't parse audio info: #{e}") end - info[:audio] = info[:streams].find { |stream| stream[:codec_type] == 'audio' } + return {} if !info + return {} if !info[:streams] + return {} if !info[:format] - if info[:audio] - info[:sample_rate] = info[:audio][:sample_rate].to_i + info[:audio] = info[:streams].find do |stream| + stream[:codec_type] == 'audio' end + return {} if !info[:audio] + + info[:sample_rate] = info[:audio][:sample_rate].to_i if info[:format][:format_name] == 'wav' # wav files generated by freeswitch can have incorrect length diff --git a/record-and-playback/core/lib/recordandplayback/edl/video.rb b/record-and-playback/core/lib/recordandplayback/edl/video.rb index 2e5d1429829eb22db9349382a3fa552a3837cd0e..c5e4084928f084531b7146af8a6365fd30945939 100644 --- a/record-and-playback/core/lib/recordandplayback/edl/video.rb +++ b/record-and-playback/core/lib/recordandplayback/edl/video.rb @@ -269,33 +269,35 @@ module BigBlueButton def self.video_info(filename) IO.popen([*FFPROBE, filename]) do |probe| - info = JSON.parse(probe.read, :symbolize_names => true) + info = nil + begin + info = JSON.parse(probe.read, :symbolize_names => true) + rescue StandardError => e + BigBlueButton.logger.warn("Couldn't parse video info: #{e}") + end return {} if !info + return {} if !info[:streams] + return {} if !info[:format] - if info[:streams] - info[:video] = info[:streams].find { |stream| stream[:codec_type] == 'video' } - info[:audio] = info[:streams].find { |stream| stream[:codec_type] == 'audio' } + info[:video] = info[:streams].find do |stream| + stream[:codec_type] == 'audio' end - if info[:video] - info[:width] = info[:video][:width].to_i - info[:height] = info[:video][:height].to_i + return {} if !info[:video] - return {} if info[:width] == 0 or info[:height] == 0 - return {} if info[:video][:display_aspect_ratio] == '0:0' + info[:width] = info[:video][:width].to_i + info[:height] = info[:video][:height].to_i - info[:aspect_ratio] = info[:video][:display_aspect_ratio].to_r - if info[:aspect_ratio] == 0 - info[:aspect_ratio] = Rational(info[:width], info[:height]) - end + return {} if info[:width] == 0 or info[:height] == 0 + return {} if info[:video][:display_aspect_ratio] == '0:0' + + info[:aspect_ratio] = Rational(*(info[:video][:display_aspect_ratio].split(':'))) + if info[:aspect_ratio] == 0 + info[:aspect_ratio] = Rational(info[:width], info[:height]) end # Convert the duration to milliseconds - if info[:format] - info[:duration] = (info[:format][:duration].to_r * 1000).to_i - else - info[:duration] = 0 - end + info[:duration] = (info[:format][:duration].to_r * 1000).to_i return info end