diff --git a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/red5/ClientConnection.java b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/red5/ClientConnection.java index 0d16f6fb57..bdb1f20948 100755 --- a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/red5/ClientConnection.java +++ b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/red5/ClientConnection.java @@ -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"}); + } } } diff --git a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/red5/Service.java b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/red5/Service.java index c77a506826..cc439b4a95 100755 --- a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/red5/Service.java +++ b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/red5/Service.java @@ -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(); diff --git a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/red5/media/CallStream.java b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/red5/media/CallStream.java index 8c8bf576ca..984e188456 100755 --- a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/red5/media/CallStream.java +++ b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/red5/media/CallStream.java @@ -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"); diff --git a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/CallAgent.java b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/CallAgent.java index 70779ac70b..33700f3334 100755 --- a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/CallAgent.java +++ b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/CallAgent.java @@ -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 */ diff --git a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/GlobalCall.java b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/GlobalCall.java new file mode 100644 index 0000000000..9f44a784f4 --- /dev/null +++ b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/GlobalCall.java @@ -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 roomToStreamMap = new ConcurrentHashMap(); + public static Map numberOfUsers = new ConcurrentHashMap(); + public static Map roomToCodecMap = new ConcurrentHashMap(); + public static Map globalAudioKeepAliverMap = new ConcurrentHashMap(); + + 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); + } +} diff --git a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/KeepAliveUdp.java b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/KeepAliveUdp.java index a0550a6b9a..9d05eeee05 100755 --- a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/KeepAliveUdp.java +++ b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/KeepAliveUdp.java @@ -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)"; } -} \ No newline at end of file +} diff --git a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/KeepGlobalAudioAlive.java b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/KeepGlobalAudioAlive.java new file mode 100644 index 0000000000..0f9b0bcb58 --- /dev/null +++ b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/KeepGlobalAudioAlive.java @@ -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 . +* +**/ +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."); + } + } +} diff --git a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/SipPeer.java b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/SipPeer.java index f30c476d8d..90efdd41f5 100755 --- a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/SipPeer.java +++ b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/SipPeer.java @@ -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; diff --git a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/SipPeerManager.java b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/SipPeerManager.java index 1cf00fbd6c..5a69eb9289 100755 --- a/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/SipPeerManager.java +++ b/bbb-voice/src/main/java/org/bigbluebutton/voiceconf/sip/SipPeerManager.java @@ -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) { diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/voice/VoiceService.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/voice/VoiceService.java index 205f74206b..5c552296ce 100755 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/voice/VoiceService.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/conference/service/voice/VoiceService.java @@ -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 arrayListToMap(ArrayList alp) { log.debug("Converting arraylist to Map " + alp.size()); diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/core/api/IBigBlueButtonInGW.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/core/api/IBigBlueButtonInGW.java index 887b19b310..d9324351a4 100755 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/core/api/IBigBlueButtonInGW.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/core/api/IBigBlueButtonInGW.java @@ -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); diff --git a/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala b/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala index a45b33b6e6..bf7fa6f152 100644 --- a/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala +++ b/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala @@ -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) } diff --git a/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/CollectorActor.scala b/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/CollectorActor.scala old mode 100755 new mode 100644 index 9408b2b7be..2f5bee3111 --- a/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/CollectorActor.scala +++ b/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/CollectorActor.scala @@ -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) } -} +} diff --git a/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala b/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala old mode 100755 new mode 100644 diff --git a/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala b/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala index ab681791a8..58eb537b0a 100755 --- a/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala +++ b/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala @@ -8,10 +8,6 @@ trait VoiceApp { val outGW: MessageOutGateway - def handleSendVoiceUsersRequest(msg: SendVoiceUsersRequest) { - - } - def handleMuteMeetingRequest(msg: MuteMeetingRequest) { } diff --git a/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceInGateway.scala b/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceInGateway.scala index 80ceabae4f..5d0324e3d4 100755 --- a/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceInGateway.scala +++ b/bigbluebutton-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceInGateway.scala @@ -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)) } diff --git a/bigbluebutton-client/locale/en_US/bbbResources.properties b/bigbluebutton-client/locale/en_US/bbbResources.properties index ca5b6b4fc1..9a81b34b08 100755 --- a/bigbluebutton-client/locale/en_US/bbbResources.properties +++ b/bigbluebutton-client/locale/en_US/bbbResources.properties @@ -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 diff --git a/bigbluebutton-client/locale/pt_BR/bbbResources.properties b/bigbluebutton-client/locale/pt_BR/bbbResources.properties index 8240cc1365..3746892af8 100755 --- a/bigbluebutton-client/locale/pt_BR/bbbResources.properties +++ b/bigbluebutton-client/locale/pt_BR/bbbResources.properties @@ -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 diff --git a/bigbluebutton-client/resources/config.xml.template b/bigbluebutton-client/resources/config.xml.template index 39054f49ee..942fddfa34 100755 --- a/bigbluebutton-client/resources/config.xml.template +++ b/bigbluebutton-client/resources/config.xml.template @@ -47,9 +47,12 @@ . } 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; } } diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/api/ExternalApiCallbacks.as b/bigbluebutton-client/src/org/bigbluebutton/main/api/ExternalApiCallbacks.as index f9b8a7367d..56fee96722 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/api/ExternalApiCallbacks.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/api/ExternalApiCallbacks.as @@ -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 { diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/events/ShortcutEvent.as b/bigbluebutton-client/src/org/bigbluebutton/main/events/ShortcutEvent.as index c743bc338b..104e676592 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/events/ShortcutEvent.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/events/ShortcutEvent.as @@ -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'; diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/ShortcutOptions.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/ShortcutOptions.as index ef8f4619a5..476cdec4f6 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/model/ShortcutOptions.as +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/ShortcutOptions.as @@ -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');} diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/PhoneOptions.as b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/PhoneOptions.as index d7e1375d17..d775f689e6 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/PhoneOptions.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/PhoneOptions.as @@ -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"); + } } } } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/events/FlashCallConnectedEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/events/FlashCallConnectedEvent.as index 050118f34b..665a41004f 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/events/FlashCallConnectedEvent.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/events/FlashCallConnectedEvent.as @@ -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); } } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/events/FlashJoinedListenOnlyVoiceConferenceEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/events/FlashJoinedListenOnlyVoiceConferenceEvent.as new file mode 100644 index 0000000000..df2095693d --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/events/FlashJoinedListenOnlyVoiceConferenceEvent.as @@ -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); + } + } +} \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/events/WebRtcEchoTestFailedEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/events/WebRtcEchoTestFailedEvent.as index a4233e2623..7897fa2d7c 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/events/WebRtcEchoTestFailedEvent.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/events/WebRtcEchoTestFailedEvent.as @@ -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; } } } \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/ConnectionManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/ConnectionManager.as index 7f80f4b344..07a4ef9282 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/ConnectionManager.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/ConnectionManager.as @@ -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 { diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/FlashCallManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/FlashCallManager.as index c8add35dd6..2cd0760511 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/FlashCallManager.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/FlashCallManager.as @@ -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; } } } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/StreamManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/StreamManager.as index 6a8b2fed8b..35a0b7b50f 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/StreamManager.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/StreamManager.as @@ -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 { diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/WebRtcCallManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/WebRtcCallManager.as index 835e1fa1b1..8e07b8ad8f 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/WebRtcCallManager.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/managers/WebRtcCallManager.as @@ -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(); + } } } \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/PhoneEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/PhoneEventMap.mxml index 5481b071b7..8fb263f9b5 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/PhoneEventMap.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/PhoneEventMap.mxml @@ -60,7 +60,7 @@ with BigBlueButton; if not, see . - + diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/PhoneEventMapDelegate.as b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/PhoneEventMapDelegate.as index fde7eff7a8..da1bd76a92 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/PhoneEventMapDelegate.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/PhoneEventMapDelegate.as @@ -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 { diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/WebRtcEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/WebRtcEventMap.mxml index adceefe345..57e1917145 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/WebRtcEventMap.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/maps/WebRtcEventMap.mxml @@ -36,6 +36,7 @@ with BigBlueButton; if not, see . 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 . + + + + diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/assets/Images.as b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/assets/Images.as index b32497ec40..6e589c6e69 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/assets/Images.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/assets/Images.as @@ -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; } } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/assets/images/sound.png b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/assets/images/sound.png new file mode 100644 index 0000000000..6056d234a9 Binary files /dev/null and b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/assets/images/sound.png differ diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/assets/images/sound_mute.png b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/assets/images/sound_mute.png new file mode 100644 index 0000000000..b652d2a71f Binary files /dev/null and b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/assets/images/sound_mute.png differ diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/components/MuteButton.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/components/MuteButton.mxml new file mode 100644 index 0000000000..9e6bd68896 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/components/MuteButton.mxml @@ -0,0 +1,122 @@ + + + + + + + + + + + + diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/components/ToolbarButton.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/components/ToolbarButton.mxml index ffbc1b7f23..1c739b1ddf 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/components/ToolbarButton.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/phone/views/components/ToolbarButton.mxml @@ -24,6 +24,7 @@ with BigBlueButton; if not, see . 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">