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