Merge branch 'multivideo' into multivideo-1404
Conflicts: bbb-video/build.gradle bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as bigbluebutton-web/grails-app/conf/bigbluebutton.properties
This commit is contained in:
commit
52502488ec
@ -76,6 +76,7 @@ dependencies {
|
||||
|
||||
// Red5
|
||||
providedCompile 'org/red5:red5:1.0.2@jar'
|
||||
compile 'org.red5:red5-client:1.0.2-RC2@jar'
|
||||
providedCompile 'org.red5:red5-io:1.0.3@jar'
|
||||
|
||||
// Logging
|
||||
@ -101,6 +102,9 @@ dependencies {
|
||||
//redis
|
||||
compile 'redis.clients:jedis:2.0.0'
|
||||
providedCompile 'commons-pool:commons-pool:1.5.6'
|
||||
|
||||
// Needed for StringUtils
|
||||
providedCompile 'org.apache.commons:commons-lang3:3.1@jar'
|
||||
}
|
||||
|
||||
test {
|
||||
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* RED5 Open Source Flash Server - http://code.google.com/p/red5/
|
||||
*
|
||||
* Copyright 2006-2012 by respective authors (see below). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.bigbluebutton.app.video;
|
||||
|
||||
import org.red5.client.net.rtmp.RTMPClient;
|
||||
import org.red5.server.service.PendingCall;
|
||||
import org.red5.server.net.rtmp.event.Ping;
|
||||
|
||||
/**
|
||||
* Custom RTMP Client
|
||||
*
|
||||
* This client behaves like the flash player plugin when requesting to play a stream
|
||||
*
|
||||
* @author Mateus Dalepiane (mdalepiane@gmail.com)
|
||||
*/
|
||||
|
||||
public class CustomRTMPClient extends RTMPClient {
|
||||
public void play(int streamId, String name) {
|
||||
System.out.println("play stream "+ streamId + ", name: " + name);
|
||||
if (conn != null) {
|
||||
// get the channel
|
||||
int channel = getChannelForStreamId(streamId);
|
||||
// send our requested buffer size
|
||||
ping(Ping.CLIENT_BUFFER, streamId, 2000);
|
||||
// send our request for a/v
|
||||
PendingCall receiveAudioCall = new PendingCall("receiveAudio");
|
||||
conn.invoke(receiveAudioCall, channel);
|
||||
PendingCall receiveVideoCall = new PendingCall("receiveVideo");
|
||||
conn.invoke(receiveVideoCall, channel);
|
||||
// call play
|
||||
Object[] params = new Object[1];
|
||||
params[0] = name;
|
||||
PendingCall pendingCall = new PendingCall("play", params);
|
||||
conn.invoke(pendingCall, channel);
|
||||
} else {
|
||||
System.out.println("Connection was null ?");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,350 @@
|
||||
/*
|
||||
* RED5 Open Source Flash Server - http://code.google.com/p/red5/
|
||||
*
|
||||
* Copyright 2006-2012 by respective authors (see below). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.bigbluebutton.app.video;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import org.red5.client.net.rtmp.ClientExceptionHandler;
|
||||
import org.red5.client.net.rtmp.INetStreamEventHandler;
|
||||
import org.red5.client.net.rtmp.RTMPClient;
|
||||
import org.red5.io.utils.ObjectMap;
|
||||
import org.red5.proxy.StreamingProxy;
|
||||
import org.red5.server.api.event.IEvent;
|
||||
import org.red5.server.api.event.IEventDispatcher;
|
||||
import org.red5.server.api.service.IPendingServiceCall;
|
||||
import org.red5.server.api.service.IPendingServiceCallback;
|
||||
import org.red5.server.net.rtmp.event.IRTMPEvent;
|
||||
import org.red5.server.net.rtmp.event.Notify;
|
||||
import org.red5.server.net.rtmp.status.StatusCodes;
|
||||
import org.red5.server.stream.message.RTMPMessage;
|
||||
|
||||
|
||||
/**
|
||||
* Relay a stream from one location to another via RTMP.
|
||||
*
|
||||
* @author Paul Gregoire (mondain@gmail.com)
|
||||
*/
|
||||
|
||||
|
||||
public class CustomStreamRelay {
|
||||
|
||||
// our consumer
|
||||
private CustomRTMPClient client;
|
||||
|
||||
// our publisher
|
||||
private StreamingProxy proxy;
|
||||
|
||||
// task timer
|
||||
private Timer timer;
|
||||
|
||||
|
||||
private String sourceHost;
|
||||
private String destHost;
|
||||
private String sourceApp;
|
||||
private String destApp;
|
||||
private int sourcePort;
|
||||
private int destPort;
|
||||
private String sourceStreamName;
|
||||
private String destStreamName;
|
||||
private String publishMode;
|
||||
Map<String, Object> defParams;
|
||||
|
||||
private boolean isDisconnecting;
|
||||
|
||||
/**
|
||||
* Creates a stream client to consume a stream from an end point and a proxy to relay the stream
|
||||
* to another end point.
|
||||
*
|
||||
* @param args application arguments
|
||||
*/
|
||||
|
||||
|
||||
public void setSourceHost(String sourceHost) {
|
||||
this.sourceHost = sourceHost;
|
||||
}
|
||||
|
||||
public void setSourcePort(int sourcePort) {
|
||||
this.sourcePort = sourcePort;
|
||||
}
|
||||
|
||||
public void setDestinationHost(String destHost) {
|
||||
this.destHost = destHost;
|
||||
}
|
||||
|
||||
public void setDestinationPort(int destPort) {
|
||||
this.destPort = destPort;
|
||||
}
|
||||
|
||||
public void setSourceApp(String sourceApp) {
|
||||
this.sourceApp = sourceApp;
|
||||
}
|
||||
|
||||
public void setDestinationApp(String destApp) {
|
||||
this.destApp = destApp;
|
||||
}
|
||||
|
||||
public void setSourceStreamName(String sourceStreamName) {
|
||||
this.sourceStreamName = sourceStreamName;
|
||||
}
|
||||
|
||||
public void setDestinationStreamName(String destStreamName) {
|
||||
this.destStreamName = destStreamName;
|
||||
}
|
||||
|
||||
public void setPublishMode(String publishMode) {
|
||||
this.publishMode = publishMode;
|
||||
}
|
||||
|
||||
public void initRelay(String... args) {
|
||||
if (args == null || args.length < 7) {
|
||||
System.out
|
||||
.println("Not enough args supplied. Usage: <source uri> <source app> <source stream name> <destination uri> <destination app> <destination stream name> <publish mode>");
|
||||
}
|
||||
else {
|
||||
sourceHost = args[0];
|
||||
destHost = args[3];
|
||||
sourceApp = args[1];
|
||||
destApp = args[4];
|
||||
sourcePort = 1935;
|
||||
destPort = 1935;
|
||||
sourceStreamName = args[2];
|
||||
destStreamName = args[5];
|
||||
publishMode = args[6]; //live, record, or append
|
||||
|
||||
int colonIdx = sourceHost.indexOf(':');
|
||||
if (colonIdx > 0) {
|
||||
sourcePort = Integer.valueOf(sourceHost.substring(colonIdx + 1));
|
||||
sourceHost = sourceHost.substring(0, colonIdx);
|
||||
System.out.printf("Source host: %s port: %d\n", sourceHost, sourcePort);
|
||||
}
|
||||
colonIdx = destHost.indexOf(':');
|
||||
if (colonIdx > 0) {
|
||||
destPort = Integer.valueOf(destHost.substring(colonIdx + 1));
|
||||
destHost = destHost.substring(0, colonIdx);
|
||||
System.out.printf("Destination host: %s port: %d\n", destHost, destPort);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void stopRelay() {
|
||||
isDisconnecting = true;
|
||||
client.disconnect();
|
||||
proxy.stop();
|
||||
}
|
||||
|
||||
public void startRelay() {
|
||||
|
||||
isDisconnecting = false;
|
||||
// create a timer
|
||||
timer = new Timer();
|
||||
// create our publisher
|
||||
proxy = new StreamingProxy();
|
||||
proxy.setHost(destHost);
|
||||
proxy.setPort(destPort);
|
||||
proxy.setApp(destApp);
|
||||
proxy.init();
|
||||
proxy.setConnectionClosedHandler(new Runnable() {
|
||||
public void run() {
|
||||
System.out.println("Publish connection has been closed, source will be disconnected");
|
||||
client.disconnect();
|
||||
}
|
||||
});
|
||||
proxy.setExceptionHandler(new ClientExceptionHandler() {
|
||||
@Override
|
||||
public void handleException(Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
proxy.stop();
|
||||
}
|
||||
});
|
||||
proxy.start(destStreamName, publishMode, new Object[] {});
|
||||
// wait for the publish state
|
||||
|
||||
// Change to use signal or something more cleaner
|
||||
|
||||
do {
|
||||
try {
|
||||
Thread.sleep(100L);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} while (!proxy.isPublished());
|
||||
System.out.println("Publishing...");
|
||||
|
||||
// create the consumer
|
||||
client = new CustomRTMPClient();
|
||||
client.setStreamEventDispatcher(new StreamEventDispatcher());
|
||||
client.setStreamEventHandler(new INetStreamEventHandler() {
|
||||
public void onStreamEvent(Notify notify) {
|
||||
System.out.printf("AQUIonStreamEvent: %s\n", notify);
|
||||
ObjectMap<?, ?> map = (ObjectMap<?, ?>) notify.getCall().getArguments()[0];
|
||||
String code = (String) map.get("code");
|
||||
System.out.printf("<:%s\n", code);
|
||||
if (StatusCodes.NS_PLAY_STREAMNOTFOUND.equals(code)) {
|
||||
System.out.println("Requested stream was not found");
|
||||
isDisconnecting = true;
|
||||
client.disconnect();
|
||||
} else if (StatusCodes.NS_PLAY_UNPUBLISHNOTIFY.equals(code) || StatusCodes.NS_PLAY_COMPLETE.equals(code)) {
|
||||
System.out.println("Source has stopped publishing or play is complete");
|
||||
isDisconnecting = true;
|
||||
client.disconnect();
|
||||
}
|
||||
}
|
||||
});
|
||||
client.setConnectionClosedHandler(new Runnable() {
|
||||
public void run() {
|
||||
System.out.println("Source connection has been closed");
|
||||
//System.exit(2);
|
||||
if(isDisconnecting) {
|
||||
System.out.println("Proxy will be stopped");
|
||||
client.disconnect();
|
||||
proxy.stop();
|
||||
} else {
|
||||
System.out.println("Reconnecting client...");
|
||||
client.connect(sourceHost, sourcePort, defParams, new ClientConnectCallback());
|
||||
}
|
||||
}
|
||||
});
|
||||
client.setExceptionHandler(new ClientExceptionHandler() {
|
||||
@Override
|
||||
public void handleException(Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
//System.exit(1);
|
||||
client.disconnect();
|
||||
proxy.stop();
|
||||
}
|
||||
});
|
||||
// connect the consumer
|
||||
defParams = client.makeDefaultConnectionParams(sourceHost, sourcePort, sourceApp);
|
||||
// add pageurl and swfurl
|
||||
defParams.put("pageUrl", "");
|
||||
defParams.put("swfUrl", "app:/Red5-StreamRelay.swf");
|
||||
// indicate for the handshake to generate swf verification data
|
||||
client.setSwfVerification(true);
|
||||
// connect the client
|
||||
System.out.println("startRelay:: ProxyRelay status is running: " + proxy.isRunning());
|
||||
client.connect(sourceHost, sourcePort, defParams, new ClientConnectCallback());
|
||||
}
|
||||
|
||||
private final class ClientConnectCallback implements IPendingServiceCallback{
|
||||
public void resultReceived(IPendingServiceCall call) {
|
||||
System.out.println("connectCallback");
|
||||
ObjectMap<?, ?> map = (ObjectMap<?, ?>) call.getResult();
|
||||
String code = (String) map.get("code");
|
||||
if ("NetConnection.Connect.Rejected".equals(code)) {
|
||||
System.out.printf("Rejected: %s\n", map.get("description"));
|
||||
client.disconnect();
|
||||
proxy.stop();
|
||||
} else if ("NetConnection.Connect.Success".equals(code)) {
|
||||
// 1. Wait for onBWDone
|
||||
timer.schedule(new BandwidthStatusTask(), 2000L);
|
||||
} else {
|
||||
System.out.printf("Unhandled response code: %s\n", code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches consumer events.
|
||||
*/
|
||||
private final class StreamEventDispatcher implements IEventDispatcher {
|
||||
|
||||
public void dispatchEvent(IEvent event) {
|
||||
System.out.println("ClientStream.dispachEvent()" + event.toString());
|
||||
System.out.println("dispatchEvent:: ProxyRelay status is running: " + proxy.isRunning());
|
||||
try {
|
||||
proxy.pushMessage(null, RTMPMessage.build((IRTMPEvent) event));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles result from subscribe call.
|
||||
*/
|
||||
private final class SubscribeStreamCallBack implements IPendingServiceCallback {
|
||||
|
||||
public void resultReceived(IPendingServiceCall call) {
|
||||
System.out.println("SubscirbeStreamCallBack::resultReceived: " + call);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a "stream" via playback, this is the source stream.
|
||||
*/
|
||||
private final class CreateStreamCallback implements IPendingServiceCallback {
|
||||
|
||||
public void resultReceived(IPendingServiceCall call) {
|
||||
System.out.println("CreateStreamCallBack::resultReceived: " + call);
|
||||
int streamId = (Integer) call.getResult();
|
||||
System.out.println("stream id: " + streamId);
|
||||
// send our buffer size request
|
||||
if (sourceStreamName.endsWith(".flv") || sourceStreamName.endsWith(".f4v") || sourceStreamName.endsWith(".mp4")) {
|
||||
System.out.println("THIS IS WRONG");
|
||||
System.out.println("play stream name " + sourceStreamName + " start 0 lenght -1");
|
||||
client.play(streamId, sourceStreamName, 0, -1);
|
||||
} else {
|
||||
System.out.println("play stream name " + sourceStreamName);
|
||||
client.play(streamId, sourceStreamName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues to check for onBWDone
|
||||
*/
|
||||
private final class BandwidthStatusTask extends TimerTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// check for onBWDone
|
||||
System.out.println("Bandwidth check done: " + client.isBandwidthCheckDone());
|
||||
// cancel this task
|
||||
this.cancel();
|
||||
// create a task to wait for subscribed
|
||||
timer.schedule(new PlayStatusTask(), 1000L);
|
||||
// 2. send FCSubscribe
|
||||
client.subscribe(new SubscribeStreamCallBack(), new Object[] { sourceStreamName });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class PlayStatusTask extends TimerTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// checking subscribed
|
||||
System.out.println("Subscribed: " + client.isSubscribed());
|
||||
// cancel this task
|
||||
this.cancel();
|
||||
// 3. create stream
|
||||
client.createStream(new CreateStreamCallback());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -21,17 +21,25 @@ package org.bigbluebutton.app.video;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import org.red5.logging.Red5LoggerFactory;
|
||||
import org.red5.server.adapter.MultiThreadedApplicationAdapter;
|
||||
import org.red5.server.api.IConnection;
|
||||
import org.red5.server.api.Red5;
|
||||
import org.red5.server.api.scope.IScope;
|
||||
import org.red5.server.api.scope.IBasicScope;
|
||||
import org.red5.server.api.scope.IBroadcastScope;
|
||||
import org.red5.server.api.scope.ScopeType;
|
||||
import org.red5.server.api.stream.IBroadcastStream;
|
||||
import org.red5.server.api.stream.IPlayItem;
|
||||
import org.red5.server.api.stream.IServerStream;
|
||||
import org.red5.server.api.stream.IStreamListener;
|
||||
import org.red5.server.api.stream.ISubscriberStream;
|
||||
import org.red5.server.stream.ClientBroadcastStream;
|
||||
import org.slf4j.Logger;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class VideoApplication extends MultiThreadedApplicationAdapter {
|
||||
private static Logger log = Red5LoggerFactory.getLogger(VideoApplication.class, "video");
|
||||
@ -42,6 +50,16 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
|
||||
private boolean recordVideoStream = false;
|
||||
private EventRecordingService recordingService;
|
||||
private final Map<String, IStreamListener> streamListeners = new HashMap<String, IStreamListener>();
|
||||
|
||||
private Map<String, CustomStreamRelay> remoteStreams = new ConcurrentHashMap<String, CustomStreamRelay>();
|
||||
private Map<String, Integer> listenersOnRemoteStream = new ConcurrentHashMap<String, Integer>();
|
||||
|
||||
// Proxy disconnection timer
|
||||
private Timer timer;
|
||||
// Proxy disconnection timeout
|
||||
// TODO: This timeout should be configurable
|
||||
private long timeout = 60000L;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean appStart(IScope app) {
|
||||
@ -49,6 +67,7 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
|
||||
log.info("oflaDemo appStart");
|
||||
System.out.println("oflaDemo appStart");
|
||||
appScope = app;
|
||||
timer = new Timer();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -72,13 +91,24 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
|
||||
super.streamPublishStart(stream);
|
||||
}
|
||||
|
||||
|
||||
public IBroadcastScope getBroadcastScope(IScope scope, String name) {
|
||||
IBasicScope basicScope = scope.getBasicScope(ScopeType.BROADCAST, name);
|
||||
if (!(basicScope instanceof IBroadcastScope)) {
|
||||
return null;
|
||||
} else {
|
||||
return (IBroadcastScope) basicScope;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void streamBroadcastStart(IBroadcastStream stream) {
|
||||
IConnection conn = Red5.getConnectionLocal();
|
||||
super.streamBroadcastStart(stream);
|
||||
log.info("streamBroadcastStart " + stream.getPublishedName() + " " + System.currentTimeMillis() + " " + conn.getScope().getName());
|
||||
|
||||
if (recordVideoStream) {
|
||||
if (recordVideoStream && stream.getPublishedName().contains("/") == false) {
|
||||
recordStream(stream);
|
||||
VideoStreamListener listener = new VideoStreamListener();
|
||||
listener.setEventRecordingService(recordingService);
|
||||
@ -142,5 +172,96 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
|
||||
public void setEventRecordingService(EventRecordingService s) {
|
||||
recordingService = s;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void streamPlayItemPlay(ISubscriberStream stream, IPlayItem item, boolean isLive) {
|
||||
// log w3c connect event
|
||||
String streamName = item.getName();
|
||||
|
||||
//rtmp://SV1/video/conferencia
|
||||
//SV2/SV3/SV4/streamName
|
||||
|
||||
|
||||
if(streamName.contains("/")) {
|
||||
synchronized(remoteStreams) {
|
||||
if(remoteStreams.containsKey(streamName) == false) {
|
||||
String[] parts = streamName.split("/");
|
||||
String sourceServer = parts[0];
|
||||
String sourceStreamName = StringUtils.join(parts, '/', 1, parts.length);
|
||||
String destinationServer = Red5.getConnectionLocal().getHost();
|
||||
String destinationStreamName = streamName;
|
||||
String app = "video/"+Red5.getConnectionLocal().getScope().getName();
|
||||
System.out.println("streamPlayItemPlay:: streamName [" + streamName + "]");
|
||||
System.out.println("streamPlayItemPlay:: sourceServer [" + sourceServer + "]");
|
||||
System.out.println("streamPlayItemPlay:: sourceStreamName [" + sourceStreamName + "]");
|
||||
System.out.println("streamPlayItemPlay:: destinationServer [" + destinationServer + "]");
|
||||
System.out.println("streamPlayItemPlay:: destinationStreamName [" + destinationStreamName + "]");
|
||||
System.out.println("streamPlayItemPlay:: app [" + app + "]");
|
||||
|
||||
CustomStreamRelay remoteRelay = new CustomStreamRelay();
|
||||
remoteRelay.initRelay(new String[]{sourceServer, app, sourceStreamName, destinationServer, app, destinationStreamName, "live"});
|
||||
remoteRelay.startRelay();
|
||||
remoteStreams.put(destinationStreamName, remoteRelay);
|
||||
listenersOnRemoteStream.put(streamName, 1);
|
||||
}
|
||||
else {
|
||||
Integer numberOfListeners = listenersOnRemoteStream.get(streamName) + 1;
|
||||
listenersOnRemoteStream.put(streamName,numberOfListeners);
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("W3C x-category:stream x-event:play c-ip:{} x-sname:{} x-name:{}", new Object[] { Red5.getConnectionLocal().getRemoteAddress(), stream.getName(), item.getName() });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void streamSubscriberClose(ISubscriberStream stream) {
|
||||
synchronized(remoteStreams) {
|
||||
super.streamSubscriberClose(stream);
|
||||
String streamName = stream.getBroadcastStreamPublishName();
|
||||
log.trace("Subscriber close for stream [{}]", streamName);
|
||||
if(streamName.contains("/")) {
|
||||
if(remoteStreams.containsKey(streamName)) {
|
||||
Integer numberOfListeners = listenersOnRemoteStream.get(streamName);
|
||||
if(numberOfListeners != null) {
|
||||
numberOfListeners = numberOfListeners - 1;
|
||||
listenersOnRemoteStream.put(streamName, numberOfListeners);
|
||||
log.trace("Stream [{}] has {} subscribers left", streamName, numberOfListeners);
|
||||
if(numberOfListeners < 1) {
|
||||
log.info("Starting timeout to close proxy for stream: {}", streamName);
|
||||
timer.schedule(new DisconnectProxyTask(streamName), timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class DisconnectProxyTask extends TimerTask {
|
||||
// Stream name that should be disconnected
|
||||
private String streamName;
|
||||
|
||||
public DisconnectProxyTask(String streamName) {
|
||||
this.streamName = streamName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Cancel this task
|
||||
this.cancel();
|
||||
// Check if someone reconnected
|
||||
synchronized(remoteStreams) {
|
||||
Integer numberOfListeners = listenersOnRemoteStream.get(streamName);
|
||||
log.trace("Stream [{}] has {} subscribers", streamName, numberOfListeners);
|
||||
if(numberOfListeners != null) {
|
||||
if(numberOfListeners < 1) {
|
||||
// No one else is connected to this stream, close relay
|
||||
log.info("Stopping relay for stream [{}]", streamName);
|
||||
listenersOnRemoteStream.remove(streamName);
|
||||
CustomStreamRelay remoteRelay = remoteStreams.remove(streamName);
|
||||
remoteRelay.stopRelay();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ public class VideoStreamListener implements IStreamListener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet instanceof VideoData) {
|
||||
/*if (packet instanceof VideoData) {
|
||||
if (! firstPacketReceived) {
|
||||
firstPacketReceived = true;
|
||||
IConnection conn = Red5.getConnectionLocal();
|
||||
@ -76,7 +76,7 @@ public class VideoStreamListener implements IStreamListener {
|
||||
|
||||
recordingService.record(conn.getScope().getName(), event);
|
||||
}
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
public void setEventRecordingService(EventRecordingService s) {
|
||||
|
@ -31,7 +31,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
|
||||
import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent;
|
||||
import org.bigbluebutton.modules.videoconf.events.VideoModuleStartEvent;
|
||||
import org.bigbluebutton.modules.videoconf.events.VideoModuleStopEvent;
|
||||
import org.bigbluebutton.modules.videoconf.events.VideoModuleStopEvent;
|
||||
|
||||
private var _moduleName:String = "Videoconf Module";
|
||||
private var _attributes:Object;
|
||||
@ -46,6 +46,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
public function get uri():String {
|
||||
return _attributes.uri + "/" + _attributes.room;
|
||||
//return "rtmp://143.54.10.63/videoproxy" + "/" + _attributes.room;
|
||||
}
|
||||
|
||||
public function get username():String {
|
||||
|
@ -29,7 +29,8 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
import flash.net.NetConnection;
|
||||
import flash.net.NetStream;
|
||||
import flash.system.Capabilities;
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
import mx.collections.ArrayCollection;
|
||||
|
||||
import org.bigbluebutton.common.LogUtil;
|
||||
@ -40,16 +41,28 @@ 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
|
||||
{
|
||||
public var videoOptions:VideoConfOptions;
|
||||
|
||||
// NetConnection used for stream publishing
|
||||
private var nc:NetConnection;
|
||||
// NetStream used for stream publishing
|
||||
private var ns:NetStream;
|
||||
private var _url:String;
|
||||
|
||||
|
||||
// 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();
|
||||
videoOptions.parseOptions();
|
||||
@ -66,11 +79,16 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
nc.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
|
||||
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);
|
||||
nc.connect(_url);
|
||||
playConnectionDict[_url] = nc;
|
||||
playConnectionCountDict[_url] = 0;
|
||||
}
|
||||
|
||||
private function onAsyncError(event:AsyncErrorEvent):void{
|
||||
@ -83,7 +101,7 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
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":
|
||||
@ -95,14 +113,118 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function onSecurityError(event:NetStatusEvent):void{
|
||||
}
|
||||
|
||||
public function get connection():NetConnection{
|
||||
public function get publishConnection():NetConnection{
|
||||
return this.nc;
|
||||
}
|
||||
|
||||
|
||||
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]){
|
||||
// Create new NetConnection and store it
|
||||
var connection:NetConnection = new NetConnection();
|
||||
connection.client = this;
|
||||
connection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
|
||||
connection.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
|
||||
connection.addEventListener(NetStatusEvent.NET_STATUS, onPlayNetStatus);
|
||||
connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
|
||||
connection.connect(newUrl);
|
||||
// TODO change to trace
|
||||
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::createPlayConnectionFor:: Found NetConnection for [" + 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{
|
||||
// If does not exist
|
||||
if(!streamNamePrefixDict[userID]){
|
||||
// TODO: change LogUtil.debug(); to trace();
|
||||
LogUtil.debug("VideoProxy:: getStreamNamePrefixFor:: streamPrefix not found. NetConnection might not exist for stream from [" + userID + "]");
|
||||
return "";
|
||||
}
|
||||
else{
|
||||
return streamNamePrefixDict[userID];
|
||||
}
|
||||
}
|
||||
|
||||
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.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
|
||||
ns.addEventListener( IOErrorEvent.IO_ERROR, onIOError );
|
||||
@ -164,7 +286,7 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
}
|
||||
|
||||
public function stopBroadcasting():void{
|
||||
trace("Closing netstream for webcam publishing");
|
||||
LogUtil.debug("Closing netstream for webcam publishing");
|
||||
|
||||
if (ns != null) {
|
||||
ns.attachCamera(null);
|
||||
@ -175,9 +297,20 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
}
|
||||
|
||||
public function disconnect():void {
|
||||
trace("VideoProxy:: disconnecting from Video application");
|
||||
LogUtil.debug("VideoProxy:: disconnecting from Video application");
|
||||
stopBroadcasting();
|
||||
// Close publish NetConnection
|
||||
if (nc != null) nc.close();
|
||||
// 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 {
|
||||
@ -189,7 +322,7 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
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.");
|
||||
LogUtil.debug("bandwidth = " + p_bw + " Kbps.");
|
||||
}
|
||||
|
||||
|
||||
|
@ -221,7 +221,7 @@ package org.bigbluebutton.modules.videoconf.business
|
||||
}
|
||||
|
||||
override public function close(event:MouseEvent = null):void{
|
||||
trace("VideoWIndowItf close window event");
|
||||
LogUtil.debug("VideoWIndowItf close window event");
|
||||
|
||||
var e:CloseWindowEvent = new CloseWindowEvent();
|
||||
e.window = this;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -55,6 +55,8 @@ 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
|
||||
{
|
||||
@ -72,6 +74,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)
|
||||
{
|
||||
@ -93,7 +98,7 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
if (!_ready) return;
|
||||
trace("VideoEventMapDelegate:: [" + me + "] Viewing [" + userID + " stream [" + stream + "]");
|
||||
if (! UserManager.getInstance().getConference().amIThisUser(userID)) {
|
||||
openViewWindowFor(userID);
|
||||
initPlayConnectionFor(userID);
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,7 +218,7 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
closeWindow(userID);
|
||||
}
|
||||
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: View user's = [" + userID + "] webcam.");
|
||||
openViewWindowFor(userID);
|
||||
initPlayConnectionFor(userID);
|
||||
} else {
|
||||
if (UsersUtil.isMe(userID) && options.autoStart) {
|
||||
trace("VideoEventMapDelegate:: [" + me + "] openWebcamWindowFor:: It's ME and AutoStart. Start publishing.");
|
||||
@ -279,6 +284,7 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
if (win != null) {
|
||||
trace("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);
|
||||
@ -286,6 +292,25 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
trace("VideoEventMapDelegate:: [" + me + "] closeWindow:: Not Closing. No window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
trace("VideoEventMapDelegate:: [" + me + "] openViewWindowFor:: Opening VIEW window for [" + userID + "] [" + UsersUtil.getUserName(userID) + "]");
|
||||
@ -299,7 +324,8 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
closeWindow(userID);
|
||||
|
||||
var bbbUser:BBBUser = UsersUtil.getUser(userID);
|
||||
window.startVideo(proxy.connection, bbbUser.streamName);
|
||||
var streamName:String = proxy.getStreamNamePrefixFor(userID) + bbbUser.streamName;
|
||||
window.startVideo(proxy.getPlayConnectionFor(userID), streamName);
|
||||
|
||||
webcamWindows.addWindow(window);
|
||||
openWindow(window);
|
||||
@ -325,7 +351,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);
|
||||
LogUtil.debug("VideoEventMapDelegate:: [" + me + "] startPublishing:: Publishing stream to: " + proxy.publishConnection.uri + "/" + e.stream);
|
||||
streamName = e.stream;
|
||||
proxy.startPublishing(e);
|
||||
|
||||
@ -487,4 +513,4 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
|
||||
import mx.collections.ArrayCollection;
|
||||
|
||||
import org.bigbluebutton.common.LogUtil;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.modules.videoconf.business.VideoWindowItf;
|
||||
import org.bigbluebutton.modules.videoconf.views.AvatarWindow;
|
||||
@ -36,13 +37,13 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
|
||||
public function addWindow(window:VideoWindowItf):void {
|
||||
webcamWindows.addItem(window);
|
||||
trace("[" + me + "] addWindow:: userID = [" + window.userID + "] numWindows = [" + webcamWindows.length + "]");
|
||||
LogUtil.debug("[" + me + "] addWindow:: userID = [" + window.userID + "] numWindows = [" + webcamWindows.length + "]");
|
||||
}
|
||||
|
||||
public function removeWindow(userID:String):VideoWindowItf {
|
||||
for (var i:int = 0; i < webcamWindows.length; i++) {
|
||||
var win:VideoWindowItf = webcamWindows.getItemAt(i) as VideoWindowItf;
|
||||
trace("[" + me + "] removeWindow:: [" + win.userID + " == " + userID + "] equal = [" + (win.userID == userID) + "]");
|
||||
LogUtil.debug("[" + me + "] removeWindow:: [" + win.userID + " == " + userID + "] equal = [" + (win.userID == userID) + "]");
|
||||
if (win.userID == userID) {
|
||||
return webcamWindows.removeItemAt(i) as VideoWindowItf;
|
||||
}
|
||||
@ -52,10 +53,10 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
}
|
||||
|
||||
public function hasWindow(userID:String):Boolean {
|
||||
trace("[" + me + "] hasWindow:: user [" + userID + "] numWindows = [" + webcamWindows.length + "]");
|
||||
LogUtil.debug("[" + me + "] hasWindow:: user [" + userID + "] numWindows = [" + webcamWindows.length + "]");
|
||||
for (var i:int = 0; i < webcamWindows.length; i++) {
|
||||
var win:VideoWindowItf = webcamWindows.getItemAt(i) as VideoWindowItf;
|
||||
trace("[" + me + "] hasWindow:: [" + win.userID + " == " + userID + "] equal = [" + (win.userID == userID) + "]");
|
||||
LogUtil.debug("[" + me + "] hasWindow:: [" + win.userID + " == " + userID + "] equal = [" + (win.userID == userID) + "]");
|
||||
if (win.userID == userID) {
|
||||
return true;
|
||||
}
|
||||
@ -67,11 +68,11 @@ package org.bigbluebutton.modules.videoconf.maps
|
||||
public function getWindow(userID:String):VideoWindowItf {
|
||||
for (var i:int = 0; i < webcamWindows.length; i++) {
|
||||
var win:VideoWindowItf = webcamWindows.getItemAt(i) as VideoWindowItf;
|
||||
trace("[" + me + "] getWindow:: [" + win.userID + " == " + userID + "] equal = [" + (win.userID == userID) + "]");
|
||||
LogUtil.debug("[" + me + "] getWindow:: [" + win.userID + " == " + userID + "] equal = [" + (win.userID == userID) + "]");
|
||||
if (win.userID == userID) return win;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,12 +190,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
private function handleMadePresenterEvent(event:MadePresenterEvent):void {
|
||||
trace("******** Avatar: HandleMadePresenter event *********");
|
||||
LogUtil.debug("******** Avatar: HandleMadePresenter event *********");
|
||||
updateControlButtons();
|
||||
}
|
||||
|
||||
private function handleSwitchedPresenterEvent(event:SwitchedPresenterEvent):void {
|
||||
trace("******** Avatar: handleSwitchedPresenterEvent event *********");
|
||||
LogUtil.debug("******** Avatar: handleSwitchedPresenterEvent event *********");
|
||||
updateControlButtons();
|
||||
}
|
||||
|
||||
@ -241,7 +241,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
private function closeThisWindow():void {
|
||||
trace("* Closing avatar window for user [" + userID + "] *");
|
||||
LogUtil.debug("* Closing avatar window for user [" + userID + "] *");
|
||||
}
|
||||
|
||||
|
||||
|
@ -127,13 +127,13 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
private function displayEjectButton(controlsForPresenter:Boolean):void {
|
||||
var userOption:UsersOptions = new UsersOptions();
|
||||
if (! userOption.allowKickUser) {
|
||||
trace("Kicking user not allowed");
|
||||
LogUtil.debug("Kicking user not allowed");
|
||||
// User kicking not enabled. Just return;
|
||||
ejectUserBtn.visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
trace("Kicking user allowed [" + userOption.allowKickUser + "]");
|
||||
LogUtil.debug("Kicking user allowed [" + userOption.allowKickUser + "]");
|
||||
|
||||
/**
|
||||
* Display button if:
|
||||
|
@ -167,12 +167,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
private function handleMadePresenterEvent(event:MadePresenterEvent):void {
|
||||
trace("******** PublishWindow: HandleMadePresenter event *********");
|
||||
LogUtil.debug("******** PublishWindow: HandleMadePresenter event *********");
|
||||
updateControlButtons();
|
||||
}
|
||||
|
||||
private function handleSwitchedPresenterEvent(event:SwitchedPresenterEvent):void {
|
||||
trace("******** PublishWindow: handleSwitchedPresenterEvent event *********");
|
||||
LogUtil.debug("******** PublishWindow: handleSwitchedPresenterEvent event *********");
|
||||
updateControlButtons();
|
||||
}
|
||||
|
||||
@ -236,7 +236,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
_camera.setQuality(videoOptions.camQualityBandwidth, videoOptions.camQualityPicture);
|
||||
|
||||
if (_camera.width != camWidth || _camera.height != camHeight) {
|
||||
trace("Resolution " + camWidth + "x" + camHeight + " is not supported, using " + _camera.width + "x" + _camera.height + " instead");
|
||||
LogUtil.debug("Resolution " + camWidth + "x" + camHeight + " is not supported, using " + _camera.width + "x" + _camera.height + " instead");
|
||||
setResolution(_camera.width, _camera.height);
|
||||
}
|
||||
|
||||
@ -262,12 +262,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
private function onActivityEvent(e:ActivityEvent):void {
|
||||
if (_waitingForActivation && e.activating) {
|
||||
trace("Cam activity event: waitingForActivation = [" + _waitingForActivation + "] activating = [" + e.activating + "]");
|
||||
LogUtil.debug("Cam activity event: waitingForActivation = [" + _waitingForActivation + "] activating = [" + e.activating + "]");
|
||||
_activationTimer.stop();
|
||||
showWarning('bbb.video.publish.hint.videoPreview', false, "0xFFFF00");
|
||||
_waitingForActivation = false;
|
||||
|
||||
trace("Starting auto-publisher timer.");
|
||||
LogUtil.debug("Starting auto-publisher timer.");
|
||||
autoPublishTimer = new Timer(3000, 1);
|
||||
autoPublishTimer.addEventListener(TimerEvent.TIMER, autopublishTimerHandler);
|
||||
autoPublishTimer.start();
|
||||
@ -333,23 +333,23 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
private var _isClosing:Boolean = false;
|
||||
|
||||
override public function close(event:MouseEvent=null):void{
|
||||
trace("PublishWindow:: closing");
|
||||
LogUtil.debug("PublishWindow:: closing");
|
||||
|
||||
if (!_isClosing) {
|
||||
_isClosing = true;
|
||||
stopPublishing();
|
||||
trace("Dispatching ClosePublishWindowEvent event");
|
||||
LogUtil.debug("Dispatching ClosePublishWindowEvent event");
|
||||
var gDispatcher:Dispatcher = new Dispatcher();
|
||||
gDispatcher.dispatchEvent(new ClosePublishWindowEvent());
|
||||
}
|
||||
}
|
||||
|
||||
private function stopCamera():void {
|
||||
trace("PublishWindow:: stopping camera");
|
||||
LogUtil.debug("PublishWindow:: stopping camera");
|
||||
|
||||
_camera = null;
|
||||
if (_video != null) {
|
||||
trace("PublishWindow:: removing video from display");
|
||||
LogUtil.debug("PublishWindow:: removing video from display");
|
||||
_videoHolder.removeChild(_video);
|
||||
|
||||
_video.attachCamera(null);
|
||||
@ -360,7 +360,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
private function stopPublishing():void{
|
||||
trace("PublishWindow::stopPublishing");
|
||||
LogUtil.debug("PublishWindow::stopPublishing");
|
||||
stopCamera();
|
||||
var e:StopBroadcastEvent = new StopBroadcastEvent()
|
||||
e.stream = streamName;
|
||||
@ -431,7 +431,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
private function closeWindow(e:CloseAllWindowsEvent):void{
|
||||
trace("PublishWindow::closeWindow");
|
||||
LogUtil.debug("PublishWindow::closeWindow");
|
||||
stopCamera();
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<![CDATA[
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import org.bigbluebutton.common.LogUtil;
|
||||
import org.bigbluebutton.common.Images;
|
||||
import org.bigbluebutton.core.events.LockControlEvent;
|
||||
import org.bigbluebutton.core.managers.UserManager;
|
||||
@ -114,14 +115,14 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
private function openPublishWindow():void{
|
||||
if(_currentState == ON_STATE) {
|
||||
trace("Close window");
|
||||
LogUtil.debug("Close window");
|
||||
_currentState = OFF_STATE;
|
||||
//this.toolTip = ResourceUtil.getInstance().getString('bbb.toolbar.video.toolTip.start');
|
||||
camIcon = images.webcam;
|
||||
this.selected = false;
|
||||
dispatchEvent(new ClosePublishWindowEvent());
|
||||
} else {
|
||||
trace("Share camera");
|
||||
LogUtil.debug("Share camera");
|
||||
_currentState = ON_STATE;
|
||||
//this.toolTip = ResourceUtil.getInstance().getString('bbb.toolbar.video.toolTip.stop');
|
||||
camIcon = images.webcamOn;
|
||||
|
@ -63,10 +63,13 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import org.bigbluebutton.modules.videoconf.business.TalkingButtonOverlay;
|
||||
import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent;
|
||||
import org.bigbluebutton.modules.videoconf.model.VideoConfOptions;
|
||||
|
||||
import flash.net.NetConnection;
|
||||
|
||||
|
||||
private var ns:NetStream;
|
||||
private var globalDispatcher:Dispatcher;
|
||||
|
||||
|
||||
[Bindable]
|
||||
public var videoOptions:VideoConfOptions = new VideoConfOptions();
|
||||
|
||||
@ -74,12 +77,19 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
[Bindable] public var glowColor:String = "";
|
||||
[Bindable] public var glowBlurSize:Number = 0;
|
||||
|
||||
public var connection2:NetConnection;
|
||||
|
||||
override public function getWindowType():String {
|
||||
return windowType;
|
||||
}
|
||||
|
||||
|
||||
private function onCreationComplete():void{
|
||||
|
||||
|
||||
//connection2.connect("rtmp://143.54.10.63/video/conferencia");
|
||||
|
||||
|
||||
this.glowColor = videoOptions.glowColor;
|
||||
this.glowBlurSize = videoOptions.glowBlurSize;
|
||||
|
||||
@ -123,12 +133,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
private function handleMadePresenterEvent(event:MadePresenterEvent):void {
|
||||
trace("******** VideoWindow: HandleMadePresenter event *********");
|
||||
LogUtil.debug("******** VideoWindow: HandleMadePresenter event *********");
|
||||
updateControlButtons();
|
||||
}
|
||||
|
||||
private function handleSwitchedPresenterEvent(event:SwitchedPresenterEvent):void {
|
||||
trace("******** VideoWindow: handleSwitchedPresenterEvent event *********");
|
||||
LogUtil.debug("******** VideoWindow: handleSwitchedPresenterEvent event *********");
|
||||
updateControlButtons();
|
||||
}
|
||||
|
||||
@ -174,6 +184,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
public function startVideo(connection:NetConnection, stream:String):void{
|
||||
|
||||
ns = new NetStream(connection);
|
||||
ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
|
||||
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
|
||||
@ -194,7 +205,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
if (videoOptions.smoothVideo) {
|
||||
trace("Smoothing video.")
|
||||
LogUtil.debug("Smoothing video.")
|
||||
_video.smoothing = true;
|
||||
}
|
||||
|
||||
@ -202,15 +213,15 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
var filter:ConvolutionFilter = new flash.filters.ConvolutionFilter();
|
||||
filter.matrixX = 3;
|
||||
filter.matrixY = 3;
|
||||
trace("Applying convolution filter =[" + videoOptions.convolutionFilter + "]");
|
||||
LogUtil.debug("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;
|
||||
@ -225,7 +236,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
public function onMetaData(info:Object):void{
|
||||
trace("metadata: width=" + info.width + " height=" + info.height);
|
||||
LogUtil.debug("metadata: width=" + info.width + " height=" + info.height);
|
||||
_video.width = info.width;
|
||||
_video.height = info.height;
|
||||
setAspectRatio(info.width, info.height);
|
||||
@ -233,6 +244,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
private function onNetStatus(e:NetStatusEvent):void{
|
||||
|
||||
LogUtil.debug(e.info.code);
|
||||
switch(e.info.code){
|
||||
case "NetStream.Publish.Start":
|
||||
LogUtil.debug("NetStream.Publish.Start for broadcast stream " + streamName);
|
||||
@ -253,6 +266,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
case "NetStream.Play.NoSupportedTrackFound":
|
||||
LogUtil.debug("The MP4 doesn't contain any supported tracks");
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ defaultDialAccessNumber=613-555-1234
|
||||
# conference. This is only used for the old scheduling which will be
|
||||
# removed in the future. Use the API to create a conference.
|
||||
defaultWelcomeMessage=<br>Welcome to <b>%%CONFNAME%%</b>!<br><br>For help on using BigBlueButton see these (short) <a href="event:http://www.bigbluebutton.org/content/videos"><u>tutorial videos</u></a>.<br><br>To join the audio bridge click the headset icon (upper-left hand corner). Use a headset to avoid causing background noise for others.<br>
|
||||
defaultWelcomeMessageFooter=This server is running a <a href="https://code.google.com/p/bigbluebutton/wiki/081Overview" target="_blank"><u>BigBlueButton 0.9.0-dev</u></a>.
|
||||
defaultWelcomeMessageFooter=This server is running <a href="https://code.google.com/p/bigbluebutton/wiki/081Overview" target="_blank"><u>BigBlueButton 0.81-RC5</u></a>.
|
||||
|
||||
# Default maximum number of users a meeting can have.
|
||||
# Doesn't get enforced yet but is the default value when the create
|
||||
|
Loading…
Reference in New Issue
Block a user