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:
Richard Alam 2014-05-12 10:39:25 -07:00
commit 42fcfe1055
39 changed files with 699 additions and 116 deletions

View File

@ -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"});
}
}
}

View File

@ -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();

View File

@ -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");

View File

@ -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 */

View File

@ -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);
}
}

View File

@ -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)";
}
}
}

View File

@ -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.");
}
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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());

View File

@ -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);

View File

@ -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)
}

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

View File

@ -8,10 +8,6 @@ trait VoiceApp {
val outGW: MessageOutGateway
def handleSendVoiceUsersRequest(msg: SendVoiceUsersRequest) {
}
def handleMuteMeetingRequest(msg: MuteMeetingRequest) {
}

View File

@ -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))
}

View File

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

View File

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

View File

@ -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"

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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';

View File

@ -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');}

View File

@ -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");
}
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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 {

View File

@ -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;
}
}
}

View File

@ -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 {

View File

@ -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();
}
}
}

View File

@ -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}">

View File

@ -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 {

View File

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

View File

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

View File

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

View File

@ -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">