diff --git a/src/main/java/org/jitsi/jigasi/AudioModeration.java b/src/main/java/org/jitsi/jigasi/AudioModeration.java
index 24f58232b..addc1b196 100644
--- a/src/main/java/org/jitsi/jigasi/AudioModeration.java
+++ b/src/main/java/org/jitsi/jigasi/AudioModeration.java
@@ -468,43 +468,9 @@ public void mute()
/**
* The xmpp provider for JvbConference has registered after connecting.
*/
- public void xmppProviderRegistered()
+ void setAvModerationAddress(String address)
{
- // we are here in the RegisterThread, and it is safe to query and wait
- // Uses disco info to discover the AV moderation address.
- // we need to query the domain part extracted from room jid
- if (this.callContext.getRoomJidDomain() != null)
- {
- try
- {
- long startQuery = System.currentTimeMillis();
-
- // in case when running unittests
- if (this.jvbConference.getConnection() == null)
- {
- return;
- }
-
- DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(this.jvbConference.getConnection())
- .discoverInfo(JidCreate.domainBareFrom(this.callContext.getRoomJidDomain()));
-
- DiscoverInfo.Identity avIdentity =
- info.getIdentities().stream().
- filter(di -> di.getCategory().equals("component") && di.getType().equals("av_moderation"))
- .findFirst().orElse(null);
-
- if (avIdentity != null)
- {
- this.avModerationAddress = avIdentity.getName();
- logger.info(String.format("%s Discovered %s for %oms.",
- this.callContext, this.avModerationAddress, System.currentTimeMillis() - startQuery));
- }
- }
- catch(Exception e)
- {
- logger.error("Error querying for av moderation address", e);
- }
- }
+ this.avModerationAddress = address;
if (this.avModerationAddress != null)
{
diff --git a/src/main/java/org/jitsi/jigasi/JvbConference.java b/src/main/java/org/jitsi/jigasi/JvbConference.java
index 77b85acf7..3f4fd6cc4 100644
--- a/src/main/java/org/jitsi/jigasi/JvbConference.java
+++ b/src/main/java/org/jitsi/jigasi/JvbConference.java
@@ -51,6 +51,7 @@
import org.jivesoftware.smackx.xdata.packet.*;
import org.jivesoftware.smackx.xdata.*;
import org.json.simple.*;
+import org.json.simple.parser.*;
import org.jxmpp.jid.*;
import org.jxmpp.jid.impl.*;
import org.jxmpp.jid.parts.*;
@@ -364,7 +365,12 @@ private static ExtensionElement addSupportedFeatures(
/**
* Listens for room configuration changes and request room config to reflect it locally.
*/
- private RoomConfigurationChangeListener roomConfigurationListener = null;
+ private final RoomConfigurationChangeListener roomConfigurationListener = new RoomConfigurationChangeListener();
+
+ /**
+ * Listens for messages from room metadata component for changes in room metadata.
+ */
+ private final RoomMetadataListener roomMetadataListener = new RoomMetadataListener();
/**
* Up-to-date list of participants in the room that are jigasi.
@@ -660,14 +666,9 @@ public void registrationStateChanged(RegistrationStateChangeEvent evt)
private synchronized void registrationStateChangedInternal(RegistrationStateChangeEvent evt)
{
- if (started
- && mucRoom == null
- && evt.getNewState() == RegistrationState.REGISTERED)
+ if (started && mucRoom == null && evt.getNewState() == RegistrationState.REGISTERED)
{
- if (this.getAudioModeration() != null)
- {
- this.getAudioModeration().xmppProviderRegistered();
- }
+ discoverComponentAddresses();
// Join the MUC
joinConferenceRoom();
@@ -729,6 +730,62 @@ else if (evt.getNewState() == RegistrationState.CONNECTION_FAILED)
}
}
+ /**
+ * Disco info the addresses, the query is cached and will be returned from cache
+ * once we retrieve it.
+ */
+ private void discoverComponentAddresses()
+ {
+ // we are here in the RegisterThread, and it is safe to query and wait
+ // Uses disco info to discover the AV moderation address.
+ // we need to query the domain part extracted from room jid
+ if (this.callContext.getRoomJidDomain() != null)
+ {
+ try
+ {
+ long startQuery = System.currentTimeMillis();
+
+ // in case when running unittests
+ if (this.getConnection() == null)
+ {
+ return;
+ }
+
+ DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(this.getConnection())
+ .discoverInfo(JidCreate.domainBareFrom(this.callContext.getRoomJidDomain()));
+
+ logger.info(String.format("%s Disco-info took %oms.", this.callContext, System.currentTimeMillis() - startQuery));
+
+ DiscoverInfo.Identity avIdentity = info.getIdentities().stream().
+ filter(di -> di.getCategory().equals("component") && di.getType().equals("av_moderation"))
+ .findFirst().orElse(null);
+
+ if (avIdentity != null && this.getAudioModeration() != null)
+ {
+ String avModerationAddress = avIdentity.getName();
+ this.getAudioModeration().setAvModerationAddress(avModerationAddress);
+ }
+
+ DiscoverInfo.Identity roomMetadataIdentity = info.getIdentities().stream().
+ filter(di -> di.getCategory().equals("component") && di.getType().equals("room_metadata"))
+ .findFirst().orElse(null);
+
+ // we process room metadata messages only when we are transcribing
+ if (roomMetadataIdentity != null && this.gatewaySession instanceof TranscriptionGatewaySession)
+ {
+ getConnection().addAsyncStanzaListener(roomMetadataListener,
+ new AndFilter(
+ MessageTypeFilter.NORMAL,
+ FromMatchesFilter.create(JidCreate.domainBareFrom(roomMetadataIdentity.getName()))));
+ }
+ }
+ catch(Exception e)
+ {
+ logger.error("Error querying for av moderation address", e);
+ }
+ }
+ }
+
/**
* Returns true if we are currently in JVB conference room.
* @return true if we are currently in JVB conference room.
@@ -885,21 +942,19 @@ public void joinConferenceRoom()
gatewaySession.notifyJvbRoomJoined();
- if (lobbyEnabled)
- {
- // let's check room config
- updateFromRoomConfiguration();
- }
-
// let's listen for any future changes in room configuration, whether lobby will be enabled/disabled
- if (roomConfigurationListener == null && mucRoom instanceof ChatRoomJabberImpl)
+ if (mucRoom instanceof ChatRoomJabberImpl)
{
- roomConfigurationListener = new RoomConfigurationChangeListener();
getConnection().addAsyncStanzaListener(roomConfigurationListener,
new AndFilter(
FromMatchesFilter.create(((ChatRoomJabberImpl)this.mucRoom).getIdentifierAsJid()),
MessageTypeFilter.GROUPCHAT));
}
+
+ // let's check room config
+ updateFromRoomConfiguration();
+
+ logger.info(this.callContext + " Joined room: " + roomName + " meetingId:" + this.getMeetingId());
}
catch (Exception e)
{
@@ -1074,15 +1129,11 @@ private void leaveConferenceRoom()
= xmppProvider.getOperationSet(OperationSetMultiUserChat.class);
muc.removePresenceListener(this);
- if (this.roomConfigurationListener != null)
+ XMPPConnection connection = getConnection();
+ if (connection != null)
{
- XMPPConnection connection = getConnection();
- if (connection != null)
- {
- connection.removeAsyncStanzaListener(roomConfigurationListener);
- }
-
- this.roomConfigurationListener = null;
+ connection.removeAsyncStanzaListener(roomConfigurationListener);
+ connection.removeAsyncStanzaListener(roomMetadataListener);
}
// remove listener needs to be after leave,
@@ -1952,6 +2003,14 @@ private void updateFromRoomConfiguration()
boolean singleModeratorEnabled = df.getField(Lobby.DATA_FORM_SINGLE_MODERATOR_FIELD) != null;
setLobbyEnabled(lobbyEnabled);
this.singleModeratorEnabled = singleModeratorEnabled;
+
+ List roomMetadataValues
+ = df.getField(TranscriptionGatewaySession.DATA_FORM_ROOM_METADATA_FIELD).getValuesAsString();
+ if (roomMetadataValues != null && !roomMetadataValues.isEmpty())
+ {
+ // it is supposed to have a single value
+ processRoomMetadataJson(roomMetadataValues.get(0));
+ }
}
catch(Exception e)
{
@@ -1959,6 +2018,36 @@ private void updateFromRoomConfiguration()
}
}
+ private void processRoomMetadataJson(String json)
+ {
+ if (!(this.gatewaySession instanceof TranscriptionGatewaySession))
+ {
+ return;
+ }
+
+ try
+ {
+ Object o = new JSONParser().parse(json);
+
+ if (o instanceof JSONObject)
+ {
+ JSONObject data = (JSONObject) o;
+
+ if (data.get("type").equals("room_metadata"))
+ {
+ JSONObject metadataObj = (JSONObject)data.getOrDefault("metadata", new JSONObject());
+ JSONObject recordingObj = (JSONObject)metadataObj.getOrDefault("recording", new JSONObject());
+ ((TranscriptionGatewaySession)this.gatewaySession).setBackendTranscribingEnabled(
+ (boolean)recordingObj.getOrDefault("isTranscribingEnabled", false));
+ }
+ }
+ }
+ catch(Exception e)
+ {
+ logger.error(callContext + " Error parsing", e);
+ }
+ }
+
/**
* Threads handles the timeout for stopping the conference.
* For waiting for conference call invite sent by the focus or for waiting
@@ -2135,6 +2224,26 @@ public void processStanza(Stanza stanza)
}
}
+ /**
+ * When a room metadata change is received.
+ */
+ private class RoomMetadataListener
+ implements StanzaListener
+ {
+ @Override
+ public void processStanza(Stanza stanza)
+ {
+ JsonMessageExtension jsonMsg = stanza.getExtension(JsonMessageExtension.class);
+
+ if (jsonMsg == null)
+ {
+ return;
+ }
+
+ processRoomMetadataJson(jsonMsg.getJson());
+ }
+ }
+
/**
* Used to check the jvb side of the call for any activity.
*/
diff --git a/src/main/java/org/jitsi/jigasi/TranscriptionGatewaySession.java b/src/main/java/org/jitsi/jigasi/TranscriptionGatewaySession.java
index c085d8747..5113017ae 100644
--- a/src/main/java/org/jitsi/jigasi/TranscriptionGatewaySession.java
+++ b/src/main/java/org/jitsi/jigasi/TranscriptionGatewaySession.java
@@ -53,6 +53,11 @@ public class TranscriptionGatewaySession
private final static Logger logger
= Logger.getLogger(TranscriptionGatewaySession.class);
+ /**
+ * The data form field added when transcription is enabled.
+ */
+ public static final String DATA_FORM_ROOM_METADATA_FIELD = "muc#roominfo_jitsimetadata";
+
/**
* The display name which should be displayed when Jigasi joins the
* room
@@ -101,6 +106,13 @@ public class TranscriptionGatewaySession
private List finalTranscriptPromises
= new LinkedList<>();
+ /**
+ * When a backend transcribing is enabled, overrides participants request for transcriptions and keep the
+ * transcriber in the room and working even though no participant is requesting it.
+ * This is used to make transcriptions available for post-processing.
+ */
+ private boolean isBackendTranscribingEnabled = false;
+
/**
* Create a TranscriptionGatewaySession which can handle the transcription
* of a JVB conference
@@ -308,8 +320,17 @@ void notifyChatRoomMemberUpdated(ChatRoomMember chatMember, Presence presence)
String identifier = getParticipantIdentifier(chatMember);
this.transcriber.updateParticipant(identifier, chatMember);
- if (transcriber.isTranscribing() &&
- !transcriber.isAnyParticipantRequestingTranscription())
+ this.maybeStopTranscription();
+ }
+
+ private boolean isTranscriptionRequested()
+ {
+ return transcriber.isAnyParticipantRequestingTranscription() || isBackendTranscribingEnabled;
+ }
+
+ private void maybeStopTranscription()
+ {
+ if (transcriber.isTranscribing() && !isTranscriptionRequested())
{
new Thread(() ->
{
@@ -322,7 +343,7 @@ void notifyChatRoomMemberUpdated(ChatRoomMember chatMember, Presence presence)
logger.error(e);
}
- if (!transcriber.isAnyParticipantRequestingTranscription())
+ if (!isTranscriptionRequested())
{
jvbConference.stop();
}
@@ -666,13 +687,10 @@ public void notify(Transcriber transcriber, TranscriptEvent event)
{
// in will_end we will be still transcribing but we need
// to explicitly send off
- TranscriptionStatusExtension.Status status
- = event.getEvent() ==
- Transcript.TranscriptEventType.WILL_END ?
- TranscriptionStatusExtension.Status.OFF
- : transcriber.isTranscribing() ?
- TranscriptionStatusExtension.Status.ON
- : TranscriptionStatusExtension.Status.OFF;
+ TranscriptionStatusExtension.Status status = event.getEvent() == Transcript.TranscriptEventType.WILL_END
+ ? TranscriptionStatusExtension.Status.OFF
+ : transcriber.isTranscribing()
+ ? TranscriptionStatusExtension.Status.ON : TranscriptionStatusExtension.Status.OFF;
TranscriptionStatusExtension extension
= new TranscriptionStatusExtension();
@@ -689,4 +707,14 @@ public boolean hasCallResumeSupport()
{
return false;
}
+
+ /**
+ * Sets whether backend transcriptions are enabled or not.
+ */
+ public void setBackendTranscribingEnabled(boolean backendTranscribingEnabled)
+ {
+ this.isBackendTranscribingEnabled = backendTranscribingEnabled;
+
+ this.maybeStopTranscription();
+ }
}
diff --git a/src/main/java/org/jitsi/jigasi/transcription/Transcriber.java b/src/main/java/org/jitsi/jigasi/transcription/Transcriber.java
index 9bf14ee49..fba47e314 100644
--- a/src/main/java/org/jitsi/jigasi/transcription/Transcriber.java
+++ b/src/main/java/org/jitsi/jigasi/transcription/Transcriber.java
@@ -798,10 +798,7 @@ private Participant getParticipant(String identifier)
*/
public boolean isAnyParticipantRequestingTranscription()
{
-
- return getParticipants()
- .stream()
- .anyMatch(Participant::isRequestingTranscription);
+ return getParticipants().stream().anyMatch(Participant::isRequestingTranscription);
}
/**