1 /*
2  * Jicofo, the Jitsi Conference Focus.
3  *
4  * Copyright @ 2015 Atlassian Pty Ltd
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.jitsi.jicofo;
19 
20 import mock.*;
21 import mock.muc.*;
22 import mock.util.*;
23 
24 import org.jitsi.xmpp.extensions.colibri.*;
25 import org.jitsi.xmpp.extensions.jitsimeet.*;
26 
27 import net.java.sip.communicator.util.*;
28 
29 import org.jitsi.protocol.xmpp.util.*;
30 import org.junit.*;
31 import org.junit.runner.*;
32 import org.junit.runners.*;
33 import org.jxmpp.jid.*;
34 import org.jxmpp.jid.impl.*;
35 
36 import java.util.*;
37 
38 import static org.junit.Assert.*;
39 
40 /**
41  *
42  */
43 @RunWith(JUnit4.class)
44 public class AdvertiseSSRCsTest
45 {
46     private static final Logger logger
47         = Logger.getLogger(AdvertiseSSRCsTest.class);
48 
49     private OSGiHandler osgi = OSGiHandler.getInstance();
50 
51     @Before
setUpClass()52     public void setUpClass()
53         throws Exception
54     {
55         osgi.init();
56     }
57 
58     @After
tearDownClass()59     public void tearDownClass()
60         throws Exception
61     {
62         osgi.shutdown();
63     }
64 
65     @Test
testOneToOneConference()66     public void testOneToOneConference()
67         throws Exception
68     {
69         //FIXME: test when there is participant without contents
70 
71         EntityBareJid roomName = JidCreate.entityBareFrom(
72                 "testSSRCs@conference.pawel.jitsi.net");
73         String serverName = "test-server";
74 
75         TestConference testConf
76             = TestConference.allocate(osgi.bc, serverName, roomName);
77 
78         MockProtocolProvider pps
79             = testConf.getFocusProtocolProvider();
80 
81         MockMultiUserChatOpSet mucOpSet = pps.getMockChatOpSet();
82 
83         MockMultiUserChat chat
84             = (MockMultiUserChat) mucOpSet.findRoom(roomName.toString());
85 
86         // Join with all users
87         MockParticipant user1 = new MockParticipant("User1");
88         user1.setSsrcVideoType(SSRCInfoPacketExtension.CAMERA_VIDEO_TYPE);
89         user1.join(chat);
90 
91         MockParticipant user2 = new MockParticipant("User2");
92         user2.setSsrcVideoType(SSRCInfoPacketExtension.SCREEN_VIDEO_TYPE);
93         user2.join(chat);
94 
95         // Accept invite with all users
96         assertNotNull(user1.acceptInvite(4000));
97         assertNotNull(user2.acceptInvite(4000));
98 
99         user1.waitForAddSource(2000);
100         user2.waitForAddSource(2000);
101 
102         assertEquals(2, user1.getRemoteSSRCs("audio").size());
103         // No groups
104         assertEquals(0, user1.getRemoteSSRCGroups("audio").size());
105 
106         // Verify SSRC owners and video types
107         // From user 1 perspective
108         assertEquals(
109             user2.getMyJid(),
110             SSRCSignaling.getSSRCOwner(user1.getRemoteSSRCs("audio").get(1)));
111         assertEquals(
112             user2.getMyJid(),
113             SSRCSignaling.getSSRCOwner(user1.getRemoteSSRCs("video").get(1)));
114         assertEquals(
115             user2.getSsrcVideoType(),
116             SSRCSignaling.getVideoType(user1.getRemoteSSRCs("video").get(1)));
117         // From user 2 perspective
118         assertEquals(
119             user1.getMyJid(),
120             SSRCSignaling.getSSRCOwner(user2.getRemoteSSRCs("audio").get(1)));
121         assertEquals(
122             user1.getMyJid(),
123             SSRCSignaling.getSSRCOwner(user2.getRemoteSSRCs("video").get(1)));
124         assertEquals(
125             user1.getSsrcVideoType(),
126             SSRCSignaling.getVideoType(user2.getRemoteSSRCs("video").get(1)));
127 
128         user2.leave();
129 
130         assertNotNull(user1.waitForRemoveSource(500));
131 
132         assertEquals(1, user1.getRemoteSSRCs("audio").size());
133         // No groups
134         assertEquals(0, user1.getRemoteSSRCGroups("audio").size());
135 
136         MockParticipant user3 = new MockParticipant("User3");
137         user3.join(chat);
138         assertNotNull(user3.acceptInvite(4000));
139 
140         user1.waitForAddSource(2000);
141 
142         assertEquals(2, user1.getRemoteSSRCs("audio").size());
143         assertEquals(2, user3.getRemoteSSRCs("audio").size());
144         // No groups
145         assertEquals(0, user1.getRemoteSSRCGroups("audio").size());
146         assertEquals(0, user3.getRemoteSSRCGroups("audio").size());
147 
148         user3.leave();
149         user1.leave();
150 
151         testConf.stop();
152     }
153 
154     @Test
testSourceRemoval()155     public void testSourceRemoval()
156             throws Exception
157     {
158         EntityBareJid roomName = JidCreate.entityBareFrom(
159                 "testSSRCremoval@conference.pawel.jitsi.net");
160         String serverName = "test-server";
161 
162         TestConference testConf
163                 = TestConference.allocate(osgi.bc, serverName, roomName);
164 
165         MockProtocolProvider pps
166                 = testConf.getFocusProtocolProvider();
167 
168         MockMultiUserChatOpSet mucOpSet = pps.getMockChatOpSet();
169 
170         MockMultiUserChat chat
171                 = (MockMultiUserChat) mucOpSet.findRoom(roomName.toString());
172 
173         // Join with all users
174         MockParticipant user1 = new MockParticipant("User1");
175         user1.setSsrcVideoType(SSRCInfoPacketExtension.CAMERA_VIDEO_TYPE);
176         user1.join(chat);
177 
178         MockParticipant user2 = new MockParticipant("User2");
179         user2.setSsrcVideoType(SSRCInfoPacketExtension.SCREEN_VIDEO_TYPE);
180         user2.join(chat);
181 
182         // Accept invite with all users
183         assertNotNull(user1.acceptInvite(4000));
184         assertNotNull(user2.acceptInvite(4000));
185 
186         user1.waitForAddSource(2000);
187         user2.waitForAddSource(2000);
188 
189         user2.audioSourceRemove(1);
190 
191         assertNotNull(user1.waitForRemoveSource(500));
192 
193         assertEquals(1, user1.getRemoteSSRCs("audio").size());
194         assertEquals(0, user1.getRemoteSSRCGroups("audio").size());
195 
196         MockParticipant user3 = new MockParticipant("User3");
197         user3.join(chat);
198         assertNotNull(user3.acceptInvite(4000));
199 
200         user1.waitForAddSource(2000);
201 
202         assertEquals(2, user1.getRemoteSSRCs("audio").size());
203         assertEquals(2, user3.getRemoteSSRCs("audio").size());
204         // No groups
205         assertEquals(0, user1.getRemoteSSRCGroups("audio").size());
206         assertEquals(0, user3.getRemoteSSRCGroups("audio").size());
207 
208         user3.leave();
209         user2.leave();
210         user1.leave();
211 
212         testConf.stop();
213     }
214 
215     @Test
testDuplicatedSSRCs()216     public void testDuplicatedSSRCs()
217         throws Exception
218     {
219         EntityBareJid roomName = JidCreate.entityBareFrom(
220                 "testSSRCs@conference.pawel.jitsi.net");
221         String serverName = "test-server";
222 
223         TestConference testConf
224             = TestConference.allocate(osgi.bc, serverName, roomName);
225 
226         MockProtocolProvider pps
227             = testConf.getFocusProtocolProvider();
228 
229         MockMultiUserChatOpSet mucOpSet = pps.getMockChatOpSet();
230 
231         MockMultiUserChat chat
232             = (MockMultiUserChat) mucOpSet.findRoom(roomName.toString());
233 
234         // Join with all users
235         MockParticipant user1 = new MockParticipant("User1");
236         user1.join(chat);
237 
238         MockParticipant user2 = new MockParticipant("User2");
239         user2.join(chat);
240 
241         // Accept invite with all users
242         long u1VideoSSRC = MockParticipant.nextSSRC();
243         user1.addLocalVideoSSRC(u1VideoSSRC, null);
244 
245         long u1VideoSSRC2 = MockParticipant.nextSSRC();
246         user1.addLocalVideoSSRC(u1VideoSSRC2, null);
247 
248         assertNotNull(user1.acceptInvite(4000));
249 
250         assertNotNull(user2.acceptInvite(4000));
251 
252         assertNotNull(user1.waitForAddSource(1000));
253         assertNotNull(user2.waitForAddSource(1000));
254 
255         // There is 1 + 2 extra we've created here in the test
256         assertEquals(1 /* jvb */ + 3, user2.getRemoteSSRCs("video").size());
257         // No groups
258         assertEquals(0, user2.getRemoteSSRCGroups("video").size());
259 
260         user1.videoSourceAdd(new long[]{ u1VideoSSRC }, false);
261 
262         user1.videoSourceAdd(
263             new long[]{
264                 u1VideoSSRC, u1VideoSSRC2, u1VideoSSRC,
265                 u1VideoSSRC, u1VideoSSRC, u1VideoSSRC2
266             }, false);
267 
268         user1.videoSourceAdd(new long[]{ u1VideoSSRC2, u1VideoSSRC }, false);
269 
270         // There should be no source-add notifications sent
271         assertEquals(null, user2.waitForAddSource(500));
272 
273         assertEquals(1 + /* jvb */ + 1, user2.getRemoteSSRCs("audio").size());
274         // There is 1 + 2 extra we've created here in the test
275         assertEquals(1 + /* jvb */ + 3, user2.getRemoteSSRCs("video").size());
276 
277         user2.leave();
278         user1.leave();
279 
280         testConf.stop();
281     }
282 
283     @Test
testSSRCLimit()284     public void testSSRCLimit()
285         throws Exception
286     {
287         EntityBareJid roomName = JidCreate.entityBareFrom(
288                 "testSSRCs@conference.pawel.jitsi.net");
289         String serverName = "test-server";
290 
291         TestConference testConf
292             = TestConference.allocate(osgi.bc, serverName, roomName);
293 
294         JitsiMeetGlobalConfig globalConfig
295             = ServiceUtils.getService(osgi.bc, JitsiMeetGlobalConfig.class);
296 
297         assertNotNull(globalConfig);
298 
299         MockProtocolProvider pps
300             = testConf.getFocusProtocolProvider();
301 
302         MockMultiUserChatOpSet mucOpSet = pps.getMockChatOpSet();
303 
304         MockMultiUserChat chat
305             = (MockMultiUserChat) mucOpSet.findRoom(roomName.toString());
306 
307         // Join with all users
308         MockParticipant user1 = new MockParticipant("User1");
309         user1.join(chat);
310         user1.waitForJoinThread(5000);
311 
312         MockParticipant user2 = new MockParticipant("User2");
313         user2.join(chat);
314         user2.waitForJoinThread(5000);
315 
316         int maxSSRCs = globalConfig.getMaxSourcesPerUser();
317 
318         // Accept invite with all users
319         // Add many SSRCs to both users
320 
321         // Video:
322         // User 1 will fit into the limit on accept, but we'll try to exceed
323         // it later
324         int user1ExtraVideoSSRCCount = maxSSRCs / 2;
325         // User 2 will exceed SSRC limit on accept already
326         int user2ExtraVideoSSRCCount = maxSSRCs + 3;
327         user1.addMultipleVideoSSRCs(user1ExtraVideoSSRCCount);
328         user2.addMultipleVideoSSRCs(user2ExtraVideoSSRCCount);
329 
330         // Audio: the opposite scenario
331         int user1ExtraAudioSSRCCount = maxSSRCs + 5;
332         int user2ExtraAudioSSRCCount = maxSSRCs / 2;
333         user1.addMultipleAudioSSRCs(user1ExtraAudioSSRCCount);
334         user2.addMultipleAudioSSRCs(user2ExtraAudioSSRCCount);
335 
336         assertNotNull(user1.acceptInvite(10000));
337         assertNotNull(user2.acceptInvite(10000));
338 
339         assertNotNull(user1.waitForAddSource(4000));
340         assertNotNull(user2.waitForAddSource(4000));
341 
342         int expectedMax
343             = 1 /* jvb's mixed */
344                 + maxSSRCs /* max that can come from 1 participant */;
345 
346         // Verify User1's SSRCs seen by User2
347         assertEquals(1 /* jvb's mixed */ + 1 + user1ExtraVideoSSRCCount,
348                      user2.getRemoteSSRCs("video").size());
349         assertEquals(expectedMax,
350                      user2.getRemoteSSRCs("audio").size());
351         // Verify User1's SSRCs seen by User1
352         assertEquals(expectedMax,
353             user1.getRemoteSSRCs("video").size());
354         assertEquals(1 /* jvb's mixed */ + 1 + user2ExtraAudioSSRCCount,
355             user1.getRemoteSSRCs("audio").size());
356 
357         // No groups
358         assertEquals(0, user2.getRemoteSSRCGroups("video").size());
359         assertEquals(0, user1.getRemoteSSRCGroups("video").size());
360         assertEquals(0, user2.getRemoteSSRCGroups("audio").size());
361         assertEquals(0, user1.getRemoteSSRCGroups("audio").size());
362 
363         // Now let's test the limits for source-add
364         // User1 will have video SSRCs filled and audio are filled already
365         user1.videoSourceAdd(maxSSRCs / 2);
366         assertNotNull(user2.waitForAddSource(300));
367         assertEquals(expectedMax, user2.getRemoteSSRCs("video").size());
368 
369         user1.audioSourceAdd(5);
370         assertTrue(null == user2.waitForAddSource(300));
371         assertEquals(expectedMax, user2.getRemoteSSRCs("audio").size());
372 
373         // User2 has video SSRCs filled already and audio will be filled
374         user2.videoSourceAdd(maxSSRCs / 2);
375         assertNull(user1.waitForAddSource(300));
376         assertEquals(expectedMax, user1.getRemoteSSRCs("video").size());
377 
378         user2.audioSourceAdd(maxSSRCs / 2);
379         assertNotNull(user1.waitForAddSource(300));
380         assertEquals(expectedMax, user1.getRemoteSSRCs("audio").size());
381 
382         user2.leave();
383         user1.leave();
384 
385         // stopping the conference also stops the bridge,
386         // but the users leaving still want the bridge to disconnect properly
387         Thread.sleep(5000);
388 
389         testConf.stop();
390     }
391 
392     // FIXME the test is broken
393     //@Test
testOneToOneSSRCGroupsConference()394     public void testOneToOneSSRCGroupsConference()
395         throws Exception
396     {
397         EntityBareJid roomName = JidCreate.entityBareFrom(
398                 "testSSRCs@conference.pawel.jitsi.net");
399         String serverName = "test-server";
400 
401         TestConference testConf
402             = TestConference.allocate(osgi.bc, serverName, roomName);
403 
404         MockProtocolProvider pps
405             = testConf.getFocusProtocolProvider();
406 
407         MockMultiUserChatOpSet mucOpSet = pps.getMockChatOpSet();
408 
409         MockMultiUserChat chat
410             = (MockMultiUserChat) mucOpSet.findRoom(roomName.toString());
411 
412         // Join with all users
413         final MockParticipant user1 = new MockParticipant("User1");
414         user1.setUseSsrcGroups(true);
415 
416         MockParticipant user2 = new MockParticipant("User2");
417         user2.setUseSsrcGroups(true);
418 
419         user1.join(chat);
420         user2.join(chat);
421 
422         // Accept invite with all users
423         assertNotNull(user1.acceptInvite(4000));
424         assertNotNull(user2.acceptInvite(4000));
425 
426         user1.waitForAddSource(2000);
427 
428         assertEquals(1, user1.getRemoteSSRCs("audio").size());
429         assertEquals(2, user1.getRemoteSSRCs("video").size());
430         // groups
431         assertEquals(0, user1.getRemoteSSRCGroups("audio").size());
432         assertEquals(1, user1.getRemoteSSRCGroups("video").size());
433 
434         // Check if layers are up-to-date on the bridge
435         verifySimulcastLayersOnTheBridge(testConf, user1);
436         verifySimulcastLayersOnTheBridge(testConf, user2);
437 
438         logger.info("Switching to desktop stream");
439 
440         // Test video stream switch(for desktop sharing)
441         long [] desktopSSRC = new long[1];
442         desktopSSRC[0] = MockParticipant.nextSSRC();
443         user2.switchVideoSSRCs(desktopSSRC, false);
444         // Wait for update
445         user1.waitForAddSource(1000);
446         user1.waitForRemoveSource(1000);
447         // Check one SSRC is received and no groups
448         assertEquals(1, user1.getRemoteSSRCs("video").size());
449         assertEquals(0, user1.getRemoteSSRCGroups("video").size());
450         // Verify on the bridge
451         verifyNOSimulcastLayersOnTheBridge(testConf, user2);
452 
453         logger.info("Switching back to camera stream");
454 
455         // Restore video stream
456         long[] videoSSRCs = new long[2];
457         videoSSRCs[0] = MockParticipant.nextSSRC();
458         videoSSRCs[1] = MockParticipant.nextSSRC();
459         user2.switchVideoSSRCs(videoSSRCs, true);
460         // Wait for update
461         user1.waitForAddSource(1000);
462         user1.waitForRemoveSource(1000);
463         // Check 2 SSRCs are received and 1 group
464         assertEquals(2, user1.getRemoteSSRCs("video").size());
465         assertEquals(1, user1.getRemoteSSRCGroups("video").size());
466         // Verify on the bridge
467         verifySimulcastLayersOnTheBridge(testConf, user2);
468 
469         // User2 - quit
470         user2.leave();
471 
472         assertNotNull(user1.waitForRemoveSource(1000));
473 
474         assertEquals(0, user1.getRemoteSSRCs("audio").size());
475         // No groups
476         assertEquals(0, user1.getRemoteSSRCGroups("audio").size());
477 
478         // This one has no groups
479         MockParticipant user3 = new MockParticipant("User3");
480         user3.join(chat);
481         assertNotNull(user3.acceptInvite(4000));
482 
483         user1.waitForAddSource(2000);
484 
485         assertEquals(1, user1.getRemoteSSRCs("audio").size());
486         assertEquals(1, user1.getRemoteSSRCs("video").size());
487         // No groups
488         assertEquals(0, user1.getRemoteSSRCGroups("audio").size());
489         assertEquals(0, user1.getRemoteSSRCGroups("video").size());
490 
491         user3.leave();
492         user1.leave();
493 
494         testConf.stop();
495     }
496 
497     /**
498      * Verifies if number of simulcast layers on the bridge matches the SSRCs
499      * count in local video group. Also checks if primary SSRCs of particular
500      * layers do match local video SSRCs.
501      * @param testConference instance of <tt>TestConference</tt> that will be
502      *                       used for obtaining videobridge backend.
503      * @param peer the <tt>MockParticipant</tt> for which simulcast layers will
504      *             be verified.
505      */
verifySimulcastLayersOnTheBridge(TestConference testConference, MockParticipant peer)506     private void verifySimulcastLayersOnTheBridge(TestConference testConference,
507                                                   MockParticipant peer)
508     {
509         long[] simulcastLayersSSRCs
510             = testConference.getSimulcastLayersSSRCs(peer.getMyJid());
511         List<SourcePacketExtension> videoSSRCs = peer.getVideoSSRCS();
512 
513         assertEquals(videoSSRCs.size(), simulcastLayersSSRCs.length);
514 
515         for (int i=0; i<videoSSRCs.size(); i++)
516         {
517             assertEquals(
518                 "idx: " + i,
519                 videoSSRCs.get(i).getSSRC(),
520                 simulcastLayersSSRCs[i]);
521         }
522     }
523 
524     /**
525      * Verifies if the are 0 simulcast layers on the bridge for given
526      * <tt>peer</tt>.
527      * @param testConference instance of <tt>TestConference</tt> that will be
528      *                       used for obtaining videobridge backend.
529      * @param peer the <tt>MockParticipant</tt> for which simulcast layers will
530      *             be verified.
531      */
verifyNOSimulcastLayersOnTheBridge( TestConference testConference, MockParticipant peer)532     private void verifyNOSimulcastLayersOnTheBridge(
533             TestConference testConference, MockParticipant peer)
534     {
535         long[] simulcastLayersSSRCs
536             = testConference.getSimulcastLayersSSRCs(peer.getMyJid());
537 
538         assertEquals(1, simulcastLayersSSRCs.length);
539     }
540 }
541