From 867e9b9b36b8d8f01f7a19c03ec1d49009bdc8f2 Mon Sep 17 00:00:00 2001 From: Felipe Cecagno Date: Thu, 19 Mar 2015 11:54:08 -0300 Subject: [PATCH] first attempt to use an auto-reconnection handler, still not working properly --- .../main/model/users/AutoReconnect.as | 55 ++ .../main/model/users/NetConnectionDelegate.as | 58 +- .../deskshare/services/red5/Connection.as | 52 +- .../view/components/DesktopViewWindow.mxml | 6 + .../modules/videoconf/business/VideoProxy.as | 435 ++++++++------ .../videoconf/maps/VideoEventMapDelegate.as | 6 +- .../modules/videoconf/views/VideoWindow.mxml | 563 +++++++++--------- 7 files changed, 680 insertions(+), 495 deletions(-) create mode 100644 bigbluebutton-client/src/org/bigbluebutton/main/model/users/AutoReconnect.as diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/AutoReconnect.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/AutoReconnect.as new file mode 100644 index 0000000000..c2f4896577 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/AutoReconnect.as @@ -0,0 +1,55 @@ +/** +* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +* +* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). +* +* This program is free software; you can redistribute it and/or modify it under the +* terms of the GNU Lesser General Public License as published by the Free Software +* Foundation; either version 3.0 of the License, or (at your option) any later +* version. +* +* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License along +* with BigBlueButton; if not, see . +* +*/ +package org.bigbluebutton.main.model.users +{ + import flash.events.TimerEvent; + import flash.utils.Timer; + + public class AutoReconnect + { + public static const LOG:String = "AutoReconnect - "; + + private var _backoff:Number = 2000; + private var _reconnectCallback:Function; + private var _reconnectParameters:Array; + + public function onDisconnect(callback:Function, ...parameters):void { + trace(LOG + "onDisconnect, parameters=" + parameters.toString()); + _reconnectCallback = callback; + _reconnectParameters = parameters; + attemptReconnect(1000); + } + + public function onConnectionAttemptFailed():void { + trace(LOG + "onConnectionAttemptFailed"); + attemptReconnect(_backoff); + } + + private function attemptReconnect(backoff:Number):void{ + trace(LOG + "attemptReconnect backoff=" + backoff); + var retryTimer:Timer = new Timer(backoff, 1); + retryTimer.addEventListener(TimerEvent.TIMER, function():void { + trace(LOG + "Reconnecting"); + _reconnectCallback.apply(null, _reconnectParameters); + }); + retryTimer.start(); + if (_backoff < 16000) _backoff = backoff *2; + } + } +} 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 79cfb5114a..0f4218705e 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/NetConnectionDelegate.as @@ -30,10 +30,11 @@ package org.bigbluebutton.main.model.users import org.bigbluebutton.core.UsersUtil; import org.bigbluebutton.core.services.BandwidthMonitor; import org.bigbluebutton.main.api.JSLog; + import org.bigbluebutton.main.events.ClientStatusEvent; import org.bigbluebutton.main.events.InvalidAuthTokenEvent; import org.bigbluebutton.main.model.ConferenceParameters; import org.bigbluebutton.main.model.users.events.ConnectionFailedEvent; - import org.bigbluebutton.main.model.users.events.UsersConnectionEvent; + import org.bigbluebutton.main.model.users.events.UsersConnectionEvent; public class NetConnectionDelegate { @@ -60,6 +61,8 @@ package org.bigbluebutton.main.model.users private var _messageListeners:Array = new Array(); private var authenticated: Boolean = false; + private var reconnect:AutoReconnect = new AutoReconnect(); + private var reconnecting:Boolean = false; public function NetConnectionDelegate():void { @@ -158,6 +161,10 @@ package org.bigbluebutton.main.model.users trace(LOG + "*** handleValidateAuthTokenTimedOut. valid=[ " + tokenValid + "] **** \n"); dispatcher.dispatchEvent(new InvalidAuthTokenEvent()); } + if (reconnecting) { + onReconnect(); + reconnecting = false; + } } private function handleValidateAuthTokenReply(msg: Object):void { @@ -173,7 +180,31 @@ package org.bigbluebutton.main.model.users trace(LOG + "*** handleValidateAuthTokenReply. valid=[ " + tokenValid + "] **** \n"); dispatcher.dispatchEvent(new InvalidAuthTokenEvent()); } + if (reconnecting) { + onReconnect(); + reconnecting = false; + } } + + private function onReconnect():void { + if (authenticated) { + onReconnectSuccess(); + } else { + onReconnectFailed(); + } + } + + private function onReconnectSuccess():void { + dispatcher.dispatchEvent(new ClientStatusEvent(ClientStatusEvent.SUCCESS_MESSAGE_EVENT, + "Connection reestablished", + "Main connection has been reestablished successfully")); + } + + private function onReconnectFailed():void { + dispatcher.dispatchEvent(new ClientStatusEvent(ClientStatusEvent.FAIL_MESSAGE_EVENT, + "Connection failed", + "It was not possible to reestablish the main connection")); + } private function sendConnectionSuccessEvent(userid:String):void{ var e:UsersConnectionEvent = new UsersConnectionEvent(UsersConnectionEvent.CONNECTION_SUCCESS); @@ -367,8 +398,20 @@ package org.bigbluebutton.main.model.users logData.reason = reason; logData.user = UsersUtil.getUserData(); JSLog.warn("User disconnected from BBB App.", logData); - var e:ConnectionFailedEvent = new ConnectionFailedEvent(reason); - dispatcher.dispatchEvent(e); + + if (reconnecting) { + reconnect.onConnectionAttemptFailed(); + } else { + dispatcher.dispatchEvent(new ClientStatusEvent(ClientStatusEvent.WARNING_MESSAGE_EVENT, + "Main connection dropped", + "Attempting to reconnect")); + reconnecting = true; + authenticated = false; + reconnect.onDisconnect(connect, _conferenceParameters, tried_tunneling); + } + + //var e:ConnectionFailedEvent = new ConnectionFailedEvent(reason); + //dispatcher.dispatchEvent(e); } } @@ -377,15 +420,6 @@ package org.bigbluebutton.main.model.users dispatcher.dispatchEvent(e); } - private function attemptReconnect(backoff:Number):void{ - var retryTimer:Timer = new Timer(backoff, 1); - retryTimer.addEventListener(TimerEvent.TIMER, function():void{ - connect(_conferenceParameters, tried_tunneling); - }); - retryTimer.start(); - if (this.backoff < 16000) this.backoff = backoff *2; - } - public function onBWCheck(... rest):Number { return 0; } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/services/red5/Connection.as b/bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/services/red5/Connection.as index fdc89ed0bd..7a5e34a86f 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/services/red5/Connection.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/services/red5/Connection.as @@ -21,6 +21,7 @@ package org.bigbluebutton.modules.deskshare.services.red5 { import com.asfusion.mate.events.Dispatcher; + import flash.events.AsyncErrorEvent; import flash.events.EventDispatcher; import flash.events.NetStatusEvent; import flash.events.SecurityErrorEvent; @@ -32,13 +33,16 @@ package org.bigbluebutton.modules.deskshare.services.red5 import flash.utils.Timer; import mx.events.MetadataEvent; + import mx.utils.ObjectUtil; import org.bigbluebutton.common.LogUtil; import org.bigbluebutton.core.UsersUtil; + import org.bigbluebutton.main.events.ClientStatusEvent; + import org.bigbluebutton.main.model.users.AutoReconnect; import org.bigbluebutton.modules.deskshare.events.AppletStartedEvent; import org.bigbluebutton.modules.deskshare.events.CursorEvent; - import org.bigbluebutton.modules.deskshare.events.ViewStreamEvent; - + import org.bigbluebutton.modules.deskshare.events.ViewStreamEvent; + public class Connection { public static const LOG:String = "Deskshare::Connection - "; @@ -54,6 +58,9 @@ package org.bigbluebutton.modules.deskshare.services.red5 private var width:Number; private var height:Number; private var room:String; + private var reconnect:AutoReconnect = new AutoReconnect(); + private var logoutOnUserCommand:Boolean = false; + private var reconnecting:Boolean = false; private var dispatcher:Dispatcher = new Dispatcher(); @@ -92,6 +99,8 @@ package org.bigbluebutton.modules.deskshare.services.red5 nc.objectEncoding = ObjectEncoding.AMF0; nc.client = this; + nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, debugAsyncErrorHandler); + nc.addEventListener(NetStatusEvent.NET_STATUS, debugNetStatusHandler); nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); @@ -113,11 +122,11 @@ package org.bigbluebutton.modules.deskshare.services.red5 nc.connect(getURI(), UsersUtil.getInternalMeetingID()); - if (!retry) { - retryTimer = new Timer(connectionTimeout, 1); - retryTimer.addEventListener(TimerEvent.TIMER_COMPLETE, connectTimeoutHandler); - retryTimer.start(); - } + //if (!retry) { + // retryTimer = new Timer(connectionTimeout, 1); + // retryTimer.addEventListener(TimerEvent.TIMER_COMPLETE, connectTimeoutHandler); + // retryTimer.start(); + //} } private function connectTimeoutHandler(e:TimerEvent):void { @@ -182,6 +191,9 @@ package org.bigbluebutton.modules.deskshare.services.red5 switch(event.info.code){ case "NetConnection.Connect.Failed": + if (reconnecting) { + reconnect.onConnectionAttemptFailed(); + } ce.status = ConnectionEvent.FAILED; dispatcher.dispatchEvent(ce); @@ -189,6 +201,12 @@ package org.bigbluebutton.modules.deskshare.services.red5 case "NetConnection.Connect.Success": ce.status = ConnectionEvent.SUCCESS; + if (reconnecting) { + dispatcher.dispatchEvent(new ClientStatusEvent(ClientStatusEvent.SUCCESS_MESSAGE_EVENT, + "Connection reestablished", + "Deskshare connection has been reestablished successfully")); + reconnecting = false; + } dispatcher.dispatchEvent(ce); connectionSuccessHandler(); break; @@ -201,7 +219,14 @@ package org.bigbluebutton.modules.deskshare.services.red5 case "NetConnection.Connect.Closed": trace(LOG + "Deskshare connection closed."); ce.status = ConnectionEvent.CLOSED; -// dispatcher.dispatchEvent(ce); + stopViewing(); + if (!logoutOnUserCommand) { + dispatcher.dispatchEvent(new ClientStatusEvent(ClientStatusEvent.WARNING_MESSAGE_EVENT, + "Deskshare connection dropped", + "Attempting to reconnect")); + reconnecting = true; + reconnect.onDisconnect(connect); + } break; case "NetConnection.Connect.InvalidApp": @@ -256,6 +281,7 @@ package org.bigbluebutton.modules.deskshare.services.red5 } public function disconnect():void{ + logoutOnUserCommand = true; if (nc != null) nc.close(); } @@ -264,10 +290,20 @@ package org.bigbluebutton.modules.deskshare.services.red5 var deskSOName:String = room + "-deskSO"; deskSO = SharedObject.getRemote(deskSOName, uri, false); deskSO.client = this; + deskSO.addEventListener(AsyncErrorEvent.ASYNC_ERROR, debugAsyncErrorHandler); + deskSO.addEventListener(NetStatusEvent.NET_STATUS, debugNetStatusHandler); deskSO.connect(nc); checkIfStreamIsPublishing(room); } + + private function debugNetStatusHandler(e:NetStatusEvent):void { + trace(LOG + "netStatusHandler target=" + e.target + " info=" + ObjectUtil.toString(e.info)); + } + + private function debugAsyncErrorHandler(e:AsyncErrorEvent):void { + trace(LOG + "asyncErrorHandler target=" + e.target + " text=" + e.text); + } public function getConnection():NetConnection{ return nc; diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/view/components/DesktopViewWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/view/components/DesktopViewWindow.mxml index da4bed5198..206bc088f0 100644 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/view/components/DesktopViewWindow.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/view/components/DesktopViewWindow.mxml @@ -41,6 +41,7 @@ with BigBlueButton; if not, see . import flash.sampler.NewObjectSample; import flexlib.mdi.events.MDIWindowEvent; import mx.core.UIComponent; + import mx.utils.ObjectUtil; import org.bigbluebutton.common.Images; import org.bigbluebutton.common.LogUtil; import org.bigbluebutton.core.managers.UserManager; @@ -72,6 +73,8 @@ with BigBlueButton; if not, see . private static const VIDEO_WIDTH_PADDING:int = 7; private static const VIDEO_HEIGHT_PADDING:int = 65; + public static const LOG:String = "Deskshare::DesktopViewWindow - "; + // The following code block is to deal with a bug in FLexLib // with MDI windows not responding well to being maximized private var savedWindowWidth:Number; @@ -178,10 +181,13 @@ with BigBlueButton; if not, see . } private function onAsyncError(e:AsyncErrorEvent):void{ + trace(LOG + "onAsyncError " + e.toString()); LogUtil.debug("VIdeoWindow::asyncerror " + e.toString()); } private function onNetStatus(e:NetStatusEvent):void{ + trace(LOG + "onNetStatus info=" + e.info.text); + switch(e.info.code){ case "NetStream.Play.Start": LogUtil.debug("NetStream.Publish.Start for broadcast stream " + stream); diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as index 03efbe1484..b0e646cd0a 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/business/VideoProxy.as @@ -1,199 +1,238 @@ -/** -* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ -* -* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). -* -* This program is free software; you can redistribute it and/or modify it under the -* terms of the GNU Lesser General Public License as published by the Free Software -* Foundation; either version 3.0 of the License, or (at your option) any later -* version. -* -* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY -* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License along -* with BigBlueButton; if not, see . -* -*/ -package org.bigbluebutton.modules.videoconf.business -{ - import com.asfusion.mate.events.Dispatcher; - - import flash.events.AsyncErrorEvent; - import flash.events.IOErrorEvent; - import flash.events.NetStatusEvent; - import flash.events.SecurityErrorEvent; - import flash.media.H264Level; - import flash.media.H264Profile; - import flash.media.H264VideoStreamSettings; - import flash.net.NetConnection; - import flash.net.NetStream; - import flash.system.Capabilities; - - import mx.collections.ArrayCollection; - - import org.bigbluebutton.common.LogUtil; - import org.bigbluebutton.core.BBB; - import org.bigbluebutton.core.UsersUtil; - import org.bigbluebutton.core.managers.UserManager; - import org.bigbluebutton.main.model.users.BBBUser; - import org.bigbluebutton.main.model.users.events.StreamStartedEvent; - import org.bigbluebutton.modules.videoconf.events.ConnectedEvent; - import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent; - import org.bigbluebutton.modules.videoconf.model.VideoConfOptions; - - - public class VideoProxy - { - public var videoOptions:VideoConfOptions; - - private var nc:NetConnection; - private var ns:NetStream; - private var _url:String; - - private function parseOptions():void { - videoOptions = new VideoConfOptions(); - videoOptions.parseOptions(); - } - - public function VideoProxy(url:String) - { - _url = url; - parseOptions(); - nc = new NetConnection(); +/** +* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +* +* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). +* +* This program is free software; you can redistribute it and/or modify it under the +* terms of the GNU Lesser General Public License as published by the Free Software +* Foundation; either version 3.0 of the License, or (at your option) any later +* version. +* +* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License along +* with BigBlueButton; if not, see . +* +*/ +package org.bigbluebutton.modules.videoconf.business +{ + import com.asfusion.mate.events.Dispatcher; + + import flash.events.AsyncErrorEvent; + import flash.events.IOErrorEvent; + import flash.events.NetStatusEvent; + import flash.events.SecurityErrorEvent; + import flash.events.TimerEvent; + import flash.media.H264Level; + import flash.media.H264Profile; + import flash.media.H264VideoStreamSettings; + import flash.net.NetConnection; + import flash.net.NetStream; + import flash.system.Capabilities; + import flash.utils.Timer; + + import mx.collections.ArrayCollection; + + import org.bigbluebutton.common.LogUtil; + import org.bigbluebutton.core.BBB; + import org.bigbluebutton.core.UsersUtil; + import org.bigbluebutton.core.managers.UserManager; + import org.bigbluebutton.main.events.ClientStatusEvent; + import org.bigbluebutton.main.model.users.AutoReconnect; + import org.bigbluebutton.main.model.users.BBBUser; + + import org.bigbluebutton.main.model.users.events.StreamStartedEvent; + import org.bigbluebutton.modules.videoconf.events.ConnectedEvent; + import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent; + import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent; + import org.bigbluebutton.modules.videoconf.model.VideoConfOptions; + + + public class VideoProxy + { + public static const LOG:String = "VideoProxy - "; + + public var videoOptions:VideoConfOptions; + + private var nc:NetConnection; + private var ns:NetStream; + private var _url:String; + private var logoutOnUserCommand:Boolean = false; + private var reconnect:AutoReconnect = new AutoReconnect(); + private var reconnecting:Boolean = false; + private var dispatcher:Dispatcher = new Dispatcher(); + + private function parseOptions():void { + videoOptions = new VideoConfOptions(); + videoOptions.parseOptions(); + } + + public function VideoProxy(url:String) + { + _url = url; + parseOptions(); + nc = new NetConnection(); nc.proxyType = "best"; - nc.client = this; - nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError); - nc.addEventListener(IOErrorEvent.IO_ERROR, onIOError); - nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus); - nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError); - - } - - public function connect():void { - nc.connect(_url, UsersUtil.getInternalMeetingID(), UsersUtil.getMyUserID()); - } - - private function onAsyncError(event:AsyncErrorEvent):void{ - } - - private function onIOError(event:NetStatusEvent):void{ - } - - private function onConnectedToVideoApp():void{ - var dispatcher:Dispatcher = new Dispatcher(); - dispatcher.dispatchEvent(new ConnectedEvent(ConnectedEvent.VIDEO_CONNECTED)); - } - - private function onNetStatus(event:NetStatusEvent):void{ - switch(event.info.code){ - case "NetConnection.Connect.Success": - ns = new NetStream(nc); - onConnectedToVideoApp(); - break; - default: - LogUtil.debug("[" + event.info.code + "] for [" + _url + "]"); - break; - } - } - - private function onSecurityError(event:NetStatusEvent):void{ - } - - public function get connection():NetConnection{ - return this.nc; - } - - public function startPublishing(e:StartBroadcastEvent):void{ - ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus ); - ns.addEventListener( IOErrorEvent.IO_ERROR, onIOError ); - ns.addEventListener( AsyncErrorEvent.ASYNC_ERROR, onAsyncError ); - ns.client = this; - ns.attachCamera(e.camera); -// Uncomment if you want to build support for H264. But you need at least FP 11. (ralam july 23, 2011) -// if (Capabilities.version.search("11,0") != -1) { - if ((BBB.getFlashPlayerVersion() >= 11) && videoOptions.enableH264) { -// if (BBB.getFlashPlayerVersion() >= 11) { - LogUtil.info("Using H264 codec for video."); - var h264:H264VideoStreamSettings = new H264VideoStreamSettings(); - var h264profile:String = H264Profile.MAIN; - if (videoOptions.h264Profile != "main") { - h264profile = H264Profile.BASELINE; - } - var h264Level:String = H264Level.LEVEL_4_1; - if (videoOptions.h264Level == "1") { - h264Level = H264Level.LEVEL_1; - } else if (videoOptions.h264Level == "1.1") { - h264Level = H264Level.LEVEL_1_1; - } else if (videoOptions.h264Level == "1.2") { - h264Level = H264Level.LEVEL_1_2; - } else if (videoOptions.h264Level == "1.3") { - h264Level = H264Level.LEVEL_1_3; - } else if (videoOptions.h264Level == "1b") { - h264Level = H264Level.LEVEL_1B; - } else if (videoOptions.h264Level == "2") { - h264Level = H264Level.LEVEL_2; - } else if (videoOptions.h264Level == "2.1") { - h264Level = H264Level.LEVEL_2_1; - } else if (videoOptions.h264Level == "2.2") { - h264Level = H264Level.LEVEL_2_2; - } else if (videoOptions.h264Level == "3") { - h264Level = H264Level.LEVEL_3; - } else if (videoOptions.h264Level == "3.1") { - h264Level = H264Level.LEVEL_3_1; - } else if (videoOptions.h264Level == "3.2") { - h264Level = H264Level.LEVEL_3_2; - } else if (videoOptions.h264Level == "4") { - h264Level = H264Level.LEVEL_4; - } else if (videoOptions.h264Level == "4.1") { - h264Level = H264Level.LEVEL_4_1; - } else if (videoOptions.h264Level == "4.2") { - h264Level = H264Level.LEVEL_4_2; - } else if (videoOptions.h264Level == "5") { - h264Level = H264Level.LEVEL_5; - } else if (videoOptions.h264Level == "5.1") { - h264Level = H264Level.LEVEL_5_1; - } - - LogUtil.info("Codec used: " + h264Level); - - h264.setProfileLevel(h264profile, h264Level); - ns.videoStreamSettings = h264; - } - - ns.publish(e.stream); - } - - public function stopBroadcasting():void{ - trace("Closing netstream for webcam publishing"); - - if (ns != null) { - ns.attachCamera(null); - ns.close(); - ns = null; - ns = new NetStream(nc); - } - } - - public function disconnect():void { - trace("VideoProxy:: disconnecting from Video application"); - stopBroadcasting(); - if (nc != null) nc.close(); - } - - 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 - trace("bandwidth = " + p_bw + " Kbps."); - } - - - } -} + nc.client = this; + nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError); + nc.addEventListener(IOErrorEvent.IO_ERROR, onIOError); + nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus); + nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError); + + } + + public function connect():void { + nc.connect(_url, UsersUtil.getInternalMeetingID(), UsersUtil.getMyUserID()); + } + + private function onAsyncError(event:AsyncErrorEvent):void{ + } + + private function onIOError(event:NetStatusEvent):void{ + } + + private function onConnectedToVideoApp():void{ + dispatcher.dispatchEvent(new ConnectedEvent(ConnectedEvent.VIDEO_CONNECTED)); + } + + private function onNetStatus(event:NetStatusEvent):void{ + trace(LOG + "[" + event.info.code + "] for [" + _url + "]"); + switch(event.info.code){ + case "NetConnection.Connect.Success": + if (reconnecting) { + dispatcher.dispatchEvent(new ClientStatusEvent(ClientStatusEvent.SUCCESS_MESSAGE_EVENT, + "Connection reestablished", + "Video connection has been reestablished successfully")); + reconnecting = false; + } + ns = new NetStream(nc); + onConnectedToVideoApp(); + break; + + case "NetConnection.Connect.Closed": + if (!logoutOnUserCommand) { + if (ns != null) { + ns.attachCamera(null); + ns.close(); + ns = null; + } + dispatcher.dispatchEvent(new StopBroadcastEvent()); + + dispatcher.dispatchEvent(new ClientStatusEvent(ClientStatusEvent.WARNING_MESSAGE_EVENT, + "Video connection dropped", + "Attempting to reconnect")); + reconnecting = true; + reconnect.onDisconnect(connect); + } + break; + + case "NetConnection.Connect.Failed": + if (reconnecting) { + reconnect.onConnectionAttemptFailed(); + } + break; + } + } + + private function onSecurityError(event:NetStatusEvent):void{ + } + + public function get connection():NetConnection{ + return this.nc; + } + + public function startPublishing(e:StartBroadcastEvent):void{ + ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus ); + ns.addEventListener( IOErrorEvent.IO_ERROR, onIOError ); + ns.addEventListener( AsyncErrorEvent.ASYNC_ERROR, onAsyncError ); + ns.client = this; + ns.attachCamera(e.camera); +// Uncomment if you want to build support for H264. But you need at least FP 11. (ralam july 23, 2011) +// if (Capabilities.version.search("11,0") != -1) { + if ((BBB.getFlashPlayerVersion() >= 11) && videoOptions.enableH264) { +// if (BBB.getFlashPlayerVersion() >= 11) { + LogUtil.info("Using H264 codec for video."); + var h264:H264VideoStreamSettings = new H264VideoStreamSettings(); + var h264profile:String = H264Profile.MAIN; + if (videoOptions.h264Profile != "main") { + h264profile = H264Profile.BASELINE; + } + var h264Level:String = H264Level.LEVEL_4_1; + if (videoOptions.h264Level == "1") { + h264Level = H264Level.LEVEL_1; + } else if (videoOptions.h264Level == "1.1") { + h264Level = H264Level.LEVEL_1_1; + } else if (videoOptions.h264Level == "1.2") { + h264Level = H264Level.LEVEL_1_2; + } else if (videoOptions.h264Level == "1.3") { + h264Level = H264Level.LEVEL_1_3; + } else if (videoOptions.h264Level == "1b") { + h264Level = H264Level.LEVEL_1B; + } else if (videoOptions.h264Level == "2") { + h264Level = H264Level.LEVEL_2; + } else if (videoOptions.h264Level == "2.1") { + h264Level = H264Level.LEVEL_2_1; + } else if (videoOptions.h264Level == "2.2") { + h264Level = H264Level.LEVEL_2_2; + } else if (videoOptions.h264Level == "3") { + h264Level = H264Level.LEVEL_3; + } else if (videoOptions.h264Level == "3.1") { + h264Level = H264Level.LEVEL_3_1; + } else if (videoOptions.h264Level == "3.2") { + h264Level = H264Level.LEVEL_3_2; + } else if (videoOptions.h264Level == "4") { + h264Level = H264Level.LEVEL_4; + } else if (videoOptions.h264Level == "4.1") { + h264Level = H264Level.LEVEL_4_1; + } else if (videoOptions.h264Level == "4.2") { + h264Level = H264Level.LEVEL_4_2; + } else if (videoOptions.h264Level == "5") { + h264Level = H264Level.LEVEL_5; + } else if (videoOptions.h264Level == "5.1") { + h264Level = H264Level.LEVEL_5_1; + } + + LogUtil.info("Codec used: " + h264Level); + + h264.setProfileLevel(h264profile, h264Level); + ns.videoStreamSettings = h264; + } + + ns.publish(e.stream); + } + + public function stopBroadcasting():void{ + trace("Closing netstream for webcam publishing"); + + if (ns != null) { + ns.attachCamera(null); + ns.close(); + ns = null; + ns = new NetStream(nc); + } + } + + public function disconnect():void { + logoutOnUserCommand = true; + trace("VideoProxy:: disconnecting from Video application"); + stopBroadcasting(); + if (nc != null) nc.close(); + } + + 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 + trace("bandwidth = " + p_bw + " Kbps."); + } + + + } +} diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as index 49e5a071aa..f7718b1796 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as @@ -297,6 +297,10 @@ package org.bigbluebutton.modules.videoconf.maps } private function openViewWindowFor(userID:String):void { + if (!proxy.connection.connected) { + return; + } + trace("VideoEventMapDelegate:: [" + me + "] openViewWindowFor:: Opening VIEW window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]"); var window:VideoWindow = new VideoWindow(); @@ -334,7 +338,7 @@ package org.bigbluebutton.modules.videoconf.maps } public function startPublishing(e:StartBroadcastEvent):void{ - LogUtil.debug("VideoEventMapDelegate:: [" + me + "] startPublishing:: Publishing stream to: " + proxy.connection.uri + "/" + e.stream); + trace("VideoEventMapDelegate:: [" + me + "] startPublishing:: Publishing stream to: " + proxy.connection.uri + "/" + e.stream); streamName = e.stream; proxy.startPublishing(e); diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/VideoWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/VideoWindow.mxml index d0a2d5b9e7..efde9bd7f0 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/VideoWindow.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/VideoWindow.mxml @@ -1,278 +1,289 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - + [Bindable] + public var videoOptions:VideoConfOptions = new VideoConfOptions(); + + private var windowType:String = "VideoWindowType"; + + [Bindable] public var glowColor:String = ""; + [Bindable] public var glowBlurSize:Number = 0; + + override public function getWindowType():String { + return windowType; + } + + private function onCreationComplete():void{ + this.glowColor = videoOptions.glowColor; + this.glowBlurSize = videoOptions.glowBlurSize; + + LogUtil.debug("checking glow values: "+this.glowColor+" and "+this.glowBlurSize); + + _videoHolder = new UIComponent(); + _videoHolder.addChild(_video); + this.addChild(_videoHolder); + + addEventListener(MDIWindowEvent.RESIZE_START, onResizeStart); + addEventListener(MDIWindowEvent.RESIZE_END, onResizeEnd); + addEventListener(MDIWindowEvent.CLOSE, onCloseEvent); + + addEventListener(MouseEvent.MOUSE_OVER, showButtons); + addEventListener(MouseEvent.MOUSE_OUT, hideButtons); + addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick); + + createButtons(); + addControlButtons(); + + globalDispatcher = new Dispatcher(); + + this.minWidth = _minWidth; + this.minHeight = _minHeight; + maximizeRestoreBtn.visible = false; + this.resizable = true; + + /** + * At this point, the function startVideo has been called, and + * the video has the exactly dimensions of the original stream. + * It's needed to call onResize() to fit the video window in the + * main canvas in case that the video dimensions are larger than + * the parent window. + */ + onResize(); + + if (videoOptions.viewerWindowMaxed) + this.maximize(); + + this.showCloseButton = videoOptions.showCloseButton; + } + + private function handleMadePresenterEvent(event:MadePresenterEvent):void { + trace("******** VideoWindow: HandleMadePresenter event *********"); + updateControlButtons(); + } + + private function handleSwitchedPresenterEvent(event:SwitchedPresenterEvent):void { + trace("******** VideoWindow: handleSwitchedPresenterEvent event *********"); + updateControlButtons(); + } + + private function handleNewRoleEvent(event:Event):void { + updateControlButtons(); + } + + private function handleUserVoiceMutedEvent(event:BBBEvent):void { + if (event.payload.userID == userID) { + userMuted(event.payload.muted); + } + } + + private var _closing:Boolean = false; + + private function onCloseEvent(event:MDIWindowEvent = null):void { + LogUtil.debug("ViewWindow closing " + streamName); + if (!_closing) { + _closing = true; + var stopEvent:StoppedViewingWebcamEvent = new StoppedViewingWebcamEvent(); + stopEvent.webcamUserID = userID; + globalDispatcher.dispatchEvent(stopEvent); + + if (UserManager.getInstance().getConference().hasUser(userID)) { + UserManager.getInstance().getConference().getUser(userID).viewingStream = false; + } + } + + } + + private function handleUserTalkingEvent(event:CoreEvent):void { + if (event.message.userID == userID) { + if (event.message.talking) { + notTalkingEffect.end(); + talkingEffect.play([this]); + simulateClick(); + } else { + talkingEffect.end(); + notTalkingEffect.play([this]); + } + } + } + + private function onConnectionNetStatus(e:NetStatusEvent):void { + trace(LOG + "onNetStatus code=" + e.info.code); + + if (e.info.code == "NetConnection.Connect.Closed") { + close(); + } + } + + public function startVideo(connection:NetConnection, stream:String):void{ + connection.addEventListener(NetStatusEvent.NET_STATUS, onConnectionNetStatus); + + ns = new NetStream(connection); + ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus ); + ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError); + ns.client = this; + ns.bufferTime = 0; + ns.receiveVideo(true); + ns.receiveAudio(false); + + var res:Array = getVideoResolution(stream); + if (res == null) // error + return; + _video = new Video(Number(res[0]), Number(res[1])); + _video.width = Number(res[0]); + _video.height = Number(res[1]); + _video.smoothing = true; + setAspectRatio(Number(res[0]), Number(res[1])); + _video.attachNetStream(ns); + + + if (videoOptions.smoothVideo) { + trace("Smoothing video.") + _video.smoothing = true; + } + + if (videoOptions.applyConvolutionFilter) { + var filter:ConvolutionFilter = new flash.filters.ConvolutionFilter(); + filter.matrixX = 3; + filter.matrixY = 3; + trace("Applying convolution filter =[" + videoOptions.convolutionFilter + "]"); + filter.matrix = videoOptions.convolutionFilter; + filter.bias = videoOptions.filterBias; + filter.divisor = videoOptions.filterDivisor; + _video.filters = [filter]; + } + + ns.play(stream); + this.streamName = stream; + + this.width = _video.width + paddingHorizontal; + this.height = _video.height + paddingVertical; + + if (UserManager.getInstance().getConference().hasUser(userID)) { + UserManager.getInstance().getConference().getUser(userID).viewingStream = true; + } + } + + private function onAsyncError(e:AsyncErrorEvent):void{ + LogUtil.debug("VideoWindow::asyncerror " + e.toString()); + } + + public function onMetaData(info:Object):void{ + trace("metadata: width=" + info.width + " height=" + info.height); + _video.width = info.width; + _video.height = info.height; + setAspectRatio(info.width, info.height); + onResize(); + } + + private function onNetStatus(e:NetStatusEvent):void{ + switch(e.info.code){ + case "NetStream.Publish.Start": + LogUtil.debug("NetStream.Publish.Start for broadcast stream " + streamName); + break; + case "NetStream.Play.UnpublishNotify": + ns.close(); + this.close(); + // shouldn't call onCloseEvent() here because of the viewer cam icon + super.close(); + break; + case "NetStream.Play.Start": + LogUtil.debug("Netstatus: " + e.info.code); + globalDispatcher.dispatchEvent(new BBBEvent(BBBEvent.VIDEO_STARTED)); + break; + case "NetStream.Play.FileStructureInvalid": + LogUtil.debug("The MP4's file structure is invalid."); + break; + case "NetStream.Play.NoSupportedTrackFound": + LogUtil.debug("The MP4 doesn't contain any supported tracks"); + break; + } + } + + override public function close(event:MouseEvent=null):void{ + ns.close(); + onCloseEvent(); + super.close(event); + } + + private function closeWindow(e:CloseAllWindowsEvent):void{ + this.close(); + } + + ]]> + + + + + + +