/* * Jicofo, the Jitsi Conference Focus. * * Copyright @ 2015 Atlassian Pty Ltd * * 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 mock.*; import mock.muc.*; import mock.util.*; import org.jitsi.xmpp.extensions.colibri.*; import org.jitsi.xmpp.extensions.jitsimeet.*; import net.java.sip.communicator.util.*; import org.jitsi.protocol.xmpp.util.*; import org.junit.*; import org.junit.runner.*; import org.junit.runners.*; import org.jxmpp.jid.*; import org.jxmpp.jid.impl.*; import java.util.*; import static org.junit.Assert.*; /** * */ @RunWith(JUnit4.class) public class AdvertiseSSRCsTest { private static final Logger logger = Logger.getLogger(AdvertiseSSRCsTest.class); private OSGiHandler osgi = OSGiHandler.getInstance(); @Before public void setUpClass() throws Exception { osgi.init(); } @After public void tearDownClass() throws Exception { osgi.shutdown(); } @Test public void testOneToOneConference() throws Exception { //FIXME: test when there is participant without contents EntityBareJid roomName = JidCreate.entityBareFrom( "testSSRCs@conference.pawel.jitsi.net"); String serverName = "test-server"; TestConference testConf = TestConference.allocate(osgi.bc, serverName, roomName); MockProtocolProvider pps = testConf.getFocusProtocolProvider(); MockMultiUserChatOpSet mucOpSet = pps.getMockChatOpSet(); MockMultiUserChat chat = (MockMultiUserChat) mucOpSet.findRoom(roomName.toString()); // Join with all users MockParticipant user1 = new MockParticipant("User1"); user1.setSsrcVideoType(SSRCInfoPacketExtension.CAMERA_VIDEO_TYPE); user1.join(chat); MockParticipant user2 = new MockParticipant("User2"); user2.setSsrcVideoType(SSRCInfoPacketExtension.SCREEN_VIDEO_TYPE); user2.join(chat); // Accept invite with all users assertNotNull(user1.acceptInvite(4000)); assertNotNull(user2.acceptInvite(4000)); user1.waitForAddSource(2000); user2.waitForAddSource(2000); assertEquals(2, user1.getRemoteSSRCs("audio").size()); // No groups assertEquals(0, user1.getRemoteSSRCGroups("audio").size()); // Verify SSRC owners and video types // From user 1 perspective assertEquals( user2.getMyJid(), SSRCSignaling.getSSRCOwner(user1.getRemoteSSRCs("audio").get(1))); assertEquals( user2.getMyJid(), SSRCSignaling.getSSRCOwner(user1.getRemoteSSRCs("video").get(1))); assertEquals( user2.getSsrcVideoType(), SSRCSignaling.getVideoType(user1.getRemoteSSRCs("video").get(1))); // From user 2 perspective assertEquals( user1.getMyJid(), SSRCSignaling.getSSRCOwner(user2.getRemoteSSRCs("audio").get(1))); assertEquals( user1.getMyJid(), SSRCSignaling.getSSRCOwner(user2.getRemoteSSRCs("video").get(1))); assertEquals( user1.getSsrcVideoType(), SSRCSignaling.getVideoType(user2.getRemoteSSRCs("video").get(1))); user2.leave(); assertNotNull(user1.waitForRemoveSource(500)); assertEquals(1, user1.getRemoteSSRCs("audio").size()); // No groups assertEquals(0, user1.getRemoteSSRCGroups("audio").size()); MockParticipant user3 = new MockParticipant("User3"); user3.join(chat); assertNotNull(user3.acceptInvite(4000)); user1.waitForAddSource(2000); assertEquals(2, user1.getRemoteSSRCs("audio").size()); assertEquals(2, user3.getRemoteSSRCs("audio").size()); // No groups assertEquals(0, user1.getRemoteSSRCGroups("audio").size()); assertEquals(0, user3.getRemoteSSRCGroups("audio").size()); user3.leave(); user1.leave(); testConf.stop(); } @Test public void testSourceRemoval() throws Exception { EntityBareJid roomName = JidCreate.entityBareFrom( "testSSRCremoval@conference.pawel.jitsi.net"); String serverName = "test-server"; TestConference testConf = TestConference.allocate(osgi.bc, serverName, roomName); MockProtocolProvider pps = testConf.getFocusProtocolProvider(); MockMultiUserChatOpSet mucOpSet = pps.getMockChatOpSet(); MockMultiUserChat chat = (MockMultiUserChat) mucOpSet.findRoom(roomName.toString()); // Join with all users MockParticipant user1 = new MockParticipant("User1"); user1.setSsrcVideoType(SSRCInfoPacketExtension.CAMERA_VIDEO_TYPE); user1.join(chat); MockParticipant user2 = new MockParticipant("User2"); user2.setSsrcVideoType(SSRCInfoPacketExtension.SCREEN_VIDEO_TYPE); user2.join(chat); // Accept invite with all users assertNotNull(user1.acceptInvite(4000)); assertNotNull(user2.acceptInvite(4000)); user1.waitForAddSource(2000); user2.waitForAddSource(2000); user2.audioSourceRemove(1); assertNotNull(user1.waitForRemoveSource(500)); assertEquals(1, user1.getRemoteSSRCs("audio").size()); assertEquals(0, user1.getRemoteSSRCGroups("audio").size()); MockParticipant user3 = new MockParticipant("User3"); user3.join(chat); assertNotNull(user3.acceptInvite(4000)); user1.waitForAddSource(2000); assertEquals(2, user1.getRemoteSSRCs("audio").size()); assertEquals(2, user3.getRemoteSSRCs("audio").size()); // No groups assertEquals(0, user1.getRemoteSSRCGroups("audio").size()); assertEquals(0, user3.getRemoteSSRCGroups("audio").size()); user3.leave(); user2.leave(); user1.leave(); testConf.stop(); } @Test public void testDuplicatedSSRCs() throws Exception { EntityBareJid roomName = JidCreate.entityBareFrom( "testSSRCs@conference.pawel.jitsi.net"); String serverName = "test-server"; TestConference testConf = TestConference.allocate(osgi.bc, serverName, roomName); MockProtocolProvider pps = testConf.getFocusProtocolProvider(); MockMultiUserChatOpSet mucOpSet = pps.getMockChatOpSet(); MockMultiUserChat chat = (MockMultiUserChat) mucOpSet.findRoom(roomName.toString()); // Join with all users MockParticipant user1 = new MockParticipant("User1"); user1.join(chat); MockParticipant user2 = new MockParticipant("User2"); user2.join(chat); // Accept invite with all users long u1VideoSSRC = MockParticipant.nextSSRC(); user1.addLocalVideoSSRC(u1VideoSSRC, null); long u1VideoSSRC2 = MockParticipant.nextSSRC(); user1.addLocalVideoSSRC(u1VideoSSRC2, null); assertNotNull(user1.acceptInvite(4000)); assertNotNull(user2.acceptInvite(4000)); assertNotNull(user1.waitForAddSource(1000)); assertNotNull(user2.waitForAddSource(1000)); // There is 1 + 2 extra we've created here in the test assertEquals(1 /* jvb */ + 3, user2.getRemoteSSRCs("video").size()); // No groups assertEquals(0, user2.getRemoteSSRCGroups("video").size()); user1.videoSourceAdd(new long[]{ u1VideoSSRC }, false); user1.videoSourceAdd( new long[]{ u1VideoSSRC, u1VideoSSRC2, u1VideoSSRC, u1VideoSSRC, u1VideoSSRC, u1VideoSSRC2 }, false); user1.videoSourceAdd(new long[]{ u1VideoSSRC2, u1VideoSSRC }, false); // There should be no source-add notifications sent assertEquals(null, user2.waitForAddSource(500)); assertEquals(1 + /* jvb */ + 1, user2.getRemoteSSRCs("audio").size()); // There is 1 + 2 extra we've created here in the test assertEquals(1 + /* jvb */ + 3, user2.getRemoteSSRCs("video").size()); user2.leave(); user1.leave(); testConf.stop(); } @Test public void testSSRCLimit() throws Exception { EntityBareJid roomName = JidCreate.entityBareFrom( "testSSRCs@conference.pawel.jitsi.net"); String serverName = "test-server"; TestConference testConf = TestConference.allocate(osgi.bc, serverName, roomName); JitsiMeetGlobalConfig globalConfig = ServiceUtils.getService(osgi.bc, JitsiMeetGlobalConfig.class); assertNotNull(globalConfig); MockProtocolProvider pps = testConf.getFocusProtocolProvider(); MockMultiUserChatOpSet mucOpSet = pps.getMockChatOpSet(); MockMultiUserChat chat = (MockMultiUserChat) mucOpSet.findRoom(roomName.toString()); // Join with all users MockParticipant user1 = new MockParticipant("User1"); user1.join(chat); user1.waitForJoinThread(5000); MockParticipant user2 = new MockParticipant("User2"); user2.join(chat); user2.waitForJoinThread(5000); int maxSSRCs = globalConfig.getMaxSourcesPerUser(); // Accept invite with all users // Add many SSRCs to both users // Video: // User 1 will fit into the limit on accept, but we'll try to exceed // it later int user1ExtraVideoSSRCCount = maxSSRCs / 2; // User 2 will exceed SSRC limit on accept already int user2ExtraVideoSSRCCount = maxSSRCs + 3; user1.addMultipleVideoSSRCs(user1ExtraVideoSSRCCount); user2.addMultipleVideoSSRCs(user2ExtraVideoSSRCCount); // Audio: the opposite scenario int user1ExtraAudioSSRCCount = maxSSRCs + 5; int user2ExtraAudioSSRCCount = maxSSRCs / 2; user1.addMultipleAudioSSRCs(user1ExtraAudioSSRCCount); user2.addMultipleAudioSSRCs(user2ExtraAudioSSRCCount); assertNotNull(user1.acceptInvite(10000)); assertNotNull(user2.acceptInvite(10000)); assertNotNull(user1.waitForAddSource(4000)); assertNotNull(user2.waitForAddSource(4000)); int expectedMax = 1 /* jvb's mixed */ + maxSSRCs /* max that can come from 1 participant */; // Verify User1's SSRCs seen by User2 assertEquals(1 /* jvb's mixed */ + 1 + user1ExtraVideoSSRCCount, user2.getRemoteSSRCs("video").size()); assertEquals(expectedMax, user2.getRemoteSSRCs("audio").size()); // Verify User1's SSRCs seen by User1 assertEquals(expectedMax, user1.getRemoteSSRCs("video").size()); assertEquals(1 /* jvb's mixed */ + 1 + user2ExtraAudioSSRCCount, user1.getRemoteSSRCs("audio").size()); // No groups assertEquals(0, user2.getRemoteSSRCGroups("video").size()); assertEquals(0, user1.getRemoteSSRCGroups("video").size()); assertEquals(0, user2.getRemoteSSRCGroups("audio").size()); assertEquals(0, user1.getRemoteSSRCGroups("audio").size()); // Now let's test the limits for source-add // User1 will have video SSRCs filled and audio are filled already user1.videoSourceAdd(maxSSRCs / 2); assertNotNull(user2.waitForAddSource(300)); assertEquals(expectedMax, user2.getRemoteSSRCs("video").size()); user1.audioSourceAdd(5); assertTrue(null == user2.waitForAddSource(300)); assertEquals(expectedMax, user2.getRemoteSSRCs("audio").size()); // User2 has video SSRCs filled already and audio will be filled user2.videoSourceAdd(maxSSRCs / 2); assertNull(user1.waitForAddSource(300)); assertEquals(expectedMax, user1.getRemoteSSRCs("video").size()); user2.audioSourceAdd(maxSSRCs / 2); assertNotNull(user1.waitForAddSource(300)); assertEquals(expectedMax, user1.getRemoteSSRCs("audio").size()); user2.leave(); user1.leave(); // stopping the conference also stops the bridge, // but the users leaving still want the bridge to disconnect properly Thread.sleep(5000); testConf.stop(); } // FIXME the test is broken //@Test public void testOneToOneSSRCGroupsConference() throws Exception { EntityBareJid roomName = JidCreate.entityBareFrom( "testSSRCs@conference.pawel.jitsi.net"); String serverName = "test-server"; TestConference testConf = TestConference.allocate(osgi.bc, serverName, roomName); MockProtocolProvider pps = testConf.getFocusProtocolProvider(); MockMultiUserChatOpSet mucOpSet = pps.getMockChatOpSet(); MockMultiUserChat chat = (MockMultiUserChat) mucOpSet.findRoom(roomName.toString()); // Join with all users final MockParticipant user1 = new MockParticipant("User1"); user1.setUseSsrcGroups(true); MockParticipant user2 = new MockParticipant("User2"); user2.setUseSsrcGroups(true); user1.join(chat); user2.join(chat); // Accept invite with all users assertNotNull(user1.acceptInvite(4000)); assertNotNull(user2.acceptInvite(4000)); user1.waitForAddSource(2000); assertEquals(1, user1.getRemoteSSRCs("audio").size()); assertEquals(2, user1.getRemoteSSRCs("video").size()); // groups assertEquals(0, user1.getRemoteSSRCGroups("audio").size()); assertEquals(1, user1.getRemoteSSRCGroups("video").size()); // Check if layers are up-to-date on the bridge verifySimulcastLayersOnTheBridge(testConf, user1); verifySimulcastLayersOnTheBridge(testConf, user2); logger.info("Switching to desktop stream"); // Test video stream switch(for desktop sharing) long [] desktopSSRC = new long[1]; desktopSSRC[0] = MockParticipant.nextSSRC(); user2.switchVideoSSRCs(desktopSSRC, false); // Wait for update user1.waitForAddSource(1000); user1.waitForRemoveSource(1000); // Check one SSRC is received and no groups assertEquals(1, user1.getRemoteSSRCs("video").size()); assertEquals(0, user1.getRemoteSSRCGroups("video").size()); // Verify on the bridge verifyNOSimulcastLayersOnTheBridge(testConf, user2); logger.info("Switching back to camera stream"); // Restore video stream long[] videoSSRCs = new long[2]; videoSSRCs[0] = MockParticipant.nextSSRC(); videoSSRCs[1] = MockParticipant.nextSSRC(); user2.switchVideoSSRCs(videoSSRCs, true); // Wait for update user1.waitForAddSource(1000); user1.waitForRemoveSource(1000); // Check 2 SSRCs are received and 1 group assertEquals(2, user1.getRemoteSSRCs("video").size()); assertEquals(1, user1.getRemoteSSRCGroups("video").size()); // Verify on the bridge verifySimulcastLayersOnTheBridge(testConf, user2); // User2 - quit user2.leave(); assertNotNull(user1.waitForRemoveSource(1000)); assertEquals(0, user1.getRemoteSSRCs("audio").size()); // No groups assertEquals(0, user1.getRemoteSSRCGroups("audio").size()); // This one has no groups MockParticipant user3 = new MockParticipant("User3"); user3.join(chat); assertNotNull(user3.acceptInvite(4000)); user1.waitForAddSource(2000); assertEquals(1, user1.getRemoteSSRCs("audio").size()); assertEquals(1, user1.getRemoteSSRCs("video").size()); // No groups assertEquals(0, user1.getRemoteSSRCGroups("audio").size()); assertEquals(0, user1.getRemoteSSRCGroups("video").size()); user3.leave(); user1.leave(); testConf.stop(); } /** * Verifies if number of simulcast layers on the bridge matches the SSRCs * count in local video group. Also checks if primary SSRCs of particular * layers do match local video SSRCs. * @param testConference instance of TestConference that will be * used for obtaining videobridge backend. * @param peer the MockParticipant for which simulcast layers will * be verified. */ private void verifySimulcastLayersOnTheBridge(TestConference testConference, MockParticipant peer) { long[] simulcastLayersSSRCs = testConference.getSimulcastLayersSSRCs(peer.getMyJid()); List videoSSRCs = peer.getVideoSSRCS(); assertEquals(videoSSRCs.size(), simulcastLayersSSRCs.length); for (int i=0; ipeer. * @param testConference instance of TestConference that will be * used for obtaining videobridge backend. * @param peer the MockParticipant for which simulcast layers will * be verified. */ private void verifyNOSimulcastLayersOnTheBridge( TestConference testConference, MockParticipant peer) { long[] simulcastLayersSSRCs = testConference.getSimulcastLayersSSRCs(peer.getMyJid()); assertEquals(1, simulcastLayersSSRCs.length); } }