From 57134d4c008f21b931743154c646ed40be3cc43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BC=D1=8F=D0=BD=20=D0=9C=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2?= Date: Thu, 19 Sep 2024 10:50:35 -0500 Subject: [PATCH] fix(breakout-rooms): Fixes the case where a single participant switches to breakout room. (#1169) * fix(breakout-rooms): Fixes the case where a single participant switches to breakout room. We keep around conferences when there is a breakout room. When a single participant is switching we first get participant left then breakout room is created, so we need to distinguish that the participant left is for joining breakout room. * squash: Fixes tests. * squash: Doc update --- .../conference/JitsiMeetConferenceImpl.java | 64 ++++++++++++++----- .../org/jitsi/jicofo/mock/MockChatRoom.kt | 1 + 2 files changed, 48 insertions(+), 17 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 4939627a61..1681879934 100644 --- a/jicofo/src/main/java/org/jitsi/jicofo/conference/JitsiMeetConferenceImpl.java +++ b/jicofo/src/main/java/org/jitsi/jicofo/conference/JitsiMeetConferenceImpl.java @@ -37,7 +37,6 @@ import org.jitsi.xmpp.extensions.jingle.*; import org.jitsi.xmpp.extensions.jitsimeet.*; -import org.jitsi.jicofo.jigasi.*; import org.jitsi.jicofo.jibri.*; import org.jitsi.xmpp.extensions.visitors.*; @@ -72,6 +71,12 @@ public class JitsiMeetConferenceImpl implements JitsiMeetConference, XmppProvider.Listener { + + /** + * Status used by participants when they are switching from a room to a breakout room. + */ + private static final String BREAKOUT_SWITCHING_STATUS = "switch_room"; + /** * Name of MUC room that is hosting Jitsi Meet conference. */ @@ -164,7 +169,9 @@ public class JitsiMeetConferenceImpl private Future singleParticipantTout; /** - * A task to stop the conference if no participants join after an initial timeout. + * A task to stop the conference if no participants or breakout rooms are present after a timeout. + * It's triggered when the conference is first created, or when the last participant leaves with an indication + * that it will join a breakout room. */ private Future conferenceStartTimeout; @@ -276,18 +283,7 @@ public JitsiMeetConferenceImpl( this.jicofoServices = jicofoServices; this.jvbVersion = jvbVersion; - conferenceStartTimeout = TaskPools.getScheduledPool().schedule( - () -> - { - if (includeInStatistics) - { - logger.info("Expiring due to initial timeout."); - } - stop(); - }, - ConferenceConfig.config.getConferenceStartTimeout().toMillis(), - TimeUnit.MILLISECONDS); - + rescheduleConferenceStartTimeout(); visitorCodecs = new PreferenceAggregator( logger, @@ -971,13 +967,14 @@ else if (participants.size() == 0) } } - maybeStop(); + maybeStop(chatRoomMember); } /** * Stop the conference if there are no members and there are no associated breakout room. + * @param chatRoomMember The participant leaving if any. */ - private void maybeStop() + private void maybeStop(ChatRoomMember chatRoomMember) { ChatRoom chatRoom = this.chatRoom; if (chatRoom == null || chatRoom.getMemberCount() == 0) @@ -986,6 +983,13 @@ private void maybeStop() { logger.info("Breakout rooms still present, will not stop."); } + else if (chatRoomMember != null + && chatRoomMember.getPresence() != null + && BREAKOUT_SWITCHING_STATUS.equals(chatRoomMember.getPresence().getStatus())) + { + logger.info("Member moving to breakout room, will not stop."); + rescheduleConferenceStartTimeout(); + } else { logger.info("Last member left, stopping."); @@ -999,7 +1003,7 @@ private void maybeStop() */ public void breakoutConferenceEnded() { - maybeStop(); + maybeStop(null); } @Override @@ -2072,6 +2076,32 @@ private void rescheduleSingleParticipantTimeout() logger.info("Scheduled single person timeout."); } + /** + * (Re)schedules conference start timeout. + */ + private void rescheduleConferenceStartTimeout() + { + conferenceStartTimeout = TaskPools.getScheduledPool().schedule( + () -> + { + if (includeInStatistics) + { + logger.info("Expiring due to initial timeout."); + } + + // in case of last participant leaving to join a breakout room, we want to skip destroy + if (jicofoServices.getFocusManager().hasBreakoutRooms(roomName)) + { + logger.info("Breakout rooms present, will not stop."); + return; + } + + stop(); + }, + ConferenceConfig.config.getConferenceStartTimeout().toMillis(), + TimeUnit.MILLISECONDS); + } + /** Called when a new visitor has been added to the conference. */ private void visitorAdded(List codecs) { diff --git a/jicofo/src/test/kotlin/org/jitsi/jicofo/mock/MockChatRoom.kt b/jicofo/src/test/kotlin/org/jitsi/jicofo/mock/MockChatRoom.kt index e0fedc12a9..d869830cf2 100644 --- a/jicofo/src/test/kotlin/org/jitsi/jicofo/mock/MockChatRoom.kt +++ b/jicofo/src/test/kotlin/org/jitsi/jicofo/mock/MockChatRoom.kt @@ -46,6 +46,7 @@ class MockChatRoom(val xmppProvider: XmppProvider) { every { features } returns Features.defaultFeatures every { debugState } returns OrderedJsonObject() every { presence } returns mockk { + every { status } returns null every { getExtension(any()) } returns null every { getExtension(any()) } returns null every { getExtension(any>()) } returns null