From ffcc8b606e1d5b37def2ecaa76fed70a3557396e Mon Sep 17 00:00:00 2001 From: Boris Grozev Date: Sun, 22 Sep 2024 13:40:50 -0500 Subject: [PATCH 1/4] feat(WIP): Add an option to export media to a pre-configured WS url. --- .../jicofo/bridge/colibri/Colibri2Session.kt | 7 +++++++ .../bridge/colibri/ColibriV2SessionManager.kt | 15 ++++++++++++++- jicofo-selector/src/main/resources/reference.conf | 3 +++ .../conference/JitsiMeetConferenceImpl.java | 4 ++++ pom.xml | 2 +- 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/Colibri2Session.kt b/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/Colibri2Session.kt index 3633b4e0d0..c8e3ec1472 100644 --- a/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/Colibri2Session.kt +++ b/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/Colibri2Session.kt @@ -36,6 +36,7 @@ import org.jitsi.xmpp.extensions.colibri2.Colibri2Relay import org.jitsi.xmpp.extensions.colibri2.ConferenceModifiedIQ import org.jitsi.xmpp.extensions.colibri2.ConferenceModifyIQ import org.jitsi.xmpp.extensions.colibri2.Endpoints +import org.jitsi.xmpp.extensions.colibri2.Export import org.jitsi.xmpp.extensions.colibri2.InitialLastN import org.jitsi.xmpp.extensions.colibri2.Media import org.jitsi.xmpp.extensions.colibri2.Sctp @@ -48,6 +49,7 @@ import org.jivesoftware.smack.StanzaCollector import org.jivesoftware.smack.packet.ErrorIQ import org.jivesoftware.smack.packet.IQ import org.jivesoftware.smackx.muc.MUCRole +import java.net.URI import java.util.Collections.singletonList import java.util.UUID @@ -57,6 +59,7 @@ class Colibri2Session( val bridge: Bridge, // Whether the session was constructed for the purpose of visitor nodes val visitor: Boolean, + val audioExportUrl: URI?, parentLogger: Logger ) : CascadeNode { private val logger = createChildLogger(parentLogger).apply { @@ -196,6 +199,10 @@ class Colibri2Session( setCreate(true) setConferenceName(colibriSessionManager.conferenceName) setRtcstatsEnabled(colibriSessionManager.rtcStatsEnabled) + audioExportUrl?.let { + logger.warn("XXX adding export to colibri iq url=$it") + addExport(Export(it, audio = true)) + } } } diff --git a/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriV2SessionManager.kt b/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriV2SessionManager.kt index c4df29f694..a190a088e7 100644 --- a/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriV2SessionManager.kt +++ b/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriV2SessionManager.kt @@ -52,6 +52,7 @@ import org.jivesoftware.smack.packet.StanzaError.Condition.conflict import org.jivesoftware.smack.packet.StanzaError.Condition.item_not_found import org.jivesoftware.smack.packet.StanzaError.Condition.service_unavailable import org.json.simple.JSONArray +import java.net.URI import java.util.Collections.singletonList /** @@ -68,6 +69,7 @@ class ColibriV2SessionManager( */ internal val meetingId: String, internal val rtcStatsEnabled: Boolean, + private val audioExportUrl: URI?, private val bridgeVersion: String?, parentLogger: Logger ) : ColibriSessionManager, Cascade { @@ -81,6 +83,8 @@ class ColibriV2SessionManager( logger.info("Using ${it.javaClass.name}") } + private var sessionForAudioRecording: Colibri2Session? = null + /** * The colibri2 sessions that are currently active, mapped by the relayId of the [Bridge] that they use. */ @@ -241,7 +245,16 @@ class ColibriV2SessionManager( return Pair(session, false) } - session = Colibri2Session(this, bridge, visitor, logger) + session = Colibri2Session( + this, + bridge, + visitor, + if (audioExportUrl != null && sessionForAudioRecording == null) audioExportUrl else null, + logger + ) + if (audioExportUrl != null && sessionForAudioRecording == null) { + sessionForAudioRecording = session + } return Pair(session, true) } diff --git a/jicofo-selector/src/main/resources/reference.conf b/jicofo-selector/src/main/resources/reference.conf index de8532a959..ce53f82f86 100644 --- a/jicofo-selector/src/main/resources/reference.conf +++ b/jicofo-selector/src/main/resources/reference.conf @@ -405,6 +405,9 @@ jicofo { # via the room-metadata service enable-live-room = false } + recording { + multi-track-recorder-url-template = "wss://example.com/recorder/MEETING_ID" + } xmpp { // Whether to use JitsiXmppStringprep to validate JIDs. If set to false uses the default validation in Smack. diff --git a/jicofo/src/main/java/org/jitsi/jicofo/conference/JitsiMeetConferenceImpl.java b/jicofo/src/main/java/org/jitsi/jicofo/conference/JitsiMeetConferenceImpl.java index d4e1503354..39df8d5e09 100644 --- a/jicofo/src/main/java/org/jitsi/jicofo/conference/JitsiMeetConferenceImpl.java +++ b/jicofo/src/main/java/org/jitsi/jicofo/conference/JitsiMeetConferenceImpl.java @@ -45,6 +45,7 @@ import org.jivesoftware.smackx.caps.packet.*; import org.jxmpp.jid.*; +import java.net.*; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; @@ -328,12 +329,15 @@ private ColibriSessionManager getColibriSessionManager() { // We initialize colibriSessionManager only after having joined the room, so meetingId must be set. String meetingId = Objects.requireNonNull(this.meetingId); + URI multiTrackRecorderUrl = RecordingConfig.config.multiTrackRecorderUrl(meetingId); + // TODO update presence to reflect colibriSessionManager = new ColibriV2SessionManager( jicofoServices.getXmppServices().getServiceConnection().getXmppConnection(), jicofoServices.getBridgeSelector(), getRoomName().toString(), meetingId, config.getRtcStatsEnabled(), + multiTrackRecorderUrl, jvbVersion, logger); colibriSessionManager.addListener(colibriSessionManagerListener); diff --git a/pom.xml b/pom.xml index 2443a04d1d..6594891512 100644 --- a/pom.xml +++ b/pom.xml @@ -169,7 +169,7 @@ ${project.groupId} jitsi-xmpp-extensions - 1.0-80-g0ce9883 + 1.0-SNAPSHOT org.slf4j From da3ff8bd1b4e3adc3c70af0de87fba29c9c8d924 Mon Sep 17 00:00:00 2001 From: Boris Grozev Date: Wed, 25 Sep 2024 15:31:16 -0500 Subject: [PATCH 2/4] Add missing RecordingConfig. --- .../org/jitsi/jicofo/RecordingConfig.kt | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 jicofo/src/main/kotlin/org/jitsi/jicofo/RecordingConfig.kt diff --git a/jicofo/src/main/kotlin/org/jitsi/jicofo/RecordingConfig.kt b/jicofo/src/main/kotlin/org/jitsi/jicofo/RecordingConfig.kt new file mode 100644 index 0000000000..26ca99764b --- /dev/null +++ b/jicofo/src/main/kotlin/org/jitsi/jicofo/RecordingConfig.kt @@ -0,0 +1,39 @@ +/* + * Jicofo, the Jitsi Conference Focus. + * + * Copyright @ 2024 - present 8x8, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jitsi.jicofo + +import org.jitsi.config.JitsiConfig +import org.jitsi.metaconfig.optionalconfig +import java.net.URI + +class RecordingConfig private constructor() { + val multiTrackRecorderUrlTemplate: String? by optionalconfig { + "jicofo.recording.multi-track-recorder-url-template".from(JitsiConfig.newConfig) + } + + fun multiTrackRecorderUrl(meetingId: String): URI? = multiTrackRecorderUrlTemplate?.let { + URI(it.replace(MEETING_ID_TEMPLATE, meetingId)) + } + + companion object { + @JvmField + val config = RecordingConfig() + + const val MEETING_ID_TEMPLATE = "MEETING_ID" + } +} From 510616736d24641ac669fae0db5eb3be2dca0940 Mon Sep 17 00:00:00 2001 From: Boris Grozev Date: Thu, 24 Oct 2024 12:43:26 -0500 Subject: [PATCH 3/4] squash: Update to use "connect". --- .../jicofo/bridge/colibri/Colibri2Session.kt | 17 ++++++++++++----- .../bridge/colibri/ColibriV2SessionManager.kt | 6 +++--- .../src/main/resources/reference.conf | 4 +++- .../kotlin/org/jitsi/jicofo/RecordingConfig.kt | 2 +- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/Colibri2Session.kt b/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/Colibri2Session.kt index c8e3ec1472..ceb0c80840 100644 --- a/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/Colibri2Session.kt +++ b/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/Colibri2Session.kt @@ -35,8 +35,8 @@ import org.jitsi.xmpp.extensions.colibri2.Colibri2Error import org.jitsi.xmpp.extensions.colibri2.Colibri2Relay import org.jitsi.xmpp.extensions.colibri2.ConferenceModifiedIQ import org.jitsi.xmpp.extensions.colibri2.ConferenceModifyIQ +import org.jitsi.xmpp.extensions.colibri2.Connect import org.jitsi.xmpp.extensions.colibri2.Endpoints -import org.jitsi.xmpp.extensions.colibri2.Export import org.jitsi.xmpp.extensions.colibri2.InitialLastN import org.jitsi.xmpp.extensions.colibri2.Media import org.jitsi.xmpp.extensions.colibri2.Sctp @@ -59,7 +59,7 @@ class Colibri2Session( val bridge: Bridge, // Whether the session was constructed for the purpose of visitor nodes val visitor: Boolean, - val audioExportUrl: URI?, + val audioRecordUrl: URI?, parentLogger: Logger ) : CascadeNode { private val logger = createChildLogger(parentLogger).apply { @@ -199,9 +199,16 @@ class Colibri2Session( setCreate(true) setConferenceName(colibriSessionManager.conferenceName) setRtcstatsEnabled(colibriSessionManager.rtcStatsEnabled) - audioExportUrl?.let { - logger.warn("XXX adding export to colibri iq url=$it") - addExport(Export(it, audio = true)) + audioRecordUrl?.let { + logger.warn("Adding export to colibri iq url=$it") + addConnect( + Connect( + url = it, + type = Connect.Types.RECORDER, + protocol = Connect.Protocols.MEDIAJSON, + audio = true + ) + ) } } } diff --git a/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriV2SessionManager.kt b/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriV2SessionManager.kt index a190a088e7..49655269cb 100644 --- a/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriV2SessionManager.kt +++ b/jicofo-selector/src/main/kotlin/org/jitsi/jicofo/bridge/colibri/ColibriV2SessionManager.kt @@ -69,7 +69,7 @@ class ColibriV2SessionManager( */ internal val meetingId: String, internal val rtcStatsEnabled: Boolean, - private val audioExportUrl: URI?, + private val audioRecordUrl: URI?, private val bridgeVersion: String?, parentLogger: Logger ) : ColibriSessionManager, Cascade { @@ -249,10 +249,10 @@ class ColibriV2SessionManager( this, bridge, visitor, - if (audioExportUrl != null && sessionForAudioRecording == null) audioExportUrl else null, + if (audioRecordUrl != null && sessionForAudioRecording == null) audioRecordUrl else null, logger ) - if (audioExportUrl != null && sessionForAudioRecording == null) { + if (audioRecordUrl != null && sessionForAudioRecording == null) { sessionForAudioRecording = session } return Pair(session, true) diff --git a/jicofo-selector/src/main/resources/reference.conf b/jicofo-selector/src/main/resources/reference.conf index ce53f82f86..d960a98eeb 100644 --- a/jicofo-selector/src/main/resources/reference.conf +++ b/jicofo-selector/src/main/resources/reference.conf @@ -406,7 +406,9 @@ jicofo { enable-live-room = false } recording { - multi-track-recorder-url-template = "wss://example.com/recorder/MEETING_ID" + # A templated URL to use for audio recording. The string "MEEETING_ID" will be replaced with the actual meeting ID. + # Note: if this is set all conferences will automatically be recorded using the specified URL. + //multi-track-recorder-url-template = "wss://example.com/recorder/MEETING_ID" } xmpp { diff --git a/jicofo/src/main/kotlin/org/jitsi/jicofo/RecordingConfig.kt b/jicofo/src/main/kotlin/org/jitsi/jicofo/RecordingConfig.kt index 26ca99764b..7d4c8474e4 100644 --- a/jicofo/src/main/kotlin/org/jitsi/jicofo/RecordingConfig.kt +++ b/jicofo/src/main/kotlin/org/jitsi/jicofo/RecordingConfig.kt @@ -22,7 +22,7 @@ import org.jitsi.metaconfig.optionalconfig import java.net.URI class RecordingConfig private constructor() { - val multiTrackRecorderUrlTemplate: String? by optionalconfig { + private val multiTrackRecorderUrlTemplate: String? by optionalconfig { "jicofo.recording.multi-track-recorder-url-template".from(JitsiConfig.newConfig) } From caa9faac27ec3d7d858acdbf20644ece3dae336d Mon Sep 17 00:00:00 2001 From: damencho Date: Mon, 28 Oct 2024 15:29:35 -0500 Subject: [PATCH 4/4] feat: Sets audio recording prop when enabled. --- .../jitsi/jicofo/conference/JitsiMeetConferenceImpl.java | 7 ++++++- pom.xml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/jicofo/src/main/java/org/jitsi/jicofo/conference/JitsiMeetConferenceImpl.java b/jicofo/src/main/java/org/jitsi/jicofo/conference/JitsiMeetConferenceImpl.java index 39df8d5e09..1e015f2ea5 100644 --- a/jicofo/src/main/java/org/jitsi/jicofo/conference/JitsiMeetConferenceImpl.java +++ b/jicofo/src/main/java/org/jitsi/jicofo/conference/JitsiMeetConferenceImpl.java @@ -330,7 +330,7 @@ private ColibriSessionManager getColibriSessionManager() // We initialize colibriSessionManager only after having joined the room, so meetingId must be set. String meetingId = Objects.requireNonNull(this.meetingId); URI multiTrackRecorderUrl = RecordingConfig.config.multiTrackRecorderUrl(meetingId); - // TODO update presence to reflect + colibriSessionManager = new ColibriV2SessionManager( jicofoServices.getXmppServices().getServiceConnection().getXmppConnection(), jicofoServices.getBridgeSelector(), @@ -341,6 +341,11 @@ private ColibriSessionManager getColibriSessionManager() jvbVersion, logger); colibriSessionManager.addListener(colibriSessionManagerListener); + + if (multiTrackRecorderUrl != null) + { + setConferenceProperty(ConferenceProperties.KEY_AUDIO_RECORDING_ENABLED, Boolean.TRUE.toString()); + } } return colibriSessionManager; } diff --git a/pom.xml b/pom.xml index 6594891512..8369826c26 100644 --- a/pom.xml +++ b/pom.xml @@ -169,7 +169,7 @@ ${project.groupId} jitsi-xmpp-extensions - 1.0-SNAPSHOT + 1.0-82-ge8aacab org.slf4j