Merge branch 'master' of github.com:bigbluebutton/bigbluebutton into record-and-playback-feature

This commit is contained in:
Marco Calderon 2010-12-17 12:03:11 +00:00
commit d6c8b30e19
45 changed files with 1095 additions and 503 deletions

View File

@ -19,14 +19,12 @@
**/
package org.bigbluebutton.voiceconf.red5;
import java.text.MessageFormat;
import java.util.List;
import org.slf4j.Logger;
import org.bigbluebutton.voiceconf.sip.PeerNotFoundException;
import org.bigbluebutton.voiceconf.sip.SipPeerManager;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.adapter.MultiThreadedApplicationAdapter;
import org.red5.server.api.IClient;
import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;
import org.red5.server.api.Red5;
@ -72,22 +70,43 @@ public class Application extends MultiThreadedApplicationAdapter {
}
@Override
public boolean appJoin(IClient client, IScope scope) {
log.debug("VoiceConferenceApplication roomJoin[" + client.getId() + "]");
clientConnManager.createClient(client.getId(), (IServiceCapableConnection) Red5.getConnectionLocal());
public boolean appConnect(IConnection conn, Object[] params) {
String userid = ((String) params[0]).toString();
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;
}
@Override
public void appLeave(IClient client, IScope scope) {
log.debug("VoiceConferenceApplication roomLeave[" + client.getId() + "]");
clientConnManager.removeClient(client.getId());
log.debug( "Red5SIP Client closing client {}", client.getId());
public void appDisconnect(IConnection conn) {
String clientId = Red5.getConnectionLocal().getClient().getId();
String userid = getUserId();
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");
if (peerId != null) {
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) {
// TODO Auto-generated catch block
e.printStackTrace();
@ -97,13 +116,16 @@ public class Application extends MultiThreadedApplicationAdapter {
@Override
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());
IConnection conn = Red5.getConnectionLocal();
String peerId = (String) conn.getAttribute("VOICE_CONF_PEER");
if (peerId != null) {
super.streamPublishStart(stream);
String clientId = conn.getClient().getId();
super.streamPublishStart(stream);
sipPeerManager.startTalkStream(peerId, clientId, stream, conn.getScope());
// recordStream(stream);
}
@ -128,12 +150,15 @@ public class Application extends MultiThreadedApplicationAdapter {
@Override
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();
String peerId = (String) conn.getAttribute("VOICE_CONF_PEER");
if (peerId != null) {
super.streamPublishStart(stream);
String clientId = conn.getClient().getId();
sipPeerManager.stopTalkStream(peerId, clientId, stream, conn.getScope());
super.streamBroadcastClose(stream);
}
@ -179,4 +204,16 @@ public class Application extends MultiThreadedApplicationAdapter {
public void setClientConnectionManager(ClientConnectionManager 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;
}
}

View File

@ -28,10 +28,14 @@ private static Logger log = Red5LoggerFactory.getLogger(ClientConnection.class,
private final IServiceCapableConnection connection;
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.connId = connId;
this.userid = userid;
this.username = username;
}
public String getConnId() {
@ -39,17 +43,17 @@ private static Logger log = Red5LoggerFactory.getLogger(ClientConnection.class,
}
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});
}
public void onJoinConferenceFail() {
log.debug("onOutgoingCallFailed");
log.debug("Notify client that {} [{}] failed to join the conference.", username, userid);
connection.invoke("failedToJoinVoiceConferenceCallback", new Object[] {"onUaCallFailed"});
}
public void onLeaveConference() {
log.debug("onCallClosed");
log.debug("Notify client that {} [{}] left the conference.", username, userid);
connection.invoke("disconnectedFromJoinVoiceConferenceCallback", new Object[] {"onUaCallClosed"});
}
}

View File

@ -31,14 +31,18 @@ public class ClientConnectionManager {
private Map<String, ClientConnection> clients = new ConcurrentHashMap<String, ClientConnection>();
public void createClient(String id, IServiceCapableConnection connection) {
ClientConnection cc = new ClientConnection(id, connection);
public void createClient(String id, String userid, String username, IServiceCapableConnection connection) {
ClientConnection cc = new ClientConnection(id, userid, username, connection);
clients.put(id, cc);
}
public void removeClient(String 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) {
@ -46,7 +50,7 @@ public class ClientConnectionManager {
if (cc != null) {
cc.onJoinConferenceSuccess(usertalkStream, userListenStream, codec);
} 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) {
cc.onJoinConferenceFail();
} 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) {
cc.onLeaveConference();
} else {
log.warn("Can't find connection {}", clientId);
log.warn("Can't find client {} to inform user that she has left the conference.", clientId);
}
}
}

View File

@ -35,7 +35,11 @@ public class Service {
private MessageFormat callExtensionPattern = new MessageFormat("{0}");
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 });
try {
sipPeerManager.call(peerId, getClientId(), callerName, extension);
@ -48,7 +52,10 @@ public class Service {
}
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 {
sipPeerManager.hangup(peerId, getClientId());
return true;
@ -70,4 +77,16 @@ public class Service {
public void setSipPeerManager(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;
}
}

View File

@ -25,7 +25,9 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;
import org.red5.server.api.Red5;
import org.red5.server.api.event.IEvent;
import org.red5.server.api.stream.IBroadcastStream;
import org.red5.server.api.stream.IStreamCodecInfo;
@ -140,11 +142,11 @@ public class AudioBroadcastStream implements IBroadcastStream, IProvider, IPipeC
}
public void start() {
log.trace("start()");
log.debug("Starting AudioBroadcastStream()");
}
public void stop() {
log.trace("stop");
log.debug("Stopping AudioBroadcastStream");
}
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) {
// log.trace("dispatchEvent(event:{})", event);
if (event instanceof IRTMPEvent) {
IRTMPEvent rtmpEvent = (IRTMPEvent) event;
if (livePipe != null) {
RTMPMessage msg = new RTMPMessage();
msg.setBody(rtmpEvent);
if (creationTime == null)
creationTime = (long)rtmpEvent.getTimestamp();
try {
// log.debug("dispatchEvent(event:)" + event);
livePipe.pushMessage(msg);

View File

@ -21,8 +21,10 @@ package org.bigbluebutton.voiceconf.red5.media;
public class AudioByteData {
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];
System.arraycopy(data, 0, this.data, 0, data.length);
}
@ -30,4 +32,8 @@ public class AudioByteData {
public byte[] getData() {
return data;
}
public boolean status() {
return poison;
}
}

View File

@ -81,8 +81,11 @@ public class CallStream implements StreamObserver {
}
public void startTalkStream(IBroadcastStream broadcastStream, IScope scope) throws StreamException {
log.debug("Starting userListenSteam");
userListenStream.start();
log.debug("userTalkStream setup");
userTalkStream.start(broadcastStream, scope);
log.debug("userTalkStream Started");
}
public void stopTalkStream(IBroadcastStream broadcastStream, IScope scope) {
@ -90,6 +93,7 @@ public class CallStream implements StreamObserver {
}
public void stop() {
log.debug("Stopping call stream");
userListenStream.stop();
}

View File

@ -19,12 +19,12 @@
**/
package org.bigbluebutton.voiceconf.red5.media;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.DatagramSocket;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import org.apache.mina.core.buffer.IoBuffer;
import org.bigbluebutton.voiceconf.red5.media.transcoder.FlashToSipTranscoder;
import org.bigbluebutton.voiceconf.red5.media.transcoder.TranscodedAudioDataListener;
@ -41,7 +41,9 @@ import org.slf4j.Logger;
public class FlashToSipAudioStream {
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 Runnable audioDataProcessor;
private volatile boolean processAudioData = false;
@ -58,6 +60,13 @@ public class FlashToSipAudioStream {
this.srcSocket = srcSocket;
this.connInfo = connInfo;
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 {
@ -73,16 +82,15 @@ public class FlashToSipAudioStream {
log.debug("skipping empty packet with no data");
return;
}
if (packet instanceof AudioData) {
byte[] data = SerializeUtils.ByteBufferToByteArray(buf);
AudioByteData abd = new AudioByteData(data);
try {
audioDataQ.put(abd);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
streamFromFlash.write(data, 1, data.length-1);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
@ -101,11 +109,23 @@ public class FlashToSipAudioStream {
}
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 {
AudioByteData abd = audioDataQ.take();
transcoder.transcode(abd, 1, abd.getData().length-1, new TranscodedAudioListener());
} catch (InterruptedException e) {
int bytesRead = streamToSip.read(nellyAudio, offset, remaining);
remaining -= bytesRead;
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
e.printStackTrace();
}
@ -114,7 +134,12 @@ public class FlashToSipAudioStream {
public void stop(IBroadcastStream broadcastStream, IScope scope) {
broadcastStream.removeStreamListener(mInputListener);
processAudioData = false;
if (broadcastStream != null) {
broadcastStream.stop();
broadcastStream.close();
}
processAudioData = false;
srcSocket.close();
}
public String getStreamName() {
@ -124,7 +149,7 @@ public class FlashToSipAudioStream {
private class TranscodedAudioListener implements TranscodedAudioDataListener {
@Override
public void handleTranscodedAudioData(byte[] audioData, long timestamp) {
if (audioData != null) {
if (audioData != null && processAudioData) {
rtpSender.sendAudio(audioData, transcoder.getCodecId(), timestamp);
} else {
log.warn("Transcodec audio is null. Discarding.");

View File

@ -84,14 +84,11 @@ public class RtpStreamReceiver {
public void receiveRtpPackets() {
int packetReceivedCounter = 0;
int internalBufferLength = payloadLength + RTP_HEADER_SIZE;
byte[] internalBuffer;
RtpPacket rtpPacket;
byte[] internalBuffer = new byte[internalBufferLength];
RtpPacket rtpPacket = new RtpPacket(internalBuffer, internalBufferLength);;
while (receivePackets) {
try {
internalBuffer = new byte[internalBufferLength];
rtpPacket = new RtpPacket(internalBuffer, internalBufferLength);
try {
rtpSocket.receive(rtpPacket);
packetReceivedCounter++;
// 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)) {
// 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() + "]");
processRtpPacket(rtpPacket);
lastSequenceNumber = rtpPacket.getSeqNum();
lastPacketTimestamp = rtpPacket.getTimestamp();
processRtpPacket(internalBuffer, RTP_HEADER_SIZE, payloadLength);
} else {
if (log.isDebugEnabled())
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) {
long now = System.currentTimeMillis();
if (now - lastPacketReceived > 100) {
if (now - lastPacketReceived > 200) {
if (log.isDebugEnabled())
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() + "]");
@ -217,11 +216,8 @@ public class RtpStreamReceiver {
return false;
}
private void processRtpPacket(RtpPacket rtpPacket) {
lastSequenceNumber = rtpPacket.getSeqNum();
lastPacketTimestamp = rtpPacket.getTimestamp();
AudioByteData audioData = new AudioByteData(rtpPacket.getPayload());
if (listener != null) listener.onAudioDataReceived(audioData);
private void processRtpPacket(byte[] rtpAudio, int offset, int len) {
if (listener != null) listener.onAudioDataReceived(rtpAudio, offset, len);
else log.debug("No listener for incoming audio packet");
}
}

View File

@ -22,5 +22,5 @@ package org.bigbluebutton.voiceconf.red5.media;
public interface RtpStreamReceiverListener {
void onStoppedReceiving();
void onAudioDataReceived(AudioByteData audioData);
void onAudioDataReceived(byte[] audioData, int offset, int len);
}

View File

@ -41,6 +41,7 @@ public class RtpStreamSender {
private final DatagramSocket srcSocket;
private final SipConnectInfo connInfo;
private boolean marked = false;
private long startTimestamp;
public RtpStreamSender(DatagramSocket srcSocket, SipConnectInfo connInfo) {
this.srcSocket = srcSocket;
@ -66,6 +67,7 @@ public class RtpStreamSender {
if (!marked) {
rtpPacket.setMarker(true);
marked = true;
startTimestamp = System.currentTimeMillis();
}
rtpPacket.setPadding(false);
rtpPacket.setExtension(false);

View File

@ -19,11 +19,12 @@
**/
package org.bigbluebutton.voiceconf.red5.media;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.DatagramSocket;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import org.apache.mina.core.buffer.IoBuffer;
import org.bigbluebutton.voiceconf.red5.media.transcoder.SipToFlashTranscoder;
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.net.rtmp.event.AudioData;
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.IBroadcastScope;
import org.red5.server.stream.IProviderService;
@ -40,7 +42,9 @@ import org.slf4j.Logger;
public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpStreamReceiverListener {
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 Runnable audioDataProcessor;
private volatile boolean processAudioData = false;
@ -51,9 +55,9 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
private RtpStreamReceiver rtpStreamReceiver;
private StreamObserver observer;
private SipToFlashTranscoder transcoder;
private long startTimestamp = 0;
private boolean sentMetadata = false;
private IoBuffer mBuffer;
private AudioData audioData;
private final byte[] fakeMetadata = new byte[] {
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);
listenStreamName = "speaker_" + System.currentTimeMillis();
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() {
@ -85,14 +100,25 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
}
public void stop() {
log.debug("Stopping stream for {}", listenStreamName);
processAudioData = false;
rtpStreamReceiver.stop();
audioBroadcastStream.stop();
audioBroadcastStream.close();
log.debug("stopping and closing stream {}", listenStreamName);
rtpStreamReceiver.stop();
log.debug("Stopped RTP Stream Receiver for {}", listenStreamName);
if (audioBroadcastStream != null) {
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() {
}
private void startNow() {
log.debug("started publishing stream in " + scope.getName());
audioBroadcastStream = new AudioBroadcastStream(listenStreamName);
audioBroadcastStream.setPublishedName(listenStreamName);
@ -111,8 +137,7 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
audioBroadcastStream.start();
processAudioData = true;
startTimestamp = System.currentTimeMillis();
audioDataProcessor = new Runnable() {
public void run() {
processAudioData();
@ -124,11 +149,23 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
}
private void processAudioData() {
int len = 160;
byte[] pcmAudio = new byte[len];
int remaining = len;
int offset = 0;
while (processAudioData) {
try {
AudioByteData abd = audioDataQ.take();
transcoder.transcode(abd, this);
} catch (InterruptedException e) {
int bytesRead = streamToFlash.read(pcmAudio, offset, remaining);
remaining -= bytesRead;
if (remaining == 0) {
remaining = len;
offset = 0;
transcoder.transcode(pcmAudio, this);
} else {
offset += bytesRead;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
@ -141,10 +178,10 @@ public class SipToFlashAudioStream implements TranscodedAudioDataListener, RtpSt
}
@Override
public void onAudioDataReceived(AudioByteData audioData) {
public void onAudioDataReceived(byte[] audioData, int offset, int len) {
try {
audioDataQ.put(audioData);
} catch (InterruptedException e) {
streamFromSip.write(audioData, offset, len);
} catch (IOException e) {
// TODO Auto-generated catch block
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
* but for Red5 0.91, doesn't yet. (ralam Sept 24, 2010).
*/
IoBuffer mBuffer = IoBuffer.allocate(1024);
mBuffer.setAutoExpand(true);
mBuffer.clear();
mBuffer.put(fakeMetadata);
mBuffer.flip();
Notify notifyData = new Notify(mBuffer);
notifyData.setTimestamp((int)timestamp );
notifyData.setTimestamp((int)timestamp);
notifyData.setSourceType(Constants.SOURCE_TYPE_LIVE);
audioBroadcastStream.dispatchEvent(notifyData);
notifyData.release();
sentMetadata = true;
}
}
private void pushAudio(byte[] audio, long timestamp) {
private void pushAudio(byte[] audio, long timestamp) {
sendFakeMetadata(timestamp);
IoBuffer buffer = IoBuffer.allocate(1024);
buffer.setAutoExpand(true);
buffer.clear();
buffer.put((byte) transcoder.getCodecId());
byte[] copy = new byte[audio.length];
System.arraycopy(audio, 0, copy, 0, audio.length );
buffer.put(copy);
buffer.flip();
AudioData audioData = new AudioData(buffer);
audioData.setTimestamp((int)timestamp );
mBuffer.clear();
mBuffer.put((byte) transcoder.getCodecId());
mBuffer.put(audio);
mBuffer.flip();
audioData.setSourceType(Constants.SOURCE_TYPE_LIVE);
/*
* Use timestamp increments passed in by codecs (i.e. 32 for nelly). This will force
* Flash Player to playback audio at proper timestamp. If we calculate timestamp using
* System.currentTimeMillis() - startTimestamp, the audio has tendency to drift and
* introduce delay. (ralam dec 14, 2010)
*/
audioData.setTimestamp((int)(timestamp));
audioData.setData(mBuffer);
audioBroadcastStream.dispatchEvent(audioData);
audioData.release();
}

View File

@ -40,6 +40,8 @@ public class RtpSocket {
/** Remote port */
int r_port;
private final byte[] payload = new byte[10];
/** Creates a new RTP socket (only receiver) */
public RtpSocket(DatagramSocket datagram_socket) {
socket=datagram_socket;
@ -59,19 +61,24 @@ public class RtpSocket {
return socket;
}
private final DatagramPacket rxDatagram = new DatagramPacket(payload, payload.length);
/** Receives a RTP packet from this socket */
public void receive(RtpPacket rtpp) throws IOException {
DatagramPacket datagram = new DatagramPacket(rtpp.getPacket(), rtpp.getLength());
socket.receive(datagram);
rtpp.setPacketLength(datagram.getLength());
rxDatagram.setData(rtpp.getPacket());
socket.receive(rxDatagram);
rtpp.setPacketLength(rxDatagram.getLength());
}
private final DatagramPacket txDatagram = new DatagramPacket(payload, payload.length);
/** Sends a RTP packet from this socket */
public void send(RtpPacket rtpp) throws IOException {
DatagramPacket datagram = new DatagramPacket(rtpp.getPacket(), rtpp.getLength());
datagram.setAddress(r_addr);
datagram.setPort(r_port);
socket.send(datagram);
txDatagram.setData(rtpp.getPacket());
txDatagram.setAddress(r_addr);
txDatagram.setPort(r_port);
if (!socket.isClosed())
socket.send(txDatagram);
}
/** Closes this socket */

View File

@ -19,10 +19,8 @@
**/
package org.bigbluebutton.voiceconf.red5.media.transcoder;
import org.bigbluebutton.voiceconf.red5.media.AudioByteData;
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 getCodecId();

View File

@ -19,13 +19,12 @@
**/
package org.bigbluebutton.voiceconf.red5.media.transcoder;
import java.nio.FloatBuffer;
import java.util.Random;
import org.slf4j.Logger;
import org.bigbluebutton.voiceconf.red5.media.AudioByteData;
import org.red5.logging.Red5LoggerFactory;
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.DecoderMap;
@ -38,28 +37,48 @@ import org.red5.app.sip.codecs.asao.DecoderMap;
public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
protected static Logger log = Red5LoggerFactory.getLogger( NellyFlashToSipTranscoderImp.class, "sip" );
private static final int NELLYMOSER_DECODED_PACKET_SIZE = 256;
private static final int NELLYMOSER_ENCODED_PACKET_SIZE = 64;
private static final int NELLY_TO_L16_AUDIO_SIZE = 256;
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 DecoderMap decoderMap;
private float[] tempBuffer; // Temporary buffer with received PCM audio from FlashPlayer.
private int tempBufferRemaining = 0; // Floats remaining on temporary buffer.
private float[] encodingBuffer; // Encoding buffer used to encode to final codec format;
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 float[] tempL16Buffer = new float[NELLY_TO_L16_AUDIO_SIZE];
private float[] tempUlawBuffer = new float[ULAW_AUDIO_LENGTH];
private byte[] ulawEncodedBuffer = new byte[ULAW_AUDIO_LENGTH];
private long timestamp = 0;
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) {
this.sipCodec = sipCodec;
decoder = new Decoder();
decoderMap = null;
Random rgen = new Random();
timestamp = rgen.nextInt(1000);
viewBuffer = l16Audio.asReadOnlyBuffer();
}
@Override
@ -73,91 +92,65 @@ public class NellyFlashToSipTranscoderImp implements FlashToSipTranscoder {
}
@Override
public void transcode(AudioByteData audioData, int startOffset, int length, TranscodedAudioDataListener listener) {
byte[] codedBuffer = new byte[length];
System.arraycopy(audioData.getData(), startOffset, codedBuffer, 0, length);
byte[] transcodedAudioData = new byte[sipCodec.getOutgoingEncodedFrameSize()];
asao_buffer_processed = false;
if (!hasInitilializedBuffers) {
tempBuffer = new float[NELLYMOSER_DECODED_PACKET_SIZE];
encodingBuffer = new float[sipCodec.getOutgoingDecodedFrameSize()];
hasInitilializedBuffers = true;
}
if (length > 0) {
do {
int encodedBytes = fillRtpPacketBuffer(codedBuffer, transcodedAudioData);
if (encodedBytes == 0) {
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);
public void transcode(byte[] audioData, int startOffset, int length, TranscodedAudioDataListener listener) {
if (audioData.length != NELLY_AUDIO_LENGTH) {
log.warn("Receiving bad nelly audio. Expecting {}, got {}.", NELLY_AUDIO_LENGTH, audioData.length);
return;
}
// Convert the Nelly audio to L16.
decoderMap = decoder.decode(decoderMap, audioData, 0, tempL16Buffer, 0);
// Store the L16 audio into the buffer
l16Audio.put(tempL16Buffer);
// Read 160-float worth of audio
viewBuffer.get(tempUlawBuffer);
// Convert the L16 audio to Ulaw
int encodedBytes = sipCodec.pcmToCodec(tempUlawBuffer, ulawEncodedBuffer);
// 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()) {
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 {
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();
}
}
}

View File

@ -19,88 +19,115 @@
**/
package org.bigbluebutton.voiceconf.red5.media.transcoder;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.FloatBuffer;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
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.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.asao.ByteStream;
import org.red5.app.sip.codecs.asao.CodecImpl;
public class NellySipToFlashTranscoderImp implements SipToFlashTranscoder {
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;
/**
* 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 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) {
this.audioCodec = audioCodec;
encoderMap = new float[64];
tempBuffer = new float[NELLYMOSER_DECODED_PACKET_SIZE];
Random rgen = new Random();
timestamp = rgen.nextInt(1000);
}
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() +"]");
}
viewBuffer = l16Audio.asReadOnlyBuffer();
}
@Override
public void transcode(AudioByteData audioData, TranscodedAudioDataListener listener) {
transcodePcmToNellymoser(audioData.getData(), listener);
}
public void transcode(byte[] audioData, TranscodedAudioDataListener 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
public int getIncomingEncodedFrameSize() {
return audioCodec.getIncomingEncodedFrameSize();
@ -111,3 +138,4 @@ public class NellySipToFlashTranscoderImp implements SipToFlashTranscoder {
return NELLYMOSER_CODEC_ID;
}
}

View File

@ -19,10 +19,8 @@
**/
package org.bigbluebutton.voiceconf.red5.media.transcoder;
import org.bigbluebutton.voiceconf.red5.media.AudioByteData;
public interface SipToFlashTranscoder {
void transcode(AudioByteData audioData, TranscodedAudioDataListener listener);
void transcode(byte[] audioData, TranscodedAudioDataListener listener);
int getCodecId();
int getIncomingEncodedFrameSize();
}

View File

@ -20,8 +20,6 @@
package org.bigbluebutton.voiceconf.red5.media.transcoder;
import java.util.Random;
import org.bigbluebutton.voiceconf.red5.media.AudioByteData;
import org.red5.app.sip.codecs.Codec;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;
@ -39,9 +37,9 @@ public class SpeexFlashToSipTranscoderImp implements FlashToSipTranscoder {
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];
System.arraycopy(audioData.getData(), startOffset, transcodedAudio, 0, length);
System.arraycopy(audioData, startOffset, transcodedAudio, 0, length);
listener.handleTranscodedAudioData(transcodedAudio, timestamp += TS_INCREMENT);
}

View File

@ -20,8 +20,6 @@
package org.bigbluebutton.voiceconf.red5.media.transcoder;
import java.util.Random;
import org.bigbluebutton.voiceconf.red5.media.AudioByteData;
import org.red5.app.sip.codecs.Codec;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;
@ -41,8 +39,8 @@ public class SpeexSipToFlashTranscoderImp implements SipToFlashTranscoder {
}
@Override
public void transcode(AudioByteData audioData, TranscodedAudioDataListener listener) {
byte[] codedBuffer = audioData.getData();
public void transcode(byte[] audioData, TranscodedAudioDataListener listener) {
byte[] codedBuffer = audioData;
listener.handleTranscodedAudioData(codedBuffer, timestamp += TS_INCREMENT);
}

View File

@ -50,4 +50,8 @@ public class AudioConferenceProvider {
public int getStartAudioPort() {
return startAudioPort;
}
public int getStopAudioPort() {
return stopAudioPort;
}
}

View File

@ -37,7 +37,9 @@ import org.red5.logging.Red5LoggerFactory;
import org.red5.server.api.IScope;
import org.red5.server.api.stream.IBroadcastStream;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Vector;
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) {
log.debug("call {}", destination);
log.debug("{} making a call to {}", callerName, destination);
try {
localSocket = getLocalAudioSocket();
userProfile.audioPort = localSocket.getLocalPort();
} catch (Exception e) {
log.debug("{} failed to allocate local port for call to {}. Notifying client that call failed.", callerName, destination);
notifyListenersOnOutgoingCallFailed();
return;
}
@ -138,18 +141,22 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
private DatagramSocket getLocalAudioSocket() throws Exception {
DatagramSocket socket = null;
boolean failedToGetSocket = true;
StringBuilder failedPorts = new StringBuilder("Failed ports: ");
for (int i = 0; i < 3; i++) {
try {
socket = new DatagramSocket(portProvider.getFreeAudioPort());
for (int i = portProvider.getStartAudioPort(); i <= portProvider.getStopAudioPort(); i++) {
int freePort = portProvider.getFreeAudioPort();
try {
socket = new DatagramSocket(freePort);
failedToGetSocket = false;
log.info("Successfully setup local audio port {}. {}", freePort, failedPorts);
break;
} catch (SocketException e) {
log.error("Failed to setup local audio socket.");
failedPorts.append(freePort + ", ");
}
}
if (failedToGetSocket) {
log.warn("Failed to setup local audio port {}.", failedPorts);
throw new Exception("Exception while initializing CallStream");
}
@ -169,22 +176,26 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
int localAudioPort = SessionDescriptorUtil.getLocalAudioPort(localSdp);
SipConnectInfo connInfo = new SipConnectInfo(localSocket, remoteMediaAddress, remoteAudioPort);
log.debug("[localAudioPort=" + localAudioPort + ",remoteAudioPort=" + remoteAudioPort + "]");
try {
localSocket.connect(InetAddress.getByName(remoteMediaAddress), remoteAudioPort);
log.debug("[localAudioPort=" + localAudioPort + ",remoteAudioPort=" + remoteAudioPort + "]");
if (userProfile.audio && localAudioPort != 0 && remoteAudioPort != 0) {
if ((callStream == null) && (sipCodec != null)) {
try {
callStream = callStreamFactory.createCallStream(sipCodec, connInfo);
callStream.addCallStreamObserver(this);
callStream.start();
notifyListenersOnCallConnected(callStream.getTalkStreamName(), callStream.getListenStreamName());
} catch (Exception e) {
log.error("Failed to create Call Stream.");
System.out.println(StackTraceUtil.getStackTrace(e));
}
}
}
if (userProfile.audio && localAudioPort != 0 && remoteAudioPort != 0) {
if ((callStream == null) && (sipCodec != null)) {
try {
callStream = callStreamFactory.createCallStream(sipCodec, connInfo);
callStream.addCallStreamObserver(this);
callStream.start();
notifyListenersOnCallConnected(callStream.getTalkStreamName(), callStream.getListenStreamName());
} catch (Exception e) {
log.error("Failed to create Call Stream.");
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() {
log.debug("closeMediaApplication" );
log.debug("Shutting down the voice streams.");
if (callStream != null) {
callStream.stop();
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() {
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 */
public void onCallClosing(Call call, Message bye) {
log.debug("onCallClosing");
if (!isCurrentCall(call)) return;
log.debug("CLOSE.");
log.info("Received a BYE from the other end telling us to hangup.");
if (!isCurrentCall(call)) return;
closeVoiceStreams();
notifyListenersOfOnCallClosed();
callState = CallState.UA_IDLE;

View File

@ -63,7 +63,7 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
@Override
public boolean roomStart(IScope room) {
log.debug("{} - roomStart ", APP);
log.debug("Starting room [{}].", room.getName());
assert participantsApplication != null;
participantsApplication.createRoom(room.getName());
return super.roomStart(room);
@ -71,21 +71,28 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
@Override
public void roomStop(IScope room) {
log.debug("{} - roomStop", APP);
log.debug("Stopping room [{}]", room.getName());
super.roomStop(room);
assert participantsApplication != null;
participantsApplication.destroyRoom(room.getName());
BigBlueButtonSession bbbSession = getBbbSession();
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;
recorderApplication.destroyRecordSession(bbbSession.getSessionName());
log.debug("{} - roomStop - destroyed RecordSession {}", APP, bbbSession.getSessionName());
log.debug("Stopped room [{}]", room.getName());
}
@Override
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 role = ((String) params[1]).toString();
@ -116,47 +123,44 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
String debugInfo = "userid=" + userid + ",username=" + username + ",role=" + role + ",conference=" + conference + "," +
"session=" + sessionName + ",voiceConf=" + voiceBridge + ",room=" + room + ",externsUserid=" + externUserID;
log.debug("roomConnect - [{}]", debugInfo);
log.info("User Joined [{}, {}]", username, room);
log.debug("User [{}] connected to room [{}]", debugInfo, room);
super.roomConnect(connection, params);
return true;
}
@Override
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);
log.info("User Left [{}, {}]", bbbSession.getUsername(), bbbSession.getRoom());
log.info("User [{}] disconnected from room [{}]", bbbSession.getUsername(), bbbSession.getRoom());
super.roomDisconnect(conn);
}
public String getMyUserId() {
log.debug("Getting userid for connection.");
BigBlueButtonSession bbbSession = (BigBlueButtonSession) Red5.getConnectionLocal().getAttribute(Constants.SESSION);
assert bbbSession != null;
return bbbSession.getUserid()+"";
}
public void setParticipantsApplication(ParticipantsApplication a) {
log.debug("Setting participants application");
participantsApplication = a;
}
public void setRecorderApplication(RecorderApplication a) {
log.debug("Setting recorder application");
recorderApplication = a;
}
public void setApplicationListeners(Set<IApplication> listeners) {
log.debug("Setting application listeners");
int count = 0;
Iterator<IApplication> iter = listeners.iterator();
while (iter.hasNext()) {
log.debug("Setting application listeners {}", count);
super.addListener((IApplication) iter.next());
count++;
}
log.debug("Finished Setting application listeners");
}
public void setVersion(String v) {
@ -172,7 +176,7 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter {
@Override
public void onApplicationEvent(ApplicationEvent event) {
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();
}
}

View File

@ -49,3 +49,4 @@
</buildCSSFiles>
</actionScriptProperties>

View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<flexProperties enableServiceManager="false" flexServerFeatures="0" flexServerType="0" toolCompile="true" useServerFlexSDK="false" version="1"/>

View File

@ -1,3 +1,6 @@
.settings/org.eclipse.core.resources.prefs
.actionScriptProperties
.flexProperties
linker-report.xml
bin
client

View 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
encoding//locale/el_GR/bbbResources.properties=UTF-8
encoding/<project>=utf-8
encoding/<project>=UTF-8

View File

@ -36,6 +36,7 @@
<module name="PhoneModule" url="PhoneModule.swf?v=VERSION"
uri="rtmp://HOST/sip"
autoJoin="false"
dependsOn="ViewersModule"
/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -8,6 +8,13 @@ Application
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
{

View 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>

View File

@ -238,7 +238,9 @@
<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: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>
<mx:ControlBar width="100%" height="20" paddingTop="0">
<mx:Label text="{ResourceUtil.getInstance().getString('bbb.mainshell.copyrightLabel2',[appVersion])}" id="copyrightLabel2"/>
<mx:Spacer width="20"/>

View File

@ -47,6 +47,7 @@ package org.bigbluebutton.modules.deskshare.services
private var width:Number;
private var height:Number;
private var uri:String;
public function DeskshareService()
{
@ -60,6 +61,7 @@ package org.bigbluebutton.modules.deskshare.services
}
public function connect(uri:String):void {
this.uri = uri;
LogUtil.debug("Deskshare Service connecting to " + uri);
conn = new Connection();
conn.addEventListener(Connection.SUCCESS, connectionSuccessHandler);
@ -93,9 +95,9 @@ package org.bigbluebutton.modules.deskshare.services
}
private function connectionSuccessHandler(e:ConnectionEvent):void{
LogUtil.debug("Successully connection to " + module.uri);
LogUtil.debug("Successully connection to " + uri);
nc = conn.getConnection();
deskSO = SharedObject.getRemote("deskSO", module.uri, false);
deskSO = SharedObject.getRemote("deskSO", uri, false);
deskSO.client = this;
deskSO.connect(nc);
@ -107,11 +109,11 @@ package org.bigbluebutton.modules.deskshare.services
}
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{
LogUtil.error("connection rejected " + module.uri + " with message " + e.toString());
LogUtil.error("connection rejected " + uri + " with message " + e.toString());
}
/**

View File

@ -26,7 +26,6 @@
title="{windowTitle}"
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.REGISTER_LISTENERS}" method="registerListeners" />
<mate:Listener type="{ListenersEvent.SET_LOCAL_MODERATOR_STATUS}" method="{setModerator}" />
@ -128,31 +127,23 @@
dispatchEvent(unmuteCommand);
}
private function firstListenerJoined(e:ListenersEvent):void{
}
private function roomMuteStateChange(e:ListenersEvent):void{
setMuteState(e.mute_state);
}
private function registerListeners(e:ListenersEvent):void{
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{
moderator = e.moderator;
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{
var item:ListenerItem = e.itemRenderer as ListenerItem;
item.onRollOver();

View File

@ -60,7 +60,7 @@ package org.bigbluebutton.modules.phone.managers {
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;
isConnected = true;
@ -68,16 +68,16 @@ package org.bigbluebutton.modules.phone.managers {
this.username = username;
this.room = room;
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 = new NetConnection();
netConnection.client = this;
netConnection.addEventListener( NetStatusEvent.NET_STATUS , netStatus );
netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
netConnection.connect(uri);
netConnection.connect(uri, externUID, username);
}
public function disconnect():void {

View File

@ -19,8 +19,6 @@
package org.bigbluebutton.modules.phone.managers
{
import flash.events.IEventDispatcher;
import org.bigbluebutton.common.LogUtil;
import org.bigbluebutton.modules.phone.events.CallConnectedEvent;
import org.bigbluebutton.modules.phone.events.JoinVoiceConferenceEvent;
@ -40,6 +38,8 @@ package org.bigbluebutton.modules.phone.managers
public function setModuleAttributes(attributes:Object):void {
this.attributes = attributes;
LogUtil.debug("Attributes Set... webvoiceconf:" + attributes.webvoiceconf);
if (attributes.autoJoin == "true") joinVoice(true);
}
private function setupMic(useMic:Boolean):void {
@ -54,28 +54,36 @@ package org.bigbluebutton.modules.phone.managers
}
public function join(e:JoinVoiceConferenceEvent):void {
setupMic(e.useMicrophone);
var uid:String = String( Math.floor( new Date().getTime() ) );
connectionManager.connect(uid, attributes.username, attributes.room, attributes.uri);
joinVoice(e.useMicrophone);
}
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 {
LogUtil.debug("Dialing...." + attributes.webvoiceconf);
LogUtil.debug("Dialing...." + attributes.webvoiceconf + "...." + attributes.externUserID);
connectionManager.doCall(attributes.webvoiceconf);
}
public function callConnected(event:CallConnectedEvent):void {
LogUtil.debug("Call connected...");
setupConnection();
LogUtil.debug("callConnected: Connection Setup");
streamManager.callConnected(event.playStreamName, event.publishStreamName, event.codec);
LogUtil.debug("callConnected::onCall set");
onCall = true;
}
public function hangup():void {
LogUtil.debug("PhoneManager hangup");
if (onCall) {
LogUtil.debug("PM OnCall");
streamManager.stopStreams();
connectionManager.doHangUp();
LogUtil.debug("PM hangup::doHangUp");
onCall = false;
}
}

View File

@ -59,7 +59,7 @@ package org.bigbluebutton.modules.phone.managers
public function initMicrophone():void {
mic = Microphone.getMicrophone();
if(mic == null){
initWithNoMicrophone();
} else {
@ -115,7 +115,9 @@ package org.bigbluebutton.modules.phone.managers
public function mute():void {
if(!muted) {
if(outgoingStream != null) {
LogUtil.debug("***** Muting the mic.");
outgoingStream.close();
outgoingStream = null;
muted = true;
@ -125,6 +127,7 @@ package org.bigbluebutton.modules.phone.managers
public function unmute():void {
if (muted) {
LogUtil.debug("***** UNMuting the mic.");
outgoingStream = new NetStream(connection);
outgoingStream.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
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 {
LogUtil.debug("SM callConnected");
isCallConnected = true;
audioCodec = codec;
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();
play(playStreamName);
publish(publishStreamName);
LogUtil.debug("SM callConnected: After setupPlayStatusHandler");
play(playStreamName);
LogUtil.debug("SM callConnected: After play");
publish(publishStreamName);
LogUtil.debug("SM callConnected: Published Stream");
}
private function play(playStreamName:String):void {
@ -155,14 +167,25 @@ package org.bigbluebutton.modules.phone.managers
private function publish(publishStreamName:String):void {
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 {
incomingStream = new NetStream(connection);
incomingStream.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
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 {
@ -178,19 +201,29 @@ package org.bigbluebutton.modules.phone.managers
custom_obj.onPlayStatus = playStatus;
custom_obj.onMetadata = onMetadata;
incomingStream.client = custom_obj;
outgoingStream.client = custom_obj;
if (mic != null)
outgoingStream.client = custom_obj;
}
public function stopStreams():void {
LogUtil.debug("Stopping Stream(s)");
if(incomingStream != null) {
LogUtil.debug("--Stopping Incoming Stream");
incomingStream.play(false);
} else {
LogUtil.debug("--Incoming Stream Null");
}
if(outgoingStream != null) {
LogUtil.debug("--Stopping Outgoing Stream");
outgoingStream.attachAudio(null);
outgoingStream.close();
} else {
LogUtil.debug("--Outgoing Stream Null");
}
isCallConnected = false;
LogUtil.debug("Stopped Stream(s)");
}
private function netStatus (evt:NetStatusEvent ):void {

View File

@ -36,24 +36,72 @@
# 2010-09-15 FFD Updates for 0.71-dev
# 2010-10-16 FFD Updates for 0.71-beta
# 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
#
# 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
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
VOICE_CONFERENCE="bbb-voice-freeswitch.xml"
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}')
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
@ -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
# $2 -- loctation of value
# $3 -- value to check
@ -80,65 +129,16 @@ check_file() {
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 the the BigBlueButton VM?
#
if [ -f /home/firstuser/.profile ]; then
echo $(cat /home/firstuser/.profile | grep BigBlueButton)
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() {
if [ ! $HEADER ]; then
@ -166,10 +166,10 @@ need_root() {
}
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
echo "$0 [options]"
echo " bbb-conf [options]"
echo
echo "Configuration:"
echo " --version Display BigBlueButton version (packages)"
@ -184,7 +184,7 @@ usage() {
echo " --watch Scan the log files for error messages every 2 seconds"
echo " --salt View the URL and security salt for the server"
echo
echo "Administration":
echo "Administration:"
echo " --restart Restart BigBueButton"
echo " --stop Stop BigBueButton"
echo " --start Start BigBueButton"
@ -247,7 +247,7 @@ uncomment () {
stop_bigbluebutton () {
/etc/init.d/red5 stop
/etc/init.d/${TOMCAT} stop
/etc/init.d/${SERVLET_CONTAINER} stop
/etc/init.d/nginx stop
if [ -a /opt/freeswitch/run/freeswitch.pid ]; then
@ -259,7 +259,10 @@ stop_bigbluebutton () {
fi
/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 () {
@ -300,14 +303,16 @@ start_bigbluebutton () {
sleep 1
done
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/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): "
@ -321,8 +326,22 @@ start_bigbluebutton () {
done
fi
if ! wget http://$NGINX_IP/bigbluebutton/api -O - --quiet | grep -q SUCCESS; then
echo "Startup unsuccessful"
if [ $PLATFORM == "ubuntu-jetty-intalio" ]; then
#
# 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
fi
@ -334,7 +353,7 @@ display_bigbluebutton_status () {
/etc/init.d/activemq status
/etc/init.d/nginx status
/etc/init.d/red5 status
/etc/init.d/${TOMCAT} status
/etc/init.d/${SERVLET_CONTAINER} status
}
if [ $# -eq 0 ]; then
@ -473,10 +492,10 @@ while [ $# -gt 0 ]; do
if [ "$1" = "--salt" -o "$1" = "-salt" -o "$1" = "--setsalt" ]; then
SALT="${2}"
if [ -z "$SALT" ]; then
IP=$(cat /var/lib/${TOMCAT}/webapps/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`;
BBB_WEB=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
SALT=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep securitySalt | cut -d= -f2);
echo
echo " URL: http://$IP/bigbluebutton/"
echo " URL: http://$BBB_WEB/bigbluebutton/"
echo " Salt: $SALT"
echo
exit 0
@ -644,12 +663,12 @@ if [ $SETUPDEV ]; then
exit 1
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
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"
cp /var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp ${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 ${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp ${BBBWEBHOME}/web-app/demo
echo "# Enabling $USER to write to /var/bigbluebutton to upload slides"
sudo chmod -R ugo+rwx /var/bigbluebutton
@ -665,7 +684,7 @@ if [ $SETUPDEV ]; then
echo "
# Done. To run your local build of bbb-web:
sudo /etc/init.d/${TOMCAT} stop
sudo /etc/init.d/${SERVLET_CONTAINER} stop
cd ${BBBWEBHOME}
ant
"
@ -713,7 +732,7 @@ if [ $SETUPDEV ]; then
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
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
cd $BBBCLIENTHOME
@ -868,22 +887,24 @@ check_configuration() {
#
# Check if BigBlueButto is defined in Nginx
#
if [ ! -L /etc/nginx/sites-enabled/bigbluebutton ]; then
echo "# Nginx: BigBlueButton appears to be disabled"
echo " - no symbolic link in /etc/nginx/sites-enabled/bigbluebutton to /etc/nginx/sites-available/bigbluebutton "
if [ $PLATFORM != "ubuntu-jetty-intalio" ]; then
if [ ! -L /etc/nginx/sites-enabled/bigbluebutton ]; then
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
#
# 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_DEMO=$(cat /var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp | tr -d '\r' | sed -n '/salt =/{s/.* = "//;s/".*//g;p}')
SALT_PROPERTIES=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | tr -d '\r' | sed -n '/securitySalt/{s/.*=//;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
echo "# Salt mismatch: "
echo "# /var/lib/${TOMCAT}/webapps/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/WEB-INF/classes/bigbluebutton.properties=$SALT_PROPERTIES"
echo "# ${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp=$SALT_DEMO"
fi
@ -891,7 +912,7 @@ check_configuration() {
# Look for properties with no values set
#
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"
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
#
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
echo "# pdf2swf, jpeg2swf and png2swf are not installed in $VARFolder"
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
echo "# ImageMagick's convert is not installed in $VARFolder"
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
echo "# Ghostscript is not installd in $VARFolder"
fi
@ -1012,14 +1033,14 @@ check_state() {
if ! netstat -ant | grep '8080' > /dev/null; then
print_header
NOT_RUNNING_APPS="${NOT_RUNNING_APPS} ${TOMCAT} or grails"
NOT_RUNNING_APPS="${NOT_RUNNING_APPS} ${SERVLET_CONTAINER} or grails"
else
if ps aux | ps -aef | grep -v grep | grep grails | grep run-app > /dev/null; then
print_header
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
RUNNING_APPS="${RUNNING_APPS} ${TOMCAT}"
RUNNING_APPS="${RUNNING_APPS} ${SERVLET_CONTAINER}"
fi
fi
@ -1120,10 +1141,10 @@ check_state() {
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
echo "# empty directory: $TOMCAT6_LOGS contains no logs"
if [ -z "$(ls -A $SERVLET_LOGS)" ]; then
echo "# empty directory: $SERVLET_LOGS contains no logs"
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
# a firewall
#
NGINX_IP=$(cat /etc/nginx/sites-available/bigbluebutton | sed -n '/server_name/{s/.*name[ ]*//;s/;//;p}')
check_no_value server_name /etc/nginx/sites-available/bigbluebutton $NGINX_IP
if ! wget http://$NGINX_IP/bigbluebutton/api -O - --quiet | grep -q SUCCESS; then
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 $BBB_WEB
if ! wget http://$BBB_WEB/bigbluebutton/api -O - --quiet | grep -q SUCCESS; then
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 "# If you are setting up BigBlueButton behind a firewall, see the FAQ"
echo "# for steps to setup BigBlueButton behind a firewall."
@ -1192,7 +1213,7 @@ check_state() {
fi
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
echo
echo "# The IP address ($SIP_SERVER_HOST) set for sip.server.host in"
@ -1252,19 +1273,19 @@ if [ $CHECK ]; then
echo
echo "/var/www/bigbluebutton/client/conf/config.xml"
IP=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/porttest /{s/.*host="//;s/".*//;p}')
echo " Port test (tunnel): $IP"
PORT_IP=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/porttest /{s/.*host="//;s/".*//;p}')
echo " Port test (tunnel): $PORT_IP"
IP=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/uri.*video/{s/.*rtmp:\/\///;s/\/.*//;p}')
echo " Red5: $IP"
RED5_IP=$(cat /var/www/bigbluebutton/client/conf/config.xml | sed -n '/uri.*video/{s/.*rtmp:\/\///;s/\/.*//;p}')
echo " Red5: $RED5_IP"
# 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
echo "/etc/nginx/sites-available/bigbluebutton"
IP=$(cat /etc/nginx/sites-available/bigbluebutton | sed -n '/server_name/{s/.*name[ ]*//;s/;//;p}')
echo " server name: $IP"
NGINX_IP=$(cat /etc/nginx/sites-available/bigbluebutton | sed -n '/server_name/{s/.*name[ ]*//;s/;//;p}')
echo " server name: $NGINX_IP"
PORT=$(cat /etc/nginx/sites-available/bigbluebutton | sed -n '/listen/{s/.*listen[ ]*//;s/;//;p}')
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}')
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 "/var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties (bbb-web)"
echo " bbb-web host: $IP"
echo "${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties (bbb-web)"
echo " bbb-web host: $BBB_WEB_IP"
if [ -f /var/lib/${TOMCAT}/webapps/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')
if [ -f ${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp ]; then
API_IP=$(cat ${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp | sed -n '/String BigBlueButtonURL/{s/.*http:\/\///;s/\/.*//;p}' | tr -d '\015')
echo
echo "/var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp (API demos)"
echo " bbb-web-api host: $IP"
echo "${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp (API demos)"
echo " bbb-web-api host: $API_IP"
fi
if [ $VOICE_CONFERENCE == "bbb-voice-freeswitch.xml" ]; then
@ -1321,7 +1342,7 @@ if [ $ZIP ]; then
touch /tmp/empty
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 $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/nginx/error.log > /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
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
echo " -- Exceptions found in $TOMCAT6_LOGS/ -- "
echo " -- Exceptions found in $SERVLET_LOGS/ -- "
cat /tmp/t
echo
fi
@ -1483,16 +1504,16 @@ if [ -n "$HOST" ]; then
#
# 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" \
/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
# 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
# cat /var/lib/${TOMCAT}/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
# cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
#
# Update nginx
@ -1511,10 +1532,10 @@ if [ -n "$HOST" ]; then
# Update api demos
#
if [ -f /var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp ]; then
echo "Assigning $HOST for api demos in /var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp"
if [ -f ${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp ]; then
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" \
/var/lib/${TOMCAT}/webapps/bigbluebutton/demo/bbb_api_conf.jsp
${SERVLET_DIR}/bigbluebutton/demo/bbb_api_conf.jsp
fi
#
@ -1758,8 +1779,8 @@ if [ $CLEAN ]; then
rm -rf $RED5_DIR/log/*
fi
if [ $TOMCAT6_LOGS ]; then
rm -rf $TOMCAT6_LOGS/*
if [ $SERVLET_LOGS ]; then
rm -rf $SERVLET_LOGS/*
fi
rm -rf /var/log/nginx/*

View File

@ -22,6 +22,9 @@
package org.bigbluebutton.deskshare.client;
import javax.swing.JApplet;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import java.awt.Image;
public class DeskShareApplet extends JApplet implements ClientListener {
@ -92,7 +95,20 @@ public class DeskShareApplet extends JApplet implements ClientListener {
}
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();
}
}
}

View File

@ -57,6 +57,32 @@ public class DeskshareClient {
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() {
System.out.println(NAME + "Stop");
screenSharer.stop();

View File

@ -63,7 +63,36 @@ public class DeskshareSystemTray {
};
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() {
if (tray != null && trayIcon != null) {
tray.remove(trayIcon);

View File

@ -41,6 +41,32 @@ public class FullScreenSharer implements ScreenSharer {
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() {
sharer.stopSharing();
}

View File

@ -50,6 +50,34 @@ public class ScreenRegionSharer implements ScreenSharer {
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() {
frame.setVisible(false);
sharer.stopSharing();

View File

@ -23,6 +23,7 @@ package org.bigbluebutton.deskshare.client;
public interface ScreenSharer {
void start();
void disconnected(); // 2010.11.19 _PTS_272_
void stop();
void addClientListener(ClientListener l);
}

View File

@ -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() {
System.out.println(NAME + "Stopping");
System.out.println(NAME + "Removing icon from system tray.");

View File

@ -19,6 +19,7 @@
*
* ===License Header===
*/
package org.bigbluebutton.deskshare.client.frame;
import java.awt.BasicStroke;
@ -30,6 +31,7 @@ import java.awt.Frame;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
@ -95,7 +97,7 @@ class WindowlessFrame implements Serializable {
}
};
// properties that change during use
private Point mTopLeft = new Point();
private Dimension mOverallSize = new Dimension();
@ -115,7 +117,105 @@ class WindowlessFrame implements Serializable {
private final BarFrame mBottomBorder;
private final BarFrame mLeftBorder;
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 static final long serialVersionUID = 1L;
@ -149,8 +249,51 @@ class WindowlessFrame implements Serializable {
@Override
public void mouseDragged(MouseEvent e) {
final int changeInX = e.getLocationOnScreen().x - mActionOffset.x - mTopLeft.x;
final int changeInY = e.getLocationOnScreen().y - mActionOffset.y - mTopLeft.y;
int changeInX = e.getLocationOnScreen().x - mActionOffset.x - mTopLeft.x;
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()) {
WindowlessFrame.this.setLocation(changeInX + mTopLeft.x, changeInY + mTopLeft.y);
}
@ -175,8 +318,8 @@ class WindowlessFrame implements Serializable {
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 Point mActionOffset = null;
@ -185,15 +328,28 @@ class WindowlessFrame implements Serializable {
@Override
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;
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension d = tk.getScreenSize();
if (mResizing.get()) {
int newH = mOriginalSize.height;
int newW = mOriginalSize.width;
if (mCorner == Corner.SOUTHEAST) {
newH += changeInY;
newW += changeInX;
} else if (mCorner == Corner.NORTHEAST) {
if (Corner.SOUTHEAST == mCorner) {
if (e.getLocationOnScreen().x < mTopLeft.x+5) {
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;
newH = mOverallSize.height + -changeInY;
newW += changeInX;
@ -206,8 +362,33 @@ class WindowlessFrame implements Serializable {
newH += changeInY;
mTopLeft.x = mTopLeft.x + changeInX;
newW = mOverallSize.width + -changeInX;
}
}*/
//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);
e.consume();
}
@ -235,13 +416,14 @@ class WindowlessFrame implements Serializable {
private Corner nearCorner(Point mouse) {
if (isNearBottomRightCorner(mouse)) {
return Corner.SOUTHEAST;
} else if (isNearTopRightCorner(mouse)) {
} /* else if (isNearTopRightCorner(mouse)) {
return Corner.NORTHEAST;
} else if (isNearTopLeftCorner(mouse)) {
return Corner.NORTHWEST;
} else if (isNearBottomLeftCorner(mouse)) {
return Corner.SOUTHWEST;
}
*/
return null;
}
@ -251,7 +433,7 @@ class WindowlessFrame implements Serializable {
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 yToTopRight = Math.abs(mTopLeft.y - mouse.y);
return xToTopRight < CORNER_SIZE && yToTopRight < CORNER_SIZE;
@ -268,17 +450,22 @@ class WindowlessFrame implements Serializable {
int yToTopLeft = Math.abs(mTopLeft.y - mouse.y);
return xToTopLeft < CORNER_SIZE && yToTopLeft < CORNER_SIZE;
}
*/
@Override
public void mouseMoved(MouseEvent e) {
final Point mouse = e.getLocationOnScreen();
/*
if (isNearTopLeftCorner(mouse)) {
e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
} else if (isNearBottomLeftCorner(mouse)) {
e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
} else if (isNearTopRightCorner(mouse)) {
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));
} else {
e.getComponent().setCursor(Cursor.getDefaultCursor());
@ -559,4 +746,4 @@ class WindowlessFrame implements Serializable {
System.out.println("Adding listeners......................");
mWindowFrame.add(mToolbarFrame);
}
}
}