Merge branch 'merging-global-audio' of github.com:mconf/bigbluebutton into merge-global-audio
Conflicts: bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/CollectorActor.scala
This commit is contained in:
commit
42fcfe1055
@ -43,16 +43,22 @@ private static Logger log = Red5LoggerFactory.getLogger(ClientConnection.class,
|
||||
|
||||
public void onJoinConferenceSuccess(String publishName, String playName, String codec) {
|
||||
log.debug("Notify client that {} [{}] has joined the conference.", username, userid);
|
||||
connection.invoke("successfullyJoinedVoiceConferenceCallback", new Object[] {publishName, playName, codec});
|
||||
if (connection.isConnected()) {
|
||||
connection.invoke("successfullyJoinedVoiceConferenceCallback", new Object[] {publishName, playName, codec});
|
||||
}
|
||||
}
|
||||
|
||||
public void onJoinConferenceFail() {
|
||||
log.debug("Notify client that {} [{}] failed to join the conference.", username, userid);
|
||||
connection.invoke("failedToJoinVoiceConferenceCallback", new Object[] {"onUaCallFailed"});
|
||||
if (connection.isConnected()) {
|
||||
connection.invoke("failedToJoinVoiceConferenceCallback", new Object[] {"onUaCallFailed"});
|
||||
}
|
||||
}
|
||||
|
||||
public void onLeaveConference() {
|
||||
log.debug("Notify client that {} [{}] left the conference.", username, userid);
|
||||
connection.invoke("disconnectedFromJoinVoiceConferenceCallback", new Object[] {"onUaCallClosed"});
|
||||
if (connection.isConnected()) {
|
||||
connection.invoke("disconnectedFromJoinVoiceConferenceCallback", new Object[] {"onUaCallClosed"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import org.bigbluebutton.voiceconf.sip.SipPeerManager;
|
||||
import org.red5.logging.Red5LoggerFactory;
|
||||
import org.red5.server.api.IConnection;
|
||||
import org.red5.server.api.Red5;
|
||||
import org.bigbluebutton.voiceconf.sip.GlobalCall;
|
||||
|
||||
public class Service {
|
||||
private static Logger log = Red5LoggerFactory.getLogger(Service.class, "sip");
|
||||
@ -33,6 +34,27 @@ public class Service {
|
||||
|
||||
private MessageFormat callExtensionPattern = new MessageFormat("{0}");
|
||||
|
||||
public Boolean call(String peerId, String callerName, String destination, Boolean listenOnly) {
|
||||
if (listenOnly) {
|
||||
if (GlobalCall.reservePlaceToCreateGlobal(destination)) {
|
||||
String extension = callExtensionPattern.format(new String[] { destination });
|
||||
try {
|
||||
sipPeerManager.call(peerId, destination, "GLOBAL_AUDIO_" + destination, extension);
|
||||
Red5.getConnectionLocal().setAttribute("VOICE_CONF_PEER", peerId);
|
||||
} catch (PeerNotFoundException e) {
|
||||
log.error("PeerNotFound {}", peerId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
sipPeerManager.connectToGlobalStream(peerId, getClientId(), destination);
|
||||
Red5.getConnectionLocal().setAttribute("VOICE_CONF_PEER", peerId);
|
||||
return true;
|
||||
} else {
|
||||
Boolean result = call(peerId, callerName, destination);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean call(String peerId, String callerName, String destination) {
|
||||
String clientId = Red5.getConnectionLocal().getClient().getId();
|
||||
String userid = getUserId();
|
||||
|
@ -37,7 +37,7 @@ public class CallStream implements StreamObserver {
|
||||
|
||||
private FlashToSipAudioStream userTalkStream;
|
||||
private SipToFlashAudioStream userListenStream;
|
||||
private final Codec sipCodec;
|
||||
public final Codec sipCodec;
|
||||
private final SipConnectInfo connInfo;
|
||||
private final IScope scope;
|
||||
private CallStreamObserver callStreamObserver;
|
||||
@ -80,6 +80,10 @@ public class CallStream implements StreamObserver {
|
||||
public String getListenStreamName() {
|
||||
return userListenStream.getStreamName();
|
||||
}
|
||||
|
||||
public Codec getSipCodec() {
|
||||
return sipCodec;
|
||||
}
|
||||
|
||||
public void startTalkStream(IBroadcastStream broadcastStream, IScope scope) throws StreamException {
|
||||
log.debug("userTalkStream setup");
|
||||
|
@ -56,6 +56,9 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
||||
private final String clientId;
|
||||
private final AudioConferenceProvider portProvider;
|
||||
private DatagramSocket localSocket;
|
||||
private String _callerName;
|
||||
private String _destination;
|
||||
private Boolean listeningToGlobal = false;
|
||||
|
||||
private enum CallState {
|
||||
UA_IDLE(0), UA_INCOMING_CALL(1), UA_OUTGOING_CALL(2), UA_ONCALL(3);
|
||||
@ -66,6 +69,10 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
||||
|
||||
private CallState callState;
|
||||
|
||||
public String getDestination() {
|
||||
return _destination;
|
||||
}
|
||||
|
||||
public CallAgent(String sipClientRtpIp, SipProvider sipProvider, SipPeerProfile userProfile, AudioConferenceProvider portProvider, String clientId) {
|
||||
this.sipProvider = sipProvider;
|
||||
this.clientRtpIp = sipClientRtpIp;
|
||||
@ -79,7 +86,7 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
||||
}
|
||||
|
||||
private void initSessionDescriptor() {
|
||||
log.debug("initSessionDescriptor");
|
||||
log.debug("initSessionDescriptor");
|
||||
SessionDescriptor newSdp = SdpUtils.createInitialSdp(userProfile.username,
|
||||
this.clientRtpIp, userProfile.audioPort,
|
||||
userProfile.videoPort, userProfile.audioCodecsPrecedence );
|
||||
@ -87,7 +94,13 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
||||
log.debug("localSession Descriptor = " + localSession );
|
||||
}
|
||||
|
||||
public Boolean isListeningToGlobal() {
|
||||
return listeningToGlobal;
|
||||
}
|
||||
|
||||
public void call(String callerName, String destination) {
|
||||
_callerName = callerName;
|
||||
_destination = destination;
|
||||
log.debug("{} making a call to {}", callerName, destination);
|
||||
try {
|
||||
localSocket = getLocalAudioSocket();
|
||||
@ -132,11 +145,15 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
||||
/** Closes an ongoing, incoming, or pending call */
|
||||
public void hangup() {
|
||||
log.debug("hangup");
|
||||
|
||||
if (callState == CallState.UA_IDLE) return;
|
||||
closeVoiceStreams();
|
||||
if (call != null) call.hangup();
|
||||
callState = CallState.UA_IDLE;
|
||||
if (listeningToGlobal) {
|
||||
callState = CallState.UA_IDLE;
|
||||
clientConnManager.leaveConference(clientId);
|
||||
} else {
|
||||
if (callState == CallState.UA_IDLE) return;
|
||||
closeVoiceStreams();
|
||||
if (call != null) call.hangup();
|
||||
callState = CallState.UA_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
private DatagramSocket getLocalAudioSocket() throws Exception {
|
||||
@ -187,7 +204,12 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
||||
callStream = callStreamFactory.createCallStream(sipCodec, connInfo);
|
||||
callStream.addCallStreamObserver(this);
|
||||
callStream.start();
|
||||
notifyListenersOnCallConnected(callStream.getTalkStreamName(), callStream.getListenStreamName());
|
||||
if (_callerName.startsWith("GLOBAL_AUDIO_")) {
|
||||
KeepGlobalAudioAlive globalAudioKeepAlive = new KeepGlobalAudioAlive(connInfo.getSocket(), connInfo, sipCodec.getCodecId());
|
||||
GlobalCall.addGlobalAudioStream(_destination, callStream.getListenStreamName(), sipCodec.getCodecName(), globalAudioKeepAlive);
|
||||
} else {
|
||||
notifyListenersOnCallConnected(callStream.getTalkStreamName(), callStream.getListenStreamName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to create Call Stream.");
|
||||
System.out.println(StackTraceUtil.getStackTrace(e));
|
||||
@ -214,6 +236,23 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
||||
callStream.stopTalkStream(broadcastStream, scope);
|
||||
}
|
||||
}
|
||||
|
||||
public void connectToGlobalStream(String clientId, String destination) {
|
||||
listeningToGlobal = true;
|
||||
_destination = destination;
|
||||
|
||||
String globalAudioStreamName = GlobalCall.getGlobalAudioStream(destination);
|
||||
while (globalAudioStreamName.equals("reserved")) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
globalAudioStreamName = GlobalCall.getGlobalAudioStream(destination);
|
||||
}
|
||||
|
||||
GlobalCall.addUser(_destination);
|
||||
clientConnManager.joinConferenceSuccess(clientId, "", globalAudioStreamName, GlobalCall.getRoomCodec(destination));
|
||||
}
|
||||
|
||||
private void closeVoiceStreams() {
|
||||
log.debug("Shutting down the voice streams.");
|
||||
@ -336,12 +375,14 @@ public class CallAgent extends CallListenerAdapter implements CallStreamObserver
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
log.debug("Closing local audio port {}", localSocket.getLocalPort());
|
||||
if (localSocket != null) {
|
||||
localSocket.close();
|
||||
} else {
|
||||
log.debug("Trying to close un-allocated port {}", localSocket.getLocalPort());
|
||||
}
|
||||
log.debug("Closing local audio port {}", localSocket.getLocalPort());
|
||||
if (localSocket != null) {
|
||||
if (!listeningToGlobal) {
|
||||
localSocket.close();
|
||||
}
|
||||
} else {
|
||||
log.debug("Trying to close un-allocated port {}", localSocket.getLocalPort());
|
||||
}
|
||||
}
|
||||
|
||||
/** Callback function called when arriving a BYE request */
|
||||
|
@ -0,0 +1,80 @@
|
||||
package org.bigbluebutton.voiceconf.sip;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.net.DatagramSocket;
|
||||
import org.red5.logging.Red5LoggerFactory;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class GlobalCall {
|
||||
private static final Logger log = Red5LoggerFactory.getLogger( GlobalCall.class, "sip" );
|
||||
|
||||
public static Map<String,String> roomToStreamMap = new ConcurrentHashMap<String,String>();
|
||||
public static Map<String,Integer> numberOfUsers = new ConcurrentHashMap<String,Integer>();
|
||||
public static Map<String,String> roomToCodecMap = new ConcurrentHashMap<String, String>();
|
||||
public static Map<String,KeepGlobalAudioAlive> globalAudioKeepAliverMap = new ConcurrentHashMap<String, KeepGlobalAudioAlive>();
|
||||
|
||||
private static boolean roomHasGlobalStream(String roomName) {
|
||||
return roomToStreamMap.containsKey(roomName);
|
||||
}
|
||||
|
||||
public static synchronized boolean reservePlaceToCreateGlobal(String roomName) {
|
||||
if (roomToStreamMap.containsKey(roomName)) {
|
||||
return false;
|
||||
} else {
|
||||
roomToStreamMap.put(roomName, "reserved");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void addGlobalAudioStream(String roomName, String globalAudioStreamName, String codecName, KeepGlobalAudioAlive globalAudioKeepAlive) {
|
||||
roomToStreamMap.put(roomName, globalAudioStreamName);
|
||||
roomToCodecMap.put(roomName, codecName);
|
||||
numberOfUsers.put(roomName, 0);
|
||||
globalAudioKeepAliverMap.put(roomName,globalAudioKeepAlive);
|
||||
globalAudioKeepAlive.start();
|
||||
}
|
||||
|
||||
public static synchronized String getGlobalAudioStream(String roomName) {
|
||||
return roomToStreamMap.get(roomName);
|
||||
}
|
||||
|
||||
public static synchronized boolean removeRoomIfUnused(String roomName) {
|
||||
if (numberOfUsers.containsKey(roomName) && numberOfUsers.get(roomName) <= 0) {
|
||||
removeRoom(roomName);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeRoom(String roomName) {
|
||||
log.debug("REMOVING GLOBAL AUDIO FROM ROOM " + roomName);
|
||||
roomToStreamMap.remove(roomName);
|
||||
numberOfUsers.remove(roomName);
|
||||
roomToCodecMap.remove(roomName);
|
||||
KeepGlobalAudioAlive globalAudioKeepAlive = globalAudioKeepAliverMap.get(roomName);
|
||||
globalAudioKeepAlive.halt();
|
||||
globalAudioKeepAliverMap.remove(roomName);
|
||||
}
|
||||
|
||||
public static synchronized void addUser(String roomName) {
|
||||
int nUsers = numberOfUsers.get(roomName);
|
||||
nUsers += 1;
|
||||
numberOfUsers.put(roomName, nUsers);
|
||||
}
|
||||
|
||||
public static synchronized void removeUser(String roomName) {
|
||||
if (numberOfUsers.containsKey(roomName)) {
|
||||
int nUsers = numberOfUsers.get(roomName);
|
||||
nUsers -=1;
|
||||
numberOfUsers.put(roomName, nUsers);
|
||||
log.debug("REMOVING USER: Number of users left is " + nUsers);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getRoomCodec(String roomName) {
|
||||
return roomToCodecMap.get(roomName);
|
||||
}
|
||||
}
|
@ -145,13 +145,11 @@ public class KeepAliveUdp extends Thread
|
||||
{ try
|
||||
{ while(!stop)
|
||||
{ sendToken();
|
||||
//System.out.print(".");
|
||||
Thread.sleep(delta_time);
|
||||
if (expire>0 && System.currentTimeMillis()>expire) halt();
|
||||
}
|
||||
}
|
||||
catch (Exception e) { e.printStackTrace(); }
|
||||
//System.out.println("o");
|
||||
udp_socket=null;
|
||||
}
|
||||
|
||||
@ -165,4 +163,4 @@ public class KeepAliveUdp extends Thread
|
||||
return str+" ("+delta_time+"ms)";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,130 @@
|
||||
/**
|
||||
*
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation; either version 2.1 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
**/
|
||||
package org.bigbluebutton.voiceconf.sip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Random;
|
||||
import org.bigbluebutton.voiceconf.red5.media.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.bigbluebutton.voiceconf.red5.media.net.RtpPacket;
|
||||
import org.bigbluebutton.voiceconf.red5.media.net.RtpSocket;
|
||||
import org.bigbluebutton.voiceconf.sip.SipConnectInfo;
|
||||
import org.bigbluebutton.voiceconf.util.StackTraceUtil;
|
||||
import org.red5.logging.Red5LoggerFactory;
|
||||
import java.lang.InterruptedException;
|
||||
|
||||
|
||||
public class KeepGlobalAudioAlive extends Thread {
|
||||
// Time is in milliseconds
|
||||
// This time should be less than rtp-timeout-sec to prevent freeswitch from kicking out
|
||||
// the global audio
|
||||
private static final long DELTA_TIME_TO_SEND_KEEPALIVE = 60000;
|
||||
|
||||
private static final int RTP_HEADER_SIZE = 12;
|
||||
private RtpSocket rtpSocket = null;
|
||||
private int sequenceNum = 0;
|
||||
private final DatagramSocket srcSocket;
|
||||
private final SipConnectInfo connInfo;
|
||||
private boolean marked = false;
|
||||
private long startTimestamp = 0;
|
||||
boolean stop=false;
|
||||
int codecId;
|
||||
private static Logger log = Red5LoggerFactory.getLogger(KeepGlobalAudioAlive.class, "sip");
|
||||
|
||||
|
||||
public KeepGlobalAudioAlive(DatagramSocket srcSocket, SipConnectInfo connInfo, int codecId) {
|
||||
this.srcSocket = srcSocket;
|
||||
this.connInfo = connInfo;
|
||||
this.codecId = codecId;
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
try {
|
||||
rtpSocket = new RtpSocket(srcSocket, InetAddress.getByName(connInfo.getRemoteAddr()), connInfo.getRemotePort());
|
||||
Random rgen = new Random();
|
||||
sequenceNum = rgen.nextInt();
|
||||
} catch (UnknownHostException e) {
|
||||
log.error("Failed to connect to {}", connInfo.getRemoteAddr());
|
||||
log.error(StackTraceUtil.getStackTrace(e));
|
||||
log.error("Rtp sender failed to connect to " + connInfo.getRemoteAddr() + ".");
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try
|
||||
{
|
||||
while(!stop)
|
||||
{
|
||||
byte array[]= new byte[]{0,0,0,0};
|
||||
sendAudio(array, startTimestamp);
|
||||
startTimestamp++;
|
||||
Thread.sleep(DELTA_TIME_TO_SEND_KEEPALIVE);
|
||||
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
log.error("Failed to sleep time in keepAlive");
|
||||
}
|
||||
}
|
||||
|
||||
public void halt()
|
||||
{
|
||||
stop=true;
|
||||
}
|
||||
|
||||
|
||||
public void sendAudio(byte[] audioData, long timestamp) {
|
||||
byte[] transcodedAudioDataBuffer = new byte[audioData.length + RTP_HEADER_SIZE];
|
||||
System.arraycopy(audioData, 0, transcodedAudioDataBuffer, RTP_HEADER_SIZE, audioData.length);
|
||||
RtpPacket rtpPacket = new RtpPacket(transcodedAudioDataBuffer, transcodedAudioDataBuffer.length);
|
||||
if (!marked) {
|
||||
rtpPacket.setMarker(true);
|
||||
marked = true;
|
||||
startTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
rtpPacket.setPadding(false);
|
||||
rtpPacket.setExtension(false);
|
||||
rtpPacket.setPayloadType(codecId);
|
||||
rtpPacket.setSeqNum(sequenceNum++);
|
||||
rtpPacket.setTimestamp(timestamp);
|
||||
rtpPacket.setPayloadLength(audioData.length);
|
||||
try {
|
||||
rtpSocketSend(rtpPacket);
|
||||
} catch (StreamException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
log.error("Failed to send data to server.");
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void rtpSocketSend(RtpPacket rtpPacket) throws StreamException {
|
||||
try {
|
||||
|
||||
rtpSocket.send(rtpPacket);
|
||||
} catch (IOException e) {
|
||||
throw new StreamException("Failed to send data to server.");
|
||||
}
|
||||
}
|
||||
}
|
@ -98,7 +98,6 @@ public class SipPeer implements SipRegisterAgentListener {
|
||||
log.debug( "SIPUser register : {}", fromURL );
|
||||
log.debug( "SIPUser register : {}", registeredProfile.contactUrl );
|
||||
}
|
||||
|
||||
|
||||
public void call(String clientId, String callerName, String destination) {
|
||||
if (!registered) {
|
||||
@ -112,13 +111,26 @@ public class SipPeer implements SipRegisterAgentListener {
|
||||
log.warn("We are not registered to FreeSWITCH. However, we will allow {} to call {}.", callerName, destination);
|
||||
// return;
|
||||
}
|
||||
|
||||
SipPeerProfile callerProfile = SipPeerProfile.copy(registeredProfile);
|
||||
|
||||
CallAgent ca = createCallAgent(clientId);
|
||||
|
||||
ca.call(callerName, destination);
|
||||
}
|
||||
|
||||
public void connectToGlobalStream(String clientId, String destination) {
|
||||
CallAgent ca = createCallAgent(clientId);
|
||||
|
||||
ca.connectToGlobalStream(clientId, destination);
|
||||
}
|
||||
|
||||
private CallAgent createCallAgent(String clientId) {
|
||||
SipPeerProfile callerProfile = SipPeerProfile.copy(registeredProfile);
|
||||
CallAgent ca = new CallAgent(this.clientRtpIp, sipProvider, callerProfile, audioconfProvider, clientId);
|
||||
ca.setClientConnectionManager(clientConnManager);
|
||||
ca.setCallStreamFactory(callStreamFactory);
|
||||
callManager.add(ca);
|
||||
ca.call(callerName, destination);
|
||||
|
||||
return ca;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
@ -134,11 +146,24 @@ public class SipPeer implements SipRegisterAgentListener {
|
||||
}
|
||||
|
||||
public void hangup(String clientId) {
|
||||
log.debug( "SIPUser hangup" );
|
||||
log.debug( "SIPUser hangup" );
|
||||
|
||||
CallAgent ca = callManager.remove(clientId);
|
||||
|
||||
CallAgent ca = callManager.remove(clientId);
|
||||
if (ca != null) {
|
||||
ca.hangup();
|
||||
if (ca.isListeningToGlobal()) {
|
||||
String destination = ca.getDestination();
|
||||
GlobalCall.removeUser(destination);
|
||||
ca.hangup();
|
||||
|
||||
boolean roomRemoved = GlobalCall.removeRoomIfUnused(destination);
|
||||
if (roomRemoved) {
|
||||
CallAgent caGlobal = callManager.remove(destination);
|
||||
caGlobal.hangup();
|
||||
}
|
||||
} else {
|
||||
ca.hangup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,7 +175,7 @@ public class SipPeer implements SipRegisterAgentListener {
|
||||
CallAgent ca = (CallAgent) iter.next();
|
||||
ca.hangup();
|
||||
}
|
||||
|
||||
|
||||
if (registerAgent != null) {
|
||||
registerAgent.unregister();
|
||||
registerAgent = null;
|
||||
|
@ -65,7 +65,7 @@ public final class SipPeerManager {
|
||||
if (sipPeer == null) throw new PeerNotFoundException("Can't find sip peer " + peerId);
|
||||
sipPeer.call(clientId, callerName, destination);
|
||||
}
|
||||
|
||||
|
||||
public void unregister(String userid) {
|
||||
SipPeer sipUser = sipPeers.get(userid);
|
||||
if (sipUser != null) {
|
||||
@ -100,6 +100,13 @@ public final class SipPeerManager {
|
||||
sipPeers.remove(userid);
|
||||
}
|
||||
|
||||
public void connectToGlobalStream(String peerId, String clientId, String destination) {
|
||||
SipPeer sipUser = sipPeers.get(peerId);
|
||||
if (sipUser != null) {
|
||||
sipUser.connectToGlobalStream(clientId, destination);
|
||||
}
|
||||
}
|
||||
|
||||
public void close(String userid) {
|
||||
SipPeer sipUser = sipPeers.get(userid);
|
||||
if (sipUser != null) {
|
||||
|
@ -32,12 +32,6 @@ public class VoiceService {
|
||||
bbbInGW = inGW;
|
||||
}
|
||||
|
||||
public void getMeetMeUsers() {
|
||||
String meetingID = Red5.getConnectionLocal().getScope().getName();
|
||||
String requesterID = getBbbSession().getInternalUserID();
|
||||
bbbInGW.getVoiceUsers(meetingID, requesterID);
|
||||
}
|
||||
|
||||
/*
|
||||
private Map<Integer, Map> arrayListToMap(ArrayList<Participant> alp) {
|
||||
log.debug("Converting arraylist to Map " + alp.size());
|
||||
|
@ -40,7 +40,6 @@ public interface IBigBlueButtonInGW {
|
||||
void getRecordingStatus(String meetingId, String userId);
|
||||
|
||||
// Voice
|
||||
void getVoiceUsers(String meetingID, String requesterID);
|
||||
void muteAllUsers(String meetingID, String requesterID, Boolean mute);
|
||||
void isMeetingMuted(String meetingID, String requesterID);
|
||||
void muteUser(String meetingID, String requesterID, String userID, Boolean mute);
|
||||
|
@ -390,10 +390,6 @@ class BigBlueButtonInGW(bbbGW: BigBlueButtonGateway) extends IBigBlueButtonInGW
|
||||
*******************************************************************/
|
||||
val voiceGW = new VoiceInGateway(bbbGW)
|
||||
|
||||
def getVoiceUsers(meetingID: String, requesterID: String) {
|
||||
voiceGW.getVoiceUsers(meetingID, requesterID)
|
||||
}
|
||||
|
||||
def muteAllUsers(meetingID: String, requesterID: String, mute: java.lang.Boolean) {
|
||||
voiceGW.muteAllUsers(meetingID, requesterID, mute)
|
||||
}
|
||||
|
3
bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/CollectorActor.scala
Executable file → Normal file
3
bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/CollectorActor.scala
Executable file → Normal file
@ -1452,7 +1452,6 @@ class CollectorActor(dispatcher: IDispatcher) extends Actor {
|
||||
header.put(Constants.NAME, MessageNames.GET_PERMISSION_SETTINGS_REPLY)
|
||||
header.put(Constants.TIMESTAMP, TimestampGenerator.generateTimestamp)
|
||||
|
||||
println("***** DISPATCHING GET PERMISSIONS SETTING REPLY *****************")
|
||||
dispatcher.dispatch(buildJson(header, payload))
|
||||
}
|
||||
|
||||
@ -2136,4 +2135,4 @@ class CollectorActor(dispatcher: IDispatcher) extends Actor {
|
||||
val json = WhiteboardMessageToJsonConverter.isWhiteboardEnabledReplyToJson(msg)
|
||||
dispatcher.dispatch(json)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
0
bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala
Executable file → Normal file
0
bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala
Executable file → Normal file
@ -8,10 +8,6 @@ trait VoiceApp {
|
||||
|
||||
val outGW: MessageOutGateway
|
||||
|
||||
def handleSendVoiceUsersRequest(msg: SendVoiceUsersRequest) {
|
||||
|
||||
}
|
||||
|
||||
def handleMuteMeetingRequest(msg: MuteMeetingRequest) {
|
||||
|
||||
}
|
||||
|
@ -4,10 +4,6 @@ import org.bigbluebutton.core.BigBlueButtonGateway
|
||||
import org.bigbluebutton.core.api._
|
||||
|
||||
class VoiceInGateway(bbbGW: BigBlueButtonGateway) {
|
||||
def getVoiceUsers(meetingID: String, requesterID: String) {
|
||||
bbbGW.accept(new SendVoiceUsersRequest(meetingID, requesterID))
|
||||
}
|
||||
|
||||
def muteAllUsers(meetingID: String, requesterID: String, mute: Boolean) {
|
||||
bbbGW.accept(new MuteMeetingRequest(meetingID, requesterID, mute))
|
||||
}
|
||||
|
@ -218,8 +218,11 @@ bbb.desktopView.actualSize = Display actual size
|
||||
bbb.desktopView.minimizeBtn.accessibilityName = Minimize the Desktop Sharing View Window
|
||||
bbb.desktopView.maximizeRestoreBtn.accessibilityName = Maximize the Desktop Sharing View Window
|
||||
bbb.desktopView.closeBtn.accessibilityName = Close the Desktop Sharing View Window
|
||||
bbb.toolbar.phone.toolTip.start = Join Audio
|
||||
bbb.toolbar.phone.toolTip.stop = Leave Audio
|
||||
bbb.toolbar.phone.toolTip.start = Share My Microphone
|
||||
bbb.toolbar.phone.toolTip.stop = Stop Sharing My Microphone
|
||||
bbb.toolbar.phone.toolTip.mute = Stop listening the conference
|
||||
bbb.toolbar.phone.toolTip.unmute = Start listening the conference
|
||||
bbb.toolbar.phone.toolTip.nomic = No microphone detected
|
||||
bbb.toolbar.deskshare.toolTip.start = Share My Desktop
|
||||
bbb.toolbar.deskshare.toolTip.stop = Stop Sharing My Desktop
|
||||
bbb.toolbar.video.toolTip.start = Share My Webcam
|
||||
@ -354,6 +357,8 @@ bbb.shortcutkey.share.desktop = 68
|
||||
bbb.shortcutkey.share.desktop.function = Open desktop sharing window
|
||||
bbb.shortcutkey.share.microphone = 79
|
||||
bbb.shortcutkey.share.microphone.function = Open audio settings window
|
||||
bbb.shortcutkey.share.pauseRemoteStream = 80
|
||||
bbb.shortcutkey.share.pauseRemoteStream.function = Start/Stop listening the conference
|
||||
bbb.shortcutkey.share.webcam = 66
|
||||
bbb.shortcutkey.share.webcam.function = Open webcam sharing window
|
||||
|
||||
|
@ -203,8 +203,11 @@ bbb.desktopView.actualSize = Exibir tamanho original
|
||||
bbb.desktopView.minimizeBtn.accessibilityName = Minimizar janela do compartilhamento de tela
|
||||
bbb.desktopView.maximizeRestoreBtn.accessibilityName = Maximizar janela do compartilhamento de tela
|
||||
bbb.desktopView.closeBtn.accessibilityName = Fechar janela do compartilhamento de tela
|
||||
bbb.toolbar.phone.toolTip.start = Habilitar o som
|
||||
bbb.toolbar.phone.toolTip.stop = Desabilitar o som
|
||||
bbb.toolbar.phone.toolTip.start = Transmitir meu microfone
|
||||
bbb.toolbar.phone.toolTip.stop = Interromper transmissão do meu microfone
|
||||
bbb.toolbar.phone.toolTip.mute = Parar de ouvir a conferência
|
||||
bbb.toolbar.phone.toolTip.unmute = Começar a ouvir a conferência
|
||||
bbb.toolbar.phone.toolTip.nomic = Nenhum microfone foi detectado
|
||||
bbb.toolbar.deskshare.toolTip.start = Compartilhar minha tela
|
||||
bbb.toolbar.deskshare.toolTip.stop = Interromper compartilhamento de tela
|
||||
bbb.toolbar.video.toolTip.start = Transmitir minha câmera
|
||||
@ -339,6 +342,8 @@ bbb.shortcutkey.share.desktop = 68
|
||||
bbb.shortcutkey.share.desktop.function = Abrir janela de compartilhamento de tela
|
||||
bbb.shortcutkey.share.microphone = 79
|
||||
bbb.shortcutkey.share.microphone.function = Abrir janela de configuração de microfone
|
||||
bbb.shortcutkey.share.pauseRemoteStream = 80
|
||||
bbb.shortcutkey.share.pauseRemoteStream.function = Começar a/Parar de ouvir a conferência
|
||||
bbb.shortcutkey.share.webcam = 66
|
||||
bbb.shortcutkey.share.webcam.function = Abrir janela de transmissão da câmera
|
||||
|
||||
|
@ -47,9 +47,12 @@
|
||||
|
||||
<module name="PhoneModule" url="http://HOST/client/PhoneModule.swf?v=VERSION"
|
||||
uri="rtmp://HOST/sip"
|
||||
autoJoin="true"
|
||||
autoJoin="false"
|
||||
listenOnlyMode="true"
|
||||
presenterShareOnly="false"
|
||||
skipCheck="false"
|
||||
showButton="true"
|
||||
showSpeakerButton="false"
|
||||
enabledEchoCancel="true"
|
||||
useWebrtcIfAvailable="true"
|
||||
echoTestApp="9196"
|
||||
|
@ -154,6 +154,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
if (ShortcutOptions.audioActive){
|
||||
keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.share.microphone') as String)] = ShortcutEvent.SHARE_MICROPHONE;
|
||||
keyCombos[globalModifier+(ResourceUtil.getInstance().getString('bbb.shortcutkey.share.pauseRemoteStream') as String)] = ShortcutEvent.PAUSE_REMOTE_STREAM;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@ package org.bigbluebutton.main.api
|
||||
import org.bigbluebutton.modules.phone.events.WebRtcCallStartedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRtcConfCallEndedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRtcConfCallStartedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRtcEchoTestFailedEvent;
|
||||
import org.bigbluebutton.modules.present.events.GetListOfPresentationsRequest;
|
||||
import org.bigbluebutton.modules.present.events.RemovePresentationEvent;
|
||||
import org.bigbluebutton.modules.present.events.UploadEvent;
|
||||
@ -413,7 +414,7 @@ package org.bigbluebutton.main.api
|
||||
|
||||
private function handleWebRtcEchoTestFailed(cause:String):void {
|
||||
trace(LOG + "handleWebRtcEchoTestFailed: cause=[" + cause + "]");
|
||||
// _dispatcher.dispatchEvent(new WebRtcCallFailedEvent(reason));
|
||||
_dispatcher.dispatchEvent(new WebRtcEchoTestFailedEvent(cause));
|
||||
}
|
||||
|
||||
private function handleWebRtcEchoTestEnded(cause:String):void {
|
||||
|
@ -50,6 +50,7 @@ package org.bigbluebutton.main.events {
|
||||
public static const SHARE_DESKTOP:String = 'SHARE_DESKTOP';
|
||||
public static const SHARE_MICROPHONE:String = 'SHARE_MICROPHONE';
|
||||
public static const SHARE_WEBCAM:String = 'SHARE_WEBCAM';
|
||||
public static const PAUSE_REMOTE_STREAM:String = 'PAUSE_REMOTE_STREAM';
|
||||
|
||||
public static const REMOTE_FOCUS_DESKTOP:String = 'REMOTE_FOCUS_DESKTOP';
|
||||
public static const REMOTE_FOCUS_WEBCAM:String = 'REMOTE_FOCUS_WEBCAM';
|
||||
|
@ -88,7 +88,10 @@ package org.bigbluebutton.main.model
|
||||
generalResource.push('bbb.shortcutkey.focus.voting');
|
||||
}
|
||||
|
||||
if (audio){generalResource.push('bbb.shortcutkey.share.microphone');}
|
||||
if (audio){
|
||||
generalResource.push('bbb.shortcutkey.share.microphone');
|
||||
generalResource.push('bbb.shortcutkey.share.pauseRemoteStream');
|
||||
}
|
||||
if (deskshare){generalResource.push('bbb.shortcutkey.share.desktop');}
|
||||
if (webcam){generalResource.push('bbb.shortcutkey.share.webcam');}
|
||||
if (polling){generalResource.push('bbb.shortcutkey.polling.buttonClick');}
|
||||
|
@ -38,9 +38,18 @@ package org.bigbluebutton.modules.phone
|
||||
[Bindable]
|
||||
public var useWebrtcIfAvailable:Boolean = true;
|
||||
|
||||
[Bindable]
|
||||
public var echoTestApp:String = "9196";
|
||||
|
||||
[Bindable]
|
||||
public var echoTestApp:String = "9196";
|
||||
|
||||
[Bindable]
|
||||
public var listenOnlyMode:Boolean = true;
|
||||
|
||||
[Bindable]
|
||||
public var presenterShareOnly:Boolean = false;
|
||||
|
||||
[Bindable]
|
||||
public var showSpeakerButton:Boolean = true;
|
||||
|
||||
public function PhoneOptions() {
|
||||
parseOptions();
|
||||
}
|
||||
@ -66,9 +75,18 @@ package org.bigbluebutton.modules.phone
|
||||
if (vxml.@useWebrtcIfAvailable != undefined) {
|
||||
useWebrtcIfAvailable = (vxml.@useWebrtcIfAvailable.toString().toUpperCase() == "TRUE");
|
||||
}
|
||||
if (vxml.@echoTestApp != undefined) {
|
||||
echoTestApp = vxml.@echoTestApp.toString();
|
||||
}
|
||||
if (vxml.@echoTestApp != undefined) {
|
||||
echoTestApp = vxml.@echoTestApp.toString();
|
||||
}
|
||||
if (vxml.@listenOnlyMode != undefined) {
|
||||
listenOnlyMode = (vxml.@listenOnlyMode.toString().toUpperCase() == "TRUE");
|
||||
}
|
||||
if (vxml.@presenterShareOnly != undefined) {
|
||||
presenterShareOnly = (vxml.@presenterShareOnly.toString().toUpperCase() == "TRUE");
|
||||
}
|
||||
if (vxml.@showSpeakerButton != undefined) {
|
||||
showSpeakerButton = (vxml.@showSpeakerButton.toString().toUpperCase() == "TRUE");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ package org.bigbluebutton.modules.phone.events
|
||||
public var publishStreamName:String;
|
||||
public var playStreamName:String;
|
||||
public var codec:String;
|
||||
public var listenOnlyCall:Boolean;
|
||||
|
||||
public function FlashCallConnectedEvent(publishStream:String, playStream:String, vcodec:String)
|
||||
{
|
||||
@ -34,6 +35,7 @@ package org.bigbluebutton.modules.phone.events
|
||||
this.publishStreamName = publishStream;
|
||||
this.playStreamName = playStream;
|
||||
this.codec = vcodec;
|
||||
this.listenOnlyCall = !(publishStreamName != null && publishStreamName.length > 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
package org.bigbluebutton.modules.phone.events
|
||||
{
|
||||
import flash.events.Event;
|
||||
|
||||
public class FlashJoinedListenOnlyVoiceConferenceEvent extends Event
|
||||
{
|
||||
public static const JOINED_LISTEN_ONLY_VOICE_CONFERENCE:String = "flash joined listen only voice conference event";
|
||||
|
||||
public function FlashJoinedListenOnlyVoiceConferenceEvent()
|
||||
{
|
||||
super(JOINED_LISTEN_ONLY_VOICE_CONFERENCE, true, false);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,10 +5,12 @@ package org.bigbluebutton.modules.phone.events
|
||||
public class WebRtcEchoTestFailedEvent extends Event
|
||||
{
|
||||
public static const WEBRTC_ECHO_TEST_FAILED:String = "webrtc echo test failed event";
|
||||
public var reason:String;
|
||||
|
||||
public function WebRtcEchoTestFailedEvent()
|
||||
public function WebRtcEchoTestFailedEvent(reason:String)
|
||||
{
|
||||
super(WEBRTC_ECHO_TEST_FAILED, true, false);
|
||||
this.reason = reason;
|
||||
}
|
||||
}
|
||||
}
|
@ -160,9 +160,9 @@ package org.bigbluebutton.modules.phone.managers {
|
||||
// SIP Actions
|
||||
//
|
||||
//********************************************************************************************
|
||||
public function doCall(dialStr:String):void {
|
||||
trace(LOG + "in doCall - Calling " + dialStr);
|
||||
netConnection.call("voiceconf.call", null, "default", username, dialStr);
|
||||
public function doCall(dialStr:String, listenOnly:Boolean = false):void {
|
||||
trace(LOG + "in doCall - Calling " + dialStr + (listenOnly? " *listen only*": ""));
|
||||
netConnection.call("voiceconf.call", null, "default", username, dialStr, listenOnly.toString());
|
||||
}
|
||||
|
||||
public function doHangUp():void {
|
||||
|
@ -14,6 +14,7 @@
|
||||
import org.bigbluebutton.modules.phone.events.FlashEchoTestStoppedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashErrorEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashJoinVoiceConferenceCommand;
|
||||
import org.bigbluebutton.modules.phone.events.FlashJoinedListenOnlyVoiceConferenceEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashJoinedVoiceConferenceEvent;
|
||||
import org.bigbluebutton.modules.phone.events.FlashLeaveVoiceConferenceCommand;
|
||||
import org.bigbluebutton.modules.phone.events.FlashLeftVoiceConferenceEvent;
|
||||
@ -36,7 +37,11 @@
|
||||
private static const CALLING_INTO_CONFERENCE:String = "calling into conference state";
|
||||
private static const IN_CONFERENCE:String = "in conference state";
|
||||
private static const STOP_ECHO_THEN_JOIN_CONF:String = "stop echo then join conf state";
|
||||
|
||||
|
||||
private static const CALL_TO_LISTEN_ONLY_STREAM:String = "call to listen only stream";
|
||||
private static const CONNECTING_TO_LISTEN_ONLY_STREAM:String = "connecting to listen only stream";
|
||||
private static const ON_LISTEN_ONLY_STREAM:String = "on listen only stream";
|
||||
|
||||
private var state:String = INITED;
|
||||
|
||||
private var options:PhoneOptions;
|
||||
@ -83,7 +88,12 @@
|
||||
}
|
||||
|
||||
private function startCall():void {
|
||||
if (options.skipCheck || echoTestDone) {
|
||||
/**
|
||||
* For echo test even if user has done echo test. This way, user is able to change mics
|
||||
* after. (richard mar 28, 2014)
|
||||
*/
|
||||
// if (options.skipCheck || echoTestDone) {
|
||||
if (options.skipCheck) {
|
||||
trace(LOG + "Calling into voice conference. skipCheck=[" + options.skipCheck + "] echoTestDone=[" + echoTestDone + "]");
|
||||
callIntoVoiceConference();
|
||||
} else {
|
||||
@ -99,6 +109,39 @@
|
||||
}
|
||||
}
|
||||
|
||||
private function joinListenOnlyCall():void {
|
||||
if (options.listenOnlyMode) {
|
||||
trace(LOG + "Joining listen only call");
|
||||
callToListenOnlyStream();
|
||||
}
|
||||
}
|
||||
|
||||
private function leaveListenOnlyCall():void {
|
||||
if (state == ON_LISTEN_ONLY_STREAM) {
|
||||
trace(LOG + "Leaving listen only call");
|
||||
hangup();
|
||||
}
|
||||
}
|
||||
|
||||
private function callToListenOnlyStream():void {
|
||||
if (isConnected()) {
|
||||
var destination:String = UsersUtil.getVoiceBridge();
|
||||
|
||||
if (destination != null && destination != "") {
|
||||
trace(LOG + "Connecting to listen only stream =[" + destination + "]");
|
||||
state = CONNECTING_TO_LISTEN_ONLY_STREAM;
|
||||
connectionManager.doCall(destination, true);
|
||||
} else {
|
||||
trace(LOG + "Invalid voice conference [" + destination + "]");
|
||||
dispatcher.dispatchEvent(new FlashErrorEvent(FlashErrorEvent.INVALID_VOICE_DESTINATION));
|
||||
}
|
||||
} else {
|
||||
trace(LOG + "Need to connect before we can join the voice conference.");
|
||||
state = CALL_TO_LISTEN_ONLY_STREAM;
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
private function callIntoVoiceConference():void {
|
||||
if (isConnected()) {
|
||||
var destination:String = UsersUtil.getVoiceBridge();
|
||||
@ -143,7 +186,8 @@
|
||||
}
|
||||
|
||||
public function userRequestedHangup():void {
|
||||
if (usingFlash) {
|
||||
trace(LOG + "userRequestedHangup, current state: " + state);
|
||||
if (usingFlash || state == ON_LISTEN_ONLY_STREAM) {
|
||||
streamManager.stopStreams();
|
||||
connectionManager.disconnect(true);
|
||||
}
|
||||
@ -158,14 +202,17 @@
|
||||
usingFlash = true;
|
||||
autoJoin();
|
||||
}
|
||||
joinListenOnlyCall();
|
||||
}
|
||||
|
||||
private function hangup():void {
|
||||
trace(LOG + "hangup, current state: " + state);
|
||||
streamManager.stopStreams();
|
||||
connectionManager.doHangUp();
|
||||
}
|
||||
|
||||
private function hangupEchoThenJoinVoiceConference():void {
|
||||
trace(LOG + "hangup EchoThenJoinVoiceConference, current state: " + state);
|
||||
state = STOP_ECHO_THEN_JOIN_CONF;
|
||||
hangup();
|
||||
}
|
||||
@ -180,28 +227,24 @@
|
||||
}
|
||||
|
||||
public function handleFlashStopEchoTestCommand(event:FlashStopEchoTestCommand):void {
|
||||
trace(LOG + "handling FlashStopEchoTestCommand.");
|
||||
trace(LOG + "handling FlashStopEchoTestCommand, current state: " + state);
|
||||
if (state == IN_ECHO_TEST) {
|
||||
hangup();
|
||||
}
|
||||
}
|
||||
|
||||
public function handleFlashEchoTestHasAudioEvent(event:FlashEchoTestHasAudioEvent):void {
|
||||
trace(LOG + "handling handleFlashEchoTestHasAudioEvent.");
|
||||
trace(LOG + "handling handleFlashEchoTestHasAudioEvent, current state: " + state);
|
||||
if (state == IN_ECHO_TEST) {
|
||||
hangupEchoThenJoinVoiceConference();
|
||||
} else {
|
||||
callIntoVoiceConference();
|
||||
}
|
||||
/**
|
||||
* For echo test even if user has done echo test. This way, user is able to change mics
|
||||
* after. (richard mar 28, 2014)
|
||||
*/
|
||||
// echoTestDone = true;
|
||||
echoTestDone = true;
|
||||
}
|
||||
|
||||
public function handleFlashEchoTestNoAudioEvent(event:FlashEchoTestNoAudioEvent):void {
|
||||
trace(LOG + "handling FlashEchoTestNoAudioEvent.");
|
||||
trace(LOG + "handling FlashEchoTestNoAudioEvent, current state: " + state);
|
||||
if (state == IN_ECHO_TEST) {
|
||||
hangup();
|
||||
}
|
||||
@ -209,35 +252,52 @@
|
||||
}
|
||||
|
||||
public function handleFlashCallConnectedEvent(event:FlashCallConnectedEvent):void {
|
||||
trace(LOG + "handling FlashCallConnectedEvent, current state: " + state);
|
||||
switch (state) {
|
||||
case CALLING_INTO_CONFERENCE:
|
||||
trace(LOG + "Successfully joined the voice conference.");
|
||||
state = IN_CONFERENCE;
|
||||
dispatcher.dispatchEvent(new FlashJoinedVoiceConferenceEvent());
|
||||
streamManager.callConnected(event.playStreamName, event.publishStreamName, event.codec);
|
||||
streamManager.callConnected(event.playStreamName, event.publishStreamName, event.codec, event.listenOnlyCall);
|
||||
break;
|
||||
case CONNECTING_TO_LISTEN_ONLY_STREAM:
|
||||
trace(LOG + "Successfully connected to the listen only stream.");
|
||||
state = ON_LISTEN_ONLY_STREAM;
|
||||
dispatcher.dispatchEvent(new FlashJoinedListenOnlyVoiceConferenceEvent());
|
||||
streamManager.callConnected(event.playStreamName, event.publishStreamName, event.codec, event.listenOnlyCall);
|
||||
break;
|
||||
case CALLING_INTO_ECHO_TEST:
|
||||
state = IN_ECHO_TEST;
|
||||
trace(LOG + "Successfully called into the echo test application. [" + event.publishStreamName + "] : [" + event.playStreamName + "] : [" + event.codec + "]");
|
||||
streamManager.callConnected(event.playStreamName, event.publishStreamName, event.codec);
|
||||
streamManager.callConnected(event.playStreamName, event.publishStreamName, event.codec, event.listenOnlyCall);
|
||||
|
||||
trace(LOG + "Successfully called into the echo test application.");
|
||||
dispatcher.dispatchEvent(new FlashEchoTestStartedEvent());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function handleFlashCallDisconnectedEvent(event:FlashCallDisconnectedEvent):void {
|
||||
// The connection fires a disconnected event when connection closes.
|
||||
// Ignore if we are not joined into the conference using Flash (richard mar 28, 2014)
|
||||
if (!usingFlash) return;
|
||||
|
||||
trace(LOG + "Flash call disconnected.");
|
||||
trace(LOG + "Flash call disconnected, current state: " + state);
|
||||
switch (state) {
|
||||
case IN_CONFERENCE:
|
||||
state = INITED;
|
||||
trace(LOG + "Flash user left voice conference.");
|
||||
dispatcher.dispatchEvent(new FlashLeftVoiceConferenceEvent());
|
||||
|
||||
trace(LOG + "Flash connecting to listen only voice conference");
|
||||
joinListenOnlyCall();
|
||||
|
||||
break;
|
||||
case ON_LISTEN_ONLY_STREAM:
|
||||
state = INITED;
|
||||
trace(LOG + "Flash user left the listen only stream.");
|
||||
|
||||
if (usingFlash) {
|
||||
trace(LOG + "Flash reconnecting to the voice conference");
|
||||
startCall();
|
||||
}
|
||||
|
||||
break;
|
||||
case IN_ECHO_TEST:
|
||||
state = INITED;
|
||||
@ -245,24 +305,34 @@
|
||||
dispatcher.dispatchEvent(new FlashEchoTestStoppedEvent());
|
||||
break;
|
||||
case STOP_ECHO_THEN_JOIN_CONF:
|
||||
trace(LOG + "Flash echo test stopped.");
|
||||
dispatcher.dispatchEvent(new FlashEchoTestStoppedEvent());
|
||||
trace(LOG + "Flash echo test stopped, now joining the voice conference.");
|
||||
callIntoVoiceConference();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function handleJoinVoiceConferenceCommand(event:JoinVoiceConferenceCommand):void {
|
||||
if (!usingFlash) return;
|
||||
|
||||
trace(LOG + "handling JoinVoiceConferenceCommand.");
|
||||
startCall();
|
||||
trace(LOG + "Handling JoinVoiceConferenceCommand.");
|
||||
switch(state) {
|
||||
case ON_LISTEN_ONLY_STREAM:
|
||||
leaveListenOnlyCall();
|
||||
break;
|
||||
case INITED:
|
||||
if (usingFlash) {
|
||||
startCall();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function handleLeaveVoiceConferenceCommand(event:LeaveVoiceConferenceCommand):void {
|
||||
if (!usingFlash) return;
|
||||
|
||||
trace(LOG + "handling LeaveVoiceConferenceCommand.");
|
||||
trace(LOG + "Handling LeaveVoiceConferenceCommand, current state: " + state + ", using flash: " + usingFlash);
|
||||
if (!usingFlash) {
|
||||
// this is the case when the user was connected to webrtc and then leaves the conference
|
||||
joinListenOnlyCall();
|
||||
return;
|
||||
}
|
||||
|
||||
hangup();
|
||||
}
|
||||
|
||||
@ -276,6 +346,9 @@
|
||||
case DO_ECHO_TEST:
|
||||
callIntoEchoTest();
|
||||
break;
|
||||
case CALL_TO_LISTEN_ONLY_STREAM:
|
||||
callToListenOnlyStream();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,19 +133,25 @@ package org.bigbluebutton.modules.phone.managers {
|
||||
}
|
||||
}
|
||||
|
||||
public function callConnected(playStreamName:String, publishStreamName:String, codec:String):void {
|
||||
public function callConnected(playStreamName:String, publishStreamName:String, codec:String, listenOnlyCall:Boolean):void {
|
||||
trace(LOG + "setting up streams. [" + playStreamName + "] : [" + publishStreamName + "] : [" + codec + "]");
|
||||
isCallConnected = true;
|
||||
audioCodec = codec;
|
||||
setupIncomingStream();
|
||||
|
||||
if (mic != null) {
|
||||
if (mic != null && !listenOnlyCall) {
|
||||
setupOutgoingStream();
|
||||
} else {
|
||||
trace(LOG + "not setting up an outgoing stream because I'm in listen only mode");
|
||||
}
|
||||
|
||||
setupPlayStatusHandler();
|
||||
play(playStreamName);
|
||||
publish(publishStreamName);
|
||||
if (!listenOnlyCall) {
|
||||
publish(publishStreamName);
|
||||
} else {
|
||||
trace(LOG + "not publishing any stream because I'm in listen only mode");
|
||||
}
|
||||
}
|
||||
|
||||
private function play(playStreamName:String):void {
|
||||
@ -193,8 +199,9 @@ package org.bigbluebutton.modules.phone.managers {
|
||||
custom_obj.onPlayStatus = playStatus;
|
||||
custom_obj.onMetadata = onMetadata;
|
||||
incomingStream.client = custom_obj;
|
||||
if (mic != null)
|
||||
outgoingStream.client = custom_obj;
|
||||
if (mic != null && outgoingStream != null) {
|
||||
outgoingStream.client = custom_obj;
|
||||
}
|
||||
}
|
||||
|
||||
public function stopStreams():void {
|
||||
|
@ -41,8 +41,13 @@ package org.bigbluebutton.modules.phone.managers
|
||||
options = new PhoneOptions();
|
||||
if (options.useWebrtcIfAvailable && isWebRtcSupported()) {
|
||||
usingWebRtc = true;
|
||||
startWebRtcEchoTest();
|
||||
askMicPermission();
|
||||
autoJoin();
|
||||
}
|
||||
}
|
||||
|
||||
private function autoJoin():void {
|
||||
if (options.autoJoin) {
|
||||
handleJoinVoiceConferenceCommand();
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,11 +114,12 @@ package org.bigbluebutton.modules.phone.managers
|
||||
public function handleJoinVoiceConferenceCommand():void {
|
||||
if (!usingWebRtc) return;
|
||||
|
||||
if (echoTestDone) {
|
||||
if (options.skipCheck || echoTestDone) {
|
||||
joinVoiceConference();
|
||||
} else {
|
||||
startWebRtcEchoTest();
|
||||
}
|
||||
askMicPermission();
|
||||
}
|
||||
}
|
||||
|
||||
public function handleLeaveVoiceConferenceCommand():void {
|
||||
@ -126,5 +132,10 @@ package org.bigbluebutton.modules.phone.managers
|
||||
usingWebRtc = false;
|
||||
hangup();
|
||||
}
|
||||
|
||||
public function handleWebrtcEchoTestFailedEvent(reason:String):void {
|
||||
endEchoTest();
|
||||
hideMicPermission();
|
||||
}
|
||||
}
|
||||
}
|
@ -60,7 +60,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{FlashCallConnectedEvent.CALL_CONNECTED_EVENT}">
|
||||
<MethodInvoker generator="{PhoneEventMapDelegate}" method="disableToolbarButton"/>
|
||||
<MethodInvoker generator="{PhoneEventMapDelegate}" method="disableToolbarButton" arguments="{event.listenOnlyCall}"/>
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{FlashCallDisconnectedEvent.CALL_DISCONNECTED_EVENT}">
|
||||
|
@ -60,9 +60,11 @@ package org.bigbluebutton.modules.phone.maps
|
||||
}
|
||||
}
|
||||
|
||||
public function disableToolbarButton():void {
|
||||
phoneButton.selected = true;
|
||||
phoneButton.enabled = true;
|
||||
public function disableToolbarButton(listenOnlyCall:Boolean):void {
|
||||
if (!listenOnlyCall) {
|
||||
phoneButton.selected = true;
|
||||
phoneButton.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function enableToolbarButton():void {
|
||||
|
@ -36,6 +36,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
import org.bigbluebutton.modules.phone.events.WebRtcCallStartedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRtcConfCallEndedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRtcConfCallStartedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRtcEchoTestFailedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRtcEchoTestHasAudioEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRtcEchoTestNoAudioEvent;
|
||||
import org.bigbluebutton.modules.phone.events.WebRtcUserChangedMicEvent;
|
||||
@ -101,5 +102,9 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<MethodInvoker generator="{WebRtcCallManager}" method="handleLeaveVoiceConferenceCommand"/>
|
||||
</EventHandlers>
|
||||
|
||||
<EventHandlers type="{WebRtcEchoTestFailedEvent.WEBRTC_ECHO_TEST_FAILED}">
|
||||
<MethodInvoker generator="{WebRtcCallManager}" method="handleWebrtcEchoTestFailedEvent" arguments="{event.reason}"/>
|
||||
</EventHandlers>
|
||||
|
||||
|
||||
</EventMap>
|
||||
|
@ -21,15 +21,29 @@ package org.bigbluebutton.modules.phone.views.assets
|
||||
{
|
||||
[Bindable]
|
||||
public class Images
|
||||
{
|
||||
[Embed(source="images/headset.png")]
|
||||
public var headsetDefaultIcon:Class;
|
||||
{
|
||||
[Embed(source="images/headset.png")]
|
||||
public var headsetDefaultIcon:Class;
|
||||
|
||||
[Embed(source="images/headset_close.png")]
|
||||
public var headsetInactiveIcon:Class;
|
||||
[Embed(source="images/headset_close.png")]
|
||||
public var headsetInactiveIcon:Class;
|
||||
|
||||
[Embed(source="images/headset_open.png")]
|
||||
public var headsetActiveIcon:Class;
|
||||
|
||||
[Embed(source="images/headset_open.png")]
|
||||
public var headsetActiveIcon:Class;
|
||||
|
||||
[Embed(source="images/headset.png")]
|
||||
public var listenOnlyDefaultIcon:Class;
|
||||
|
||||
[Embed(source="images/headset_close.png")]
|
||||
public var listenOnlyInactiveIcon:Class;
|
||||
|
||||
[Embed(source="images/headset_open.png")]
|
||||
public var listenOnlyActiveIcon:Class;
|
||||
|
||||
[Embed(source="images/sound.png")]
|
||||
public var speakerActiveIcon:Class;
|
||||
|
||||
[Embed(source="images/sound_mute.png")]
|
||||
public var speakerInactiveIcon:Class;
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 610 B |
Binary file not shown.
After Width: | Height: | Size: 474 B |
@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
BigBlueButton open source conferencing system - http://www.bigbluebutton.org
|
||||
|
||||
Copyright (c) 2010 BigBlueButton Inc. and by respective authors (see below).
|
||||
|
||||
BigBlueButton is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU Lesser General Public License as published by the Free Software
|
||||
Foundation; either version 2.1 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$Id: $
|
||||
-->
|
||||
|
||||
<mx:Button xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:mate="http://mate.asfusion.com/"
|
||||
icon="{phoneIcon}" click="startSound()"
|
||||
mouseOver = "mouseOverHandler(event)"
|
||||
mouseOut = "mouseOutHandler(event)"
|
||||
creationComplete = "initMuteButton()"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.toolbar.phone.toolTip.mute')}"
|
||||
implements="org.bigbluebutton.common.IBbbToolbarComponent">
|
||||
|
||||
<mate:Listener type="{ShortcutEvent.PAUSE_REMOTE_STREAM}" method="remoteClick" />
|
||||
|
||||
<mx:Script>
|
||||
<![CDATA[
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import org.bigbluebutton.common.LogUtil;
|
||||
import org.bigbluebutton.core.BBB;
|
||||
import org.bigbluebutton.core.managers.UserManager;
|
||||
import org.bigbluebutton.main.events.BBBEvent;
|
||||
import org.bigbluebutton.main.events.ShortcutEvent;
|
||||
import org.bigbluebutton.main.views.MainToolbar;
|
||||
import org.bigbluebutton.modules.phone.PhoneOptions;
|
||||
import org.bigbluebutton.modules.phone.events.CallConnectedEvent;
|
||||
import org.bigbluebutton.modules.phone.events.CallDisconnectedEvent;
|
||||
import org.bigbluebutton.modules.phone.views.assets.Images;
|
||||
import org.bigbluebutton.util.i18n.ResourceUtil;
|
||||
|
||||
private var images:Images = new Images();
|
||||
private var dispatcher:Dispatcher = new Dispatcher();
|
||||
private var talking:Boolean = false;
|
||||
|
||||
public const MUTE_STATE:Number = 0;
|
||||
public const LISTENING_STATE:Number = 1;
|
||||
private var _currentState:Number = LISTENING_STATE;
|
||||
|
||||
[Bindable] public var phoneIcon:Class = images.speakerActiveIcon;
|
||||
|
||||
private function initMuteButton():void {
|
||||
this.selected = true;
|
||||
this.enabled = true;
|
||||
phoneIcon = images.speakerActiveIcon;
|
||||
}
|
||||
|
||||
public function remoteClick(event:ShortcutEvent):void{
|
||||
startSound();
|
||||
}
|
||||
|
||||
private function startSound():void {
|
||||
this.enabled = false;
|
||||
if (this.selected) {
|
||||
muteLocalUser();
|
||||
this.selected = false;
|
||||
phoneIcon = images.speakerInactiveIcon;
|
||||
_currentState = MUTE_STATE;
|
||||
this.toolTip = ResourceUtil.getInstance().getString('bbb.toolbar.phone.toolTip.unmute');
|
||||
} else {
|
||||
unmuteLocalUser();
|
||||
this.selected = true;
|
||||
phoneIcon = images.speakerActiveIcon;
|
||||
_currentState = LISTENING_STATE;
|
||||
this.toolTip = ResourceUtil.getInstance().getString('bbb.toolbar.phone.toolTip.mute');
|
||||
}
|
||||
}
|
||||
|
||||
public function muteLocalUser():void {
|
||||
var e:BBBEvent = new BBBEvent("MUTE_AUDIO_VOICE_CONFERENCE");
|
||||
dispatcher.dispatchEvent(e);
|
||||
}
|
||||
|
||||
public function unmuteLocalUser():void {
|
||||
var e:BBBEvent = new BBBEvent("UNMUTE_AUDIO_VOICE_CONFERENCE");
|
||||
dispatcher.dispatchEvent(e);
|
||||
}
|
||||
|
||||
private function mouseOverHandler(event:MouseEvent):void {
|
||||
if (_currentState == LISTENING_STATE) {
|
||||
phoneIcon = images.speakerInactiveIcon;
|
||||
} else {
|
||||
phoneIcon = images.speakerActiveIcon;
|
||||
}
|
||||
}
|
||||
|
||||
private function mouseOutHandler(event:MouseEvent):void {
|
||||
if (_currentState == LISTENING_STATE) {
|
||||
phoneIcon = images.speakerActiveIcon;
|
||||
} else {
|
||||
phoneIcon = images.speakerInactiveIcon;
|
||||
}
|
||||
}
|
||||
|
||||
// For whatever reason the tooltip does not update when localization is changed dynamically. Overrideing it here
|
||||
override protected function resourcesChanged():void {
|
||||
this.toolTip = ResourceUtil.getInstance().getString('bbb.toolbar.phone.toolTip.start');
|
||||
}
|
||||
|
||||
public function getAlignment():String {
|
||||
return MainToolbar.ALIGN_LEFT;
|
||||
}
|
||||
]]>
|
||||
</mx:Script>
|
||||
</mx:Button>
|
@ -24,6 +24,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
icon="{phoneIcon}" click="startPhone()"
|
||||
mouseOver = "mouseOverHandler(event)"
|
||||
mouseOut = "mouseOutHandler(event)"
|
||||
height="24"
|
||||
toolTip="{ResourceUtil.getInstance().getString('bbb.toolbar.phone.toolTip.start')}"
|
||||
implements="org.bigbluebutton.common.IBbbToolbarComponent">
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user