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 mock;
19 
20 import mock.muc.*;
21 import mock.util.*;
22 import mock.xmpp.*;
23 
24 import org.jitsi.xmpp.extensions.colibri.*;
25 import org.jitsi.xmpp.extensions.jingle.*;
26 import org.jitsi.xmpp.extensions.jitsimeet.*;
27 import net.java.sip.communicator.util.*;
28 
29 import org.jitsi.impl.protocol.xmpp.*;
30 import org.jitsi.protocol.xmpp.*;
31 import org.jitsi.protocol.xmpp.util.*;
32 import org.jivesoftware.smack.packet.*;
33 import org.jivesoftware.smack.packet.id.*;
34 import org.jxmpp.jid.*;
35 import org.jxmpp.jid.impl.*;
36 import org.jxmpp.stringprep.*;
37 
38 import java.util.*;
39 import java.util.concurrent.*;
40 
41 /**
42  *
43  */
44 public class MockParticipant
45 {
46     /**
47      * The logger.
48      */
49     private final static Logger logger
50         = Logger.getLogger(MockParticipant.class);
51 
52     private final static Random random = new Random(System.nanoTime());
53 
54     private final String nick;
55 
56     private final boolean useBundle;
57 
58     private XmppPeer xmppPeer;
59 
60     private MockRoomMember user;
61 
62     private XmppConnection mockConnection;
63 
64     private UtilityJingleOpSet jingle;
65 
66     private ArrayList<ContentPacketExtension> myContents;
67 
68     private final Object sourceLock = new Object();
69 
70     private final Object joinLock = new Object();
71 
72     private MediaSourceMap remoteSSRCs = new MediaSourceMap();
73 
74     private MediaSourceGroupMap remoteSSRCgroups = new MediaSourceGroupMap();
75 
76     private HashMap<String, IceUdpTransportPacketExtension> transportMap;
77 
78     private JingleSession jingleSession;
79 
80     private JingleHandler jingleHandler = new JingleHandler();
81 
82     private Jid myJid;
83 
84     private Jid remoteJid;
85 
86     private MediaSourceMap localSSRCs = new MediaSourceMap();
87 
88     private MediaSourceGroupMap localSSRCGroups = new MediaSourceGroupMap();
89 
90     private String ssrcVideoType = SSRCInfoPacketExtension.CAMERA_VIDEO_TYPE;
91 
92     private boolean useSsrcGroups;
93 
94     private BlockingQueue<JingleIQ> ssrcAddQueue
95         = new LinkedBlockingQueue<>();
96 
97     private BlockingQueue<JingleIQ> ssrcRemoveQueue
98         = new LinkedBlockingQueue<>();
99 
MockParticipant(String nick)100     public MockParticipant(String nick)
101     {
102         this(nick, true);
103     }
104 
MockParticipant(String nick, boolean useBundle)105     public MockParticipant(String nick, boolean useBundle)
106     {
107         this.nick = nick;
108         this.useBundle = useBundle;
109     }
110 
getChatMember()111     public MockRoomMember getChatMember()
112     {
113         return user;
114     }
115 
joinInNewThread(final MockMultiUserChat chat)116     public void joinInNewThread(final MockMultiUserChat chat)
117     {
118         new Thread(new Runnable()
119         {
120             @Override
121             public void run()
122             {
123                 join(chat);
124             }
125         }).start();
126     }
127 
waitForJoinThread(long timeout)128     public void waitForJoinThread(long timeout)
129             throws InterruptedException
130     {
131         synchronized (joinLock)
132         {
133             if (xmppPeer == null)
134             {
135                 joinLock.wait(timeout);
136             }
137             if (xmppPeer == null)
138             {
139                 throw new RuntimeException(
140                     "Failed to join the room within" +
141                             " the time limit specified: " + timeout);
142             }
143         }
144     }
145 
join(MockMultiUserChat chat)146     public void join(MockMultiUserChat chat)
147     {
148         try
149         {
150             user = chat.createMockRoomMember(nick);
151         }
152         catch (XmppStringprepException e)
153         {
154             throw new RuntimeException(e);
155         }
156 
157         user.setupFeatures(useBundle);
158 
159 
160         try
161         {
162             myJid = JidCreate.entityFullFrom(chat.getName() + "/" + user.getName());
163         }
164         catch (XmppStringprepException e)
165         {
166             throw new RuntimeException(e);
167         }
168 
169         mockConnection = new MockXmppConnection(myJid);
170         jingle = new UtilityJingleOpSet(mockConnection);
171         jingle.mockParticipant = this;
172         mockConnection.registerIQRequestHandler(jingle);
173 
174         chat.mockJoin(user);
175         synchronized (joinLock)
176         {
177             xmppPeer = new XmppPeer(user.getJid(), mockConnection);
178             xmppPeer.start();
179             joinLock.notifyAll();
180         }
181     }
182 
nextSSRC()183     public static long nextSSRC()
184     {
185         return random.nextInt(Integer.MAX_VALUE);
186     }
187 
newSSRC(long ssrc, String ssrcVideoType)188     private SourcePacketExtension newSSRC(long ssrc, String ssrcVideoType)
189     {
190         SourcePacketExtension ssrcPe = new SourcePacketExtension();
191 
192         ssrcPe.setSSRC(ssrc);
193 
194         SSRCSignaling.setSSRCOwner(ssrcPe, myJid);
195 
196         if (ssrcVideoType != null)
197             SSRCSignaling.setSSRCVideoType(ssrcPe, ssrcVideoType);
198 
199         return ssrcPe;
200     }
201 
initContents()202     private void initContents()
203     {
204         myContents = new ArrayList<>();
205 
206         // AUDIO
207         ContentPacketExtension audio = new ContentPacketExtension();
208         audio.setName("audio");
209 
210         RtpDescriptionPacketExtension audioRtpDesc
211             = new RtpDescriptionPacketExtension();
212         audioRtpDesc.setMedia("audio");
213         audio.addChildExtension(audioRtpDesc);
214 
215         addLocalAudioSSRC(nextSSRC());
216 
217         for (SourcePacketExtension ssrc : localSSRCs.getSourcesForMedia("audio"))
218         {
219             audioRtpDesc.addChildExtension(ssrc);
220         }
221 
222         myContents.add(audio);
223 
224         // VIDEO
225         ContentPacketExtension video = new ContentPacketExtension();
226         video.setName("video");
227 
228         RtpDescriptionPacketExtension videoRtpDesc
229             = new RtpDescriptionPacketExtension();
230         videoRtpDesc.setMedia("video");
231         video.addChildExtension(videoRtpDesc);
232 
233         // Add video SSRC
234         addLocalVideoSSRC(nextSSRC(), ssrcVideoType);
235         if (useSsrcGroups)
236         {
237             // 2nd video SSRC
238             addLocalVideoSSRC(nextSSRC(), ssrcVideoType);
239         }
240 
241         for (SourcePacketExtension videoSSRC : getVideoSSRCS())
242         {
243             videoRtpDesc.addChildExtension(videoSSRC);
244         }
245 
246         if (useSsrcGroups)
247         {
248             // Video SSRC group
249             SourceGroup videoGroup = getLocalSSRCGroup("video");
250             videoGroup.addSources(getVideoSSRCS());
251 
252             videoRtpDesc.addChildExtension(videoGroup.getExtensionCopy());
253         }
254 
255         myContents.add(video);
256     }
257 
acceptInviteInBg()258     public void acceptInviteInBg()
259     {
260         new Thread(new Runnable()
261         {
262             @Override
263             public void run()
264             {
265                 try
266                 {
267                     acceptInvite(5000);
268                 }
269                 catch (InterruptedException e)
270                 {
271                     logger.error(e, e);
272                     Thread.currentThread().interrupt();
273                 }
274             }
275         },"Accept invite " + nick).start();
276     }
277 
acceptInvite(long timeout)278     public JingleIQ[] acceptInvite(long timeout)
279         throws InterruptedException
280     {
281         JingleIQ invite = jingle.acceptSession(timeout, jingleHandler);
282         if (invite == null)
283         {
284             throw new RuntimeException(nick + " - wait for invite timeout");
285         }
286 
287         logger.info(nick + " invite: " + invite.toXML());
288 
289         // ACK invite
290         IQ inviteAck = JingleIQ.createResultIQ(invite);
291         mockConnection.sendStanza(inviteAck);
292 
293         initContents();
294 
295         processStanza(invite);
296 
297         JingleIQ user1Accept = generateSessionAccept(
298             invite,
299             createTransportMap(invite));
300 
301         logger.info(nick + " accept: " + user1Accept.toXML());
302 
303         mockConnection.sendStanza(user1Accept);
304 
305         this.myJid = user1Accept.getFrom();
306         this.remoteJid = user1Accept.getTo();
307         this.jingleSession = jingle.getSession(invite.getSID());
308 
309         return new JingleIQ[] { invite, user1Accept };
310     }
311 
leave()312     public void leave()
313     {
314         xmppPeer.stop();
315 
316         user.leave();
317     }
318 
createTransportMap( JingleIQ user1Invite)319     private Map<String, IceUdpTransportPacketExtension> createTransportMap(
320             JingleIQ user1Invite)
321     {
322         this.transportMap = new HashMap<>();
323 
324         for (ContentPacketExtension content : user1Invite.getContentList())
325         {
326             IceUdpTransportPacketExtension transport
327                 = new IceUdpTransportPacketExtension();
328 
329             if (useBundle)
330             {
331                 // Bundle uses RTCP mux
332                 transport.addChildExtension(new RtcpmuxPacketExtension());
333             }
334 
335             DtlsFingerprintPacketExtension dtlsFingerprint
336                 = new DtlsFingerprintPacketExtension();
337 
338             IceUdpTransportPacketExtension offerTransport
339                 = content.getFirstChildOfType(
340                         IceUdpTransportPacketExtension.class);
341 
342             DtlsFingerprintPacketExtension offerFingerprint
343                 = offerTransport.getFirstChildOfType(
344                         DtlsFingerprintPacketExtension.class);
345 
346             dtlsFingerprint.setHash(offerFingerprint.getHash());
347             dtlsFingerprint.setFingerprint(
348                 offerFingerprint.getFingerprint()
349                     .replace("A", "B")
350                     .replace("1", "2")
351                     .replace("C", "D")
352             );
353 
354             transport.addChildExtension(dtlsFingerprint);
355 
356             transportMap.put(content.getName(), transport);
357         }
358 
359         return transportMap;
360     }
361 
generateFakeCandidates()362     public Map<String, IceUdpTransportPacketExtension> generateFakeCandidates()
363     {
364         for (ContentPacketExtension content : myContents)
365         {
366             IceUdpTransportPacketExtension iceTransport
367                 = transportMap.get(content.getName());
368 
369             CandidatePacketExtension fakeCandidate
370                 = new CandidatePacketExtension();
371 
372             fakeCandidate.setIP("127.0.0.1");
373             fakeCandidate.setPort(60000);
374             fakeCandidate.setType(CandidateType.host);
375 
376             iceTransport.addCandidate(fakeCandidate);
377         }
378         return transportMap;
379     }
380 
sendTransportInfo()381     public JingleIQ sendTransportInfo()
382     {
383         List<ContentPacketExtension> contents = new ArrayList<>();
384 
385         for (ContentPacketExtension myContent : myContents)
386         {
387             ContentPacketExtension content = new ContentPacketExtension();
388             content.setName(myContent.getName());
389 
390             try
391             {
392                 IceUdpTransportPacketExtension transportCopy
393                     = IceUdpTransportPacketExtension
394                             .cloneTransportAndCandidates(
395                                 transportMap.get(content.getName()), true);
396 
397                 content.addChildExtension(transportCopy);
398 
399                 contents.add(content);
400             }
401             catch (Exception e)
402             {
403                 throw new RuntimeException(e);
404             }
405         }
406 
407         JingleIQ transportInfoIq
408             = JinglePacketFactory.createTransportInfo(
409                 myJid, remoteJid, jingleSession.getSessionID(), contents);
410 
411         mockConnection.sendStanza(transportInfoIq);
412 
413         return transportInfoIq;
414     }
415 
generateSessionAccept( JingleIQ sessionInit, Map<String, IceUdpTransportPacketExtension> transportMap)416     private JingleIQ generateSessionAccept(
417             JingleIQ sessionInit,
418             Map<String, IceUdpTransportPacketExtension> transportMap)
419     {
420         JingleIQ accept = new JingleIQ(
421                 JingleAction.SESSION_ACCEPT,
422                 sessionInit.getSID());
423 
424         accept.setStanzaId(StanzaIdUtil.newStanzaId());
425         accept.setType(IQ.Type.set);
426         accept.setFrom(sessionInit.getTo());
427         accept.setTo(sessionInit.getFrom());
428 
429         // Jingle BUNDLE extension
430         accept.addExtension(
431             GroupPacketExtension.createBundleGroup(
432                 sessionInit.getContentList()));
433 
434         for (ContentPacketExtension contentOffer : myContents)
435         {
436             ContentPacketExtension acceptContent
437                 = new ContentPacketExtension();
438 
439             acceptContent.setName(contentOffer.getName());
440 
441             acceptContent.setCreator(
442                 ContentPacketExtension.CreatorEnum.responder);
443 
444             acceptContent.setSenders(contentOffer.getSenders());
445 
446             acceptContent.addChildExtension(
447                 transportMap.get(contentOffer.getName()));
448 
449             // Copy RTPDescription
450             acceptContent.addChildExtension(
451                 contentOffer.getFirstChildOfType(
452                     RtpDescriptionPacketExtension.class));
453 
454             accept.addContent(acceptContent);
455         }
456 
457         return accept;
458     }
459 
getNickname()460     public String getNickname()
461     {
462         return nick;
463     }
464 
processStanza(IQ packet)465     public void processStanza(IQ packet)
466     {
467         JingleIQ modifySSRcIq = (JingleIQ) packet;
468         JingleAction action = modifySSRcIq.getAction();
469 
470         if (JingleAction.SESSION_INITIATE.equals(action))
471         {
472             synchronized (sourceLock)
473             {
474                 MediaSourceMap ssrcMap
475                         = MediaSourceMap.getSourcesFromContent(
476                         modifySSRcIq.getContentList());
477 
478                 remoteSSRCs.add(ssrcMap);
479 
480                 MediaSourceGroupMap ssrcGroupMap
481                         = MediaSourceGroupMap.getSourceGroupsForContents(
482                         modifySSRcIq.getContentList());
483 
484                 remoteSSRCgroups.add(ssrcGroupMap);
485 
486                 logger.info(nick + " received session-initiate: " + ssrcMap  + " groups: " + ssrcGroupMap);
487 
488                 sourceLock.notifyAll();
489             }
490         }
491         else  if (JingleAction.SOURCEADD.equals(action)
492                     || JingleAction.ADDSOURCE.equals(action))
493         {
494             synchronized (sourceLock)
495             {
496                 MediaSourceMap ssrcMap
497                     = MediaSourceMap.getSourcesFromContent(
498                             modifySSRcIq.getContentList());
499 
500                 remoteSSRCs.add(ssrcMap);
501 
502                 MediaSourceGroupMap ssrcGroupMap
503                     = MediaSourceGroupMap.getSourceGroupsForContents(
504                             modifySSRcIq.getContentList());
505 
506                 remoteSSRCgroups.add(ssrcGroupMap);
507 
508                 logger.info(nick + " received source-add " + ssrcMap);
509 
510                 try
511                 {
512                     ssrcAddQueue.put(modifySSRcIq);
513                 }
514                 catch (InterruptedException e)
515                 {
516                     throw new RuntimeException(e);
517                 }
518 
519                 sourceLock.notifyAll();
520             }
521         }
522         else if (JingleAction.SOURCEREMOVE.equals(action)
523                 || JingleAction.REMOVESOURCE.equals(action))
524         {
525             synchronized (sourceLock)
526             {
527                 MediaSourceMap ssrcsToRemove
528                     = MediaSourceMap.getSourcesFromContent(
529                             modifySSRcIq.getContentList());
530 
531                 remoteSSRCs.remove(ssrcsToRemove);
532 
533                 MediaSourceGroupMap ssrcGroupsToRemove
534                     = MediaSourceGroupMap.getSourceGroupsForContents(
535                             modifySSRcIq.getContentList());
536 
537                 remoteSSRCgroups.remove(ssrcGroupsToRemove);
538 
539                 logger.info(nick + " source-remove received " + ssrcsToRemove);
540 
541                 try
542                 {
543                     ssrcRemoveQueue.put(modifySSRcIq);
544                 }
545                 catch (InterruptedException e)
546                 {
547                     throw new RuntimeException(e);
548                 }
549 
550                 sourceLock.notifyAll();
551             }
552         }
553     }
554 
switchVideoSSRCs(long[] newVideoSSRCs, boolean useSsrcGroups)555     public void switchVideoSSRCs(long[] newVideoSSRCs, boolean useSsrcGroups)
556     {
557         MediaSourceMap toRemove = new MediaSourceMap();
558         toRemove.addSources("video", localSSRCs.getSourcesForMedia("video"));
559 
560         // Send source-remove
561         jingle.sendRemoveSourceIQ(
562             toRemove, localSSRCGroups.copy(), jingleSession);
563 
564         // Remove old ssrcs(and clear groups)
565         localSSRCs.remove(toRemove);
566         localSSRCGroups = new MediaSourceGroupMap();
567 
568         videoSourceAdd(newVideoSSRCs, useSsrcGroups);
569     }
570 
getLocalSSRCGroup(String media)571     private SourceGroup getLocalSSRCGroup(String media)
572     {
573         List<SourceGroup> videoGroups
574             = localSSRCGroups.getSourceGroupsForMedia(media);
575         SourceGroup group = null;
576         if (videoGroups.size() > 0)
577         {
578             group = videoGroups.get(0);
579         }
580         if (videoGroups.size() == 0)
581         {
582             SourceGroupPacketExtension ssrcGroup
583                 = SourceGroupPacketExtension.createSimulcastGroup();
584 
585             group = new SourceGroup(ssrcGroup);
586 
587             localSSRCGroups.addSourceGroup(media, group);
588         }
589         return group;
590     }
591 
videoSourceAdd(int count)592     public List<SourcePacketExtension> videoSourceAdd(int count)
593     {
594         return sourceAdd("video", count, false, null);
595     }
596 
audioSourceAdd(int count)597     public List<SourcePacketExtension> audioSourceAdd(int count)
598     {
599         return sourceAdd("audio", count, false, null);
600     }
601 
audioSourceRemove(int count)602     public void audioSourceRemove(int count)
603     {
604         List<SourcePacketExtension> audioSources
605                 = this.localSSRCs.getSourcesForMedia("audio");
606 
607         if (audioSources.size() < count)
608         {
609             throw new IllegalArgumentException(
610                     "audio source size(" + audioSources.size()
611                             + ") < count(" + count + ")");
612         }
613 
614         List<SourcePacketExtension> toRemove = new ArrayList<>(count);
615 
616         for(int i = 0; i < count; i++)
617         {
618             toRemove.add(audioSources.remove(0));
619         }
620 
621         MediaSourceMap removeMap = new MediaSourceMap();
622 
623         removeMap.addSources("audio", toRemove);
624 
625         jingle.sendRemoveSourceIQ(removeMap, null, jingleSession);
626     }
627 
sourceAdd( String media, int count, boolean useGroups, String[] videoTypes)628     private List<SourcePacketExtension> sourceAdd(
629             String media, int count, boolean useGroups, String[] videoTypes)
630     {
631         long[] ssrcs = new long[count];
632         for (int i=0; i<count; i++)
633         {
634             ssrcs[i] = nextSSRC();
635         }
636         return sourceAdd(media, ssrcs, useGroups, videoTypes);
637     }
638 
videoSourceAdd( long[] newSSRCs, boolean useSsrcGroups)639     public List<SourcePacketExtension> videoSourceAdd(
640             long[] newSSRCs, boolean useSsrcGroups)
641     {
642         return sourceAdd("video", newSSRCs, useSsrcGroups, null);
643     }
644 
sourceAdd( String media, long[] newSSRCs, boolean useSsrcGroups, String[] ssrcsVideoTypes)645     public List<SourcePacketExtension> sourceAdd(
646         String media, long[] newSSRCs, boolean useSsrcGroups, String[] ssrcsVideoTypes)
647     {
648         List<SourcePacketExtension> addedSSRCs
649             = new ArrayList<>(newSSRCs.length);
650         MediaSourceMap toAdd = new MediaSourceMap();
651         SourceGroup sourceGroup = getLocalSSRCGroup(media);
652 
653         // Create new SSRCs
654         for (int i=0; i<newSSRCs.length; i++)
655         {
656             String videoType = null;
657             if (ssrcsVideoTypes != null)
658             {
659                 videoType = ssrcsVideoTypes[i];
660             }
661 
662             SourcePacketExtension ssrcPe
663                 = addLocalSSRC(media, newSSRCs[i], videoType);
664 
665             toAdd.addSource(media, ssrcPe);
666             addedSSRCs.add(ssrcPe);
667 
668             if (useSsrcGroups)
669             {
670                 sourceGroup.addSource(ssrcPe);
671             }
672         }
673 
674         // Send source-add
675         jingle.sendAddSourceIQ(toAdd, localSSRCGroups, jingleSession);
676 
677         return addedSSRCs;
678     }
679 
getRemoteSSRCs(String media)680     public List<SourcePacketExtension> getRemoteSSRCs(String media)
681     {
682         return remoteSSRCs.getSourcesForMedia(media);
683     }
684 
getRemoteSSRCGroups(String media)685     public List<SourceGroup> getRemoteSSRCGroups(String media)
686     {
687         return remoteSSRCgroups.getSourceGroupsForMedia(media);
688     }
689 
waitForAddSource(long timeout)690     public JingleIQ waitForAddSource(long timeout)
691         throws InterruptedException
692     {
693         return ssrcAddQueue.poll(timeout, TimeUnit.MILLISECONDS);
694     }
695 
waitForRemoveSource(long timeout)696     public JingleIQ waitForRemoveSource(long timeout)
697         throws InterruptedException
698     {
699         return ssrcRemoveQueue.poll(timeout, TimeUnit.MILLISECONDS);
700     }
701 
waitForSSRCCondition( SSRCCondition condition, long timeout)702     public void waitForSSRCCondition( SSRCCondition    condition,
703                                       long             timeout)
704         throws InterruptedException
705     {
706         synchronized (sourceLock)
707         {
708             long start = System.currentTimeMillis();
709             long end = start + timeout;
710 
711             while (!condition.checkCondition(this) &&
712                        System.currentTimeMillis() < end)
713             {
714                 long wait = end - System.currentTimeMillis();
715                 if (wait > 0)
716                 {
717                     sourceLock.wait(wait);
718                 }
719             }
720 
721             //if (!condition.checkCondition(this))
722               // fail(errorMsg);
723         }
724     }
725 
isUseSsrcGroups()726     public boolean isUseSsrcGroups()
727     {
728         return useSsrcGroups;
729     }
730 
setUseSsrcGroups(boolean useSsrcGroups)731     public void setUseSsrcGroups(boolean useSsrcGroups)
732     {
733         this.useSsrcGroups = useSsrcGroups;
734     }
735 
getMyJid()736     public Jid getMyJid()
737     {
738         return myJid;
739     }
740 
getVideoSSRCS()741     public List<SourcePacketExtension> getVideoSSRCS()
742     {
743         return localSSRCs.getSourcesForMedia("video");
744     }
745 
getSsrcVideoType()746     public String getSsrcVideoType()
747     {
748         return ssrcVideoType;
749     }
750 
setSsrcVideoType(String ssrcVideoType)751     public void setSsrcVideoType(String ssrcVideoType)
752     {
753         this.ssrcVideoType = ssrcVideoType;
754     }
755 
addMultipleAudioSSRCs(int count)756     public List<SourcePacketExtension> addMultipleAudioSSRCs(int count)
757     {
758         List<SourcePacketExtension> newSSRCs = new ArrayList<>(count);
759         for (int i=0; i<count; i++)
760         {
761             newSSRCs.add(addLocalAudioSSRC(nextSSRC()));
762         }
763         return newSSRCs;
764     }
765 
addMultipleVideoSSRCs(int count)766     public List<SourcePacketExtension> addMultipleVideoSSRCs(int count)
767     {
768         List<SourcePacketExtension> newSSRCs = new ArrayList<>(count);
769         for (int i=0; i<count; i++)
770         {
771             newSSRCs.add(addLocalVideoSSRC(nextSSRC(), null));
772         }
773         return newSSRCs;
774     }
775 
addLocalSSRC( String media, long ssrc, String videoType)776     public SourcePacketExtension addLocalSSRC(
777         String media, long ssrc, String videoType)
778     {
779         SourcePacketExtension newSSRC = newSSRC(ssrc, videoType);
780 
781         localSSRCs.addSource(media, newSSRC);
782 
783         return newSSRC;
784     }
785 
addLocalVideoSSRC(long ssrc, String videoType)786     public SourcePacketExtension addLocalVideoSSRC(long ssrc, String videoType)
787     {
788         return addLocalSSRC("video", ssrc, videoType);
789     }
790 
addLocalAudioSSRC(long ssrc)791     public SourcePacketExtension addLocalAudioSSRC(long ssrc)
792     {
793         return addLocalSSRC("audio", ssrc, null);
794     }
795 
796     class JingleHandler extends DefaultJingleRequestHandler
797     {
798 
799     }
800 
801     public interface SSRCCondition
802     {
checkCondition(MockParticipant me)803         boolean checkCondition(MockParticipant me);
804     }
805 }
806