New implementaion of multiple connections

This implemetantion create a NetConnection when a webcam view is requested,
when the connection is ready an event triggers the view window creation.
Also, this implementation keep track of how many streams are using a
connection, and if no one is using it anymore the connection is closed.
This commit is contained in:
Mateus Dalepiane 2014-05-09 10:35:54 -03:00
parent 2870cacf8d
commit 660730fb01
4 changed files with 165 additions and 40 deletions

View File

@ -41,6 +41,7 @@ package org.bigbluebutton.modules.videoconf.business
import org.bigbluebutton.modules.videoconf.events.ConnectedEvent;
import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent;
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
import org.bigbluebutton.modules.videoconf.events.PlayConnectionReady;
public class VideoProxy
@ -55,8 +56,12 @@ package org.bigbluebutton.modules.videoconf.business
// Dictionary<url,NetConnection> used for stream playing
private var playConnectionDict:Dictionary;
// Dictionary<url,int> used to keep track of how many streams use a URL
private var playConnectionCountDict:Dictionary;
// Dictionary<userID,streamNamePrefix> used for stream playing
private var streamNamePrefixDict:Dictionary;
// Dictionary<userID,url>
private var userUrlDict:Dictionary;
private function parseOptions():void {
videoOptions = new VideoConfOptions();
@ -74,12 +79,15 @@ package org.bigbluebutton.modules.videoconf.business
nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
playConnectionDict = new Dictionary();
playConnectionCountDict = new Dictionary();
streamNamePrefixDict = new Dictionary();
userUrlDict = new Dictionary();
}
public function connect():void {
nc.connect(_url);
playConnectionDict[_url] = nc;
playConnectionCountDict[_url] = 0;
}
private function onAsyncError(event:AsyncErrorEvent):void{
@ -96,6 +104,7 @@ package org.bigbluebutton.modules.videoconf.business
private function onNetStatus(event:NetStatusEvent):void{
switch(event.info.code){
case "NetConnection.Connect.Success":
ns = new NetStream(nc);
onConnectedToVideoApp();
break;
default:
@ -111,19 +120,36 @@ package org.bigbluebutton.modules.videoconf.business
return this.nc;
}
public function getPlayConnectionFor(userID:String):NetConnection{
LogUtil.debug("VideoProxy::getPlayConnectionFor:: Looking for connection for stream from [" + userID + "]");
private function onPlayNetStatus(event:NetStatusEvent):void {
switch(event.info.code){
case "NetConnection.Connect.Success":
var dispatcher:Dispatcher = new Dispatcher();
dispatcher.dispatchEvent(new PlayConnectionReady(PlayConnectionReady.PLAY_CONNECTION_READY));
break;
default:
LogUtil.debug("[" + event.info.code + "] for a play connection");
break;
}
}
public function createPlayConnectionFor(userID:String):void {
LogUtil.debug("VideoProxy::createPlayConnectionFor:: Creating connection for stream from [" + userID + "]");
// TODO: Ask LB for path to current user
var connectionPath:String = "10.0.3.203/10.0.3.254/10.0.3.79";
var serverIp:String = connectionPath.split("/")[0];
var ipRegex:RegExp = /([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/;
var newUrl:String = _url.replace(ipRegex, serverIp);
// Store URL for this user
userUrlDict[userID] = newUrl;
var streamPrefix:String;
if(connectionPath != serverIp) // More than one server -> has prefix
streamPrefix = connectionPath.replace(serverIp + "/", "") + "/";
else
streamPrefix = "";
// Set current user streamPrefix to use the current path
streamNamePrefixDict[userID] = streamPrefix;
// If connection with this URL does not exist
if(!playConnectionDict[newUrl]){
@ -131,20 +157,38 @@ package org.bigbluebutton.modules.videoconf.business
var connection:NetConnection = new NetConnection();
connection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
connection.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
connection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
connection.addEventListener(NetStatusEvent.NET_STATUS, onPlayNetStatus);
connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
connection.connect(newUrl);
// TODO change to trace
LogUtil.debug("VideoProxy::getPlayConnectionFor:: Creating NetConnection for [" + newUrl + "]");
LogUtil.debug("VideoProxy::createPlayConnectionFor:: Creating NetConnection for [" + newUrl + "]");
playConnectionDict[newUrl] = connection;
playConnectionCountDict[newUrl] = 0;
}
else {
if(playConnectionDict[newUrl].connected) {
// Connection is ready, send event
var dispatcher:Dispatcher = new Dispatcher();
dispatcher.dispatchEvent(new PlayConnectionReady(PlayConnectionReady.PLAY_CONNECTION_READY));
}
// TODO change to trace
LogUtil.debug("VideoProxy::getPlayConnectionFor:: Found NetConnection for [" + newUrl + "]");
LogUtil.debug("VideoProxy::createPlayConnectionFor:: Found NetConnection for [" + newUrl + "]");
}
// Set current user streamPrefix to use the current path
streamNamePrefixDict[userID] = streamPrefix;
return playConnectionDict[newUrl];
}
public function playConnectionIsReadyFor(userID:String):Boolean {
var userUrl:String = userUrlDict[userID];
if(playConnectionDict[userUrl].connected)
return true;
return false;
}
public function getPlayConnectionFor(userID:String):NetConnection {
var userUrl:String = userUrlDict[userID];
playConnectionCountDict[userUrl] = playConnectionCountDict[userUrl] + 1;
// TODO: change to trace
LogUtil.debug("VideoProxy:: getPlayConnection:: URL: [" + userUrl + "], count: [" + playConnectionCountDict[userUrl] + "]");
return playConnectionDict[userUrl];
}
public function getStreamNamePrefixFor(userID:String):String{
@ -159,8 +203,27 @@ package org.bigbluebutton.modules.videoconf.business
}
}
public function closePlayConnectionFor(userID:String):void {
var userUrl:String = userUrlDict[userID];
// Do not close publish connection, no matter what
if(playConnectionDict[userUrl] == nc)
return;
if(userUrl != null) {
var count:int = playConnectionCountDict[userUrl] - 1;
// TODO: change to trace
LogUtil.debug("VideoProxy:: closePlayConnectionFor:: userID: [" + userID + "], URL: [" + userUrl + "], new streamCount: [" + count + "]");
playConnectionCountDict[userUrl] = count;
if(count <= 0) {
// No one else is using this NetConnection
var connection:NetConnection = playConnectionDict[userUrl];
if(connection != null) connection.close();
delete playConnectionDict[userUrl];
delete playConnectionCountDict[userUrl];
}
}
}
public function startPublishing(e:StartBroadcastEvent):void{
ns = new NetStream(nc);
ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
ns.addEventListener( IOErrorEvent.IO_ERROR, onIOError );
ns.addEventListener( AsyncErrorEvent.ASYNC_ERROR, onAsyncError );
@ -227,14 +290,25 @@ package org.bigbluebutton.modules.videoconf.business
ns.attachCamera(null);
ns.close();
ns = null;
}
ns = new NetStream(nc);
}
}
public function disconnect():void {
LogUtil.debug("VideoProxy:: disconnecting from Video application");
stopBroadcasting();
// Close publish NetConnection
if (nc != null) nc.close();
//TODO: Close play NetConnections
// Close play NetConnections
for (var k:Object in playConnectionDict) {
var connection:NetConnection = playConnectionDict[k];
connection.close();
}
// Reset dictionaries
playConnectionDict = new Dictionary();
playConnectionCountDict = new Dictionary();
streamNamePrefixDict = new Dictionary();
userUrlDict = new Dictionary();
}
public function onBWCheck(... rest):Number {

View File

@ -0,0 +1,32 @@
/**
* 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.events
{
import flash.events.Event;
public class PlayConnectionReady extends Event
{
public static const PLAY_CONNECTION_READY:String = "a netconnetion is ready";
public function PlayConnectionReady(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}

View File

@ -38,6 +38,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
import org.bigbluebutton.modules.videoconf.events.VideoModuleStartEvent;
import org.bigbluebutton.modules.videoconf.events.VideoModuleStopEvent;
import org.bigbluebutton.modules.users.events.ViewCameraEvent;
import org.bigbluebutton.modules.videoconf.events.PlayConnectionReady;
]]>
</mx:Script>
@ -115,6 +116,9 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<MethodInvoker generator="{VideoEventMapDelegate}" method="handleCamSettingsClosedEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{PlayConnectionReady.PLAY_CONNECTION_READY}">
<MethodInvoker generator="{VideoEventMapDelegate}" method="handlePlayConnectionReady" />
</EventHandlers>
<!-- ~~~~~~~~~~~~~~~~~~ INJECTORS ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
</EventMap>

View File

@ -20,7 +20,6 @@ package org.bigbluebutton.modules.videoconf.maps
{
import flash.events.IEventDispatcher;
import flash.media.Camera;
import flash.net.NetConnection;
import mx.collections.ArrayCollection;
@ -56,6 +55,7 @@ package org.bigbluebutton.modules.videoconf.maps
import org.bigbluebutton.modules.videoconf.views.ToolbarButton;
import org.bigbluebutton.modules.videoconf.views.VideoWindow;
import org.flexunit.runner.manipulation.filters.IncludeAllFilter;
import org.bigbluebutton.modules.videoconf.events.PlayConnectionReady;
public class VideoEventMapDelegate
{
@ -73,6 +73,9 @@ package org.bigbluebutton.modules.videoconf.maps
private var _isPublishing:Boolean = false;
private var _isPreviewWebcamOpen:Boolean = false;
private var _isWaitingActivation:Boolean = false;
// Store userID of windows waiting for a NetConnection
private var pendingVideoWindowsList:Object = new Object();
public function VideoEventMapDelegate(dispatcher:IEventDispatcher)
{
@ -94,7 +97,7 @@ package org.bigbluebutton.modules.videoconf.maps
if (!_ready) return;
LogUtil.debug("VideoEventMapDelegate:: [" + me + "] Viewing [" + userID + " stream [" + stream + "]");
if (! UserManager.getInstance().getConference().amIThisUser(userID)) {
openViewWindowFor(userID);
initPlayConnectionFor(userID);
}
}
@ -214,7 +217,7 @@ package org.bigbluebutton.modules.videoconf.maps
closeWindow(userID);
}
LogUtil.debug("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: View user's = [" + userID + "] webcam.");
openViewWindowFor(userID);
initPlayConnectionFor(userID);
} else {
if (UsersUtil.isMe(userID) && options.autoStart) {
LogUtil.debug("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: It's ME and AutoStart. Start publishing.");
@ -280,6 +283,7 @@ package org.bigbluebutton.modules.videoconf.maps
if (win != null) {
LogUtil.debug("VideoEventMapDelegate:: [" + me + "] closeWindow:: Closing [" + win.getWindowType() + "] for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
win.close();
proxy.closePlayConnectionFor(userID);
var cwe:CloseWindowEvent = new CloseWindowEvent();
cwe.window = win;
_dispatcher.dispatchEvent(cwe);
@ -288,34 +292,45 @@ package org.bigbluebutton.modules.videoconf.maps
}
}
private function openViewWindowFor(userID:String):void {
LogUtil.debug("VideoEventMapDelegate:: [" + me + "] openViewWindowFor:: Opening VIEW window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
// Check if NetConnection is ready
var playConnection:NetConnection = proxy.getPlayConnectionFor(userID);
if (playConnection.connected) {
var window:VideoWindow = new VideoWindow();
window.userID = userID;
window.videoOptions = options;
window.resolutions = options.resolutions.split(",");
window.title = UsersUtil.getUserName(userID);
closeWindow(userID);
var bbbUser:BBBUser = UsersUtil.getUser(userID);
var playStream:String = proxy.getStreamNamePrefixFor(userID) + bbbUser.streamName;
LogUtil.debug("VideoEventMapDelegate:: [" + me + "] openViewWindowFor:: StreamName for [" + userID + "] : [" + playStream + "]");
window.startVideo(playConnection, playStream);
webcamWindows.addWindow(window);
openWindow(window);
dockWindow(window);
}
else {
LogUtil.debug("VideoEventMapDelegate:: [" + me + "] openViewWindowFor:: NetConnection for [" + userID + "] isn't ready yet.");
}
private function initPlayConnectionFor(userID:String):void {
//TODO: Change to trace
LogUtil.debug("VideoEventMapDelegate:: initPlayConnectionFor : [" + userID + "]");
// Store the userID
pendingVideoWindowsList[userID] = true;
// Request the connection
proxy.createPlayConnectionFor(userID);
}
public function handlePlayConnectionReady():void {
// Iterate through all pending windows
for(var userID:String in pendingVideoWindowsList) {
if(proxy.playConnectionIsReadyFor(userID)) {
delete pendingVideoWindowsList[userID];
openViewWindowFor(userID);
}
}
}
private function openViewWindowFor(userID:String):void {
LogUtil.debug("VideoEventMapDelegate:: [" + me + "] openViewWindowFor:: Opening VIEW window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
var window:VideoWindow = new VideoWindow();
window.userID = userID;
window.videoOptions = options;
window.resolutions = options.resolutions.split(",");
window.title = UsersUtil.getUserName(userID);
closeWindow(userID);
var bbbUser:BBBUser = UsersUtil.getUser(userID);
var streamName:String = proxy.getStreamNamePrefixFor(userID) + bbbUser.streamName;
window.startVideo(proxy.getPlayConnectionFor(userID), streamName);
webcamWindows.addWindow(window);
openWindow(window);
dockWindow(window);
}
private function openWindow(window:VideoWindowItf):void {
var windowEvent:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
windowEvent.window = window;