diff --git a/src/main/java/org/jitsi/jigasi/AbstractGatewaySession.java b/src/main/java/org/jitsi/jigasi/AbstractGatewaySession.java
index 89bc798e7..181f0e018 100644
--- a/src/main/java/org/jitsi/jigasi/AbstractGatewaySession.java
+++ b/src/main/java/org/jitsi/jigasi/AbstractGatewaySession.java
@@ -207,6 +207,12 @@ abstract void onJvbConferenceWillStop(JvbConference jvbConference,
*/
public void onJvbCallEnded() {}
+ /**
+ * Method called by JvbConference to notify JVB call has been
+ * established.
+ */
+ public void onJvbCallEstablished() {}
+
/**
* Cancels current session by leaving the muc room
*/
@@ -410,6 +416,11 @@ public String getFocusResourceAddr()
return focusResourceAddr;
}
+ /**
+ * If muting is supported will mute the participant.
+ */
+ public abstract void mute();
+
/**
* Whether the gateway implementation supports call resuming. Where we can
* keep the gateway session while the xmpp call is been disconnected or
diff --git a/src/main/java/org/jitsi/jigasi/JigasiBundleActivator.java b/src/main/java/org/jitsi/jigasi/JigasiBundleActivator.java
index ee097f6e3..354d06882 100644
--- a/src/main/java/org/jitsi/jigasi/JigasiBundleActivator.java
+++ b/src/main/java/org/jitsi/jigasi/JigasiBundleActivator.java
@@ -183,6 +183,15 @@ public void start(final BundleContext bundleContext)
if(isSipEnabled())
{
+ MuteIqProvider.registerMuteIqProvider();
+
+ // recording status, to detect recording start/stop
+ ProviderManager.addExtensionProvider(
+ RecordingStatus.ELEMENT_NAME,
+ RecordingStatus.NAMESPACE,
+ new DefaultPacketExtensionProvider<>(RecordingStatus.class)
+ );
+
logger.info("initialized SipGateway");
sipGateway = new SipGateway(bundleContext)
{
@@ -222,9 +231,14 @@ void notifyCallEnded(CallContext callContext)
else
{
logger.info("skipped initialization of TranscriptionGateway");
-
}
+ // Register Jitsi Meet media presence extension.
+ MediaPresenceExtension.registerExtensions();
+
+ // Register Rayo IQs
+ new RayoIqProvider().registerRayoIQs();
+
bundleContext.addServiceListener(this);
Collection> refs
@@ -273,19 +287,6 @@ public void serviceChanged(ServiceEvent serviceEvent)
return;
}
- // Register Jitsi Meet media presence extension.
- MediaPresenceExtension.registerExtensions();
-
- // Register Rayo IQs
- new RayoIqProvider().registerRayoIQs();
-
- // recording status, to detect recording start/stop
- ProviderManager.addExtensionProvider(
- RecordingStatus.ELEMENT_NAME,
- RecordingStatus.NAMESPACE,
- new DefaultPacketExtensionProvider<>(RecordingStatus.class)
- );
-
ProtocolProviderService pps = (ProtocolProviderService) service;
if (sipGateway != null && sipGateway.getSipProvider() == null &&
ProtocolNames.SIP.equals(pps.getProtocolName()))
diff --git a/src/main/java/org/jitsi/jigasi/JvbConference.java b/src/main/java/org/jitsi/jigasi/JvbConference.java
index f9ff5617d..12411e853 100644
--- a/src/main/java/org/jitsi/jigasi/JvbConference.java
+++ b/src/main/java/org/jitsi/jigasi/JvbConference.java
@@ -36,6 +36,7 @@
import org.jitsi.xmpp.extensions.rayo.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.bosh.*;
+import org.jivesoftware.smack.iqrequest.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smackx.nick.packet.*;
import org.jxmpp.jid.*;
@@ -582,14 +583,12 @@ public synchronized void registrationStateChanged(
// Join the MUC
joinConferenceRoom();
+ XMPPConnection connection = getConnection();
if (xmppProvider != null
- && xmppProvider instanceof ProtocolProviderServiceJabberImpl
- && ((ProtocolProviderServiceJabberImpl) xmppProvider)
- .getConnection() instanceof XMPPBOSHConnection)
+ && connection != null
+ && connection instanceof XMPPBOSHConnection)
{
- Object sessionId = Util.getConnSessionId(
- ((ProtocolProviderServiceJabberImpl) xmppProvider)
- .getConnection());
+ Object sessionId = Util.getConnSessionId(connection);
if (sessionId != null)
{
logger.error(this.callContext + " Registered bosh sid: "
@@ -643,6 +642,15 @@ public boolean isInTheRoom()
return mucRoom != null && mucRoom.isJoined();
}
+ /**
+ * Indicates whether this conference has been started.
+ * @return true is this conference is started, false otherwise.
+ */
+ public boolean isStarted()
+ {
+ return started;
+ }
+
private void joinConferenceRoom()
{
// Advertise gateway feature before joining
@@ -747,6 +755,11 @@ private void joinConferenceRoom()
"is not an instance of ChatRoomJabberImpl");
}
+ if (JigasiBundleActivator.isSipStartMutedEnabled())
+ {
+ getConnection().registerIQRequestHandler(new MuteIqHandler());
+ }
+
// we invite focus and wait for its response
// to be sure that if it is not in the room, the focus will be the
// first to join, mimic the web behaviour
@@ -1158,6 +1171,7 @@ public synchronized void callStateChanged(CallChangeEvent evt)
if (jvbCall.getCallState() == CallState.CALL_IN_PROGRESS)
{
logger.info(callContext + " JVB conference call IN_PROGRESS.");
+ gatewaySession.onJvbCallEstablished();
}
else if(jvbCall.getCallState() == CallState.CALL_ENDED)
{
@@ -1407,8 +1421,7 @@ private void inviteFocus(final EntityBareJid roomIdentifier)
StanzaCollector collector = null;
try
{
- collector = ((ProtocolProviderServiceJabberImpl) xmppProvider)
- .getConnection()
+ collector = getConnection()
.createStanzaCollectorAndSend(focusInviteIQ);
collector.nextResultOrThrow();
}
@@ -1687,4 +1700,52 @@ private XMPPConnection getConnection()
return null;
}
+
+ /**
+ * Handles mute requests received by jicofo if enabled.
+ */
+ private class MuteIqHandler
+ extends AbstractIqRequestHandler
+ {
+ MuteIqHandler()
+ {
+ super(
+ MuteIq.ELEMENT_NAME,
+ MuteIq.NAMESPACE,
+ IQ.Type.set,
+ Mode.sync);
+ }
+
+ @Override
+ public IQ handleIQRequest(IQ iqRequest)
+ {
+ return handleMuteIq((MuteIq) iqRequest);
+ }
+
+ /**
+ * Handles the incoming mute request only if it is from the focus.
+ * @param muteIq the incoming iq.
+ * @return the result iq.
+ */
+ private IQ handleMuteIq(MuteIq muteIq)
+ {
+ Boolean doMute = muteIq.getMute();
+ Jid from = muteIq.getFrom();
+
+ if (doMute == null
+ || !from.getResourceOrEmpty().equals(
+ gatewaySession.getFocusResourceAddr()))
+ {
+ return IQ.createErrorResponse(muteIq, XMPPError.getBuilder(
+ XMPPError.Condition.item_not_found));
+ }
+
+ if (doMute)
+ {
+ gatewaySession.mute();
+ }
+
+ return IQ.createResultIQ(muteIq);
+ }
+ }
}
diff --git a/src/main/java/org/jitsi/jigasi/SipGatewaySession.java b/src/main/java/org/jitsi/jigasi/SipGatewaySession.java
index d6dc01ada..35abfc072 100644
--- a/src/main/java/org/jitsi/jigasi/SipGatewaySession.java
+++ b/src/main/java/org/jitsi/jigasi/SipGatewaySession.java
@@ -22,7 +22,6 @@
import net.java.sip.communicator.service.protocol.media.*;
import net.java.sip.communicator.util.Logger;
import org.jitsi.impl.neomedia.*;
-import org.jitsi.jigasi.JigasiBundleActivator;
import org.jitsi.jigasi.stats.*;
import org.jitsi.jigasi.util.*;
import org.jitsi.service.neomedia.*;
@@ -594,6 +593,8 @@ private void sipCallEnded()
statsHandler = null;
}
+ jitsiMeetTools.removeRequestListener(SipGatewaySession.this);
+
if (peerStateListener != null)
peerStateListener.unregister();
@@ -644,7 +645,10 @@ public void onJoinJitsiMeetRequest(
@Override
public void onSessionStartMuted(boolean[] startMutedFlags)
{
- this.startAudioMuted = startMutedFlags[0];
+ if (isMutingSupported())
+ {
+ this.startAudioMuted = startMutedFlags[0];
+ }
}
/**
@@ -661,7 +665,6 @@ public void onJSONReceived(CallPeer callPeer,
{
try
{
-
if (callPeer.getCall() != this.sipCall)
{
if (logger.isTraceEnabled())
@@ -671,13 +674,13 @@ public void onJSONReceived(CallPeer callPeer,
return;
}
- if (jsonObject.containsKey("type") == false)
+ if (!jsonObject.containsKey("type"))
{
logger.error("Unknown json object type!");
return;
}
- if (jsonObject.containsKey("id") == false)
+ if (!jsonObject.containsKey("id"))
{
logger.error("Unknown json object id!");
return;
@@ -686,16 +689,15 @@ public void onJSONReceived(CallPeer callPeer,
String id = (String)jsonObject.get("id");
String type = (String)jsonObject.get("type");
- if (type.equalsIgnoreCase("muteResponse") == true)
+ if (type.equalsIgnoreCase("muteResponse"))
{
- if (jsonObject.containsKey("status") == false)
+ if (!jsonObject.containsKey("status"))
{
logger.error("muteResponse without status!");
return;
}
- if ( ((String)jsonObject.get("status"))
- .equalsIgnoreCase("OK") == true)
+ if (((String) jsonObject.get("status")).equalsIgnoreCase("OK"))
{
JSONObject data = (JSONObject) jsonObject.get("data");
@@ -705,14 +707,14 @@ public void onJSONReceived(CallPeer callPeer,
this.jvbConference.setChatRoomAudioMuted(bMute);
}
}
- else if (type.equalsIgnoreCase("muteRequest") == true)
+ else if (type.equalsIgnoreCase("muteRequest"))
{
JSONObject data = (JSONObject) jsonObject.get("data");
boolean bAudioMute = (boolean)data.get("audio");
// Send request to jicofo
- if (jvbConference.requestAudioMute(bAudioMute) == true)
+ if (jvbConference.requestAudioMute(bAudioMute))
{
// Send response through sip
respondRemoteAudioMute(bAudioMute,
@@ -765,8 +767,8 @@ private JSONObject createSIPJSONAudioMuteRequest(boolean bMuted)
{
JSONObject muteSettingsJson = new JSONObject();
muteSettingsJson.put("audio", bMuted);
- JSONObject muteRequestJson = createSIPJSON("muteRequest", muteSettingsJson, null);
- return muteRequestJson;
+
+ return createSIPJSON("muteRequest", muteSettingsJson, null);
}
/**
@@ -783,8 +785,9 @@ private JSONObject createSIPJSONAudioMuteResponse(boolean bMuted,
{
JSONObject muteSettingsJson = new JSONObject();
muteSettingsJson.put("audio", bMuted);
- JSONObject muteResponseJson = createSIPJSON("muteResponse", muteSettingsJson, id);
- muteResponseJson.put("status", bSucceeded == true ? "OK" : "FAILED");
+ JSONObject muteResponseJson
+ = createSIPJSON("muteResponse", muteSettingsJson, id);
+ muteResponseJson.put("status", bSucceeded ? "OK" : "FAILED");
return muteResponseJson;
}
@@ -795,9 +798,8 @@ private JSONObject createSIPJSONAudioMuteResponse(boolean bMuted,
* @param callPeer CallPeer to send JSON to.
* @throws OperationFailedException
*/
- private void requestRemoteAudioMute(boolean bMuted,
- CallPeer callPeer)
- throws OperationFailedException
+ private void requestRemoteAudioMute(boolean bMuted, CallPeer callPeer)
+ throws OperationFailedException
{
// Mute audio
JSONObject muteRequestJson = createSIPJSONAudioMuteRequest(bMuted);
@@ -822,7 +824,7 @@ private void respondRemoteAudioMute(boolean bMuted,
boolean bSucceeded,
CallPeer callPeer,
String id)
- throws OperationFailedException
+ throws OperationFailedException
{
JSONObject muteResponseJson
= createSIPJSONAudioMuteResponse(bMuted, bSucceeded, id);
@@ -830,9 +832,9 @@ private void respondRemoteAudioMute(boolean bMuted,
jitsiMeetTools.sendJSON(callPeer,
muteResponseJson,
new HashMap() {{
- put("VIA", (Object)("SIP.INFO"));
+ put("VIA", "SIP.INFO");
}});
- }
+ }
/**
* Initializes the sip call listeners.
@@ -851,6 +853,7 @@ private void initSipCall(String sipCallIdentifier)
DEFAULT_STATS_REMOTE_ID + "-" + sipCallIdentifier);
}
sipCall.addCallChangeListener(statsHandler);
+ jitsiMeetTools.addRequestListener(this);
if (mediaDroppedThresholdMs != -1)
{
@@ -931,8 +934,6 @@ private void waitForRoomName()
waitThread = new WaitForJvbRoomNameThread();
- jitsiMeetTools.addRequestListener(this);
-
waitThread.start();
}
@@ -1195,6 +1196,76 @@ public boolean hasCallResumeSupport()
return true;
}
+ /**
+ * When
+ */
+ @Override
+ public void onJvbCallEstablished()
+ {
+ maybeProcessStartMuted();
+ }
+
+ /**
+ * Processes start muted in case:
+ * - we had received that flag
+ * - start muted is enabled through the flag
+ * - jvb call is in progress as we will be muting the channels
+ * - sip call is in progress we will be sending SIP Info messages
+ */
+ private void maybeProcessStartMuted()
+ {
+ if (this.startAudioMuted
+ && isMutingSupported()
+ && jvbConferenceCall != null
+ && jvbConferenceCall.getCallState() == CallState.CALL_IN_PROGRESS
+ && sipCall != null
+ && sipCall.getCallState() == CallState.CALL_IN_PROGRESS)
+ {
+ if (jvbConference.requestAudioMute(startAudioMuted))
+ {
+ mute();
+ }
+
+ // in case we reconnect start muted maybe no-longer set
+ this.startAudioMuted = false;
+ }
+ }
+
+ /**
+ * Sends mute request to be remotely muted.
+ * This is a SIP Info message to the IVR so the user will be notified of it
+ * When we receive confirmation for the announcement we will update
+ * our presence status in the conference.
+ */
+ public void mute()
+ {
+ if (!isMutingSupported())
+ return;
+
+ // Notify peer
+ CallPeer callPeer = sipCall.getCallPeers().next();
+
+ try
+ {
+ logger.info(
+ SipGatewaySession.this.callContext + " Sending mute request ");
+ requestRemoteAudioMute(true, callPeer);
+ }
+ catch (Exception ex)
+ {
+ logger.error(ex.getMessage());
+ }
+ }
+
+ /**
+ * Muting is supported when it is enabled by configuration.
+ * @return true if mute support is enabled.
+ */
+ public boolean isMutingSupported()
+ {
+ return JigasiBundleActivator.isSipStartMutedEnabled();
+ }
+
/**
* PeriodicRunnable that will check incoming RTP and if needed to hangup.
*/
@@ -1312,6 +1383,8 @@ void handleCallState(Call call, CallPeerChangeEvent cause)
logger.info(SipGatewaySession.this.callContext
+ " SIP call format used: "
+ Util.getFirstPeerMediaFormat(call));
+
+ maybeProcessStartMuted();
}
else if(call.getCallState() == CallState.CALL_ENDED)
{
@@ -1386,39 +1459,6 @@ public void peerStateChanged(final CallPeerChangeEvent evt)
if (jvbConference != null)
jvbConference.setPresenceStatus(stateString);
- if (JigasiBundleActivator.isSipStartMutedEnabled() == true)
- {
- if (CallPeerState.CONNECTED.equals(callPeerState) == true)
- {
- // After CallPeer is in CONNECTED state handle startmuted flags
- jitsiMeetTools.addRequestListener(SipGatewaySession.this);
-
- if (SipGatewaySession.this.startAudioMuted == true)
- {
- // Send request to jicofo
- if (jvbConference.requestAudioMute(startAudioMuted) == true)
- {
- // Notify peer
- CallPeer callPeer = evt.getSourceCallPeer();
-
- try
- {
- requestRemoteAudioMute(startAudioMuted, callPeer);
- }
- catch (Exception ex)
- {
- logger.error(ex.getMessage());
- }
- }
- }
- }
-
- if (CallPeerState.DISCONNECTED.equals(callPeerState) == true)
- {
- jitsiMeetTools.removeRequestListener(SipGatewaySession.this);
- }
- }
-
soundNotificationManager.process(callPeerState);
}
@@ -1487,10 +1527,6 @@ public void run()
{
Thread.currentThread().interrupt();
}
- finally
- {
- jitsiMeetTools.removeRequestListener(SipGatewaySession.this);
- }
}
}
diff --git a/src/main/java/org/jitsi/jigasi/SoundNotificationManager.java b/src/main/java/org/jitsi/jigasi/SoundNotificationManager.java
index 21ebfa0da..6b04b4333 100644
--- a/src/main/java/org/jitsi/jigasi/SoundNotificationManager.java
+++ b/src/main/java/org/jitsi/jigasi/SoundNotificationManager.java
@@ -531,7 +531,12 @@ public void notifyChatRoomMemberJoined(ChatRoomMember member)
*/
public void notifyChatRoomMemberLeft(ChatRoomMember member)
{
- playParticipantLeftNotification();
+ // if this is the sip hanging up (stopping) skip playing
+ if (gatewaySession.jvbConference.isStarted()
+ && gatewaySession.getSipCall() != null)
+ {
+ playParticipantLeftNotification();
+ }
}
/**
@@ -603,7 +608,7 @@ private void playParticipantJoinedNotification()
if (getParticipantJoinedRateLimiter().on() == false)
{
Call sipCall = gatewaySession.getSipCall();
-
+
if (sipCall != null)
{
injectSoundFile(sipCall, PARTICIPANT_JOINED);
@@ -704,7 +709,6 @@ private class SoundRateLimiter implements RateLimiter
/**
* SoundRateLimiter constructor.
*
- * @param timePoint Initial start timepoint.
* @param maxTimeout Timeout in milliseconds to block notification.
*/
SoundRateLimiter(long maxTimeout)
@@ -720,8 +724,7 @@ private class SoundRateLimiter implements RateLimiter
*/
public boolean on()
{
- if (this.startTimePoint
- .compareAndSet(null, Instant.now()) == true)
+ if (this.startTimePoint.compareAndSet(null, Instant.now()))
{
return false;
}
diff --git a/src/main/java/org/jitsi/jigasi/TranscriptionGatewaySession.java b/src/main/java/org/jitsi/jigasi/TranscriptionGatewaySession.java
index 085d76c13..c13a6c8f5 100644
--- a/src/main/java/org/jitsi/jigasi/TranscriptionGatewaySession.java
+++ b/src/main/java/org/jitsi/jigasi/TranscriptionGatewaySession.java
@@ -697,4 +697,9 @@ public boolean hasCallResumeSupport()
{
return false;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public void mute(){}
}