Merge branch 'master' of github.com:bigbluebutton/bigbluebutton into record-and-playback-feature
This commit is contained in:
commit
d6c8b30e19
@ -19,14 +19,12 @@
|
|||||||
**/
|
**/
|
||||||
package org.bigbluebutton.voiceconf.red5;
|
package org.bigbluebutton.voiceconf.red5;
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.bigbluebutton.voiceconf.sip.PeerNotFoundException;
|
import org.bigbluebutton.voiceconf.sip.PeerNotFoundException;
|
||||||
import org.bigbluebutton.voiceconf.sip.SipPeerManager;
|
import org.bigbluebutton.voiceconf.sip.SipPeerManager;
|
||||||
import org.red5.logging.Red5LoggerFactory;
|
import org.red5.logging.Red5LoggerFactory;
|
||||||
import org.red5.server.adapter.MultiThreadedApplicationAdapter;
|
import org.red5.server.adapter.MultiThreadedApplicationAdapter;
|
||||||
import org.red5.server.api.IClient;
|
|
||||||
import org.red5.server.api.IConnection;
|
import org.red5.server.api.IConnection;
|
||||||
import org.red5.server.api.IScope;
|
import org.red5.server.api.IScope;
|
||||||
import org.red5.server.api.Red5;
|
import org.red5.server.api.Red5;
|
||||||
@ -72,22 +70,43 @@ public class Application extends MultiThreadedApplicationAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean appJoin(IClient client, IScope scope) {
|
public boolean appConnect(IConnection conn, Object[] params) {
|
||||||
log.debug("VoiceConferenceApplication roomJoin[" + client.getId() + "]");
|
String userid = ((String) params[0]).toString();
|
||||||
clientConnManager.createClient(client.getId(), (IServiceCapableConnection) Red5.getConnectionLocal());
|
String username = ((String) params[1]).toString();
|
||||||
|
String clientId = Red5.getConnectionLocal().getClient().getId();
|
||||||
|
String remoteHost = Red5.getConnectionLocal().getRemoteAddress();
|
||||||
|
int remotePort = Red5.getConnectionLocal().getRemotePort();
|
||||||
|
|
||||||
|
if ((userid == null) || ("".equals(userid))) userid = "unknown-userid";
|
||||||
|
if ((username == null) || ("".equals(username))) username = "UNKNOWN-CALLER";
|
||||||
|
Red5.getConnectionLocal().setAttribute("USERID", userid);
|
||||||
|
Red5.getConnectionLocal().setAttribute("USERNAME", username);
|
||||||
|
|
||||||
|
log.info("{} [clientid={}] has connected to the voice conf app.", username + "[uid=" + userid + "]", clientId);
|
||||||
|
log.info("[clientid={}] connected from {}.", clientId, remoteHost + ":" + remotePort);
|
||||||
|
|
||||||
|
clientConnManager.createClient(clientId, userid, username, (IServiceCapableConnection) Red5.getConnectionLocal());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appLeave(IClient client, IScope scope) {
|
public void appDisconnect(IConnection conn) {
|
||||||
log.debug("VoiceConferenceApplication roomLeave[" + client.getId() + "]");
|
String clientId = Red5.getConnectionLocal().getClient().getId();
|
||||||
clientConnManager.removeClient(client.getId());
|
String userid = getUserId();
|
||||||
log.debug( "Red5SIP Client closing client {}", client.getId());
|
String username = getUsername();
|
||||||
|
|
||||||
|
String remoteHost = Red5.getConnectionLocal().getRemoteAddress();
|
||||||
|
int remotePort = Red5.getConnectionLocal().getRemotePort();
|
||||||
|
log.info("[clientid={}] disconnnected from {}.", clientId, remoteHost + ":" + remotePort);
|
||||||
|
log.debug("{} [clientid={}] is leaving the voice conf app. Removing from ConnectionManager.", username + "[uid=" + userid + "]", clientId);
|
||||||
|
|
||||||
|
clientConnManager.removeClient(clientId);
|
||||||
|
|
||||||
String peerId = (String) Red5.getConnectionLocal().getAttribute("VOICE_CONF_PEER");
|
String peerId = (String) Red5.getConnectionLocal().getAttribute("VOICE_CONF_PEER");
|
||||||
if (peerId != null) {
|
if (peerId != null) {
|
||||||
try {
|
try {
|
||||||
sipPeerManager.hangup(peerId, client.getId());
|
log.debug("Forcing hang up {} [clientid={}] in case the user is still in the conference.", username + "[uid=" + userid + "]", clientId);
|
||||||
|
sipPeerManager.hangup(peerId, clientId);
|
||||||
} catch (PeerNotFoundException e) {
|
} catch (PeerNotFoundException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -97,13 +116,16 @@ public class Application extends MultiThreadedApplicationAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void streamPublishStart(IBroadcastStream stream) {
|
public void streamPublishStart(IBroadcastStream stream) {
|
||||||
log.debug("streamPublishStart: {}; {}", stream, stream.getPublishedName());
|
String clientId = Red5.getConnectionLocal().getClient().getId();
|
||||||
|
String userid = getUserId();
|
||||||
|
String username = getUsername();
|
||||||
|
|
||||||
|
log.debug("{} has started publishing stream [{}]", username + "[uid=" + userid + "][clientid=" + clientId + "]", stream.getPublishedName());
|
||||||
System.out.println("streamPublishStart: " + stream.getPublishedName());
|
System.out.println("streamPublishStart: " + stream.getPublishedName());
|
||||||
IConnection conn = Red5.getConnectionLocal();
|
IConnection conn = Red5.getConnectionLocal();
|
||||||
String peerId = (String) conn.getAttribute("VOICE_CONF_PEER");
|
String peerId = (String) conn.getAttribute("VOICE_CONF_PEER");
|
||||||
if (peerId != null) {
|
if (peerId != null) {
|
||||||
super.streamPublishStart(stream);
|
super.streamPublishStart(stream);
|
||||||
String clientId = conn.getClient().getId();
|
|
||||||
sipPeerManager.startTalkStream(peerId, clientId, stream, conn.getScope());
|
sipPeerManager.startTalkStream(peerId, clientId, stream, conn.getScope());
|
||||||
// recordStream(stream);
|
// recordStream(stream);
|
||||||
}
|
}
|
||||||
@ -128,12 +150,15 @@ public class Application extends MultiThreadedApplicationAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void streamBroadcastClose(IBroadcastStream stream) {
|
public void streamBroadcastClose(IBroadcastStream stream) {
|
||||||
System.out.println("streamBroadcastClose: " + stream.getPublishedName());
|
String clientId = Red5.getConnectionLocal().getClient().getId();
|
||||||
|
String userid = getUserId();
|
||||||
|
String username = getUsername();
|
||||||
|
|
||||||
|
log.debug("{} has stopped publishing stream [{}]", username + "[uid=" + userid + "][clientid=" + clientId + "]", stream.getPublishedName());
|
||||||
IConnection conn = Red5.getConnectionLocal();
|
IConnection conn = Red5.getConnectionLocal();
|
||||||
String peerId = (String) conn.getAttribute("VOICE_CONF_PEER");
|
String peerId = (String) conn.getAttribute("VOICE_CONF_PEER");
|
||||||
if (peerId != null) {
|
if (peerId != null) {
|
||||||
super.streamPublishStart(stream);
|
super.streamPublishStart(stream);
|
||||||
String clientId = conn.getClient().getId();
|
|
||||||
sipPeerManager.stopTalkStream(peerId, clientId, stream, conn.getScope());
|
sipPeerManager.stopTalkStream(peerId, clientId, stream, conn.getScope());
|
||||||
super.streamBroadcastClose(stream);
|
super.streamBroadcastClose(stream);
|
||||||
}
|
}
|
||||||
@ -179,4 +204,16 @@ public class Application extends MultiThreadedApplicationAdapter {
|
|||||||
public void setClientConnectionManager(ClientConnectionManager ccm) {
|
public void setClientConnectionManager(ClientConnectionManager ccm) {
|
||||||
clientConnManager = ccm;
|
clientConnManager = ccm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getUserId() {
|
||||||
|
String userid = (String) Red5.getConnectionLocal().getAttribute("USERID");
|
||||||
|
if ((userid == null) || ("".equals(userid))) userid = "unknown-userid";
|
||||||
|
return userid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUsername() {
|
||||||
|
String username = (String) Red5.getConnectionLocal().getAttribute("USERNAME");
|
||||||
|
if ((username == null) || ("".equals(username))) username = "UNKNOWN-CALLER";
|
||||||
|
return username;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,10 +28,14 @@ private static Logger log = Red5LoggerFactory.getLogger(ClientConnection.class,
|
|||||||
|
|
||||||
private final IServiceCapableConnection connection;
|
private final IServiceCapableConnection connection;
|
||||||
private final String connId;
|
private final String connId;
|
||||||
|
private final String userid;
|
||||||
|
private final String username;
|
||||||
|
|
||||||
public ClientConnection(String connId, IServiceCapableConnection connection) {
|
public ClientConnection(String connId, String userid, String username, IServiceCapableConnection connection) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.connId = connId;
|
this.connId = connId;
|
||||||
|
this.userid = userid;
|
||||||
|
this.username = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getConnId() {
|
public String getConnId() {
|
||||||
@ -39,17 +43,17 @@ private static Logger log = Red5LoggerFactory.getLogger(ClientConnection.class,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onJoinConferenceSuccess(String publishName, String playName, String codec) {
|
public void onJoinConferenceSuccess(String publishName, String playName, String codec) {
|
||||||
log.debug( "SIP Call Connected" );
|
log.debug("Notify client that {} [{}] has joined the conference.", username, userid);
|
||||||
connection.invoke("successfullyJoinedVoiceConferenceCallback", new Object[] {publishName, playName, codec});
|
connection.invoke("successfullyJoinedVoiceConferenceCallback", new Object[] {publishName, playName, codec});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onJoinConferenceFail() {
|
public void onJoinConferenceFail() {
|
||||||
log.debug("onOutgoingCallFailed");
|
log.debug("Notify client that {} [{}] failed to join the conference.", username, userid);
|
||||||
connection.invoke("failedToJoinVoiceConferenceCallback", new Object[] {"onUaCallFailed"});
|
connection.invoke("failedToJoinVoiceConferenceCallback", new Object[] {"onUaCallFailed"});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLeaveConference() {
|
public void onLeaveConference() {
|
||||||
log.debug("onCallClosed");
|
log.debug("Notify client that {} [{}] left the conference.", username, userid);
|
||||||
connection.invoke("disconnectedFromJoinVoiceConferenceCallback", new Object[] {"onUaCallClosed"});
|
connection.invoke("disconnectedFromJoinVoiceConferenceCallback", new Object[] {"onUaCallClosed"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,14 +31,18 @@ public class ClientConnectionManager {
|
|||||||
|
|
||||||
private Map<String, ClientConnection> clients = new ConcurrentHashMap<String, ClientConnection>();
|
private Map<String, ClientConnection> clients = new ConcurrentHashMap<String, ClientConnection>();
|
||||||
|
|
||||||
public void createClient(String id, IServiceCapableConnection connection) {
|
public void createClient(String id, String userid, String username, IServiceCapableConnection connection) {
|
||||||
ClientConnection cc = new ClientConnection(id, connection);
|
ClientConnection cc = new ClientConnection(id, userid, username, connection);
|
||||||
clients.put(id, cc);
|
clients.put(id, cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeClient(String id) {
|
public void removeClient(String id) {
|
||||||
ClientConnection cc = clients.remove(id);
|
ClientConnection cc = clients.remove(id);
|
||||||
if (cc == null) log.warn("Failed to remove client {}.", id);
|
if (cc == null) {
|
||||||
|
log.warn("Failed to remove client {}.", id);
|
||||||
|
} else {
|
||||||
|
log.debug("Removed client {} from ConnectionManager.", id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void joinConferenceSuccess(String clientId, String usertalkStream, String userListenStream, String codec) {
|
public void joinConferenceSuccess(String clientId, String usertalkStream, String userListenStream, String codec) {
|
||||||
@ -46,7 +50,7 @@ public class ClientConnectionManager {
|
|||||||
if (cc != null) {
|
if (cc != null) {
|
||||||
cc.onJoinConferenceSuccess(usertalkStream, userListenStream, codec);
|
cc.onJoinConferenceSuccess(usertalkStream, userListenStream, codec);
|
||||||
} else {
|
} else {
|
||||||
log.warn("Can't find connection {}", clientId);
|
log.warn("Can't find client {} to inform user that she has joined the conference.", clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +59,7 @@ public class ClientConnectionManager {
|
|||||||
if (cc != null) {
|
if (cc != null) {
|
||||||
cc.onJoinConferenceFail();
|
cc.onJoinConferenceFail();
|
||||||
} else {
|
} else {
|
||||||
log.warn("Can't find connection {}", clientId);
|
log.warn("Can't find client {} to inform user that she failed to join conference.", clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +68,7 @@ public class ClientConnectionManager {
|
|||||||
if (cc != null) {
|
if (cc != null) {
|
||||||
cc.onLeaveConference();
|
cc.onLeaveConference();
|
||||||
} else {
|
} else {
|
||||||
log.warn("Can't find connection {}", clientId);
|
log.warn("Can't find client {} to inform user that she has left the conference.", clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,11 @@ public class Service {
|
|||||||
private MessageFormat callExtensionPattern = new MessageFormat("{0}");
|
private MessageFormat callExtensionPattern = new MessageFormat("{0}");
|
||||||
|
|
||||||
public Boolean call(String peerId, String callerName, String destination) {
|
public Boolean call(String peerId, String callerName, String destination) {
|
||||||
log.debug("Joining voice conference " + destination);
|
String clientId = Red5.getConnectionLocal().getClient().getId();
|
||||||
|
String userid = getUserId();
|
||||||
|
String username = getUsername();
|
||||||
|
log.debug("{} is requesting to join into the conference {}", username + "[uid=" + userid + "][clientid=" + clientId + "]", destination);
|
||||||
|
|
||||||
String extension = callExtensionPattern.format(new String[] { destination });
|
String extension = callExtensionPattern.format(new String[] { destination });
|
||||||
try {
|
try {
|
||||||
sipPeerManager.call(peerId, getClientId(), callerName, extension);
|
sipPeerManager.call(peerId, getClientId(), callerName, extension);
|
||||||
@ -48,7 +52,10 @@ public class Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Boolean hangup(String peerId) {
|
public Boolean hangup(String peerId) {
|
||||||
log.debug("Red5SIP Hangup");
|
String clientId = Red5.getConnectionLocal().getClient().getId();
|
||||||
|
String userid = getUserId();
|
||||||
|
String username = getUsername();
|
||||||
|
log.debug("{} is requesting to hang up from the conference.", username + "[uid=" + userid + "][clientid=" + clientId + "]");
|
||||||
try {
|
try {
|
||||||
sipPeerManager.hangup(peerId, getClientId());
|
sipPeerManager.hangup(peerId, getClientId());
|
||||||
return true;
|
return true;
|
||||||
@ -70,4 +77,16 @@ public class Service {
|
|||||||
public void setSipPeerManager(SipPeerManager sum) {
|
public void setSipPeerManager(SipPeerManager sum) {
|
||||||
sipPeerManager = sum;
|
sipPeerManager = sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getUserId() {
|
||||||
|
String userid = (String) Red5.getConnectionLocal().getAttribute("USERID");
|
||||||
|
if ((userid == null) || ("".equals(userid))) userid = "unknown-userid";
|
||||||
|
return userid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUsername() {
|
||||||
|
String username = (String) Red5.getConnectionLocal().getAttribute("USERNAME");
|
||||||
|
if ((username == null) || ("".equals(username))) username = "UNKNOWN-CALLER";
|
||||||
|
return username;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,9 @@ import java.util.Set;
|
|||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
import org.red5.logging.Red5LoggerFactory;
|
import org.red5.logging.Red5LoggerFactory;
|
||||||
|
import org.red5.server.api.IConnection;
|
||||||
import org.red5.server.api.IScope;
|
import org.red5.server.api.IScope;
|
||||||
|
import org.red5.server.api.Red5;
|
||||||
import org.red5.server.api.event.IEvent;
|
import org.red5.server.api.event.IEvent;
|
||||||
import org.red5.server.api.stream.IBroadcastStream;
|
import org.red5.server.api.stream.IBroadcastStream;
|
||||||
import org.red5.server.api.stream.IStreamCodecInfo;
|
import org.red5.server.api.stream.IStreamCodecInfo;
|
||||||
@ -140,11 +142,11 @@ public class AudioBroadcastStream implements IBroadcastStream, IProvider, IPipeC
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
log.trace("start()");
|
log.debug("Starting AudioBroadcastStream()");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
log.trace("stop");
|
log.debug("Stopping AudioBroadcastStream");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onOOBControlMessage(IMessageComponent source, IPipe pipe, OOBControlMessage oobCtrlMsg) {
|
public void onOOBControlMessage(IMessageComponent source, IPipe pipe, OOBControlMessage oobCtrlMsg) {
|
||||||
@ -183,18 +185,21 @@ public class AudioBroadcastStream implements IBroadcastStream, IProvider, IPipeC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final RTMPMessage msg = new RTMPMessage();
|
||||||
|
|
||||||
public void dispatchEvent(IEvent event) {
|
public void dispatchEvent(IEvent event) {
|
||||||
// log.trace("dispatchEvent(event:{})", event);
|
// log.trace("dispatchEvent(event:{})", event);
|
||||||
if (event instanceof IRTMPEvent) {
|
if (event instanceof IRTMPEvent) {
|
||||||
IRTMPEvent rtmpEvent = (IRTMPEvent) event;
|
IRTMPEvent rtmpEvent = (IRTMPEvent) event;
|
||||||
if (livePipe != null) {
|
if (livePipe != null) {
|
||||||
RTMPMessage msg = new RTMPMessage();
|
|
||||||
msg.setBody(rtmpEvent);
|
msg.setBody(rtmpEvent);
|
||||||
|
|
||||||
if (creationTime == null)
|
if (creationTime == null)
|
||||||
creationTime = (long)rtmpEvent.getTimestamp();
|
creationTime = (long)rtmpEvent.getTimestamp();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// log.debug("dispatchEvent(event:)" + event);
|
// log.debug("dispatchEvent(event:)" + event);
|
||||||
livePipe.pushMessage(msg);
|
livePipe.pushMessage(msg);
|
||||||
|
|
||||||
|
@ -21,8 +21,10 @@ package org.bigbluebutton.voiceconf.red5.media;
|
|||||||
|
|
||||||
public class AudioByteData {
|
public class AudioByteData {
|
||||||
private final byte[] data;
|
private final byte[] data;
|
||||||
|
private boolean poison = false;
|
||||||
|
|
||||||
public AudioByteData(byte[] data) {
|
public AudioByteData(byte[] data, boolean stop) {
|
||||||
|
poison = stop;
|
||||||
this.data = new byte[data.length];
|
this.data = new byte[data.length];
|
||||||
System.arraycopy(data, 0, this.data, 0, data.length);
|
System.arraycopy(data, 0, this.data, 0, data.length);
|
||||||
}
|
}
|
||||||
@ -30,4 +32,8 @@ public class AudioByteData {
|
|||||||
public byte[] getData() {
|
public byte[] getData() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean status() {
|
||||||
|
return poison;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,8 +81,11 @@ public class CallStream implements StreamObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startTalkStream(IBroadcastStream broadcastStream, IScope scope) throws StreamException {
|
public void startTalkStream(IBroadcastStream broadcastStream, IScope scope) throws StreamException {
|
||||||
|
log.debug("Starting userListenSteam");
|
||||||
userListenStream.start();
|
userListenStream.start();
|
||||||
|
log.debug("userTalkStream setup");
|
||||||
userTalkStream.start(broadcastStream, scope);
|
userTalkStream.start(broadcastStream, scope);
|
||||||
|
log.debug("userTalkStream Started");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopTalkStream(IBroadcastStream broadcastStream, IScope scope) {
|
public void stopTalkStream(IBroadcastStream broadcastStream, IScope scope) {
|
||||||
@ -90,6 +93,7 @@ public class CallStream implements StreamObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
|
log.debug("Stopping call stream");
|
||||||
userListenStream.stop();
|
userListenStream.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +19,12 @@
|
|||||||
**/
|
**/
|
||||||
package org.bigbluebutton.voiceconf.red5.media;
|
package org.bigbluebutton.voiceconf.red5.media;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PipedInputStream;
|
||||||
|
import java.io.PipedOutputStream;
|
||||||
import java.net.DatagramSocket;
|
import java.net.DatagramSocket;
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
|
|
||||||
import org.apache.mina.core.buffer.IoBuffer;
|
import org.apache.mina.core.buffer.IoBuffer;
|
||||||
import org.bigbluebutton.voiceconf.red5.media.transcoder.FlashToSipTranscoder;
|
import org.bigbluebutton.voiceconf.red5.media.transcoder.FlashToSipTranscoder;
|
||||||
import org.bigbluebutton.voiceconf.red5.media.transcoder.TranscodedAudioDataListener;
|
import org.bigbluebutton.voiceconf.red5.media.transcoder.TranscodedAudioDataListener;
|
||||||
@ -41,7 +41,9 @@ import org.slf4j.Logger;
|
|||||||
public class FlashToSipAudioStream {
|
public class FlashToSipAudioStream {
|
||||||
private final static Logger log = Red5LoggerFactory.getLogger(FlashToSipAudioStream.class, "sip");
|
private final static Logger log = Red5LoggerFactory.getLogger(FlashToSipAudioStream.class, "sip");
|
||||||
|
|
||||||
private final BlockingQueue<AudioByteData> audioDataQ = new LinkedBlockingQueue<AudioByteData>();
|
private final PipedOutputStream streamFromFlash;
|
||||||
|
private PipedInputStream streamToSip;
|
||||||
|
|
||||||
private final Executor exec = Executors.newSingleThreadExecutor();
|
private final Executor exec = Executors.newSingleThreadExecutor();
|
||||||
private Runnable audioDataProcessor;
|
private Runnable audioDataProcessor;
|
||||||
private volatile boolean processAudioData = false;
|
private volatile boolean processAudioData = false;
|
||||||
@ -58,6 +60,13 @@ public class FlashToSipAudioStream {
|
|||||||
this.srcSocket = srcSocket;
|
this.srcSocket = srcSocket;
|
||||||
this.connInfo = connInfo;
|
this.connInfo = connInfo;
|
||||||
talkStreamName = "microphone_" + System.currentTimeMillis();
|
talkStreamName = "microphone_" + System.currentTimeMillis();
|
||||||
|
streamFromFlash = new PipedOutputStream();
|
||||||
|
try {
|
||||||
|
streamToSip = new PipedInputStream(streamFromFlash);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(IBroadcastStream broadcastStream, IScope scope) throws StreamException {
|
public void start(IBroadcastStream broadcastStream, IScope scope) throws StreamException {
|
||||||
@ -73,16 +82,15 @@ public class FlashToSipAudioStream {
|
|||||||
log.debug("skipping empty packet with no data");
|
log.debug("skipping empty packet with no data");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet instanceof AudioData) {
|
if (packet instanceof AudioData) {
|
||||||
byte[] data = SerializeUtils.ByteBufferToByteArray(buf);
|
byte[] data = SerializeUtils.ByteBufferToByteArray(buf);
|
||||||
AudioByteData abd = new AudioByteData(data);
|
try {
|
||||||
try {
|
streamFromFlash.write(data, 1, data.length-1);
|
||||||
audioDataQ.put(abd);
|
} catch (IOException e) {
|
||||||
} catch (InterruptedException e) {
|
// TODO Auto-generated catch block
|
||||||
// TODO Auto-generated catch block
|
e.printStackTrace();
|
||||||
e.printStackTrace();
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -101,11 +109,23 @@ public class FlashToSipAudioStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processAudioData() {
|
private void processAudioData() {
|
||||||
while (processAudioData) {
|
int len = 64;
|
||||||
|
byte[] nellyAudio = new byte[len];
|
||||||
|
int remaining = len;
|
||||||
|
int offset = 0;
|
||||||
|
TranscodedAudioListener transcodedAudioListener = new TranscodedAudioListener();
|
||||||
|
while (processAudioData) {
|
||||||
try {
|
try {
|
||||||
AudioByteData abd = audioDataQ.take();
|
int bytesRead = streamToSip.read(nellyAudio, offset, remaining);
|
||||||
transcoder.transcode(abd, 1, abd.getData().length-1, new TranscodedAudioListener());
|
remaining -= bytesRead;
|
||||||
} catch (InterruptedException e) {
|
if (remaining == 0) {
|
||||||
|
remaining = len;
|
||||||
|
offset = 0;
|
||||||
|
transcoder.transcode(nellyAudio, 0, nellyAudio.length, transcodedAudioListener);
|
||||||
|
} else {
|
||||||
|
offset += bytesRead;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -114,7 +134,12 @@ public class FlashToSipAudioStream {
|
|||||||
|
|
||||||
public void stop(IBroadcastStream broadcastStream, IScope scope) {
|
public void stop(IBroadcastStream broadcastStream, IScope scope) {
|
||||||
broadcastStream.removeStreamListener(mInputListener);
|
broadcastStream.removeStreamListener(mInputListener);
|
||||||
processAudioData = false;
|
if (broadcastStream != null) {
|
||||||
|
broadcastStream.stop();
|
||||||
|
broadcastStream.close();
|
||||||
|
}
|
||||||
|
processAudioData = false;
|
||||||
|
srcSocket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStreamName() {
|
public String getStreamName() {
|
||||||
@ -124,7 +149,7 @@ public class FlashToSipAudioStream {
|
|||||||
private class TranscodedAudioListener implements TranscodedAudioDataListener {
|
private class TranscodedAudioListener implements TranscodedAudioDataListener {
|
||||||
@Override
|
@Override
|
||||||
public void handleTranscodedAudioData(byte[] audioData, long timestamp) {
|
public void handleTranscodedAudioData(byte[] audioData, long timestamp) {
|
||||||
if (audioData != null) {
|
if (audioData != null && processAudioData) {
|
||||||
rtpSender.sendAudio(audioData, transcoder.getCodecId(), timestamp);
|
rtpSender.sendAudio(audioData, transcoder.getCodecId(), timestamp);
|
||||||
} else {
|
} else {
|
||||||
log.warn("Transcodec audio is null. Discarding.");
|
log.warn("Transcodec audio is null. Discarding.");
|
||||||
|
@ -84,14 +84,11 @@ public class RtpStreamReceiver {
|
|||||||
public void receiveRtpPackets() {
|
public void receiveRtpPackets() {
|
||||||
int packetReceivedCounter = 0;
|
int packetReceivedCounter = 0;
|
||||||
int internalBufferLength = payloadLength + RTP_HEADER_SIZE;
|
int internalBufferLength = payloadLength + RTP_HEADER_SIZE;
|
||||||
byte[] internalBuffer;
|
byte[] internalBuffer = new byte[internalBufferLength];
|
||||||
RtpPacket rtpPacket;
|
RtpPacket rtpPacket = new RtpPacket(internalBuffer, internalBufferLength);;
|
||||||
|
|
||||||
while (receivePackets) {
|
while (receivePackets) {
|
||||||
try {
|
try {
|
||||||
internalBuffer = new byte[internalBufferLength];
|
|
||||||
rtpPacket = new RtpPacket(internalBuffer, internalBufferLength);
|
|
||||||
|
|
||||||
rtpSocket.receive(rtpPacket);
|
rtpSocket.receive(rtpPacket);
|
||||||
packetReceivedCounter++;
|
packetReceivedCounter++;
|
||||||
// log.debug("Received packet [" + rtpPacket.getRtcpPayloadType() + "," + rtpPacket.getPayloadType() + ", length=" + rtpPacket.getPayloadLength() + "] seqNum[rtpSeqNum=" + rtpPacket.getSeqNum() + ",lastSeqNum=" + lastSequenceNumber
|
// log.debug("Received packet [" + rtpPacket.getRtcpPayloadType() + "," + rtpPacket.getPayloadType() + ", length=" + rtpPacket.getPayloadLength() + "] seqNum[rtpSeqNum=" + rtpPacket.getSeqNum() + ",lastSeqNum=" + lastSequenceNumber
|
||||||
@ -112,7 +109,9 @@ public class RtpStreamReceiver {
|
|||||||
if (shouldHandlePacket(rtpPacket)) {
|
if (shouldHandlePacket(rtpPacket)) {
|
||||||
// log.debug("Handling packet [" + rtpPacket.getRtcpPayloadType() + "," + rtpPacket.getPayloadType() + ", length=" + rtpPacket.getPayloadLength() + "] seqNum[rtpSeqNum=" + rtpPacket.getSeqNum() + ",lastSeqNum=" + lastSequenceNumber
|
// log.debug("Handling packet [" + rtpPacket.getRtcpPayloadType() + "," + rtpPacket.getPayloadType() + ", length=" + rtpPacket.getPayloadLength() + "] seqNum[rtpSeqNum=" + rtpPacket.getSeqNum() + ",lastSeqNum=" + lastSequenceNumber
|
||||||
// + "][rtpTS=" + rtpPacket.getTimestamp() + ",lastTS=" + lastPacketTimestamp + "][port=" + rtpSocket.getDatagramSocket().getLocalPort() + "]");
|
// + "][rtpTS=" + rtpPacket.getTimestamp() + ",lastTS=" + lastPacketTimestamp + "][port=" + rtpSocket.getDatagramSocket().getLocalPort() + "]");
|
||||||
processRtpPacket(rtpPacket);
|
lastSequenceNumber = rtpPacket.getSeqNum();
|
||||||
|
lastPacketTimestamp = rtpPacket.getTimestamp();
|
||||||
|
processRtpPacket(internalBuffer, RTP_HEADER_SIZE, payloadLength);
|
||||||
} else {
|
} else {
|
||||||
if (log.isDebugEnabled())
|
if (log.isDebugEnabled())
|
||||||
log.debug("Corrupt packet [" + rtpPacket.getRtcpPayloadType() + "," + rtpPacket.getPayloadType() + ", length=" + rtpPacket.getPayloadLength() + "] seqNum[rtpSeqNum=" + rtpPacket.getSeqNum() + ",lastSeqNum=" + lastSequenceNumber
|
log.debug("Corrupt packet [" + rtpPacket.getRtcpPayloadType() + "," + rtpPacket.getPayloadType() + ", length=" + rtpPacket.getPayloadLength() + "] seqNum[rtpSeqNum=" + rtpPacket.getSeqNum() + ",lastSeqNum=" + lastSequenceNumber
|
||||||
@ -132,7 +131,7 @@ public class RtpStreamReceiver {
|
|||||||
|
|
||||||
private boolean shouldDropDelayedPacket(RtpPacket rtpPacket) {
|
private boolean shouldDropDelayedPacket(RtpPacket rtpPacket) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
if (now - lastPacketReceived > 100) {
|
if (now - lastPacketReceived > 200) {
|
||||||
if (log.isDebugEnabled())
|
if (log.isDebugEnabled())
|
||||||
log.debug("Delayed packet [" + rtpPacket.getRtcpPayloadType() + "," + rtpPacket.getPayloadType() + ", length=" + rtpPacket.getPayloadLength() + "] seqNum[rtpSeqNum=" + rtpPacket.getSeqNum() + ",lastSeqNum=" + lastSequenceNumber
|
log.debug("Delayed packet [" + rtpPacket.getRtcpPayloadType() + "," + rtpPacket.getPayloadType() + ", length=" + rtpPacket.getPayloadLength() + "] seqNum[rtpSeqNum=" + rtpPacket.getSeqNum() + ",lastSeqNum=" + lastSequenceNumber
|
||||||
+ "][rtpTS=" + rtpPacket.getTimestamp() + ",lastTS=" + lastPacketTimestamp + "][port=" + rtpSocket.getDatagramSocket().getLocalPort() + "]");
|
+ "][rtpTS=" + rtpPacket.getTimestamp() + ",lastTS=" + lastPacketTimestamp + "][port=" + rtpSocket.getDatagramSocket().getLocalPort() + "]");
|
||||||
@ -217,11 +216,8 @@ public class RtpStreamReceiver {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processRtpPacket(RtpPacket rtpPacket) {
|
private void processRtpPacket(byte[] rtpAudio, int offset, int len) {
|
||||||
lastSequenceNumber = rtpPacket.getSeqNum();
|
if (listener != null) listener.onAudioDataReceived(rtpAudio, offset, len);
|
||||||
lastPacketTimestamp = rtpPacket.getTimestamp();
|
|
||||||
AudioByteData audioData = new AudioByteData(rtpPacket.getPayload());
|
|
||||||
if (listener != null) listener.onAudioDataReceived(audioData);
|
|
||||||
else log.debug("No listener for incoming audio packet");
|
else log.debug("No listener for incoming audio packet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,5 +22,5 @@ package org.bigbluebutton.voiceconf.red5.media;
|
|||||||
public interface RtpStreamReceiverListener {
|
public interface RtpStreamReceiverListener {
|
||||||
|
|
||||||
void onStoppedReceiving();
|
void onStoppedReceiving();
|
||||||
void onAudioDataReceived(AudioByteData audioData);
|
void onAudioDataReceived(byte[] audioData, int offset, int len);
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ public class RtpStreamSender {
|
|||||||
private final DatagramSocket srcSocket;
|
private final DatagramSocket srcSocket;
|
||||||
private final SipConnectInfo connInfo;
|
private final SipConnectInfo connInfo;
|
||||||
private boolean marked = false;
|
private boolean marked = false;
|
||||||
|
private long startTimestamp;
|
||||||
|
|
||||||
public RtpStreamSender(DatagramSocket srcSocket, SipConnectInfo connInfo) {
|
public RtpStreamSender(DatagramSocket srcSocket, SipConnectInfo connInfo) {
|
||||||
this.srcSocket = srcSocket;
|
this.srcSocket = srcSocket;
|
||||||
@ -66,6 +67,7 @@ public class RtpStreamSender {
|
|||||||
if (!marked) {
|
if (!marked) {
|
||||||
rtpPacket.setMarker(true);
|
rtpPacket.setMarker(true);
|
||||||
marked = true;
|
marked = true;
|
||||||
|
startTimestamp = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
rtpPacket.setPadding(false);
|
rtpPacket.setPadding(false);
|
||||||
rtpPacket.setExtension(false);
|
rtpPacket.setExtension(false);
|
||||||
|
@ -19,11 +19,12 @@
|
|||||||
**/
|
**/
|
||||||
package org.bigbluebutton.voiceconf.red5.media;
|
package org.bigbluebutton.voiceconf.red5.media;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PipedInputStream;
|
||||||
|
import java.io.PipedOutputStream;
|
||||||
import java.net.DatagramSocket;
|
import java.net.DatagramSocket;
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import org.apache.mina.core.buffer.IoBuffer;
|
import org.apache.mina.core.buffer.IoBuffer;
|
||||||
import org.bigbluebutton.voiceconf.red5.media.transcoder.SipToFlashTranscoder;
|
import org.bigbluebutton.voiceconf.red5.media.transcoder.SipToFlashTranscoder;
|
||||||
import org.bigbluebutton.voiceconf.red5.media.transcoder.TranscodedAudioDataListener;
|
import org.bigbluebutton.voiceconf.red5.media.transcoder.TranscodedAudioDataListener;
|
||||||
@ -32,6 +33,7 @@ import org.red5.server.api.IContext;
|
|||||||
import org.red5.server.api.IScope;
|
import org.red5.server.api.IScope;
|
||||||
import org.red5.server.net.rtmp.event.AudioData;
|
import org.red5.server.net.rtmp.event.AudioData;
|
||||||
import org.red5.server.net.rtmp.event.Notify;
|
import org.red5.server.net.rtmp.event.Notify;
|
||||||
|
import org.red5.server.net.rtmp.message.Constants;
|
||||||
import org.red5.server.stream.BroadcastScope;
|
import org.red5.server.stream.BroadcastScope;
|
||||||
import org.red5.server.stream.IBroadcastScope;
|
import org.red5.server.stream.IBroadcastScope;
|
||||||
import org.red5.server.stream.IProviderService;
|
import org.red5.server.stream.IProviderService;
|
||||||
@ -40,7 +42,9 @@ import org.slf4j.Logger;
|
|||||||
public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpStreamReceiverListener {
|
public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpStreamReceiverListener {
|
||||||
final private Logger log = Red5LoggerFactory.getLogger(SipToFlashAudioStream.class, "sip");
|
final private Logger log = Red5LoggerFactory.getLogger(SipToFlashAudioStream.class, "sip");
|
||||||
|
|
||||||
private final BlockingQueue<AudioByteData> audioDataQ = new LinkedBlockingQueue<AudioByteData>();
|
private final PipedOutputStream streamFromSip;
|
||||||
|
private PipedInputStream streamToFlash;
|
||||||
|
|
||||||
private final Executor exec = Executors.newSingleThreadExecutor();
|
private final Executor exec = Executors.newSingleThreadExecutor();
|
||||||
private Runnable audioDataProcessor;
|
private Runnable audioDataProcessor;
|
||||||
private volatile boolean processAudioData = false;
|
private volatile boolean processAudioData = false;
|
||||||
@ -51,9 +55,9 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
|
|||||||
private RtpStreamReceiver rtpStreamReceiver;
|
private RtpStreamReceiver rtpStreamReceiver;
|
||||||
private StreamObserver observer;
|
private StreamObserver observer;
|
||||||
private SipToFlashTranscoder transcoder;
|
private SipToFlashTranscoder transcoder;
|
||||||
|
|
||||||
private long startTimestamp = 0;
|
|
||||||
private boolean sentMetadata = false;
|
private boolean sentMetadata = false;
|
||||||
|
private IoBuffer mBuffer;
|
||||||
|
private AudioData audioData;
|
||||||
|
|
||||||
private final byte[] fakeMetadata = new byte[] {
|
private final byte[] fakeMetadata = new byte[] {
|
||||||
0x02, 0x00, 0x0a, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x08, 0x00, 0x00,
|
0x02, 0x00, 0x0a, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x08, 0x00, 0x00,
|
||||||
@ -74,6 +78,17 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
|
|||||||
rtpStreamReceiver.setRtpStreamReceiverListener(this);
|
rtpStreamReceiver.setRtpStreamReceiverListener(this);
|
||||||
listenStreamName = "speaker_" + System.currentTimeMillis();
|
listenStreamName = "speaker_" + System.currentTimeMillis();
|
||||||
scope.setName(listenStreamName);
|
scope.setName(listenStreamName);
|
||||||
|
streamFromSip = new PipedOutputStream();
|
||||||
|
try {
|
||||||
|
streamToFlash = new PipedInputStream(streamFromSip);
|
||||||
|
startNow();
|
||||||
|
mBuffer = IoBuffer.allocate(1024);
|
||||||
|
mBuffer = mBuffer.setAutoExpand(true);
|
||||||
|
audioData = new AudioData();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStreamName() {
|
public String getStreamName() {
|
||||||
@ -85,14 +100,25 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
|
log.debug("Stopping stream for {}", listenStreamName);
|
||||||
processAudioData = false;
|
processAudioData = false;
|
||||||
rtpStreamReceiver.stop();
|
rtpStreamReceiver.stop();
|
||||||
audioBroadcastStream.stop();
|
log.debug("Stopped RTP Stream Receiver for {}", listenStreamName);
|
||||||
audioBroadcastStream.close();
|
if (audioBroadcastStream != null) {
|
||||||
log.debug("stopping and closing stream {}", listenStreamName);
|
audioBroadcastStream.stop();
|
||||||
|
log.debug("Stopped audioBroadcastStream for {}", listenStreamName);
|
||||||
|
audioBroadcastStream.close();
|
||||||
|
log.debug("Closed audioBroadcastStream for {}", listenStreamName);
|
||||||
|
} else
|
||||||
|
log.debug("audioBroadcastStream is null, couldn't stop");
|
||||||
|
log.debug("Stream(s) stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startNow() {
|
||||||
log.debug("started publishing stream in " + scope.getName());
|
log.debug("started publishing stream in " + scope.getName());
|
||||||
audioBroadcastStream = new AudioBroadcastStream(listenStreamName);
|
audioBroadcastStream = new AudioBroadcastStream(listenStreamName);
|
||||||
audioBroadcastStream.setPublishedName(listenStreamName);
|
audioBroadcastStream.setPublishedName(listenStreamName);
|
||||||
@ -111,8 +137,7 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
|
|||||||
|
|
||||||
audioBroadcastStream.start();
|
audioBroadcastStream.start();
|
||||||
processAudioData = true;
|
processAudioData = true;
|
||||||
startTimestamp = System.currentTimeMillis();
|
|
||||||
|
|
||||||
audioDataProcessor = new Runnable() {
|
audioDataProcessor = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
processAudioData();
|
processAudioData();
|
||||||
@ -124,11 +149,23 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processAudioData() {
|
private void processAudioData() {
|
||||||
|
int len = 160;
|
||||||
|
byte[] pcmAudio = new byte[len];
|
||||||
|
int remaining = len;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
while (processAudioData) {
|
while (processAudioData) {
|
||||||
try {
|
try {
|
||||||
AudioByteData abd = audioDataQ.take();
|
int bytesRead = streamToFlash.read(pcmAudio, offset, remaining);
|
||||||
transcoder.transcode(abd, this);
|
remaining -= bytesRead;
|
||||||
} catch (InterruptedException e) {
|
if (remaining == 0) {
|
||||||
|
remaining = len;
|
||||||
|
offset = 0;
|
||||||
|
transcoder.transcode(pcmAudio, this);
|
||||||
|
} else {
|
||||||
|
offset += bytesRead;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -141,10 +178,10 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAudioDataReceived(AudioByteData audioData) {
|
public void onAudioDataReceived(byte[] audioData, int offset, int len) {
|
||||||
try {
|
try {
|
||||||
audioDataQ.put(audioData);
|
streamFromSip.write(audioData, offset, len);
|
||||||
} catch (InterruptedException e) {
|
} catch (IOException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -166,39 +203,34 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
|
|||||||
* We create a fake one here to get it going. Red5 should do this automatically
|
* We create a fake one here to get it going. Red5 should do this automatically
|
||||||
* but for Red5 0.91, doesn't yet. (ralam Sept 24, 2010).
|
* but for Red5 0.91, doesn't yet. (ralam Sept 24, 2010).
|
||||||
*/
|
*/
|
||||||
IoBuffer mBuffer = IoBuffer.allocate(1024);
|
|
||||||
mBuffer.setAutoExpand(true);
|
|
||||||
|
|
||||||
mBuffer.clear();
|
mBuffer.clear();
|
||||||
mBuffer.put(fakeMetadata);
|
mBuffer.put(fakeMetadata);
|
||||||
mBuffer.flip();
|
mBuffer.flip();
|
||||||
|
|
||||||
Notify notifyData = new Notify(mBuffer);
|
Notify notifyData = new Notify(mBuffer);
|
||||||
notifyData.setTimestamp((int)timestamp );
|
notifyData.setTimestamp((int)timestamp);
|
||||||
|
notifyData.setSourceType(Constants.SOURCE_TYPE_LIVE);
|
||||||
audioBroadcastStream.dispatchEvent(notifyData);
|
audioBroadcastStream.dispatchEvent(notifyData);
|
||||||
notifyData.release();
|
notifyData.release();
|
||||||
sentMetadata = true;
|
sentMetadata = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushAudio(byte[] audio, long timestamp) {
|
private void pushAudio(byte[] audio, long timestamp) {
|
||||||
|
|
||||||
sendFakeMetadata(timestamp);
|
sendFakeMetadata(timestamp);
|
||||||
|
mBuffer.clear();
|
||||||
IoBuffer buffer = IoBuffer.allocate(1024);
|
mBuffer.put((byte) transcoder.getCodecId());
|
||||||
buffer.setAutoExpand(true);
|
mBuffer.put(audio);
|
||||||
|
mBuffer.flip();
|
||||||
buffer.clear();
|
audioData.setSourceType(Constants.SOURCE_TYPE_LIVE);
|
||||||
|
/*
|
||||||
buffer.put((byte) transcoder.getCodecId());
|
* Use timestamp increments passed in by codecs (i.e. 32 for nelly). This will force
|
||||||
byte[] copy = new byte[audio.length];
|
* Flash Player to playback audio at proper timestamp. If we calculate timestamp using
|
||||||
System.arraycopy(audio, 0, copy, 0, audio.length );
|
* System.currentTimeMillis() - startTimestamp, the audio has tendency to drift and
|
||||||
|
* introduce delay. (ralam dec 14, 2010)
|
||||||
buffer.put(copy);
|
*/
|
||||||
buffer.flip();
|
audioData.setTimestamp((int)(timestamp));
|
||||||
|
audioData.setData(mBuffer);
|
||||||
AudioData audioData = new AudioData(buffer);
|
|
||||||
audioData.setTimestamp((int)timestamp );
|
|
||||||
audioBroadcastStream.dispatchEvent(audioData);
|
audioBroadcastStream.dispatchEvent(audioData);
|
||||||
audioData.release();
|
audioData.release();
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,8 @@ public class RtpSocket {
|
|||||||
/** Remote port */
|
/** Remote port */
|
||||||
int r_port;
|
int r_port;
|
||||||
|
|
||||||
|
private final byte[] payload = new byte[10];
|
||||||
|
|
||||||
/** Creates a new RTP socket (only receiver) */
|
/** Creates a new RTP socket (only receiver) */
|
||||||
public RtpSocket(DatagramSocket datagram_socket) {
|
public RtpSocket(DatagramSocket datagram_socket) {
|
||||||
socket=datagram_socket;
|
socket=datagram_socket;
|
||||||
@ -59,19 +61,24 @@ public class RtpSocket {
|
|||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final DatagramPacket rxDatagram = new DatagramPacket(payload, payload.length);
|
||||||
|
|
||||||
/** Receives a RTP packet from this socket */
|
/** Receives a RTP packet from this socket */
|
||||||
public void receive(RtpPacket rtpp) throws IOException {
|
public void receive(RtpPacket rtpp) throws IOException {
|
||||||
DatagramPacket datagram = new DatagramPacket(rtpp.getPacket(), rtpp.getLength());
|
rxDatagram.setData(rtpp.getPacket());
|
||||||
socket.receive(datagram);
|
socket.receive(rxDatagram);
|
||||||
rtpp.setPacketLength(datagram.getLength());
|
rtpp.setPacketLength(rxDatagram.getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final DatagramPacket txDatagram = new DatagramPacket(payload, payload.length);
|
||||||
|
|
||||||
/** Sends a RTP packet from this socket */
|
/** Sends a RTP packet from this socket */
|
||||||
public void send(RtpPacket rtpp) throws IOException {
|
public void send(RtpPacket rtpp) throws IOException {
|
||||||
DatagramPacket datagram = new DatagramPacket(rtpp.getPacket(), rtpp.getLength());
|
txDatagram.setData(rtpp.getPacket());
|
||||||
datagram.setAddress(r_addr);
|
txDatagram.setAddress(r_addr);
|
||||||
datagram.setPort(r_port);
|
txDatagram.setPort(r_port);
|
||||||
socket.send(datagram);
|
if (!socket.isClosed())
|
||||||
|
socket.send(txDatagram);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Closes this socket */
|
/** Closes this socket */
|
||||||
|
@ -19,10 +19,8 @@
|
|||||||
**/
|
**/
|
||||||
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
||||||
|
|
||||||
import org.bigbluebutton.voiceconf.red5.media.AudioByteData;
|
|
||||||
|
|
||||||
public interface FlashToSipTranscoder {
|
public interface FlashToSipTranscoder {
|
||||||
void transcode(AudioByteData audioData, int startOffset, int length, TranscodedAudioDataListener listener);
|
void transcode(byte[] audioData, int startOffset, int length, TranscodedAudioDataListener listener);
|
||||||
|
|
||||||
int getOutgoingEncodedFrameSize();
|
int getOutgoingEncodedFrameSize();
|
||||||
int getCodecId();
|
int getCodecId();
|
||||||
|
@ -19,13 +19,12 @@
|
|||||||
**/
|
**/
|
||||||
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
||||||
|
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.bigbluebutton.voiceconf.red5.media.AudioByteData;
|
|
||||||
import org.red5.logging.Red5LoggerFactory;
|
import org.red5.logging.Red5LoggerFactory;
|
||||||
import org.red5.app.sip.codecs.Codec;
|
import org.red5.app.sip.codecs.Codec;
|
||||||
import org.red5.app.sip.codecs.asao.ByteStream;
|
|
||||||
import org.red5.app.sip.codecs.asao.Decoder;
|
import org.red5.app.sip.codecs.asao.Decoder;
|
||||||
import org.red5.app.sip.codecs.asao.DecoderMap;
|
import org.red5.app.sip.codecs.asao.DecoderMap;
|
||||||
|
|
||||||
@ -38,28 +37,48 @@ import org.red5.app.sip.codecs.asao.DecoderMap;
|
|||||||
public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
||||||
protected static Logger log = Red5LoggerFactory.getLogger( NellyFlashToSipTranscoderImp.class, "sip" );
|
protected static Logger log = Red5LoggerFactory.getLogger( NellyFlashToSipTranscoderImp.class, "sip" );
|
||||||
|
|
||||||
private static final int NELLYMOSER_DECODED_PACKET_SIZE = 256;
|
private static final int NELLY_TO_L16_AUDIO_SIZE = 256;
|
||||||
private static final int NELLYMOSER_ENCODED_PACKET_SIZE = 64;
|
private static final int NELLY_AUDIO_LENGTH = 64;
|
||||||
|
private static final int ULAW_AUDIO_LENGTH = 160;
|
||||||
|
|
||||||
private Codec sipCodec = null; // Sip codec to be used on audio session
|
/**
|
||||||
|
* Max buffer length when 5 Nelly/L16 packets equals 8 Ulaw packets.
|
||||||
|
*/
|
||||||
|
private static final int MAX_BUFFER_LENGTH = 1280;
|
||||||
|
/**
|
||||||
|
* Allocate a fixed buffer length so we don't have to copy elements around. We'll use the
|
||||||
|
* position, limit, mart attributes of the NIO Buffer.
|
||||||
|
*/
|
||||||
|
private final FloatBuffer l16Audio = FloatBuffer.allocate(MAX_BUFFER_LENGTH);
|
||||||
|
/**
|
||||||
|
* This is a view read-only copy of the buffer to track which byte are being transcoded from L16->Ulaw
|
||||||
|
*/
|
||||||
|
private FloatBuffer viewBuffer;
|
||||||
|
|
||||||
|
private Codec sipCodec = null;
|
||||||
private Decoder decoder;
|
private Decoder decoder;
|
||||||
private DecoderMap decoderMap;
|
private DecoderMap decoderMap;
|
||||||
private float[] tempBuffer; // Temporary buffer with received PCM audio from FlashPlayer.
|
private float[] tempL16Buffer = new float[NELLY_TO_L16_AUDIO_SIZE];
|
||||||
private int tempBufferRemaining = 0; // Floats remaining on temporary buffer.
|
private float[] tempUlawBuffer = new float[ULAW_AUDIO_LENGTH];
|
||||||
private float[] encodingBuffer; // Encoding buffer used to encode to final codec format;
|
private byte[] ulawEncodedBuffer = new byte[ULAW_AUDIO_LENGTH];
|
||||||
private int encodingOffset = 0; // Offset of encoding buffer.
|
|
||||||
private boolean asao_buffer_processed = false; // Indicates whether the current asao buffer was processed.
|
|
||||||
private boolean hasInitilializedBuffers = false; // Indicates whether the handling buffers have already been initialized.
|
|
||||||
|
|
||||||
private long timestamp = 0;
|
private long timestamp = 0;
|
||||||
private final static int TS_INCREMENT = 180; // Determined from PCAP traces.
|
private final static int TS_INCREMENT = 180; // Determined from PCAP traces.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The transcode process works by taking a 64-byte-array Nelly audio and converting it into a 256-float-array L16 audio. From the
|
||||||
|
* 256-float-array L16 audio, we take 160-float-array and convert it to a 160-byte-array Ulaw audio. The remaining 96-float-array
|
||||||
|
* will be used in the next iteration.
|
||||||
|
* Therefore, 5 Nelly/L16 packets (5x256 = 1280) will result into 8 Ulaw packets (8x160 = 1280).
|
||||||
|
*
|
||||||
|
*/
|
||||||
public NellyFlashToSipTranscoderImp(Codec sipCodec) {
|
public NellyFlashToSipTranscoderImp(Codec sipCodec) {
|
||||||
this.sipCodec = sipCodec;
|
this.sipCodec = sipCodec;
|
||||||
decoder = new Decoder();
|
decoder = new Decoder();
|
||||||
decoderMap = null;
|
decoderMap = null;
|
||||||
Random rgen = new Random();
|
Random rgen = new Random();
|
||||||
timestamp = rgen.nextInt(1000);
|
timestamp = rgen.nextInt(1000);
|
||||||
|
viewBuffer = l16Audio.asReadOnlyBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -73,91 +92,65 @@ public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transcode(AudioByteData audioData, int startOffset, int length, TranscodedAudioDataListener listener) {
|
public void transcode(byte[] audioData, int startOffset, int length, TranscodedAudioDataListener listener) {
|
||||||
byte[] codedBuffer = new byte[length];
|
if (audioData.length != NELLY_AUDIO_LENGTH) {
|
||||||
System.arraycopy(audioData.getData(), startOffset, codedBuffer, 0, length);
|
log.warn("Receiving bad nelly audio. Expecting {}, got {}.", NELLY_AUDIO_LENGTH, audioData.length);
|
||||||
byte[] transcodedAudioData = new byte[sipCodec.getOutgoingEncodedFrameSize()];
|
return;
|
||||||
|
}
|
||||||
asao_buffer_processed = false;
|
|
||||||
|
// Convert the Nelly audio to L16.
|
||||||
if (!hasInitilializedBuffers) {
|
decoderMap = decoder.decode(decoderMap, audioData, 0, tempL16Buffer, 0);
|
||||||
tempBuffer = new float[NELLYMOSER_DECODED_PACKET_SIZE];
|
|
||||||
encodingBuffer = new float[sipCodec.getOutgoingDecodedFrameSize()];
|
// Store the L16 audio into the buffer
|
||||||
hasInitilializedBuffers = true;
|
l16Audio.put(tempL16Buffer);
|
||||||
}
|
|
||||||
|
// Read 160-float worth of audio
|
||||||
if (length > 0) {
|
viewBuffer.get(tempUlawBuffer);
|
||||||
do {
|
|
||||||
int encodedBytes = fillRtpPacketBuffer(codedBuffer, transcodedAudioData);
|
// Convert the L16 audio to Ulaw
|
||||||
if (encodedBytes == 0) {
|
int encodedBytes = sipCodec.pcmToCodec(tempUlawBuffer, ulawEncodedBuffer);
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (encodingOffset == sipCodec.getOutgoingDecodedFrameSize()) {
|
|
||||||
encodingOffset = 0;
|
|
||||||
listener.handleTranscodedAudioData(transcodedAudioData, timestamp += TS_INCREMENT);
|
|
||||||
}
|
|
||||||
} while (!asao_buffer_processed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int fillRtpPacketBuffer(byte[] audioData, byte[] transcodedAudioData) {
|
|
||||||
int copyingSize = 0;
|
|
||||||
int finalCopySize = 0;
|
|
||||||
byte[] codedBuffer = new byte[sipCodec.getOutgoingEncodedFrameSize()];
|
|
||||||
|
|
||||||
if ((tempBufferRemaining + encodingOffset) >= sipCodec.getOutgoingDecodedFrameSize()) {
|
|
||||||
copyingSize = encodingBuffer.length - encodingOffset;
|
|
||||||
|
|
||||||
System.arraycopy(tempBuffer, tempBuffer.length-tempBufferRemaining, encodingBuffer, encodingOffset, copyingSize);
|
|
||||||
|
|
||||||
encodingOffset = sipCodec.getOutgoingDecodedFrameSize();
|
|
||||||
tempBufferRemaining -= copyingSize;
|
|
||||||
finalCopySize = sipCodec.getOutgoingDecodedFrameSize();
|
|
||||||
} else {
|
|
||||||
if (tempBufferRemaining > 0) {
|
|
||||||
System.arraycopy(tempBuffer, tempBuffer.length - tempBufferRemaining, encodingBuffer, encodingOffset, tempBufferRemaining);
|
|
||||||
|
|
||||||
encodingOffset += tempBufferRemaining;
|
|
||||||
finalCopySize += tempBufferRemaining;
|
|
||||||
tempBufferRemaining = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode new asao packet.
|
|
||||||
asao_buffer_processed = true;
|
|
||||||
ByteStream audioStream = new ByteStream(audioData, 0, NELLYMOSER_ENCODED_PACKET_SIZE);
|
|
||||||
decoderMap = decoder.decode(decoderMap, audioStream.bytes, 0, tempBuffer, 0);
|
|
||||||
|
|
||||||
tempBufferRemaining = tempBuffer.length;
|
|
||||||
|
|
||||||
if (tempBuffer.length <= 0) {
|
|
||||||
log.error("Asao decoder Error." );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to complete the encodingBuffer with necessary data.
|
|
||||||
if ((encodingOffset + tempBufferRemaining) > sipCodec.getOutgoingDecodedFrameSize()) {
|
|
||||||
copyingSize = encodingBuffer.length - encodingOffset;
|
|
||||||
} else {
|
|
||||||
copyingSize = tempBufferRemaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.arraycopy(tempBuffer, 0, encodingBuffer, encodingOffset, copyingSize);
|
|
||||||
|
|
||||||
encodingOffset += copyingSize;
|
|
||||||
tempBufferRemaining -= copyingSize;
|
|
||||||
finalCopySize += copyingSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (encodingOffset == encodingBuffer.length) {
|
|
||||||
int encodedBytes = sipCodec.pcmToCodec(encodingBuffer, codedBuffer);
|
|
||||||
|
|
||||||
|
// Send it to the server
|
||||||
|
listener.handleTranscodedAudioData(ulawEncodedBuffer, timestamp += TS_INCREMENT);
|
||||||
|
|
||||||
|
if (l16Audio.position() == l16Audio.capacity()) {
|
||||||
|
/**
|
||||||
|
* This means we already processed 5 Nelly packets and sent 5 Ulaw packets.
|
||||||
|
* However, we have 3 extra Ulaw packets.
|
||||||
|
* Fire them off to the server. We don't want to discard them as it will
|
||||||
|
* result in choppy audio.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Get the 6th packet and send
|
||||||
|
viewBuffer.get(tempUlawBuffer);
|
||||||
|
encodedBytes = sipCodec.pcmToCodec(tempUlawBuffer, ulawEncodedBuffer);
|
||||||
if (encodedBytes == sipCodec.getOutgoingEncodedFrameSize()) {
|
if (encodedBytes == sipCodec.getOutgoingEncodedFrameSize()) {
|
||||||
System.arraycopy(codedBuffer, 0, transcodedAudioData, 0, codedBuffer.length);
|
listener.handleTranscodedAudioData(ulawEncodedBuffer, timestamp += TS_INCREMENT);
|
||||||
|
} else {
|
||||||
|
log.error("Failure encoding buffer." );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the 7th packet and send
|
||||||
|
viewBuffer.get(tempUlawBuffer);
|
||||||
|
encodedBytes = sipCodec.pcmToCodec(tempUlawBuffer, ulawEncodedBuffer);
|
||||||
|
if (encodedBytes == sipCodec.getOutgoingEncodedFrameSize()) {
|
||||||
|
listener.handleTranscodedAudioData(ulawEncodedBuffer, timestamp += TS_INCREMENT);
|
||||||
} else {
|
} else {
|
||||||
log.error("Failure encoding buffer." );
|
log.error("Failure encoding buffer." );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return finalCopySize;
|
// Get the 8th packet and send
|
||||||
}
|
viewBuffer.get(tempUlawBuffer);
|
||||||
|
encodedBytes = sipCodec.pcmToCodec(tempUlawBuffer, ulawEncodedBuffer);
|
||||||
|
if (encodedBytes == sipCodec.getOutgoingEncodedFrameSize()) {
|
||||||
|
listener.handleTranscodedAudioData(ulawEncodedBuffer, timestamp += TS_INCREMENT);
|
||||||
|
} else {
|
||||||
|
log.error("Failure encoding buffer." );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the buffer's position back to zero and start over.
|
||||||
|
l16Audio.clear();
|
||||||
|
viewBuffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,88 +19,115 @@
|
|||||||
**/
|
**/
|
||||||
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.nio.FloatBuffer;
|
||||||
import java.io.PipedInputStream;
|
|
||||||
import java.io.PipedOutputStream;
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.bigbluebutton.voiceconf.red5.media.AudioByteData;
|
|
||||||
import org.bigbluebutton.voiceconf.util.StackTraceUtil;
|
|
||||||
import org.red5.logging.Red5LoggerFactory;
|
import org.red5.logging.Red5LoggerFactory;
|
||||||
|
import org.red5.server.api.IConnection;
|
||||||
|
import org.red5.server.api.Red5;
|
||||||
|
import org.red5.server.net.rtmp.RTMPMinaConnection;
|
||||||
import org.red5.app.sip.codecs.Codec;
|
import org.red5.app.sip.codecs.Codec;
|
||||||
import org.red5.app.sip.codecs.asao.ByteStream;
|
|
||||||
import org.red5.app.sip.codecs.asao.CodecImpl;
|
import org.red5.app.sip.codecs.asao.CodecImpl;
|
||||||
|
|
||||||
public class NellySipToFlashTranscoderImp implements SipToFlashTranscoder {
|
public class NellySipToFlashTranscoderImp implements SipToFlashTranscoder {
|
||||||
protected static Logger log = Red5LoggerFactory.getLogger(NellySipToFlashTranscoderImp.class, "sip");
|
protected static Logger log = Red5LoggerFactory.getLogger(NellySipToFlashTranscoderImp.class, "sip");
|
||||||
|
|
||||||
private static final int NELLYMOSER_DECODED_PACKET_SIZE = 256;
|
|
||||||
private static final int NELLYMOSER_ENCODED_PACKET_SIZE = 64;
|
|
||||||
private static final int NELLYMOSER_CODEC_ID = 82;
|
private static final int NELLYMOSER_CODEC_ID = 82;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of resulting L16 audio converted from 160-byte Ulaw audio.
|
||||||
|
*/
|
||||||
|
private static final int L16_AUDIO_LENGTH = 256;
|
||||||
|
/**
|
||||||
|
* The length of Nelly audio that gets sent to Flash Player.
|
||||||
|
*/
|
||||||
|
private static final int NELLY_AUDIO_LENGTH = 64;
|
||||||
|
/**
|
||||||
|
* The length of received Ulaw audio.
|
||||||
|
*/
|
||||||
|
private static final int ULAW_AUDIO_LENGTH = 160;
|
||||||
|
/**
|
||||||
|
* The maximum size of our processing buffer. 8 Ulaw packets (8x160 = 1280) yields 5 L16/Nelly audio (5x256 = 1280).
|
||||||
|
*/
|
||||||
|
private static final int MAX_BUFFER_LENGTH = 1280;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffer that contain L16 transcoded audio from Ulaw.
|
||||||
|
*/
|
||||||
|
private final FloatBuffer l16Audio = FloatBuffer.allocate(MAX_BUFFER_LENGTH);
|
||||||
|
/*
|
||||||
|
* A view read-only buffer that keeps track of which part of the L16 buffer will be converted to Nelly.
|
||||||
|
*/
|
||||||
|
private FloatBuffer viewBuffer;
|
||||||
|
|
||||||
|
private final float[] tempL16Buffer = new float[ULAW_AUDIO_LENGTH];
|
||||||
|
private float[] tempNellyBuffer = new float[L16_AUDIO_LENGTH];
|
||||||
|
private final byte[] nellyBytes = new byte[NELLY_AUDIO_LENGTH];
|
||||||
|
|
||||||
private float[] encoderMap;
|
private float[] encoderMap;
|
||||||
private Codec audioCodec = null;
|
private Codec audioCodec = null;
|
||||||
private float[] tempBuffer; // Temporary buffer with PCM audio to be sent to FlashPlayer.
|
|
||||||
private int tempBufferOffset = 0;
|
|
||||||
|
|
||||||
private long timestamp = 0;
|
|
||||||
private final static int TS_INCREMENT = 32; // Determined from PCAP traces.
|
|
||||||
|
|
||||||
|
private long timestamp = 0;
|
||||||
|
private final static int TS_INCREMENT = 32; // Determined from PCAP traces.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The transcode takes a 160-byte Ulaw audio and converts it to a 160-float L16 audio. Whenever there is an
|
||||||
|
* available 256-float L16 audio, that gets converted into a 64-byte Nelly audio. Therefore, 8 Ulaw packets
|
||||||
|
* are needed to generate 5 Nelly packets.
|
||||||
|
* @param audioCodec
|
||||||
|
*/
|
||||||
public NellySipToFlashTranscoderImp(Codec audioCodec) {
|
public NellySipToFlashTranscoderImp(Codec audioCodec) {
|
||||||
this.audioCodec = audioCodec;
|
this.audioCodec = audioCodec;
|
||||||
encoderMap = new float[64];
|
encoderMap = new float[64];
|
||||||
tempBuffer = new float[NELLYMOSER_DECODED_PACKET_SIZE];
|
|
||||||
|
|
||||||
Random rgen = new Random();
|
Random rgen = new Random();
|
||||||
timestamp = rgen.nextInt(1000);
|
timestamp = rgen.nextInt(1000);
|
||||||
}
|
viewBuffer = l16Audio.asReadOnlyBuffer();
|
||||||
|
|
||||||
private void transcodePcmToNellymoser(byte[] codedBuffer, TranscodedAudioDataListener listener) {
|
|
||||||
float[] decodingBuffer = new float[codedBuffer.length];
|
|
||||||
int decodedBytes = audioCodec.codecToPcm(codedBuffer, decodingBuffer);
|
|
||||||
|
|
||||||
if (decodedBytes == audioCodec.getIncomingDecodedFrameSize()) {
|
|
||||||
int pcmBufferOffset = 0;
|
|
||||||
int copySize = 0;
|
|
||||||
boolean pcmBufferProcessed = false;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if ((tempBuffer.length - tempBufferOffset) <= (decodingBuffer.length - pcmBufferOffset)) {
|
|
||||||
copySize = tempBuffer.length - tempBufferOffset;
|
|
||||||
} else {
|
|
||||||
copySize = decodingBuffer.length - pcmBufferOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.arraycopy(decodingBuffer, pcmBufferOffset, tempBuffer, tempBufferOffset, copySize);
|
|
||||||
|
|
||||||
tempBufferOffset += copySize;
|
|
||||||
pcmBufferOffset += copySize;
|
|
||||||
|
|
||||||
if (tempBufferOffset == NELLYMOSER_DECODED_PACKET_SIZE) {
|
|
||||||
ByteStream encodedStream = new ByteStream(NELLYMOSER_ENCODED_PACKET_SIZE);
|
|
||||||
encoderMap = CodecImpl.encode(encoderMap, tempBuffer, encodedStream.bytes);
|
|
||||||
tempBufferOffset = 0;
|
|
||||||
listener.handleTranscodedAudioData(encodedStream.bytes, timestamp += TS_INCREMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pcmBufferOffset == decodingBuffer.length) {
|
|
||||||
pcmBufferProcessed = true;
|
|
||||||
}
|
|
||||||
} while (!pcmBufferProcessed);
|
|
||||||
} else {
|
|
||||||
log.warn("[IncomingBytes=" + codedBuffer.length + ",DecodedBytes=" + decodedBytes +", ExpectedDecodedBytes=" + audioCodec.getIncomingDecodedFrameSize() +"]");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transcode(AudioByteData audioData, TranscodedAudioDataListener listener) {
|
public void transcode(byte[] audioData, TranscodedAudioDataListener listener) {
|
||||||
transcodePcmToNellymoser(audioData.getData(), listener);
|
if (audioData.length != ULAW_AUDIO_LENGTH) {
|
||||||
}
|
log.warn("Received corrupt audio. Got {}, expected {}.", audioData.length, ULAW_AUDIO_LENGTH);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Ulaw to L16
|
||||||
|
int decodedBytes = audioCodec.codecToPcm(audioData, tempL16Buffer);
|
||||||
|
|
||||||
|
// Store into the buffer
|
||||||
|
l16Audio.put(tempL16Buffer);
|
||||||
|
|
||||||
|
if ((l16Audio.position() - viewBuffer.position()) >= L16_AUDIO_LENGTH) {
|
||||||
|
// We have enough L16 audio to generate a Nelly audio.
|
||||||
|
// Get some L16 audio
|
||||||
|
viewBuffer.get(tempNellyBuffer);
|
||||||
|
// Convert it into Nelly
|
||||||
|
encoderMap = CodecImpl.encode(encoderMap, tempNellyBuffer, nellyBytes);
|
||||||
|
|
||||||
|
// Having done all of that, we now see if we need to send the audio or drop it.
|
||||||
|
// We have to encode to build the encoderMap so that data from previous audio packet
|
||||||
|
// will be used for the next packet.
|
||||||
|
boolean sendPacket = true;
|
||||||
|
IConnection conn = Red5.getConnectionLocal();
|
||||||
|
if (conn instanceof RTMPMinaConnection) {
|
||||||
|
long pendingMessages = ((RTMPMinaConnection)conn).getPendingMessages();
|
||||||
|
if (pendingMessages > 25) {
|
||||||
|
// Message backed up probably due to slow connection to client (25 messages * 20ms ptime = 500ms audio)
|
||||||
|
sendPacket = false;
|
||||||
|
log.info("Dropping packet. Connection {} congested with {} pending messages (~500ms worth of audio) .", conn.getClient().getId(), pendingMessages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendPacket) listener.handleTranscodedAudioData(nellyBytes, timestamp += TS_INCREMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l16Audio.position() == l16Audio.capacity()) {
|
||||||
|
// We've processed 8 Ulaw packets (5 Nelly packets), reset the buffers.
|
||||||
|
l16Audio.clear();
|
||||||
|
viewBuffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIncomingEncodedFrameSize() {
|
public int getIncomingEncodedFrameSize() {
|
||||||
return audioCodec.getIncomingEncodedFrameSize();
|
return audioCodec.getIncomingEncodedFrameSize();
|
||||||
@ -111,3 +138,4 @@ public class NellySipToFlashTranscoderImp implements SipToFlashTranscoder {
|
|||||||
return NELLYMOSER_CODEC_ID;
|
return NELLYMOSER_CODEC_ID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,10 +19,8 @@
|
|||||||
**/
|
**/
|
||||||
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
||||||
|
|
||||||
import org.bigbluebutton.voiceconf.red5.media.AudioByteData;
|
|
||||||
|
|
||||||
public interface SipToFlashTranscoder {
|
public interface SipToFlashTranscoder {
|
||||||
void transcode(AudioByteData audioData, TranscodedAudioDataListener listener);
|
void transcode(byte[] audioData, TranscodedAudioDataListener listener);
|
||||||
int getCodecId();
|
int getCodecId();
|
||||||
int getIncomingEncodedFrameSize();
|
int getIncomingEncodedFrameSize();
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,6 @@
|
|||||||
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.bigbluebutton.voiceconf.red5.media.AudioByteData;
|
|
||||||
import org.red5.app.sip.codecs.Codec;
|
import org.red5.app.sip.codecs.Codec;
|
||||||
import org.red5.logging.Red5LoggerFactory;
|
import org.red5.logging.Red5LoggerFactory;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -39,9 +37,9 @@ public class SpeexFlashToSipTranscoderImp implements FlashToSipTranscoder {
|
|||||||
timestamp = rgen.nextInt(1000);
|
timestamp = rgen.nextInt(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void transcode(AudioByteData audioData, int startOffset, int length, TranscodedAudioDataListener listener) {
|
public void transcode(byte[] audioData, int startOffset, int length, TranscodedAudioDataListener listener) {
|
||||||
byte[] transcodedAudio = new byte[length];
|
byte[] transcodedAudio = new byte[length];
|
||||||
System.arraycopy(audioData.getData(), startOffset, transcodedAudio, 0, length);
|
System.arraycopy(audioData, startOffset, transcodedAudio, 0, length);
|
||||||
listener.handleTranscodedAudioData(transcodedAudio, timestamp += TS_INCREMENT);
|
listener.handleTranscodedAudioData(transcodedAudio, timestamp += TS_INCREMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,8 +20,6 @@
|
|||||||
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
package org.bigbluebutton.voiceconf.red5.media.transcoder;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.bigbluebutton.voiceconf.red5.media.AudioByteData;
|
|
||||||
import org.red5.app.sip.codecs.Codec;
|
import org.red5.app.sip.codecs.Codec;
|
||||||
import org.red5.logging.Red5LoggerFactory;
|
import org.red5.logging.Red5LoggerFactory;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -41,8 +39,8 @@ public class SpeexSipToFlashTranscoderImp implements SipToFlashTranscoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transcode(AudioByteData audioData, TranscodedAudioDataListener listener) {
|
public void transcode(byte[] audioData, TranscodedAudioDataListener listener) {
|
||||||
byte[] codedBuffer = audioData.getData();
|
byte[] codedBuffer = audioData;
|
||||||
listener.handleTranscodedAudioData(codedBuffer, timestamp += TS_INCREMENT);
|
listener.handleTranscodedAudioData(codedBuffer, timestamp += TS_INCREMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,4 +50,8 @@ public class AudioConferenceProvider {
|
|||||||
public int getStartAudioPort() {
|
public int getStartAudioPort() {
|
||||||
return startAudioPort;
|
return startAudioPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getStopAudioPort() {
|
||||||
|
return stopAudioPort;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,9 @@ import org.red5.logging.Red5LoggerFactory;
|
|||||||
import org.red5.server.api.IScope;
|
import org.red5.server.api.IScope;
|
||||||
import org.red5.server.api.stream.IBroadcastStream;
|
import org.red5.server.api.stream.IBroadcastStream;
|
||||||
import java.net.DatagramSocket;
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
public class CallAgent extends CallListenerAdapter implements CallStreamObserver {
|
public class CallAgent extends CallListenerAdapter implements CallStreamObserver {
|
||||||
@ -85,11 +87,12 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void call(String callerName, String destination) {
|
public void call(String callerName, String destination) {
|
||||||
log.debug("call {}", destination);
|
log.debug("{} making a call to {}", callerName, destination);
|
||||||
try {
|
try {
|
||||||
localSocket = getLocalAudioSocket();
|
localSocket = getLocalAudioSocket();
|
||||||
userProfile.audioPort = localSocket.getLocalPort();
|
userProfile.audioPort = localSocket.getLocalPort();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
log.debug("{} failed to allocate local port for call to {}. Notifying client that call failed.", callerName, destination);
|
||||||
notifyListenersOnOutgoingCallFailed();
|
notifyListenersOnOutgoingCallFailed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -138,18 +141,22 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
|||||||
private DatagramSocket getLocalAudioSocket() throws Exception {
|
private DatagramSocket getLocalAudioSocket() throws Exception {
|
||||||
DatagramSocket socket = null;
|
DatagramSocket socket = null;
|
||||||
boolean failedToGetSocket = true;
|
boolean failedToGetSocket = true;
|
||||||
|
StringBuilder failedPorts = new StringBuilder("Failed ports: ");
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = portProvider.getStartAudioPort(); i <= portProvider.getStopAudioPort(); i++) {
|
||||||
try {
|
int freePort = portProvider.getFreeAudioPort();
|
||||||
socket = new DatagramSocket(portProvider.getFreeAudioPort());
|
try {
|
||||||
|
socket = new DatagramSocket(freePort);
|
||||||
failedToGetSocket = false;
|
failedToGetSocket = false;
|
||||||
|
log.info("Successfully setup local audio port {}. {}", freePort, failedPorts);
|
||||||
break;
|
break;
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
log.error("Failed to setup local audio socket.");
|
failedPorts.append(freePort + ", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failedToGetSocket) {
|
if (failedToGetSocket) {
|
||||||
|
log.warn("Failed to setup local audio port {}.", failedPorts);
|
||||||
throw new Exception("Exception while initializing CallStream");
|
throw new Exception("Exception while initializing CallStream");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,22 +176,26 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
|||||||
int localAudioPort = SessionDescriptorUtil.getLocalAudioPort(localSdp);
|
int localAudioPort = SessionDescriptorUtil.getLocalAudioPort(localSdp);
|
||||||
|
|
||||||
SipConnectInfo connInfo = new SipConnectInfo(localSocket, remoteMediaAddress, remoteAudioPort);
|
SipConnectInfo connInfo = new SipConnectInfo(localSocket, remoteMediaAddress, remoteAudioPort);
|
||||||
|
try {
|
||||||
log.debug("[localAudioPort=" + localAudioPort + ",remoteAudioPort=" + remoteAudioPort + "]");
|
localSocket.connect(InetAddress.getByName(remoteMediaAddress), remoteAudioPort);
|
||||||
|
log.debug("[localAudioPort=" + localAudioPort + ",remoteAudioPort=" + remoteAudioPort + "]");
|
||||||
|
|
||||||
if (userProfile.audio && localAudioPort != 0 && remoteAudioPort != 0) {
|
if (userProfile.audio && localAudioPort != 0 && remoteAudioPort != 0) {
|
||||||
if ((callStream == null) && (sipCodec != null)) {
|
if ((callStream == null) && (sipCodec != null)) {
|
||||||
try {
|
try {
|
||||||
callStream = callStreamFactory.createCallStream(sipCodec, connInfo);
|
callStream = callStreamFactory.createCallStream(sipCodec, connInfo);
|
||||||
callStream.addCallStreamObserver(this);
|
callStream.addCallStreamObserver(this);
|
||||||
callStream.start();
|
callStream.start();
|
||||||
notifyListenersOnCallConnected(callStream.getTalkStreamName(), callStream.getListenStreamName());
|
notifyListenersOnCallConnected(callStream.getTalkStreamName(), callStream.getListenStreamName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to create Call Stream.");
|
log.error("Failed to create Call Stream.");
|
||||||
System.out.println(StackTraceUtil.getStackTrace(e));
|
System.out.println(StackTraceUtil.getStackTrace(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (UnknownHostException e1) {
|
||||||
|
log.error(StackTraceUtil.getStackTrace(e1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -204,11 +215,12 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void closeVoiceStreams() {
|
private void closeVoiceStreams() {
|
||||||
log.debug("closeMediaApplication" );
|
log.debug("Shutting down the voice streams.");
|
||||||
|
|
||||||
if (callStream != null) {
|
if (callStream != null) {
|
||||||
callStream.stop();
|
callStream.stop();
|
||||||
callStream = null;
|
callStream = null;
|
||||||
|
} else {
|
||||||
|
log.debug("Can't shutdown voice stream. callstream is NULL");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,19 +335,20 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void cleanup() {
|
private void cleanup() {
|
||||||
localSocket.close();
|
log.debug("Closing local audio port {}", localSocket.getLocalPort());
|
||||||
|
if (localSocket != null) {
|
||||||
|
localSocket.close();
|
||||||
|
} else {
|
||||||
|
log.debug("Trying to close un-allocated port {}", localSocket.getLocalPort());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Callback function called when arriving a BYE request */
|
/** Callback function called when arriving a BYE request */
|
||||||
public void onCallClosing(Call call, Message bye) {
|
public void onCallClosing(Call call, Message bye) {
|
||||||
log.debug("onCallClosing");
|
log.info("Received a BYE from the other end telling us to hangup.");
|
||||||
|
|
||||||
if (!isCurrentCall(call)) return;
|
|
||||||
|
|
||||||
log.debug("CLOSE.");
|
|
||||||
|
|
||||||
|
if (!isCurrentCall(call)) return;
|
||||||
closeVoiceStreams();
|
closeVoiceStreams();
|
||||||
|
|
||||||
notifyListenersOfOnCallClosed();
|
notifyListenersOfOnCallClosed();
|
||||||
callState = CallState.UA_IDLE;
|
callState = CallState.UA_IDLE;
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean roomStart(IScope room) {
|
public boolean roomStart(IScope room) {
|
||||||
log.debug("{} - roomStart ", APP);
|
log.debug("Starting room [{}].", room.getName());
|
||||||
assert participantsApplication != null;
|
assert participantsApplication != null;
|
||||||
participantsApplication.createRoom(room.getName());
|
participantsApplication.createRoom(room.getName());
|
||||||
return super.roomStart(room);
|
return super.roomStart(room);
|
||||||
@ -71,21 +71,28 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void roomStop(IScope room) {
|
public void roomStop(IScope room) {
|
||||||
log.debug("{} - roomStop", APP);
|
log.debug("Stopping room [{}]", room.getName());
|
||||||
super.roomStop(room);
|
super.roomStop(room);
|
||||||
assert participantsApplication != null;
|
assert participantsApplication != null;
|
||||||
participantsApplication.destroyRoom(room.getName());
|
participantsApplication.destroyRoom(room.getName());
|
||||||
BigBlueButtonSession bbbSession = getBbbSession();
|
BigBlueButtonSession bbbSession = getBbbSession();
|
||||||
assert bbbSession != null;
|
assert bbbSession != null;
|
||||||
log.debug("{} - roomStop - destroying RecordSession {}", APP, bbbSession.getSessionName());
|
|
||||||
|
/**
|
||||||
|
* Need to figure out if the next 2 lines should be removed. (ralam nov 25, 2010).
|
||||||
|
*/
|
||||||
assert recorderApplication != null;
|
assert recorderApplication != null;
|
||||||
recorderApplication.destroyRecordSession(bbbSession.getSessionName());
|
recorderApplication.destroyRecordSession(bbbSession.getSessionName());
|
||||||
log.debug("{} - roomStop - destroyed RecordSession {}", APP, bbbSession.getSessionName());
|
|
||||||
|
log.debug("Stopped room [{}]", room.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean roomConnect(IConnection connection, Object[] params) {
|
public boolean roomConnect(IConnection connection, Object[] params) {
|
||||||
log.debug("{} - roomConnect - ", APP);
|
String remoteHost = Red5.getConnectionLocal().getRemoteAddress();
|
||||||
|
int remotePort = Red5.getConnectionLocal().getRemotePort();
|
||||||
|
String clientId = Red5.getConnectionLocal().getClient().getId();
|
||||||
|
log.info("[clientid={}] connected from {}.", clientId, remoteHost + ":" + remotePort);
|
||||||
|
|
||||||
String username = ((String) params[0]).toString();
|
String username = ((String) params[0]).toString();
|
||||||
String role = ((String) params[1]).toString();
|
String role = ((String) params[1]).toString();
|
||||||
@ -116,47 +123,44 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
|
|||||||
|
|
||||||
String debugInfo = "userid=" + userid + ",username=" + username + ",role=" + role + ",conference=" + conference + "," +
|
String debugInfo = "userid=" + userid + ",username=" + username + ",role=" + role + ",conference=" + conference + "," +
|
||||||
"session=" + sessionName + ",voiceConf=" + voiceBridge + ",room=" + room + ",externsUserid=" + externUserID;
|
"session=" + sessionName + ",voiceConf=" + voiceBridge + ",room=" + room + ",externsUserid=" + externUserID;
|
||||||
log.debug("roomConnect - [{}]", debugInfo);
|
log.debug("User [{}] connected to room [{}]", debugInfo, room);
|
||||||
|
|
||||||
log.info("User Joined [{}, {}]", username, room);
|
|
||||||
super.roomConnect(connection, params);
|
super.roomConnect(connection, params);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void roomDisconnect(IConnection conn) {
|
public void roomDisconnect(IConnection conn) {
|
||||||
|
String remoteHost = Red5.getConnectionLocal().getRemoteAddress();
|
||||||
|
int remotePort = Red5.getConnectionLocal().getRemotePort();
|
||||||
|
String clientId = Red5.getConnectionLocal().getClient().getId();
|
||||||
|
log.info("[clientid={}] disconnnected from {}.", clientId, remoteHost + ":" + remotePort);
|
||||||
|
|
||||||
BigBlueButtonSession bbbSession = (BigBlueButtonSession) Red5.getConnectionLocal().getAttribute(Constants.SESSION);
|
BigBlueButtonSession bbbSession = (BigBlueButtonSession) Red5.getConnectionLocal().getAttribute(Constants.SESSION);
|
||||||
log.info("User Left [{}, {}]", bbbSession.getUsername(), bbbSession.getRoom());
|
log.info("User [{}] disconnected from room [{}]", bbbSession.getUsername(), bbbSession.getRoom());
|
||||||
super.roomDisconnect(conn);
|
super.roomDisconnect(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMyUserId() {
|
public String getMyUserId() {
|
||||||
log.debug("Getting userid for connection.");
|
|
||||||
BigBlueButtonSession bbbSession = (BigBlueButtonSession) Red5.getConnectionLocal().getAttribute(Constants.SESSION);
|
BigBlueButtonSession bbbSession = (BigBlueButtonSession) Red5.getConnectionLocal().getAttribute(Constants.SESSION);
|
||||||
assert bbbSession != null;
|
assert bbbSession != null;
|
||||||
return bbbSession.getUserid()+"";
|
return bbbSession.getUserid()+"";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParticipantsApplication(ParticipantsApplication a) {
|
public void setParticipantsApplication(ParticipantsApplication a) {
|
||||||
log.debug("Setting participants application");
|
|
||||||
participantsApplication = a;
|
participantsApplication = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRecorderApplication(RecorderApplication a) {
|
public void setRecorderApplication(RecorderApplication a) {
|
||||||
log.debug("Setting recorder application");
|
|
||||||
recorderApplication = a;
|
recorderApplication = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setApplicationListeners(Set<IApplication> listeners) {
|
public void setApplicationListeners(Set<IApplication> listeners) {
|
||||||
log.debug("Setting application listeners");
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
Iterator<IApplication> iter = listeners.iterator();
|
Iterator<IApplication> iter = listeners.iterator();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
log.debug("Setting application listeners {}", count);
|
|
||||||
super.addListener((IApplication) iter.next());
|
super.addListener((IApplication) iter.next());
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
log.debug("Finished Setting application listeners");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVersion(String v) {
|
public void setVersion(String v) {
|
||||||
@ -172,7 +176,7 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
|
|||||||
@Override
|
@Override
|
||||||
public void onApplicationEvent(ApplicationEvent event) {
|
public void onApplicationEvent(ApplicationEvent event) {
|
||||||
if (event instanceof org.springframework.context.event.ContextStoppedEvent) {
|
if (event instanceof org.springframework.context.event.ContextStoppedEvent) {
|
||||||
log.info("Received shutdown event. Destroying all rooms.");
|
log.info("Received shutdown event. Red5 is shutting down. Destroying all rooms.");
|
||||||
participantsApplication.destroyAllRooms();
|
participantsApplication.destroyAllRooms();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,3 +49,4 @@
|
|||||||
</buildCSSFiles>
|
</buildCSSFiles>
|
||||||
</actionScriptProperties>
|
</actionScriptProperties>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<flexProperties enableServiceManager="false" flexServerFeatures="0" flexServerType="0" toolCompile="true" useServerFlexSDK="false" version="1"/>
|
<flexProperties enableServiceManager="false" flexServerFeatures="0" flexServerType="0" toolCompile="true" useServerFlexSDK="false" version="1"/>
|
||||||
|
|
||||||
|
|
||||||
|
3
bigbluebutton-client/.gitignore
vendored
3
bigbluebutton-client/.gitignore
vendored
@ -1,3 +1,6 @@
|
|||||||
|
.settings/org.eclipse.core.resources.prefs
|
||||||
|
.actionScriptProperties
|
||||||
|
.flexProperties
|
||||||
linker-report.xml
|
linker-report.xml
|
||||||
bin
|
bin
|
||||||
client
|
client
|
||||||
|
4
bigbluebutton-client/.settings/org.eclipse.core.resources.prefs
Normal file → Executable file
4
bigbluebutton-client/.settings/org.eclipse.core.resources.prefs
Normal file → Executable file
@ -1,4 +1,4 @@
|
|||||||
#Tue Feb 23 11:59:38 EST 2010
|
#Mon Dec 13 14:02:21 EST 2010
|
||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
encoding//locale/el_GR/bbbResources.properties=UTF-8
|
encoding//locale/el_GR/bbbResources.properties=UTF-8
|
||||||
encoding/<project>=utf-8
|
encoding/<project>=UTF-8
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
<module name="PhoneModule" url="PhoneModule.swf?v=VERSION"
|
<module name="PhoneModule" url="PhoneModule.swf?v=VERSION"
|
||||||
uri="rtmp://HOST/sip"
|
uri="rtmp://HOST/sip"
|
||||||
|
autoJoin="false"
|
||||||
dependsOn="ViewersModule"
|
dependsOn="ViewersModule"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
BIN
bigbluebutton-client/src/branding/css/assets/img/BBBLogo.png
Executable file
BIN
bigbluebutton-client/src/branding/css/assets/img/BBBLogo.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
@ -8,6 +8,13 @@ Application
|
|||||||
backgroundGradientAlphas: 0.62, 0.26;
|
backgroundGradientAlphas: 0.62, 0.26;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*The image for your logo is 200x200 pixels. No other size is currently supported so resize your logo accordingly. The logo will always appear in the lower right corner. */
|
||||||
|
BrandingLogo
|
||||||
|
{
|
||||||
|
backgroundImage: Embed(source="assets/img/BBBlogo.png");
|
||||||
|
backgroundSize: "100%";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
MDICanvas
|
MDICanvas
|
||||||
{
|
{
|
||||||
|
4
bigbluebutton-client/src/org/bigbluebutton/main/views/BrandingLogo.mxml
Executable file
4
bigbluebutton-client/src/org/bigbluebutton/main/views/BrandingLogo.mxml
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="200" height="200">
|
||||||
|
|
||||||
|
</mx:Canvas>
|
@ -238,7 +238,9 @@
|
|||||||
<views:MainToolbar id="toolbar" dock="true" width="100%" height="30" visible="false" verticalAlign="middle"/>
|
<views:MainToolbar id="toolbar" dock="true" width="100%" height="30" visible="false" verticalAlign="middle"/>
|
||||||
<views:MainCanvas id="mdiCanvas" horizontalScrollPolicy="off" verticalScrollPolicy="off" effectsLib="{flexlib.mdi.effects.effectsLib.MDIVistaEffects}" width="100%" height="100%">
|
<views:MainCanvas id="mdiCanvas" horizontalScrollPolicy="off" verticalScrollPolicy="off" effectsLib="{flexlib.mdi.effects.effectsLib.MDIVistaEffects}" width="100%" height="100%">
|
||||||
<views:LoadingBar id="progressBar" x="{this.width/2 - progressBar.width/2}" y="{this.height/2 - progressBar.height/2}" width="{this.width/2}" />
|
<views:LoadingBar id="progressBar" x="{this.width/2 - progressBar.width/2}" y="{this.height/2 - progressBar.height/2}" width="{this.width/2}" />
|
||||||
|
<views:BrandingLogo x="{this.width - 300}" y="{this.height - 300}" />
|
||||||
</views:MainCanvas>
|
</views:MainCanvas>
|
||||||
|
|
||||||
<mx:ControlBar width="100%" height="20" paddingTop="0">
|
<mx:ControlBar width="100%" height="20" paddingTop="0">
|
||||||
<mx:Label text="{ResourceUtil.getInstance().getString('bbb.mainshell.copyrightLabel2',[appVersion])}" id="copyrightLabel2"/>
|
<mx:Label text="{ResourceUtil.getInstance().getString('bbb.mainshell.copyrightLabel2',[appVersion])}" id="copyrightLabel2"/>
|
||||||
<mx:Spacer width="20"/>
|
<mx:Spacer width="20"/>
|
||||||
|
@ -47,6 +47,7 @@ package org.bigbluebutton.modules.deskshare.services
|
|||||||
|
|
||||||
private var width:Number;
|
private var width:Number;
|
||||||
private var height:Number;
|
private var height:Number;
|
||||||
|
private var uri:String;
|
||||||
|
|
||||||
public function DeskshareService()
|
public function DeskshareService()
|
||||||
{
|
{
|
||||||
@ -60,6 +61,7 @@ package org.bigbluebutton.modules.deskshare.services
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function connect(uri:String):void {
|
public function connect(uri:String):void {
|
||||||
|
this.uri = uri;
|
||||||
LogUtil.debug("Deskshare Service connecting to " + uri);
|
LogUtil.debug("Deskshare Service connecting to " + uri);
|
||||||
conn = new Connection();
|
conn = new Connection();
|
||||||
conn.addEventListener(Connection.SUCCESS, connectionSuccessHandler);
|
conn.addEventListener(Connection.SUCCESS, connectionSuccessHandler);
|
||||||
@ -93,9 +95,9 @@ package org.bigbluebutton.modules.deskshare.services
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function connectionSuccessHandler(e:ConnectionEvent):void{
|
private function connectionSuccessHandler(e:ConnectionEvent):void{
|
||||||
LogUtil.debug("Successully connection to " + module.uri);
|
LogUtil.debug("Successully connection to " + uri);
|
||||||
nc = conn.getConnection();
|
nc = conn.getConnection();
|
||||||
deskSO = SharedObject.getRemote("deskSO", module.uri, false);
|
deskSO = SharedObject.getRemote("deskSO", uri, false);
|
||||||
deskSO.client = this;
|
deskSO.client = this;
|
||||||
deskSO.connect(nc);
|
deskSO.connect(nc);
|
||||||
|
|
||||||
@ -107,11 +109,11 @@ package org.bigbluebutton.modules.deskshare.services
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function connectionFailedHandler(e:ConnectionEvent):void{
|
public function connectionFailedHandler(e:ConnectionEvent):void{
|
||||||
LogUtil.error("connection failed to " + module.uri + " with message " + e.toString());
|
LogUtil.error("connection failed to " + uri + " with message " + e.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function connectionRejectedHandler(e:ConnectionEvent):void{
|
public function connectionRejectedHandler(e:ConnectionEvent):void{
|
||||||
LogUtil.error("connection rejected " + module.uri + " with message " + e.toString());
|
LogUtil.error("connection rejected " + uri + " with message " + e.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
title="{windowTitle}"
|
title="{windowTitle}"
|
||||||
creationComplete="onCreationComplete()" xmlns:mate="http://mate.asfusion.com/">
|
creationComplete="onCreationComplete()" xmlns:mate="http://mate.asfusion.com/">
|
||||||
|
|
||||||
<mate:Listener type="{ListenersEvent.FIRST_LISTENER_JOINED_EVENT}" method="firstListenerJoined" />
|
|
||||||
<mate:Listener type="{ListenersEvent.ROOM_MUTE_STATE}" method="roomMuteStateChange" />
|
<mate:Listener type="{ListenersEvent.ROOM_MUTE_STATE}" method="roomMuteStateChange" />
|
||||||
<mate:Listener type="{ListenersEvent.REGISTER_LISTENERS}" method="registerListeners" />
|
<mate:Listener type="{ListenersEvent.REGISTER_LISTENERS}" method="registerListeners" />
|
||||||
<mate:Listener type="{ListenersEvent.SET_LOCAL_MODERATOR_STATUS}" method="{setModerator}" />
|
<mate:Listener type="{ListenersEvent.SET_LOCAL_MODERATOR_STATUS}" method="{setModerator}" />
|
||||||
@ -128,31 +127,23 @@
|
|||||||
dispatchEvent(unmuteCommand);
|
dispatchEvent(unmuteCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function firstListenerJoined(e:ListenersEvent):void{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private function roomMuteStateChange(e:ListenersEvent):void{
|
private function roomMuteStateChange(e:ListenersEvent):void{
|
||||||
setMuteState(e.mute_state);
|
setMuteState(e.mute_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function registerListeners(e:ListenersEvent):void{
|
private function registerListeners(e:ListenersEvent):void{
|
||||||
this.listeners = e.listeners.listeners;
|
this.listeners = e.listeners.listeners;
|
||||||
|
/*
|
||||||
|
* Bind into this object to display the number of listeners.
|
||||||
|
*/
|
||||||
|
BindingUtils.bindSetter(updateNumberOfListeners, listeners, "length");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setModerator(e:ListenersEvent):void{
|
private function setModerator(e:ListenersEvent):void{
|
||||||
moderator = e.moderator;
|
moderator = e.moderator;
|
||||||
showCloseButton = false;
|
showCloseButton = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected function resourcesChanged():void{
|
|
||||||
super.resourcesChanged();
|
|
||||||
if (listeners.length > 4)
|
|
||||||
windowTitle = ResourceUtil.getInstance().getString('bbb.listeners.title', [":", listeners.length]) ;
|
|
||||||
else
|
|
||||||
windowTitle = ResourceUtil.getInstance().getString('bbb.listeners.title', ["", ""]) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function onItemRollOver(e:ListEvent):void{
|
private function onItemRollOver(e:ListEvent):void{
|
||||||
var item:ListenerItem = e.itemRenderer as ListenerItem;
|
var item:ListenerItem = e.itemRenderer as ListenerItem;
|
||||||
item.onRollOver();
|
item.onRollOver();
|
||||||
|
@ -60,7 +60,7 @@ package org.bigbluebutton.modules.phone.managers {
|
|||||||
return netConnection;
|
return netConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function connect(uid:String, username:String, room:String, uri:String):void {
|
public function connect(uid:String, externUID:String, username:String, room:String, uri:String):void {
|
||||||
if (isConnected) return;
|
if (isConnected) return;
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
|
|
||||||
@ -68,16 +68,16 @@ package org.bigbluebutton.modules.phone.managers {
|
|||||||
this.username = username;
|
this.username = username;
|
||||||
this.room = room;
|
this.room = room;
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
connectToServer();
|
connectToServer(externUID, username);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function connectToServer():void {
|
private function connectToServer(externUID:String, username:String):void {
|
||||||
NetConnection.defaultObjectEncoding = flash.net.ObjectEncoding.AMF0;
|
NetConnection.defaultObjectEncoding = flash.net.ObjectEncoding.AMF0;
|
||||||
netConnection = new NetConnection();
|
netConnection = new NetConnection();
|
||||||
netConnection.client = this;
|
netConnection.client = this;
|
||||||
netConnection.addEventListener( NetStatusEvent.NET_STATUS , netStatus );
|
netConnection.addEventListener( NetStatusEvent.NET_STATUS , netStatus );
|
||||||
netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
|
netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
|
||||||
netConnection.connect(uri);
|
netConnection.connect(uri, externUID, username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function disconnect():void {
|
public function disconnect():void {
|
||||||
|
@ -19,8 +19,6 @@
|
|||||||
|
|
||||||
package org.bigbluebutton.modules.phone.managers
|
package org.bigbluebutton.modules.phone.managers
|
||||||
{
|
{
|
||||||
import flash.events.IEventDispatcher;
|
|
||||||
|
|
||||||
import org.bigbluebutton.common.LogUtil;
|
import org.bigbluebutton.common.LogUtil;
|
||||||
import org.bigbluebutton.modules.phone.events.CallConnectedEvent;
|
import org.bigbluebutton.modules.phone.events.CallConnectedEvent;
|
||||||
import org.bigbluebutton.modules.phone.events.JoinVoiceConferenceEvent;
|
import org.bigbluebutton.modules.phone.events.JoinVoiceConferenceEvent;
|
||||||
@ -40,6 +38,8 @@ package org.bigbluebutton.modules.phone.managers
|
|||||||
public function setModuleAttributes(attributes:Object):void {
|
public function setModuleAttributes(attributes:Object):void {
|
||||||
this.attributes = attributes;
|
this.attributes = attributes;
|
||||||
LogUtil.debug("Attributes Set... webvoiceconf:" + attributes.webvoiceconf);
|
LogUtil.debug("Attributes Set... webvoiceconf:" + attributes.webvoiceconf);
|
||||||
|
|
||||||
|
if (attributes.autoJoin == "true") joinVoice(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setupMic(useMic:Boolean):void {
|
private function setupMic(useMic:Boolean):void {
|
||||||
@ -54,28 +54,36 @@ package org.bigbluebutton.modules.phone.managers
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function join(e:JoinVoiceConferenceEvent):void {
|
public function join(e:JoinVoiceConferenceEvent):void {
|
||||||
setupMic(e.useMicrophone);
|
joinVoice(e.useMicrophone);
|
||||||
var uid:String = String( Math.floor( new Date().getTime() ) );
|
|
||||||
connectionManager.connect(uid, attributes.username, attributes.room, attributes.uri);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function joinVoice(autoJoin:Boolean):void {
|
||||||
|
setupMic(autoJoin);
|
||||||
|
var uid:String = String( Math.floor( new Date().getTime() ) );
|
||||||
|
connectionManager.connect(uid, attributes.externUserID, attributes.username, attributes.room, attributes.uri);
|
||||||
|
}
|
||||||
|
|
||||||
public function dialConference():void {
|
public function dialConference():void {
|
||||||
LogUtil.debug("Dialing...." + attributes.webvoiceconf);
|
LogUtil.debug("Dialing...." + attributes.webvoiceconf + "...." + attributes.externUserID);
|
||||||
connectionManager.doCall(attributes.webvoiceconf);
|
connectionManager.doCall(attributes.webvoiceconf);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function callConnected(event:CallConnectedEvent):void {
|
public function callConnected(event:CallConnectedEvent):void {
|
||||||
LogUtil.debug("Call connected...");
|
LogUtil.debug("Call connected...");
|
||||||
setupConnection();
|
setupConnection();
|
||||||
|
LogUtil.debug("callConnected: Connection Setup");
|
||||||
streamManager.callConnected(event.playStreamName, event.publishStreamName, event.codec);
|
streamManager.callConnected(event.playStreamName, event.publishStreamName, event.codec);
|
||||||
|
LogUtil.debug("callConnected::onCall set");
|
||||||
onCall = true;
|
onCall = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hangup():void {
|
public function hangup():void {
|
||||||
LogUtil.debug("PhoneManager hangup");
|
LogUtil.debug("PhoneManager hangup");
|
||||||
if (onCall) {
|
if (onCall) {
|
||||||
|
LogUtil.debug("PM OnCall");
|
||||||
streamManager.stopStreams();
|
streamManager.stopStreams();
|
||||||
connectionManager.doHangUp();
|
connectionManager.doHangUp();
|
||||||
|
LogUtil.debug("PM hangup::doHangUp");
|
||||||
onCall = false;
|
onCall = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ package org.bigbluebutton.modules.phone.managers
|
|||||||
|
|
||||||
public function initMicrophone():void {
|
public function initMicrophone():void {
|
||||||
mic = Microphone.getMicrophone();
|
mic = Microphone.getMicrophone();
|
||||||
|
|
||||||
if(mic == null){
|
if(mic == null){
|
||||||
initWithNoMicrophone();
|
initWithNoMicrophone();
|
||||||
} else {
|
} else {
|
||||||
@ -115,7 +115,9 @@ package org.bigbluebutton.modules.phone.managers
|
|||||||
|
|
||||||
public function mute():void {
|
public function mute():void {
|
||||||
if(!muted) {
|
if(!muted) {
|
||||||
|
|
||||||
if(outgoingStream != null) {
|
if(outgoingStream != null) {
|
||||||
|
LogUtil.debug("***** Muting the mic.");
|
||||||
outgoingStream.close();
|
outgoingStream.close();
|
||||||
outgoingStream = null;
|
outgoingStream = null;
|
||||||
muted = true;
|
muted = true;
|
||||||
@ -125,6 +127,7 @@ package org.bigbluebutton.modules.phone.managers
|
|||||||
|
|
||||||
public function unmute():void {
|
public function unmute():void {
|
||||||
if (muted) {
|
if (muted) {
|
||||||
|
LogUtil.debug("***** UNMuting the mic.");
|
||||||
outgoingStream = new NetStream(connection);
|
outgoingStream = new NetStream(connection);
|
||||||
outgoingStream.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
|
outgoingStream.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
|
||||||
outgoingStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
|
outgoingStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
|
||||||
@ -140,13 +143,22 @@ package org.bigbluebutton.modules.phone.managers
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function callConnected(playStreamName:String, publishStreamName:String, codec:String):void {
|
public function callConnected(playStreamName:String, publishStreamName:String, codec:String):void {
|
||||||
|
LogUtil.debug("SM callConnected");
|
||||||
isCallConnected = true;
|
isCallConnected = true;
|
||||||
audioCodec = codec;
|
audioCodec = codec;
|
||||||
setupIncomingStream();
|
setupIncomingStream();
|
||||||
setupOutgoingStream();
|
LogUtil.debug("SM callConnected: Incoming Stream Setup");
|
||||||
|
if (mic != null) {
|
||||||
|
setupOutgoingStream();
|
||||||
|
LogUtil.debug("SM callConnected: Setup Outgoing Stream");
|
||||||
|
}
|
||||||
|
LogUtil.debug("SM callConnected: Setup Stream(s)");
|
||||||
setupPlayStatusHandler();
|
setupPlayStatusHandler();
|
||||||
play(playStreamName);
|
LogUtil.debug("SM callConnected: After setupPlayStatusHandler");
|
||||||
publish(publishStreamName);
|
play(playStreamName);
|
||||||
|
LogUtil.debug("SM callConnected: After play");
|
||||||
|
publish(publishStreamName);
|
||||||
|
LogUtil.debug("SM callConnected: Published Stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function play(playStreamName:String):void {
|
private function play(playStreamName:String):void {
|
||||||
@ -155,14 +167,25 @@ package org.bigbluebutton.modules.phone.managers
|
|||||||
|
|
||||||
private function publish(publishStreamName:String):void {
|
private function publish(publishStreamName:String):void {
|
||||||
LogUtil.debug("Publishing stream " + publishStreamName);
|
LogUtil.debug("Publishing stream " + publishStreamName);
|
||||||
outgoingStream.publish(publishStreamName, "live");
|
if (mic != null)
|
||||||
|
outgoingStream.publish(publishStreamName, "live");
|
||||||
|
else
|
||||||
|
LogUtil.debug("SM publish: No Microphone to publish");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setupIncomingStream():void {
|
private function setupIncomingStream():void {
|
||||||
incomingStream = new NetStream(connection);
|
incomingStream = new NetStream(connection);
|
||||||
incomingStream.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
|
incomingStream.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
|
||||||
incomingStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
|
incomingStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
|
||||||
incomingStream.bufferTime = 0.180;
|
/*
|
||||||
|
* Set the bufferTime to 0 (zero) for live stream as suggested in the doc.
|
||||||
|
* http://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/flash/net/NetStream.html#bufferTime
|
||||||
|
* If we don't, we'll have a long audio delay when a momentary network congestion occurs. When the congestion
|
||||||
|
* disappears, a flood of audio packets will arrive at the client and Flash will buffer them all and play them.
|
||||||
|
* http://stackoverflow.com/questions/1079935/actionscript-netstream-stutters-after-buffering
|
||||||
|
* ralam (Dec 13, 2010)
|
||||||
|
*/
|
||||||
|
incomingStream.bufferTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setupOutgoingStream():void {
|
private function setupOutgoingStream():void {
|
||||||
@ -178,19 +201,29 @@ package org.bigbluebutton.modules.phone.managers
|
|||||||
custom_obj.onPlayStatus = playStatus;
|
custom_obj.onPlayStatus = playStatus;
|
||||||
custom_obj.onMetadata = onMetadata;
|
custom_obj.onMetadata = onMetadata;
|
||||||
incomingStream.client = custom_obj;
|
incomingStream.client = custom_obj;
|
||||||
outgoingStream.client = custom_obj;
|
if (mic != null)
|
||||||
|
outgoingStream.client = custom_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stopStreams():void {
|
public function stopStreams():void {
|
||||||
|
LogUtil.debug("Stopping Stream(s)");
|
||||||
if(incomingStream != null) {
|
if(incomingStream != null) {
|
||||||
|
LogUtil.debug("--Stopping Incoming Stream");
|
||||||
incomingStream.play(false);
|
incomingStream.play(false);
|
||||||
|
} else {
|
||||||
|
LogUtil.debug("--Incoming Stream Null");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(outgoingStream != null) {
|
if(outgoingStream != null) {
|
||||||
|
LogUtil.debug("--Stopping Outgoing Stream");
|
||||||
outgoingStream.attachAudio(null);
|
outgoingStream.attachAudio(null);
|
||||||
|
outgoingStream.close();
|
||||||
|
} else {
|
||||||
|
LogUtil.debug("--Outgoing Stream Null");
|
||||||
}
|
}
|
||||||
|
|
||||||
isCallConnected = false;
|
isCallConnected = false;
|
||||||
|
LogUtil.debug("Stopped Stream(s)");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function netStatus (evt:NetStatusEvent ):void {
|
private function netStatus (evt:NetStatusEvent ):void {
|
||||||
|
@ -36,24 +36,72 @@
|
|||||||
# 2010-09-15 FFD Updates for 0.71-dev
|
# 2010-09-15 FFD Updates for 0.71-dev
|
||||||
# 2010-10-16 FFD Updates for 0.71-beta
|
# 2010-10-16 FFD Updates for 0.71-beta
|
||||||
# 2010-11-06 FFD Added logic to ensure red5 shuts down
|
# 2010-11-06 FFD Added logic to ensure red5 shuts down
|
||||||
|
# 2010-12-12 FFD Fixed bug #778
|
||||||
|
# 2010-12-12 FFD Added support for Intalio VM
|
||||||
|
|
||||||
#set -x
|
#set -x
|
||||||
|
|
||||||
#
|
|
||||||
# Setup some global variables
|
|
||||||
#
|
|
||||||
|
|
||||||
|
BBB_VERSION="0.71a-dev"
|
||||||
|
|
||||||
|
if [ ! -f /usr/share/red5/webapps/bigbluebutton/WEB-INF/red5-web.xml ]; then
|
||||||
|
echo "#"
|
||||||
|
echo "# BigBlueButton does not appear to be installed ... exiting"
|
||||||
|
echo "#"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Figure out if we're using Asterisk or FreeSWITCH
|
||||||
|
#
|
||||||
if cat /usr/share/red5/webapps/bigbluebutton/WEB-INF/red5-web.xml | grep -v '<!--' | grep -q 'bbb-voice-asterisk.xml'; then
|
if cat /usr/share/red5/webapps/bigbluebutton/WEB-INF/red5-web.xml | grep -v '<!--' | grep -q 'bbb-voice-asterisk.xml'; then
|
||||||
VOICE_CONFERENCE="bbb-voice-asterisk.xml"
|
VOICE_CONFERENCE="bbb-voice-asterisk.xml"
|
||||||
elif cat /usr/share/red5/webapps/bigbluebutton/WEB-INF/red5-web.xml | grep -v '<!--' | grep -q 'bbb-voice-freeswitch.xml'; then
|
elif cat /usr/share/red5/webapps/bigbluebutton/WEB-INF/red5-web.xml | grep -v '<!--' | grep -q 'bbb-voice-freeswitch.xml'; then
|
||||||
VOICE_CONFERENCE="bbb-voice-freeswitch.xml"
|
VOICE_CONFERENCE="bbb-voice-freeswitch.xml"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Determine IP so it works on multilingual installations
|
||||||
|
#
|
||||||
IP=$(ifconfig | grep -v '127.0.0.1' | grep -E "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | head -1 | cut -d: -f2 | awk '{ print $1}')
|
IP=$(ifconfig | grep -v '127.0.0.1' | grep -E "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | head -1 | cut -d: -f2 | awk '{ print $1}')
|
||||||
|
|
||||||
GENTOO=$(uname -r | grep gentoo | cut -d- -f2);
|
|
||||||
TOMCAT=""
|
#
|
||||||
BBB_VERSION="0.71"
|
# Figure out our environemtn
|
||||||
|
#
|
||||||
|
|
||||||
|
PLATFORM=ubuntu
|
||||||
|
|
||||||
|
RED5_DIR=/usr/share/red5
|
||||||
|
ACTIVEMQ_DIR=/usr/share/activemq
|
||||||
|
|
||||||
|
SERVLET_CONTAINER=tomcat6
|
||||||
|
SERVLET_LOGS=/var/lib/tomcat6/logs
|
||||||
|
SERVLET_DIR=/var/lib/tomcat6/webapps
|
||||||
|
|
||||||
|
|
||||||
|
if [ -f /etc/lsb-release ]; then
|
||||||
|
if grep -q Ubuntu /etc/lsb-release; then
|
||||||
|
if [ -f /etc/init.d/cloud ]; then
|
||||||
|
#
|
||||||
|
# Support configuration on Intalio Cloud VM
|
||||||
|
#
|
||||||
|
PLATFORM=ubuntu-jetty-intalio
|
||||||
|
SERVLET_CONTAINER=cloud
|
||||||
|
SERVLET_LOGS=/var/www/cloud/jetty/logs
|
||||||
|
SERVLET_DIR=/var/www/cloud/webappsviacontext
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /etc/redhat-release ]; then
|
||||||
|
#
|
||||||
|
# unsupported
|
||||||
|
#
|
||||||
|
PLATFORM=redhat
|
||||||
|
SERVLET_LOGS=/var/log/tomcat6
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Helper functions
|
# Helper functions
|
||||||
@ -61,6 +109,7 @@ BBB_VERSION="0.71"
|
|||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
# Check if the function has a value and, if not, print an error message
|
||||||
# $1 -- name of value
|
# $1 -- name of value
|
||||||
# $2 -- loctation of value
|
# $2 -- loctation of value
|
||||||
# $3 -- value to check
|
# $3 -- value to check
|
||||||
@ -80,65 +129,16 @@ check_file() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
get_platform() {
|
|
||||||
|
|
||||||
if [ -f /etc/lsb-release ]; then
|
|
||||||
if grep -q Ubuntu /etc/lsb-release; then
|
|
||||||
echo "ubuntu"
|
|
||||||
fi
|
|
||||||
elif [ ${GENTOO} ]; then
|
|
||||||
echo "gentoo"
|
|
||||||
else
|
|
||||||
echo "redhat"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PLATFORM=$(get_platform)
|
|
||||||
|
|
||||||
is_redhat() {
|
|
||||||
if [ "$PLATFORM" == "redhat" ]; then
|
|
||||||
echo "yes"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
is_ubuntu() {
|
|
||||||
if [ "$PLATFORM" == "ubuntu" ]; then
|
|
||||||
echo "yes"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
is_gentoo() {
|
|
||||||
if [ "$PLATFORM" == "gentoo" ]; then
|
|
||||||
echo "yes"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
is_vm() {
|
is_vm() {
|
||||||
|
#
|
||||||
|
# Is the the BigBlueButton VM?
|
||||||
|
#
|
||||||
if [ -f /home/firstuser/.profile ]; then
|
if [ -f /home/firstuser/.profile ]; then
|
||||||
echo $(cat /home/firstuser/.profile | grep BigBlueButton)
|
echo $(cat /home/firstuser/.profile | grep BigBlueButton)
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ "$(is_redhat)" ]; then
|
|
||||||
TOMCAT="tomcat6"
|
|
||||||
RED5_DIR="/usr/share/red5"
|
|
||||||
ACTIVEMQ_DIR="/usr/share/activemq"
|
|
||||||
TOMCAT6_LOGS="/var/log/${TOMCAT}"
|
|
||||||
elif [ "$(is_gentoo)" ]; then
|
|
||||||
TOMCAT="tomcat-6"
|
|
||||||
RED5_DIR="/usr/share/red5"
|
|
||||||
ACTIVEMQ_DIR="/usr/share/activemq"
|
|
||||||
TOMCAT6_LOGS="/var/lib/${TOMCAT}/logs"
|
|
||||||
else
|
|
||||||
if [ "$(is_ubuntu)" ]; then
|
|
||||||
TOMCAT="tomcat6"
|
|
||||||
RED5_DIR="/usr/share/red5"
|
|
||||||
ACTIVEMQ_DIR="/usr/share/activemq"
|
|
||||||
TOMCAT6_LOGS="/var/lib/${TOMCAT}/logs"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
print_header() {
|
print_header() {
|
||||||
if [ ! $HEADER ]; then
|
if [ ! $HEADER ]; then
|
||||||
@ -166,10 +166,10 @@ need_root() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "BigBlueButton Configuration Utility - Version $BBB_VERSION-dev6"
|
echo "BigBlueButton Configuration Utility - Version $BBB_VERSION"
|
||||||
echo "http://code.google.com/p/bigbluebutton/wiki/BBBConf"
|
echo "http://code.google.com/p/bigbluebutton/wiki/BBBConf"
|
||||||
echo
|
echo
|
||||||
echo "$0 [options]"
|
echo " bbb-conf [options]"
|
||||||
echo
|
echo
|
||||||
echo "Configuration:"
|
echo "Configuration:"
|
||||||
echo " --version Display BigBlueButton version (packages)"
|
echo " --version Display BigBlueButton version (packages)"
|
||||||
@ -184,7 +184,7 @@ usage() {
|
|||||||
echo " --watch Scan the log files for error messages every 2 seconds"
|
echo " --watch Scan the log files for error messages every 2 seconds"
|
||||||
echo " --salt View the URL and security salt for the server"
|
echo " --salt View the URL and security salt for the server"
|
||||||
echo
|
echo
|
||||||
echo "Administration":
|
echo "Administration:"
|
||||||
echo " --restart Restart BigBueButton"
|
echo " --restart Restart BigBueButton"
|
||||||
echo " --stop Stop BigBueButton"
|
echo " --stop Stop BigBueButton"
|
||||||
echo " --start Start BigBueButton"
|
echo " --start Start BigBueButton"
|
||||||
@ -247,7 +247,7 @@ uncomment () {
|
|||||||
|
|
||||||
stop_bigbluebutton () {
|
stop_bigbluebutton () {
|
||||||
/etc/init.d/red5 stop
|
/etc/init.d/red5 stop
|
||||||
/etc/init.d/${TOMCAT} stop
|
/etc/init.d/${SERVLET_CONTAINER} stop
|
||||||
/etc/init.d/nginx stop
|
/etc/init.d/nginx stop
|
||||||
|
|
||||||
if [ -a /opt/freeswitch/run/freeswitch.pid ]; then
|
if [ -a /opt/freeswitch/run/freeswitch.pid ]; then
|
||||||
@ -259,7 +259,10 @@ stop_bigbluebutton () {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
/etc/init.d/activemq stop
|
/etc/init.d/activemq stop
|
||||||
/etc/init.d/bbb-openoffice-headless stop
|
|
||||||
|
if [ -f /etc/init.d/bbb-openoffice-headless ]; then
|
||||||
|
/etc/init.d/bbb-openoffice-headless stop
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
start_bigbluebutton () {
|
start_bigbluebutton () {
|
||||||
@ -300,14 +303,16 @@ start_bigbluebutton () {
|
|||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
/etc/init.d/bbb-openoffice-headless start
|
if [ -f /etc/init.d/bbb-openoffice-headless ]; then
|
||||||
|
/etc/init.d/bbb-openoffice-headless start
|
||||||
|
fi
|
||||||
/etc/init.d/nginx start
|
/etc/init.d/nginx start
|
||||||
/etc/init.d/red5 start
|
/etc/init.d/red5 start
|
||||||
/etc/init.d/${TOMCAT} start
|
/etc/init.d/${SERVLET_CONTAINER} start
|
||||||
|
|
||||||
#
|
#
|
||||||
# At this point the red5 and tomcat6 applications are starting up.
|
# At this point the red5 and servlet container applications are starting up.
|
||||||
#
|
#
|
||||||
echo -n "Waiting for BigBlueButton to finish starting up (this may take a minute): "
|
echo -n "Waiting for BigBlueButton to finish starting up (this may take a minute): "
|
||||||
|
|
||||||
@ -321,8 +326,22 @@ start_bigbluebutton () {
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! wget http://$NGINX_IP/bigbluebutton/api -O - --quiet | grep -q SUCCESS; then
|
if [ $PLATFORM == "ubuntu-jetty-intalio" ]; then
|
||||||
echo "Startup unsuccessful"
|
#
|
||||||
|
# Wait until jetty has finished starting before checking for bbb-web
|
||||||
|
#
|
||||||
|
if ! nc -z -w 1 127.0.0.1 8443; then
|
||||||
|
while ! nc -z -w 1 127.0.0.1 8443; do
|
||||||
|
echo -n "."
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
BBB_WEB=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
|
||||||
|
if ! wget http://$BBB_WEB/bigbluebutton/api -O - --quiet | grep -q SUCCESS; then
|
||||||
|
echo "Startup unsuccessful: could not connect to http://$BBB_WEB/bigbluebutton/api"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -334,7 +353,7 @@ display_bigbluebutton_status () {
|
|||||||
/etc/init.d/activemq status
|
/etc/init.d/activemq status
|
||||||
/etc/init.d/nginx status
|
/etc/init.d/nginx status
|
||||||
/etc/init.d/red5 status
|
/etc/init.d/red5 status
|
||||||
/etc/init.d/${TOMCAT} status
|
/etc/init.d/${SERVLET_CONTAINER} status
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
@ -473,10 +492,10 @@ while [ $# -gt 0 ]; do
|
|||||||
if [ "$1" = "--salt" -o "$1" = "-salt" -o "$1" = "--setsalt" ]; then
|
if [ "$1" = "--salt" -o "$1" = "-salt" -o "$1" = "--setsalt" ]; then
|
||||||
SALT="${2}"
|
SALT="${2}"
|
||||||
if [ -z "$SALT" ]; then
|
if [ -z "$SALT" ]; then
|
||||||
IP=$(cat /var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
|
BBB_WEB=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
|
||||||
SALT=`cat /var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep securitySalt | cut -d= -f2`;
|
SALT=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep securitySalt | cut -d= -f2);
|
||||||
echo
|
echo
|
||||||
echo " URL: http://$IP/bigbluebutton/"
|
echo " URL: http://$BBB_WEB/bigbluebutton/"
|
||||||
echo " Salt: $SALT"
|
echo " Salt: $SALT"
|
||||||
echo
|
echo
|
||||||
exit 0
|
exit 0
|
||||||
@ -644,12 +663,12 @@ if [ $SETUPDEV ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "# Copying the bigbluebutton.properites in /var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties to ~/.grails/bigbluebutton-config.properties"
|
echo "# Copying the bigbluebutton.properites in ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties to ~/.grails/bigbluebutton-config.properties"
|
||||||
mkdir -p ~/.grails
|
mkdir -p ~/.grails
|
||||||
cp /var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties ~/.grails/bigbluebutton-config.properties
|
cp ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties ~/.grails/bigbluebutton-config.properties
|
||||||
|
|
||||||
echo "# Copying the bbb_api_conf.jsp into /var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties to ${BBBWEBHOME}/web-app/demo"
|
echo "# Copying the bbb_api_conf.jsp into ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties to ${BBBWEBHOME}/web-app/demo"
|
||||||
cp /var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp ${BBBWEBHOME}/web-app/demo
|
cp ${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp ${BBBWEBHOME}/web-app/demo
|
||||||
|
|
||||||
echo "# Enabling $USER to write to /var/bigbluebutton to upload slides"
|
echo "# Enabling $USER to write to /var/bigbluebutton to upload slides"
|
||||||
sudo chmod -R ugo+rwx /var/bigbluebutton
|
sudo chmod -R ugo+rwx /var/bigbluebutton
|
||||||
@ -665,7 +684,7 @@ if [ $SETUPDEV ]; then
|
|||||||
echo "
|
echo "
|
||||||
# Done. To run your local build of bbb-web:
|
# Done. To run your local build of bbb-web:
|
||||||
|
|
||||||
sudo /etc/init.d/${TOMCAT} stop
|
sudo /etc/init.d/${SERVLET_CONTAINER} stop
|
||||||
cd ${BBBWEBHOME}
|
cd ${BBBWEBHOME}
|
||||||
ant
|
ant
|
||||||
"
|
"
|
||||||
@ -713,7 +732,7 @@ if [ $SETUPDEV ]; then
|
|||||||
echo "# Creating /var/bigbluebutton/conference-mock-default/conference-mock-default/room-mock-default"
|
echo "# Creating /var/bigbluebutton/conference-mock-default/conference-mock-default/room-mock-default"
|
||||||
sudo mkdir -p /var/bigbluebutton/conference-mock-default/conference-mock-default/room-mock-default
|
sudo mkdir -p /var/bigbluebutton/conference-mock-default/conference-mock-default/room-mock-default
|
||||||
echo "# chown /var/bigbluebutton/conference-mock-default to tomcat6"
|
echo "# chown /var/bigbluebutton/conference-mock-default to tomcat6"
|
||||||
sudo chown -R tomcat6.tomcat6 /var/bigbluebutton/conference-mock-default
|
sudo chown -R tomcat6:tomcat6 /var/bigbluebutton/conference-mock-default
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd $BBBCLIENTHOME
|
cd $BBBCLIENTHOME
|
||||||
@ -868,22 +887,24 @@ check_configuration() {
|
|||||||
#
|
#
|
||||||
# Check if BigBlueButto is defined in Nginx
|
# Check if BigBlueButto is defined in Nginx
|
||||||
#
|
#
|
||||||
if [ ! -L /etc/nginx/sites-enabled/bigbluebutton ]; then
|
if [ $PLATFORM != "ubuntu-jetty-intalio" ]; then
|
||||||
echo "# Nginx: BigBlueButton appears to be disabled"
|
if [ ! -L /etc/nginx/sites-enabled/bigbluebutton ]; then
|
||||||
echo " - no symbolic link in /etc/nginx/sites-enabled/bigbluebutton to /etc/nginx/sites-available/bigbluebutton "
|
echo "# Nginx: BigBlueButton appears to be disabled"
|
||||||
|
echo " - no symbolic link in /etc/nginx/sites-enabled/bigbluebutton to /etc/nginx/sites-available/bigbluebutton "
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Make sure the salt for the API matches the server
|
# Make sure the salt for the API matches the server
|
||||||
#
|
#
|
||||||
SALT_PROPERTIES=$(cat /var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | tr -d '\r' | sed -n '/securitySalt/{s/.*=//;p}')
|
SALT_PROPERTIES=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | tr -d '\r' | sed -n '/securitySalt/{s/.*=//;p}')
|
||||||
SALT_DEMO=$(cat /var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp | tr -d '\r' | sed -n '/salt =/{s/.* = "//;s/".*//g;p}')
|
SALT_DEMO=$(cat ${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp | tr -d '\r' | sed -n '/salt =/{s/.* = "//;s/".*//g;p}')
|
||||||
|
|
||||||
if [ "$SALT_PROPERTIES" != "$SALT_DEMO" ]; then
|
if [ "$SALT_PROPERTIES" != "$SALT_DEMO" ]; then
|
||||||
echo "# Salt mismatch: "
|
echo "# Salt mismatch: "
|
||||||
echo "# /var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties=$SALT_PROPERTIES"
|
echo "# ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties=$SALT_PROPERTIES"
|
||||||
echo "# /var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp=$SALT_DEMO"
|
echo "# ${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp=$SALT_DEMO"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
@ -891,7 +912,7 @@ check_configuration() {
|
|||||||
# Look for properties with no values set
|
# Look for properties with no values set
|
||||||
#
|
#
|
||||||
CONFIG_FILES="$RED5_DIR/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties \
|
CONFIG_FILES="$RED5_DIR/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties \
|
||||||
/var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties \
|
${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties \
|
||||||
$RED5_DIR/webapps/sip/WEB-INF/bigbluebutton-sip.properties"
|
$RED5_DIR/webapps/sip/WEB-INF/bigbluebutton-sip.properties"
|
||||||
|
|
||||||
for file in $CONFIG_FILES ; do
|
for file in $CONFIG_FILES ; do
|
||||||
@ -908,17 +929,17 @@ $RED5_DIR/webapps/sip/WEB-INF/bigbluebutton-sip.properties"
|
|||||||
#
|
#
|
||||||
# Check that the supporting applications are installed
|
# Check that the supporting applications are installed
|
||||||
#
|
#
|
||||||
VARFolder=$(cat /var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep swfToolsDir | cut -d= -f2)
|
VARFolder=$(cat $SERVLET_DIR/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep swfToolsDir | cut -d= -f2)
|
||||||
if [ ! -x $VARFolder/pdf2swf ] && [ ! -x $VARFolder/jpeg2swf ] && [ ! -x $VARFolder/png2swf ]; then
|
if [ ! -x $VARFolder/pdf2swf ] && [ ! -x $VARFolder/jpeg2swf ] && [ ! -x $VARFolder/png2swf ]; then
|
||||||
echo "# pdf2swf, jpeg2swf and png2swf are not installed in $VARFolder"
|
echo "# pdf2swf, jpeg2swf and png2swf are not installed in $VARFolder"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
VARFolder=$(cat /var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep imageMagickDir | cut -d= -f2)
|
VARFolder=$(cat $SERVLET_DIR/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep imageMagickDir | cut -d= -f2)
|
||||||
if [ ! -x $VARFolder/convert ]; then
|
if [ ! -x $VARFolder/convert ]; then
|
||||||
echo "# ImageMagick's convert is not installed in $VARFolder"
|
echo "# ImageMagick's convert is not installed in $VARFolder"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
VARFolder=$(cat /var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep ghostScriptExec | cut -d= -f2)
|
VARFolder=$(cat $SERVLET_DIR/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep ghostScriptExec | cut -d= -f2)
|
||||||
if [ ! -x $VARFolder ]; then
|
if [ ! -x $VARFolder ]; then
|
||||||
echo "# Ghostscript is not installd in $VARFolder"
|
echo "# Ghostscript is not installd in $VARFolder"
|
||||||
fi
|
fi
|
||||||
@ -1012,14 +1033,14 @@ check_state() {
|
|||||||
|
|
||||||
if ! netstat -ant | grep '8080' > /dev/null; then
|
if ! netstat -ant | grep '8080' > /dev/null; then
|
||||||
print_header
|
print_header
|
||||||
NOT_RUNNING_APPS="${NOT_RUNNING_APPS} ${TOMCAT} or grails"
|
NOT_RUNNING_APPS="${NOT_RUNNING_APPS} ${SERVLET_CONTAINER} or grails"
|
||||||
else
|
else
|
||||||
if ps aux | ps -aef | grep -v grep | grep grails | grep run-app > /dev/null; then
|
if ps aux | ps -aef | grep -v grep | grep grails | grep run-app > /dev/null; then
|
||||||
print_header
|
print_header
|
||||||
RUNNING_APPS="${RUNNING_APPS} Grails"
|
RUNNING_APPS="${RUNNING_APPS} Grails"
|
||||||
echo "# ${TOMCAT}: noticed you are running grails run-app instead of tomcat"
|
echo "# ${SERVLET_CONTAINER}: noticed you are running grails run-app instead of ${SERVLET_CONTAINER}"
|
||||||
else
|
else
|
||||||
RUNNING_APPS="${RUNNING_APPS} ${TOMCAT}"
|
RUNNING_APPS="${RUNNING_APPS} ${SERVLET_CONTAINER}"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -1120,10 +1141,10 @@ check_state() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
#
|
#
|
||||||
# Check that tomcat6 started properly and has created log files
|
# Check that the servlet container has started properly and has created log files
|
||||||
#
|
#
|
||||||
if [ -z "$(ls -A $TOMCAT6_LOGS)" ]; then
|
if [ -z "$(ls -A $SERVLET_LOGS)" ]; then
|
||||||
echo "# empty directory: $TOMCAT6_LOGS contains no logs"
|
echo "# empty directory: $SERVLET_LOGS contains no logs"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1144,11 +1165,11 @@ check_state() {
|
|||||||
# Check if the local server can access the API. This is a common problem when setting up BigBlueButton behind
|
# Check if the local server can access the API. This is a common problem when setting up BigBlueButton behind
|
||||||
# a firewall
|
# a firewall
|
||||||
#
|
#
|
||||||
NGINX_IP=$(cat /etc/nginx/sites-available/bigbluebutton | sed -n '/server_name/{s/.*name[ ]*//;s/;//;p}')
|
BBB_WEB=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
|
||||||
check_no_value server_name /etc/nginx/sites-available/bigbluebutton $NGINX_IP
|
check_no_value server_name /etc/nginx/sites-available/bigbluebutton $BBB_WEB
|
||||||
if ! wget http://$NGINX_IP/bigbluebutton/api -O - --quiet | grep -q SUCCESS; then
|
if ! wget http://$BBB_WEB/bigbluebutton/api -O - --quiet | grep -q SUCCESS; then
|
||||||
echo
|
echo
|
||||||
echo "# This server could not connect to BigBlueButton through http://$NGINX_IP/"
|
echo "# This server could not connect to BigBlueButton through http://$BBB_WEB/"
|
||||||
echo "#"
|
echo "#"
|
||||||
echo "# If you are setting up BigBlueButton behind a firewall, see the FAQ"
|
echo "# If you are setting up BigBlueButton behind a firewall, see the FAQ"
|
||||||
echo "# for steps to setup BigBlueButton behind a firewall."
|
echo "# for steps to setup BigBlueButton behind a firewall."
|
||||||
@ -1192,7 +1213,7 @@ check_state() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
SIP_SERVER_HOST=$(cat /usr/share/red5/webapps/sip/WEB-INF/bigbluebutton-sip.properties | sed -n '/sip.server.host=/{s/.*=//;s/;//;p}')
|
SIP_SERVER_HOST=$(cat /usr/share/red5/webapps/sip/WEB-INF/bigbluebutton-sip.properties | sed -n '/sip.server.host=/{s/.*=//;s/;//;p}')
|
||||||
IP=$(ifconfig | grep -v '127.0.0.1' | grep -E "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | head -1 | cut -d: -f2 | awk '{ print $1}')
|
#IP=$(ifconfig | grep -v '127.0.0.1' | grep -E "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | head -1 | cut -d: -f2 | awk '{ print $1}')
|
||||||
if [ $SIP_SERVER_HOST != $IP ]; then
|
if [ $SIP_SERVER_HOST != $IP ]; then
|
||||||
echo
|
echo
|
||||||
echo "# The IP address ($SIP_SERVER_HOST) set for sip.server.host in"
|
echo "# The IP address ($SIP_SERVER_HOST) set for sip.server.host in"
|
||||||
@ -1252,19 +1273,19 @@ if [ $CHECK ]; then
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
echo "/var/www/bigbluebutton/client/conf/config.xml"
|
echo "/var/www/bigbluebutton/client/conf/config.xml"
|
||||||
IP=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/porttest /{s/.*host="//;s/".*//;p}')
|
PORT_IP=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/porttest /{s/.*host="//;s/".*//;p}')
|
||||||
echo " Port test (tunnel): $IP"
|
echo " Port test (tunnel): $PORT_IP"
|
||||||
|
|
||||||
IP=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/uri.*video/{s/.*rtmp:\/\///;s/\/.*//;p}')
|
RED5_IP=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/uri.*video/{s/.*rtmp:\/\///;s/\/.*//;p}')
|
||||||
echo " Red5: $IP"
|
echo " Red5: $RED5_IP"
|
||||||
|
|
||||||
# HOST=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/recordingHost/{s/.*recordingHost="http:\/\///;s/"//;p}')
|
# HOST=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/recordingHost/{s/.*recordingHost="http:\/\///;s/"//;p}')
|
||||||
# echo " host for bbb-web interface: $HOST"
|
# echo " host for bbb-web interface: $HOST"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "/etc/nginx/sites-available/bigbluebutton"
|
echo "/etc/nginx/sites-available/bigbluebutton"
|
||||||
IP=$(cat /etc/nginx/sites-available/bigbluebutton | sed -n '/server_name/{s/.*name[ ]*//;s/;//;p}')
|
NGINX_IP=$(cat /etc/nginx/sites-available/bigbluebutton | sed -n '/server_name/{s/.*name[ ]*//;s/;//;p}')
|
||||||
echo " server name: $IP"
|
echo " server name: $NGINX_IP"
|
||||||
|
|
||||||
PORT=$(cat /etc/nginx/sites-available/bigbluebutton | sed -n '/listen/{s/.*listen[ ]*//;s/;//;p}')
|
PORT=$(cat /etc/nginx/sites-available/bigbluebutton | sed -n '/listen/{s/.*listen[ ]*//;s/;//;p}')
|
||||||
echo " port: $PORT"
|
echo " port: $PORT"
|
||||||
@ -1272,16 +1293,16 @@ if [ $CHECK ]; then
|
|||||||
BBB_CLIENT_DOC_ROOT=$(cat /etc/nginx/sites-available/bigbluebutton | grep \/client -A 1 | head -n 2 | grep root | sed -n '{s/[ ]*root[ ]*//;s/;//;p}')
|
BBB_CLIENT_DOC_ROOT=$(cat /etc/nginx/sites-available/bigbluebutton | grep \/client -A 1 | head -n 2 | grep root | sed -n '{s/[ ]*root[ ]*//;s/;//;p}')
|
||||||
echo " bbb-client dir: $BBB_CLIENT_DOC_ROOT"
|
echo " bbb-client dir: $BBB_CLIENT_DOC_ROOT"
|
||||||
|
|
||||||
IP=$(cat /var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
|
BBB_WEB_IP=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
|
||||||
echo
|
echo
|
||||||
echo "/var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties (bbb-web)"
|
echo "${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties (bbb-web)"
|
||||||
echo " bbb-web host: $IP"
|
echo " bbb-web host: $BBB_WEB_IP"
|
||||||
|
|
||||||
if [ -f /var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp ]; then
|
if [ -f ${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp ]; then
|
||||||
IP=$(cat /var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp | sed -n '/String BigBlueButtonURL/{s/.*http:\/\///;s/\/.*//;p}' | tr -d '\015')
|
API_IP=$(cat ${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp | sed -n '/String BigBlueButtonURL/{s/.*http:\/\///;s/\/.*//;p}' | tr -d '\015')
|
||||||
echo
|
echo
|
||||||
echo "/var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp (API demos)"
|
echo "${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp (API demos)"
|
||||||
echo " bbb-web-api host: $IP"
|
echo " bbb-web-api host: $API_IP"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $VOICE_CONFERENCE == "bbb-voice-freeswitch.xml" ]; then
|
if [ $VOICE_CONFERENCE == "bbb-voice-freeswitch.xml" ]; then
|
||||||
@ -1321,7 +1342,7 @@ if [ $ZIP ]; then
|
|||||||
touch /tmp/empty
|
touch /tmp/empty
|
||||||
tar cf /tmp/$LOG_FILE.tar /tmp/empty > /dev/null 2>&1
|
tar cf /tmp/$LOG_FILE.tar /tmp/empty > /dev/null 2>&1
|
||||||
tar rf /tmp/$LOG_FILE.tar $RED5_DIR/log > /dev/null 2>&1
|
tar rf /tmp/$LOG_FILE.tar $RED5_DIR/log > /dev/null 2>&1
|
||||||
tar rf /tmp/$LOG_FILE.tar $TOMCAT6_LOGS > /dev/null 2>&1
|
tar rf /tmp/$LOG_FILE.tar $SERVLET_LOGS > /dev/null 2>&1
|
||||||
tar rf /tmp/$LOG_FILE.tar /var/log/bigbluebutton/* > /dev/null 2>&1
|
tar rf /tmp/$LOG_FILE.tar /var/log/bigbluebutton/* > /dev/null 2>&1
|
||||||
tar rf /tmp/$LOG_FILE.tar /var/log/nginx/error.log > /dev/null 2>&1
|
tar rf /tmp/$LOG_FILE.tar /var/log/nginx/error.log > /dev/null 2>&1
|
||||||
tar rf /tmp/$LOG_FILE.tar /var/log/syslog > /dev/null 2>&1
|
tar rf /tmp/$LOG_FILE.tar /var/log/syslog > /dev/null 2>&1
|
||||||
@ -1388,9 +1409,9 @@ if [ $DEBUG ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
rm -rf /tmp/t
|
rm -rf /tmp/t
|
||||||
sudo grep Exception $TOMCAT6_LOGS/* | grep -v CacheExceptionHandlerFactory > /tmp/t
|
sudo grep Exception $SERVLET_LOGS/* | grep -v CacheExceptionHandlerFactory > /tmp/t
|
||||||
if [ -s /tmp/t ]; then
|
if [ -s /tmp/t ]; then
|
||||||
echo " -- Exceptions found in $TOMCAT6_LOGS/ -- "
|
echo " -- Exceptions found in $SERVLET_LOGS/ -- "
|
||||||
cat /tmp/t
|
cat /tmp/t
|
||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
@ -1483,16 +1504,16 @@ if [ -n "$HOST" ]; then
|
|||||||
#
|
#
|
||||||
# Update configuration for BigBlueButton web app
|
# Update configuration for BigBlueButton web app
|
||||||
#
|
#
|
||||||
echo "Assigning $HOST for web application URL in /var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties"
|
echo "Assigning $HOST for web application URL in ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties"
|
||||||
|
|
||||||
sudo sed -i "s/bigbluebutton.web.serverURL=http:\/\/.*/bigbluebutton.web.serverURL=http:\/\/$HOST/g" \
|
sudo sed -i "s/bigbluebutton.web.serverURL=http:\/\/.*/bigbluebutton.web.serverURL=http:\/\/$HOST/g" \
|
||||||
/var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
|
${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
|
||||||
|
|
||||||
# 3 paramenter: the file, the variable name, the new value
|
# 3 paramenter: the file, the variable name, the new value
|
||||||
# echo "Assigning $HOST for FreeSWITCH Event Socket Layer URL in /var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties"
|
# echo "Assigning $HOST for FreeSWITCH Event Socket Layer URL in ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties"
|
||||||
# change_var_ip /usr/share/red5/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties esl.host $HOST
|
# change_var_ip /usr/share/red5/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties esl.host $HOST
|
||||||
|
|
||||||
# cat /var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
|
# cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
|
||||||
|
|
||||||
#
|
#
|
||||||
# Update nginx
|
# Update nginx
|
||||||
@ -1511,10 +1532,10 @@ if [ -n "$HOST" ]; then
|
|||||||
# Update api demos
|
# Update api demos
|
||||||
#
|
#
|
||||||
|
|
||||||
if [ -f /var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp ]; then
|
if [ -f ${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp ]; then
|
||||||
echo "Assigning $HOST for api demos in /var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp"
|
echo "Assigning $HOST for api demos in ${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp"
|
||||||
sudo sed -i "s/BigBlueButtonURL = \"http:\/\/\([^\"\/]*\)\([\"\/]\)/BigBlueButtonURL = \"http:\/\/$HOST\2/g" \
|
sudo sed -i "s/BigBlueButtonURL = \"http:\/\/\([^\"\/]*\)\([\"\/]\)/BigBlueButtonURL = \"http:\/\/$HOST\2/g" \
|
||||||
/var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp
|
${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -1758,8 +1779,8 @@ if [ $CLEAN ]; then
|
|||||||
rm -rf $RED5_DIR/log/*
|
rm -rf $RED5_DIR/log/*
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $TOMCAT6_LOGS ]; then
|
if [ $SERVLET_LOGS ]; then
|
||||||
rm -rf $TOMCAT6_LOGS/*
|
rm -rf $SERVLET_LOGS/*
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -rf /var/log/nginx/*
|
rm -rf /var/log/nginx/*
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
package org.bigbluebutton.deskshare.client;
|
package org.bigbluebutton.deskshare.client;
|
||||||
|
|
||||||
import javax.swing.JApplet;
|
import javax.swing.JApplet;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
|
|
||||||
public class DeskShareApplet extends JApplet implements ClientListener {
|
public class DeskShareApplet extends JApplet implements ClientListener {
|
||||||
@ -92,7 +95,20 @@ public class DeskShareApplet extends JApplet implements ClientListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onClientStop(ExitCode reason) {
|
public void onClientStop(ExitCode reason) {
|
||||||
client.stop();
|
// determine if client is disconnected _PTS_272_
|
||||||
|
if ( ExitCode.CONNECTION_TO_DESKSHARE_SERVER_DROPPED == reason ){
|
||||||
|
JFrame pframe = new JFrame("Desktop Sharing Disconneted");
|
||||||
|
if ( null != pframe ){
|
||||||
|
client.disconnected();
|
||||||
|
JOptionPane.showMessageDialog(pframe,
|
||||||
|
"Disconnected. Reason: Lost connection to the server." + reason ,
|
||||||
|
"Disconnected" ,JOptionPane.ERROR_MESSAGE );
|
||||||
|
}else{
|
||||||
|
System.out.println("Desktop sharing allocate memory failed.");
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
client.stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,32 @@ public class DeskshareClient {
|
|||||||
screenSharer.start();
|
screenSharer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
; disconnected
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
; DESCRIPTION
|
||||||
|
; This routine is used to set the desktop sharing string to disconnected.
|
||||||
|
;
|
||||||
|
; RETURNS : N/A
|
||||||
|
;
|
||||||
|
; INTERFACE NOTES
|
||||||
|
;
|
||||||
|
; INPUT : N/A
|
||||||
|
;
|
||||||
|
; OUTPUT : N/A
|
||||||
|
;
|
||||||
|
; IMPLEMENTATION
|
||||||
|
;
|
||||||
|
; HISTORY
|
||||||
|
; __date__ : PTS:
|
||||||
|
; 2010.11.19 problem 272
|
||||||
|
;
|
||||||
|
******************************************************************************/
|
||||||
|
public void disconnected(){
|
||||||
|
System.out.println(NAME + "Disconneted");
|
||||||
|
screenSharer.disconnected();
|
||||||
|
} // END FUNCTION disconnected
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
System.out.println(NAME + "Stop");
|
System.out.println(NAME + "Stop");
|
||||||
screenSharer.stop();
|
screenSharer.stop();
|
||||||
|
@ -63,7 +63,36 @@ public class DeskshareSystemTray {
|
|||||||
};
|
};
|
||||||
EventQueue.invokeLater(runner);
|
EventQueue.invokeLater(runner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
; disconnectIconSystemTrayMessage
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
; DESCRIPTION
|
||||||
|
; This routine is used to change icon system tray message string
|
||||||
|
; to disconnect.
|
||||||
|
;
|
||||||
|
; RETURNS : N/A
|
||||||
|
;
|
||||||
|
; INTERFACE NOTES
|
||||||
|
;
|
||||||
|
; INPUT : N/A
|
||||||
|
;
|
||||||
|
; OUTPUT : N/A
|
||||||
|
;
|
||||||
|
; IMPLEMENTATION
|
||||||
|
;
|
||||||
|
; HISTORY
|
||||||
|
; __date__ : PTS:
|
||||||
|
; 2010.11.19 problem 272
|
||||||
|
;
|
||||||
|
******************************************************************************/
|
||||||
|
public void disconnectIconSystemTrayMessage(){
|
||||||
|
trayIcon.setToolTip("Disconnected");
|
||||||
|
trayIcon.displayMessage("Deskshare Disconnected" ,
|
||||||
|
"You're disconnected from desktop sharing",
|
||||||
|
TrayIcon.MessageType.ERROR);
|
||||||
|
} // END FUNCTION disconnectIconSystemTrayMessage
|
||||||
|
|
||||||
public void removeIconFromSystemTray() {
|
public void removeIconFromSystemTray() {
|
||||||
if (tray != null && trayIcon != null) {
|
if (tray != null && trayIcon != null) {
|
||||||
tray.remove(trayIcon);
|
tray.remove(trayIcon);
|
||||||
|
@ -41,6 +41,32 @@ public class FullScreenSharer implements ScreenSharer {
|
|||||||
listener = l;
|
listener = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
; disconnected
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
; DESCRIPTION
|
||||||
|
; This routine is used to pop up the dialog box and change icon try
|
||||||
|
; message when client is disconnected from the server.
|
||||||
|
;
|
||||||
|
; RETURNS : N/A
|
||||||
|
;
|
||||||
|
; INTERFACE NOTES
|
||||||
|
;
|
||||||
|
; INPUT : N/A
|
||||||
|
;
|
||||||
|
; OUTPUT : N/A
|
||||||
|
;
|
||||||
|
; IMPLEMENTATION
|
||||||
|
;
|
||||||
|
; HISTORY
|
||||||
|
; __date__ : PTS:
|
||||||
|
; 2010.11.19 problem 272
|
||||||
|
;
|
||||||
|
******************************************************************************/
|
||||||
|
public void disconnected(){
|
||||||
|
sharer.disconnectSharing();
|
||||||
|
} // END FUNCTION disconnected
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
sharer.stopSharing();
|
sharer.stopSharing();
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,34 @@ public class ScreenRegionSharer implements ScreenSharer {
|
|||||||
listener = l;
|
listener = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
; disconnected
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
; DESCRIPTION
|
||||||
|
; This routine is used to pop up the dialog and change icon try message when
|
||||||
|
; client is disconnected from server.
|
||||||
|
;
|
||||||
|
; RETURNS : N/A
|
||||||
|
;
|
||||||
|
; INTERFACE NOTES
|
||||||
|
;
|
||||||
|
; INPUT : N/A
|
||||||
|
;
|
||||||
|
; OUTPUT : N/A
|
||||||
|
;
|
||||||
|
; IMPLEMENTATION
|
||||||
|
;
|
||||||
|
; HISTORY
|
||||||
|
; __date__ : PTS:
|
||||||
|
; 2010.11.19 problem 272
|
||||||
|
;
|
||||||
|
******************************************************************************/
|
||||||
|
public void disconnected(){
|
||||||
|
frame.setVisible(false);
|
||||||
|
sharer.disconnectSharing();
|
||||||
|
System.out.println(NAME + "Desktop sharing disconneted");
|
||||||
|
} // END FUNCTION disconnected
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
frame.setVisible(false);
|
frame.setVisible(false);
|
||||||
sharer.stopSharing();
|
sharer.stopSharing();
|
||||||
|
@ -23,6 +23,7 @@ package org.bigbluebutton.deskshare.client;
|
|||||||
|
|
||||||
public interface ScreenSharer {
|
public interface ScreenSharer {
|
||||||
void start();
|
void start();
|
||||||
|
void disconnected(); // 2010.11.19 _PTS_272_
|
||||||
void stop();
|
void stop();
|
||||||
void addClientListener(ClientListener l);
|
void addClientListener(ClientListener l);
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,36 @@ public class ScreenSharerRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
; disconnectSharing
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
; DESCRIPTION
|
||||||
|
; This routine is used to stop the screen capture, change desktop
|
||||||
|
; sharing system icon tray message.
|
||||||
|
;
|
||||||
|
; RETURNS : N/A
|
||||||
|
;
|
||||||
|
; INTERFACE NOTES
|
||||||
|
;
|
||||||
|
; INPUT : N/A
|
||||||
|
;
|
||||||
|
; OUTPUT : N/A
|
||||||
|
;
|
||||||
|
; IMPLEMENTATION
|
||||||
|
;
|
||||||
|
; HISTORY
|
||||||
|
; __date__ : PTS:
|
||||||
|
; 2010.11.19 problem 272
|
||||||
|
;
|
||||||
|
******************************************************************************/
|
||||||
|
public void disconnectSharing(){
|
||||||
|
System.out.println(NAME + "Disconneted");
|
||||||
|
System.out.println(NAME + "Change system tray icon message");
|
||||||
|
tray.disconnectIconSystemTrayMessage();
|
||||||
|
captureTaker.stop();
|
||||||
|
mouseLocTaker.stop();
|
||||||
|
} // END FUNCTION disconnectSharing
|
||||||
|
|
||||||
public void stopSharing() {
|
public void stopSharing() {
|
||||||
System.out.println(NAME + "Stopping");
|
System.out.println(NAME + "Stopping");
|
||||||
System.out.println(NAME + "Removing icon from system tray.");
|
System.out.println(NAME + "Removing icon from system tray.");
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
*
|
*
|
||||||
* ===License Header===
|
* ===License Header===
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.bigbluebutton.deskshare.client.frame;
|
package org.bigbluebutton.deskshare.client.frame;
|
||||||
|
|
||||||
import java.awt.BasicStroke;
|
import java.awt.BasicStroke;
|
||||||
@ -30,6 +31,7 @@ import java.awt.Frame;
|
|||||||
import java.awt.GradientPaint;
|
import java.awt.GradientPaint;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.GraphicsConfiguration;
|
||||||
import java.awt.GraphicsDevice;
|
import java.awt.GraphicsDevice;
|
||||||
import java.awt.GraphicsEnvironment;
|
import java.awt.GraphicsEnvironment;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
@ -95,7 +97,7 @@ class WindowlessFrame implements Serializable {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// properties that change during use
|
// properties that change during use
|
||||||
private Point mTopLeft = new Point();
|
private Point mTopLeft = new Point();
|
||||||
private Dimension mOverallSize = new Dimension();
|
private Dimension mOverallSize = new Dimension();
|
||||||
@ -115,7 +117,105 @@ class WindowlessFrame implements Serializable {
|
|||||||
private final BarFrame mBottomBorder;
|
private final BarFrame mBottomBorder;
|
||||||
private final BarFrame mLeftBorder;
|
private final BarFrame mLeftBorder;
|
||||||
private ToolbarFrame mToolbarFrame;
|
private ToolbarFrame mToolbarFrame;
|
||||||
|
|
||||||
|
private MultiScreen mScreen = new MultiScreen();
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
; Class MultiScreen
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
; DESCRIPTION
|
||||||
|
; This class is used to detect if the system has more than one screen.
|
||||||
|
******************************************************************************/
|
||||||
|
private class MultiScreen {
|
||||||
|
private int minX=0 ; //minimum of x position
|
||||||
|
private int totalWidth=0 ; // total screen resolution
|
||||||
|
private int curWidth=0 ; // primary screen width
|
||||||
|
private GraphicsEnvironment ge ;
|
||||||
|
private GraphicsDevice[] screenDevice ;
|
||||||
|
private boolean ismultiscreen=false ;
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
; MultiScreen
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
; DESCRIPTION
|
||||||
|
; This is the class constructor.
|
||||||
|
;
|
||||||
|
; RETURNS : N/A
|
||||||
|
;
|
||||||
|
; INTERFACE NOTES
|
||||||
|
;
|
||||||
|
; INPUT : N/A
|
||||||
|
;
|
||||||
|
; OUTPUT : N/A
|
||||||
|
;
|
||||||
|
; IMPLEMENTATION
|
||||||
|
;
|
||||||
|
; HISTORY
|
||||||
|
; __date__ : PTS:
|
||||||
|
; 2010.11.16 problem 644 and 647
|
||||||
|
;
|
||||||
|
******************************************************************************/
|
||||||
|
private MultiScreen(){
|
||||||
|
int i ;
|
||||||
|
|
||||||
|
ge = GraphicsEnvironment.getLocalGraphicsEnvironment() ;
|
||||||
|
screenDevice = ge.getScreenDevices() ;
|
||||||
|
|
||||||
|
if ( 1 < screenDevice.length ){
|
||||||
|
// this is the case for multiple devices.
|
||||||
|
// set the flag to indicate multiple devices on the system.
|
||||||
|
ismultiscreen=true ;
|
||||||
|
for ( i=0; i<screenDevice.length; i++){
|
||||||
|
GraphicsConfiguration[] gc = screenDevice[i].getConfigurations() ;
|
||||||
|
|
||||||
|
// determine the minimum x position for the main screen
|
||||||
|
if ( gc[0].getBounds().x <= minX ){
|
||||||
|
minX = gc[0].getBounds().x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine the total screen size
|
||||||
|
if ( gc[0].getBounds().x >= 0){
|
||||||
|
totalWidth = totalWidth + gc[0].getBounds().width;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
// this is the case for one screen only.
|
||||||
|
ismultiscreen = false ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the main screen width
|
||||||
|
curWidth = screenDevice[0].getConfigurations()[0].getBounds().width ;
|
||||||
|
|
||||||
|
} // END FUNCTION MultiScreen
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
; isMultiScreen
|
||||||
|
;----------------------------------------------------------------------------
|
||||||
|
; DESCRIPTION
|
||||||
|
; This routine returns if the system is multi-screen or not.
|
||||||
|
;
|
||||||
|
; RETURNS : true/false
|
||||||
|
;
|
||||||
|
; INTERFACE NOTES
|
||||||
|
;
|
||||||
|
; INPUT : N/A
|
||||||
|
;
|
||||||
|
;
|
||||||
|
; IMPLEMENTATION
|
||||||
|
;
|
||||||
|
; HISTORY
|
||||||
|
; __date__ : PTS:
|
||||||
|
; 2010.11.16 problem 644 and 647
|
||||||
|
;
|
||||||
|
******************************************************************************/
|
||||||
|
public boolean isMultiScreen(){
|
||||||
|
|
||||||
|
return ismultiscreen ;
|
||||||
|
} // END FUNCTION isMultiScreen
|
||||||
|
|
||||||
|
} // END CLASS MultiScreen
|
||||||
|
|
||||||
private class ToolbarFrame extends Window implements LocationAndSizeUpdateable {
|
private class ToolbarFrame extends Window implements LocationAndSizeUpdateable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@ -149,8 +249,51 @@ class WindowlessFrame implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseDragged(MouseEvent e) {
|
public void mouseDragged(MouseEvent e) {
|
||||||
final int changeInX = e.getLocationOnScreen().x - mActionOffset.x - mTopLeft.x;
|
int changeInX = e.getLocationOnScreen().x - mActionOffset.x - mTopLeft.x;
|
||||||
final int changeInY = e.getLocationOnScreen().y - mActionOffset.y - mTopLeft.y;
|
int changeInY = e.getLocationOnScreen().y - mActionOffset.y - mTopLeft.y;
|
||||||
|
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||||
|
Dimension d = tk.getScreenSize();
|
||||||
|
|
||||||
|
// check if multiscreen
|
||||||
|
if ( false == mScreen.isMultiScreen() ){
|
||||||
|
// case one screen only
|
||||||
|
if (mTopLeft.x < 1 && changeInX < 0) {
|
||||||
|
mTopLeft.x = 0;
|
||||||
|
changeInX = 0;
|
||||||
|
}
|
||||||
|
if (mTopLeft.y < 1 && changeInY < 0) {
|
||||||
|
mTopLeft.y = 0;
|
||||||
|
changeInY = 0;
|
||||||
|
}
|
||||||
|
if (mTopLeft.x + mOverallSize.width > (d.width-6) && changeInX > 0) {
|
||||||
|
mTopLeft.x = d.width - mOverallSize.width-5;
|
||||||
|
changeInX = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (mTopLeft.y + mOverallSize.height > (d.height-6) && changeInY > 0) {
|
||||||
|
mTopLeft.y = d.height - mOverallSize.height-5;
|
||||||
|
changeInY = 0;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
// case multiple screen
|
||||||
|
if (mTopLeft.x < mScreen.minX+1 && changeInX < 0) {
|
||||||
|
mTopLeft.x = mScreen.minX;
|
||||||
|
changeInX = 0;
|
||||||
|
}
|
||||||
|
if (mTopLeft.y < 1 && changeInY < 0) {
|
||||||
|
mTopLeft.y = 0;
|
||||||
|
changeInY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mTopLeft.x + mOverallSize.width > (mScreen.totalWidth-6) && changeInX > 0) {
|
||||||
|
mTopLeft.x = mScreen.totalWidth - mOverallSize.width-5;
|
||||||
|
changeInX = 0;
|
||||||
|
}
|
||||||
|
if (mTopLeft.y + mOverallSize.height > (d.height-6) && changeInY > 0) {
|
||||||
|
mTopLeft.y = d.height - mOverallSize.height-5;
|
||||||
|
changeInY = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (mMoving.get() && !e.isConsumed()) {
|
if (mMoving.get() && !e.isConsumed()) {
|
||||||
WindowlessFrame.this.setLocation(changeInX + mTopLeft.x, changeInY + mTopLeft.y);
|
WindowlessFrame.this.setLocation(changeInX + mTopLeft.x, changeInY + mTopLeft.y);
|
||||||
}
|
}
|
||||||
@ -175,8 +318,8 @@ class WindowlessFrame implements Serializable {
|
|||||||
|
|
||||||
private class WindowlessFrameResizingMouseListener extends MouseAdapter {
|
private class WindowlessFrameResizingMouseListener extends MouseAdapter {
|
||||||
|
|
||||||
private static final int CORNER_SIZE = 15;
|
private static final int CORNER_SIZE = 150;
|
||||||
|
|
||||||
private AtomicBoolean mResizing = new AtomicBoolean(false);
|
private AtomicBoolean mResizing = new AtomicBoolean(false);
|
||||||
|
|
||||||
private Point mActionOffset = null;
|
private Point mActionOffset = null;
|
||||||
@ -185,15 +328,28 @@ class WindowlessFrame implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseDragged(MouseEvent e) {
|
public void mouseDragged(MouseEvent e) {
|
||||||
final int changeInX = e.getLocationOnScreen().x - mActionOffset.x - mTopLeft.x;
|
int changeInX = e.getLocationOnScreen().x - mActionOffset.x - mTopLeft.x;
|
||||||
final int changeInY = e.getLocationOnScreen().y - mActionOffset.y - mTopLeft.y;
|
final int changeInY = e.getLocationOnScreen().y - mActionOffset.y - mTopLeft.y;
|
||||||
|
|
||||||
|
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||||
|
Dimension d = tk.getScreenSize();
|
||||||
|
|
||||||
if (mResizing.get()) {
|
if (mResizing.get()) {
|
||||||
int newH = mOriginalSize.height;
|
int newH = mOriginalSize.height;
|
||||||
int newW = mOriginalSize.width;
|
int newW = mOriginalSize.width;
|
||||||
if (mCorner == Corner.SOUTHEAST) {
|
if (Corner.SOUTHEAST == mCorner) {
|
||||||
newH += changeInY;
|
|
||||||
newW += changeInX;
|
if (e.getLocationOnScreen().x < mTopLeft.x+5) {
|
||||||
} else if (mCorner == Corner.NORTHEAST) {
|
newW = 5;
|
||||||
|
} else {
|
||||||
|
newW += changeInX;
|
||||||
|
}
|
||||||
|
if (e.getLocationOnScreen().y < mTopLeft.y+5) {
|
||||||
|
newH = 5;
|
||||||
|
} else {
|
||||||
|
newH += changeInY;
|
||||||
|
}
|
||||||
|
} /*else if (mCorner == Corner.NORTHEAST) {
|
||||||
mTopLeft.y = mTopLeft.y + changeInY;
|
mTopLeft.y = mTopLeft.y + changeInY;
|
||||||
newH = mOverallSize.height + -changeInY;
|
newH = mOverallSize.height + -changeInY;
|
||||||
newW += changeInX;
|
newW += changeInX;
|
||||||
@ -206,8 +362,33 @@ class WindowlessFrame implements Serializable {
|
|||||||
newH += changeInY;
|
newH += changeInY;
|
||||||
mTopLeft.x = mTopLeft.x + changeInX;
|
mTopLeft.x = mTopLeft.x + changeInX;
|
||||||
newW = mOverallSize.width + -changeInX;
|
newW = mOverallSize.width + -changeInX;
|
||||||
}
|
}*/
|
||||||
//System.out.println("orig size: " + mOriginalSize + ", newH: " + newH + ", newW: " + newW + ", X: " + changeInX + ", Y: " + changeInY);
|
//System.out.println("orig size: " + mOriginalSize + ", newH: " + newH + ", newW: " + newW + ", X: " + changeInX + ", Y: " + changeInY);
|
||||||
|
|
||||||
|
if (newH + mTopLeft.y > d.height-5){
|
||||||
|
newH = d.height - mTopLeft.y-5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if multiple screen _PTS_644_ _PTS_647_
|
||||||
|
if ( false == mScreen.isMultiScreen() ){
|
||||||
|
// one screen only
|
||||||
|
if (newW + mTopLeft.x > d.width-5){
|
||||||
|
newW = d.width - mTopLeft.x-5;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
int mWidth=0 ;
|
||||||
|
if ( mTopLeft.x > mScreen.curWidth ){
|
||||||
|
mWidth = mScreen.totalWidth ;
|
||||||
|
}else{
|
||||||
|
mWidth = d.width ;
|
||||||
|
}
|
||||||
|
if (newW + mTopLeft.x > mWidth-5 && mTopLeft.x >= 0){
|
||||||
|
newW = mWidth - mTopLeft.x-5;
|
||||||
|
}else if (mTopLeft.x<0 && mTopLeft.x + newW > -5){
|
||||||
|
newW = - mTopLeft.x-5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WindowlessFrame.this.setSize(newH, newW);
|
WindowlessFrame.this.setSize(newH, newW);
|
||||||
e.consume();
|
e.consume();
|
||||||
}
|
}
|
||||||
@ -235,13 +416,14 @@ class WindowlessFrame implements Serializable {
|
|||||||
private Corner nearCorner(Point mouse) {
|
private Corner nearCorner(Point mouse) {
|
||||||
if (isNearBottomRightCorner(mouse)) {
|
if (isNearBottomRightCorner(mouse)) {
|
||||||
return Corner.SOUTHEAST;
|
return Corner.SOUTHEAST;
|
||||||
} else if (isNearTopRightCorner(mouse)) {
|
} /* else if (isNearTopRightCorner(mouse)) {
|
||||||
return Corner.NORTHEAST;
|
return Corner.NORTHEAST;
|
||||||
} else if (isNearTopLeftCorner(mouse)) {
|
} else if (isNearTopLeftCorner(mouse)) {
|
||||||
return Corner.NORTHWEST;
|
return Corner.NORTHWEST;
|
||||||
} else if (isNearBottomLeftCorner(mouse)) {
|
} else if (isNearBottomLeftCorner(mouse)) {
|
||||||
return Corner.SOUTHWEST;
|
return Corner.SOUTHWEST;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +433,7 @@ class WindowlessFrame implements Serializable {
|
|||||||
return xToBotLeft < CORNER_SIZE && yToBotLeft < CORNER_SIZE;
|
return xToBotLeft < CORNER_SIZE && yToBotLeft < CORNER_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isNearTopRightCorner(Point mouse) {
|
/* private boolean isNearTopRightCorner(Point mouse) {
|
||||||
int xToTopRight = Math.abs(mTopLeft.x + (int) mOverallSize.getWidth() - mouse.x);
|
int xToTopRight = Math.abs(mTopLeft.x + (int) mOverallSize.getWidth() - mouse.x);
|
||||||
int yToTopRight = Math.abs(mTopLeft.y - mouse.y);
|
int yToTopRight = Math.abs(mTopLeft.y - mouse.y);
|
||||||
return xToTopRight < CORNER_SIZE && yToTopRight < CORNER_SIZE;
|
return xToTopRight < CORNER_SIZE && yToTopRight < CORNER_SIZE;
|
||||||
@ -268,17 +450,22 @@ class WindowlessFrame implements Serializable {
|
|||||||
int yToTopLeft = Math.abs(mTopLeft.y - mouse.y);
|
int yToTopLeft = Math.abs(mTopLeft.y - mouse.y);
|
||||||
return xToTopLeft < CORNER_SIZE && yToTopLeft < CORNER_SIZE;
|
return xToTopLeft < CORNER_SIZE && yToTopLeft < CORNER_SIZE;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseMoved(MouseEvent e) {
|
public void mouseMoved(MouseEvent e) {
|
||||||
final Point mouse = e.getLocationOnScreen();
|
final Point mouse = e.getLocationOnScreen();
|
||||||
|
|
||||||
|
/*
|
||||||
if (isNearTopLeftCorner(mouse)) {
|
if (isNearTopLeftCorner(mouse)) {
|
||||||
e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
|
e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
|
||||||
} else if (isNearBottomLeftCorner(mouse)) {
|
} else if (isNearBottomLeftCorner(mouse)) {
|
||||||
e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
|
e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
|
||||||
} else if (isNearTopRightCorner(mouse)) {
|
} else if (isNearTopRightCorner(mouse)) {
|
||||||
e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR));
|
e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR));
|
||||||
} else if (isNearBottomRightCorner(mouse)) {
|
} else
|
||||||
|
*/
|
||||||
|
if (isNearBottomRightCorner(mouse)) {
|
||||||
e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
|
e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
|
||||||
} else {
|
} else {
|
||||||
e.getComponent().setCursor(Cursor.getDefaultCursor());
|
e.getComponent().setCursor(Cursor.getDefaultCursor());
|
||||||
@ -559,4 +746,4 @@ class WindowlessFrame implements Serializable {
|
|||||||
System.out.println("Adding listeners......................");
|
System.out.println("Adding listeners......................");
|
||||||
mWindowFrame.add(mToolbarFrame);
|
mWindowFrame.add(mToolbarFrame);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user