first attempt to use an auto-reconnection handler, still not working properly

This commit is contained in:
Felipe Cecagno 2015-03-19 11:54:08 -03:00
parent 423ffdeffa
commit 867e9b9b36
7 changed files with 680 additions and 495 deletions

View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
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;
}
}
}

View File

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

View File

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

View File

@ -41,6 +41,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
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 <http://www.gnu.org/licenses/>.
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 <http://www.gnu.org/licenses/>.
}
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);

View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
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 <http://www.gnu.org/licenses/>.
*
*/
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.");
}
}
}

View File

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

View File

@ -1,278 +1,289 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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 <http://www.gnu.org/licenses/>.
-->
<viewVid:VideoWindowItf
xmlns:viewVid="org.bigbluebutton.modules.videoconf.business.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="onCreationComplete()"
implements="org.bigbluebutton.common.IBbbModuleWindow"
xmlns:mate="http://mate.asfusion.com/"
styleNameFocus="videoViewStyleFocus"
styleNameNoFocus="videoViewStyleNoFocus"
horizontalScrollPolicy="off"
verticalScrollPolicy="off"
resize="onResize()"
layout="absolute">
<mate:Listener type="{BBBEvent.USER_VOICE_MUTED}" method="handleUserVoiceMutedEvent" />
<mate:Listener type="{EventConstants.USER_TALKING}" method="handleUserTalkingEvent" />
<mate:Listener type="{SwitchedPresenterEvent.SWITCHED_PRESENTER}" method="handleSwitchedPresenterEvent" />
<mate:Listener type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}" method="handleMadePresenterEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_JOINED}" method="handleNewRoleEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_LEFT}" method="handleNewRoleEvent" />
<mate:Listener type="{CloseAllWindowsEvent.CLOSE_ALL_WINDOWS}" method="closeWindow" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flexlib.mdi.events.MDIWindowEvent;
import mx.core.UIComponent;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.Role;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.core.EventConstants;
import org.bigbluebutton.core.events.CoreEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
import org.bigbluebutton.main.events.SwitchedPresenterEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.videoconf.business.TalkingButtonOverlay;
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
private var ns:NetStream;
private var globalDispatcher:Dispatcher;
<?xml version="1.0" encoding="utf-8"?>
<!--
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 <http://www.gnu.org/licenses/>.
-->
<viewVid:VideoWindowItf
xmlns:viewVid="org.bigbluebutton.modules.videoconf.business.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="onCreationComplete()"
implements="org.bigbluebutton.common.IBbbModuleWindow"
xmlns:mate="http://mate.asfusion.com/"
styleNameFocus="videoViewStyleFocus"
styleNameNoFocus="videoViewStyleNoFocus"
horizontalScrollPolicy="off"
verticalScrollPolicy="off"
resize="onResize()"
layout="absolute">
<mate:Listener type="{BBBEvent.USER_VOICE_MUTED}" method="handleUserVoiceMutedEvent" />
<mate:Listener type="{EventConstants.USER_TALKING}" method="handleUserTalkingEvent" />
<mate:Listener type="{SwitchedPresenterEvent.SWITCHED_PRESENTER}" method="handleSwitchedPresenterEvent" />
<mate:Listener type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}" method="handleMadePresenterEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_JOINED}" method="handleNewRoleEvent" />
<mate:Listener type="{BBBEvent.USER_VOICE_LEFT}" method="handleNewRoleEvent" />
<mate:Listener type="{CloseAllWindowsEvent.CLOSE_ALL_WINDOWS}" method="closeWindow" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import flexlib.mdi.events.MDIWindowEvent;
import mx.core.UIComponent;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.common.Role;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.core.EventConstants;
import org.bigbluebutton.core.events.CoreEvent;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.main.events.StoppedViewingWebcamEvent;
import org.bigbluebutton.main.events.SwitchedPresenterEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.videoconf.business.TalkingButtonOverlay;
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
[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;
}
public static const LOG:String = "VideoWindow - ";
private var ns:NetStream;
private var globalDispatcher:Dispatcher;
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]);
}
}
}
public function startVideo(connection:NetConnection, stream:String):void{
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();
}
]]>
</mx:Script>
<mx:Glow id="talkingEffect" duration="500" alphaFrom="1.0" alphaTo="0.3"
blurXFrom="0.0" blurXTo="{glowBlurSize}" blurYFrom="0.0" blurYTo="{glowBlurSize}" color="{glowColor}"/>
<mx:Glow id="notTalkingEffect" duration="500" alphaFrom="0.3" alphaTo="1.0"
blurXFrom="{glowBlurSize}" blurXTo="0.0" blurYFrom="{glowBlurSize}" blurYTo="0.0" color="{glowColor}"/>
</viewVid:VideoWindowItf>
[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();
}
]]>
</mx:Script>
<mx:Glow id="talkingEffect" duration="500" alphaFrom="1.0" alphaTo="0.3"
blurXFrom="0.0" blurXTo="{glowBlurSize}" blurYFrom="0.0" blurYTo="{glowBlurSize}" color="{glowColor}"/>
<mx:Glow id="notTalkingEffect" duration="500" alphaFrom="0.3" alphaTo="1.0"
blurXFrom="{glowBlurSize}" blurXTo="0.0" blurYFrom="{glowBlurSize}" blurYTo="0.0" color="{glowColor}"/>
</viewVid:VideoWindowItf>