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 java.util.*;
21 import org.jitsi.xmpp.extensions.colibri.*;
22 import org.jitsi.xmpp.extensions.jingle.*;
23 import org.jitsi.protocol.xmpp.util.*;
24 import org.jitsi.utils.logging.*;
25 
26 /**
27  * Represents an entity in a {@link JitsiMeetConferenceImpl} which has
28  * associated Colibri channels, and a set of SSRCs described as "sources"
29  * and "source groups". This can be associated either with an actual participant
30  * in the conference (which has a chat room member, and a Jingle session), or
31  * a bridge-to-bridge (Octo) channel on a particular bridge instance.
32  *
33  * @author Pawel Domas
34  * @author Boris Grozev
35  */
36 public abstract class AbstractParticipant
37 {
38     /**
39      * The class logger which can be used to override logging level inherited
40      * from {@link JitsiMeetConference}.
41      */
42     private final static Logger classLogger
43         = Logger.getLogger(AbstractParticipant.class);
44 
45     /**
46      * Information about Colibri channels allocated for this peer (if any).
47      */
48     private ColibriConferenceIQ colibriChannelsInfo;
49 
50     /**
51      * The map of the most recently received RTP description for each Colibri
52      * content.
53      */
54     private Map<String, RtpDescriptionPacketExtension> rtpDescriptionMap;
55 
56     /**
57      * Peer's media sources.
58      */
59     protected final MediaSourceMap sources = new MediaSourceMap();
60 
61     /**
62      * Peer's media source groups.
63      */
64     protected final MediaSourceGroupMap sourceGroups = new MediaSourceGroupMap();
65 
66     /**
67      * sources received from other peers scheduled for later addition, because
68      * of the Jingle session not being ready at the point when sources appeared in
69      * the conference.
70      */
71     private MediaSourceMap sourcesToAdd = new MediaSourceMap();
72 
73     /**
74      * source groups received from other peers scheduled for later addition.
75      * @see #sourcesToAdd
76      */
77     private MediaSourceGroupMap sourceGroupsToAdd = new MediaSourceGroupMap();
78 
79     /**
80      * sources received from other peers scheduled for later removal, because
81      * of the Jingle session not being ready at the point when sources appeared in
82      * the conference.
83      * FIXME: do we need that since these were never added ? - check
84      */
85     private MediaSourceMap sourcesToRemove = new MediaSourceMap();
86 
87     /**
88      * source groups received from other peers scheduled for later removal.
89      * @see #sourcesToRemove
90      */
91     private MediaSourceGroupMap sourceGroupsToRemove = new MediaSourceGroupMap();
92 
93     /**
94      * Tells how many unique sources per media participant is allowed to advertise
95      */
96     protected int maxSourceCount = -1;
97 
98     /**
99      * Returns currently stored map of RTP description to Colibri content name.
100      * @return a <tt>Map<String,RtpDescriptionPacketExtension></tt> which maps
101      *         the RTP descriptions to the corresponding Colibri content names.
102      */
getRtpDescriptionMap()103     public Map<String, RtpDescriptionPacketExtension> getRtpDescriptionMap()
104     {
105         return rtpDescriptionMap;
106     }
107 
108     /**
109      * Used to synchronize access to {@link #channelAllocator}.
110      */
111     private final Object channelAllocatorSyncRoot = new Object();
112 
113     /**
114      * The {@link AbstractChannelAllocator}, if any, which is currently
115      * allocating channels for this participant.
116      */
117     private AbstractChannelAllocator channelAllocator = null;
118 
119     /**
120      * The logger for this instance. Uses the logging level either of the
121      * {@link #classLogger} or {@link JitsiMeetConference#getLogger()}
122      * whichever is higher.
123      */
124     private final Logger logger;
125 
AbstractParticipant(Logger conferenceLogger)126     protected AbstractParticipant(Logger conferenceLogger)
127     {
128         this.logger = Logger.getLogger(classLogger, conferenceLogger);
129     }
130 
131     /**
132      * Extracts and stores RTP description for each content type from given
133      * Jingle contents.
134      * @param jingleContents the list of Jingle content packet extension from
135      *        <tt>Participant</tt>'s answer.
136      */
setRTPDescription(List<ContentPacketExtension> jingleContents)137     public void setRTPDescription(List<ContentPacketExtension> jingleContents)
138     {
139         Map<String, RtpDescriptionPacketExtension> rtpDescMap = new HashMap<>();
140 
141         for (ContentPacketExtension content : jingleContents)
142         {
143             RtpDescriptionPacketExtension rtpDesc
144                 = content.getFirstChildOfType(
145                 RtpDescriptionPacketExtension.class);
146 
147             if (rtpDesc != null)
148             {
149                 rtpDescMap.put(content.getName(), rtpDesc);
150             }
151         }
152 
153         this.rtpDescriptionMap = rtpDescMap;
154     }
155 
156     /**
157      * Removes given media sources from this peer state.
158      * @param sourceMap the source map that contains the sources to be removed.
159      * @return <tt>MediaSourceMap</tt> which contains sources removed from this map.
160      */
removeSources(MediaSourceMap sourceMap)161     public MediaSourceMap removeSources(MediaSourceMap sourceMap)
162     {
163         return sources.remove(sourceMap);
164     }
165 
166     /**
167      * Returns deep copy of this peer's media source map.
168      */
getSourcesCopy()169     public MediaSourceMap getSourcesCopy()
170     {
171         return sources.copyDeep();
172     }
173 
174     /**
175      * Returns deep copy of this peer's media source group map.
176      */
getSourceGroupsCopy()177     public MediaSourceGroupMap getSourceGroupsCopy()
178     {
179         return sourceGroups.copy();
180     }
181 
182     /**
183      * Returns <tt>true</tt> if this peer has any not synchronized sources
184      * scheduled for addition.
185      */
hasSourcesToAdd()186     public boolean hasSourcesToAdd()
187     {
188         return !sourcesToAdd.isEmpty() || !sourceGroupsToAdd.isEmpty();
189     }
190 
191     /**
192      * Reset the queue that holds not synchronized sources scheduled for future
193      * addition.
194      */
clearSourcesToAdd()195     public void clearSourcesToAdd()
196     {
197         sourcesToAdd = new MediaSourceMap();
198         sourceGroupsToAdd = new MediaSourceGroupMap();
199     }
200 
201     /**
202      * Reset the queue that holds not synchronized sources scheduled for future
203      * removal.
204      */
clearSourcesToRemove()205     public void clearSourcesToRemove()
206     {
207         sourcesToRemove = new MediaSourceMap();
208         sourceGroupsToRemove = new MediaSourceGroupMap();
209     }
210 
211     /**
212      * Returns <tt>true</tt> if this peer has any not synchronized sources
213      * scheduled for removal.
214      */
hasSourcesToRemove()215     public boolean hasSourcesToRemove()
216     {
217         return !sourcesToRemove.isEmpty() || !sourceGroupsToRemove.isEmpty();
218     }
219 
220     /**
221      * Returns <tt>true</tt> if this peer has any not synchronized sources
222      * scheduled for addition.
223      */
getSourcesToAdd()224     public MediaSourceMap getSourcesToAdd()
225     {
226         return sourcesToAdd;
227     }
228 
229     /**
230      * Returns <tt>true</tt> if this peer has any not synchronized sources
231      * scheduled for removal.
232      */
getSourcesToRemove()233     public MediaSourceMap getSourcesToRemove()
234     {
235         return sourcesToRemove;
236     }
237 
238     /**
239      * Schedules sources received from other peer for future 'source-add'
240      * update.
241      *
242      * @param sourceMap the media source map that contains sources for future
243      * updates.
244      */
scheduleSourcesToAdd(MediaSourceMap sourceMap)245     public void scheduleSourcesToAdd(MediaSourceMap sourceMap)
246     {
247         sourcesToAdd.add(sourceMap);
248     }
249 
250     /**
251      * Schedules sources received from other peer for future 'source-remove'
252      * update.
253      *
254      * @param sourceMap the media source map that contains sources for future
255      * updates.
256      */
scheduleSourcesToRemove(MediaSourceMap sourceMap)257     public void scheduleSourcesToRemove(MediaSourceMap sourceMap)
258     {
259         sourcesToRemove.add(sourceMap);
260     }
261 
262     /**
263      * Sets information about Colibri channels allocated for this participant.
264      *
265      * @param colibriChannelsInfo the IQ that holds colibri channels state.
266      */
setColibriChannelsInfo(ColibriConferenceIQ colibriChannelsInfo)267     public void setColibriChannelsInfo(ColibriConferenceIQ colibriChannelsInfo)
268     {
269         this.colibriChannelsInfo = colibriChannelsInfo;
270     }
271 
272     /**
273      * Returns {@link ColibriConferenceIQ} that describes Colibri channels
274      * allocated for this participant.
275      */
getColibriChannelsInfo()276     public ColibriConferenceIQ getColibriChannelsInfo()
277     {
278         return colibriChannelsInfo;
279     }
280 
281     /**
282      * Returns the list of source groups of given media type that belong ot this
283      * participant.
284      * @param media the name of media type("audio","video", ...)
285      * @return the list of {@link SourceGroup} for given media type.
286      */
getSourceGroupsForMedia(String media)287     public List<SourceGroup> getSourceGroupsForMedia(String media)
288     {
289         return sourceGroups.getSourceGroupsForMedia(media);
290     }
291 
292     /**
293      * Returns <tt>MediaSourceGroupMap</tt> that contains the mapping of media
294      * source groups that describe media of this participant.
295      */
getSourceGroups()296     public MediaSourceGroupMap getSourceGroups()
297     {
298         return sourceGroups;
299     }
300 
301     /**
302      * Schedules given media source groups for later addition.
303      * @param sourceGroups the <tt>MediaSourceGroupMap</tt> to be scheduled for
304      *                   later addition.
305      */
scheduleSourceGroupsToAdd(MediaSourceGroupMap sourceGroups)306     public void scheduleSourceGroupsToAdd(MediaSourceGroupMap sourceGroups)
307     {
308         sourceGroupsToAdd.add(sourceGroups);
309     }
310 
311     /**
312      * Schedules given media source groups for later removal.
313      * @param sourceGroups the <tt>MediaSourceGroupMap</tt> to be scheduled for
314      *                   later removal.
315      */
scheduleSourceGroupsToRemove(MediaSourceGroupMap sourceGroups)316     public void scheduleSourceGroupsToRemove(MediaSourceGroupMap sourceGroups)
317     {
318         sourceGroupsToRemove.add(sourceGroups);
319     }
320 
321     /**
322      * Returns the map of source groups that are waiting for synchronization.
323      */
getSourceGroupsToAdd()324     public MediaSourceGroupMap getSourceGroupsToAdd()
325     {
326         return sourceGroupsToAdd;
327     }
328 
329     /**
330      * Returns the map of source groups that are waiting for being removed from
331      * peer session.
332      */
getSourceGroupsToRemove()333     public MediaSourceGroupMap getSourceGroupsToRemove()
334     {
335         return sourceGroupsToRemove;
336     }
337 
338     /**
339      * Removes source groups from this participant state.
340      * @param groupsToRemove the map of source groups that will be removed
341      *                       from this participant media state description.
342      * @return <tt>MediaSourceGroupMap</tt> which contains source groups removed
343      *         from this map.
344      */
removeSourceGroups(MediaSourceGroupMap groupsToRemove)345     public MediaSourceGroupMap removeSourceGroups(MediaSourceGroupMap groupsToRemove)
346     {
347         return sourceGroups.remove(groupsToRemove);
348     }
349 
350     /**
351      * Replaces the {@link AbstractChannelAllocator}, which is currently
352      * allocating channels for this participant (if any) with the specified
353      * channel allocator (if any).
354      * @param channelAllocator the channel allocator to set, or {@code null}
355      * to clear it.
356      */
setChannelAllocator( AbstractChannelAllocator channelAllocator)357     public void setChannelAllocator(
358         AbstractChannelAllocator channelAllocator)
359     {
360         synchronized (channelAllocatorSyncRoot)
361         {
362             if (this.channelAllocator != null)
363             {
364                 // There is an ongoing thread allocating channels and sending
365                 // an invite for this participant. Tell it to stop.
366                 logger.warn("Canceling " + this.channelAllocator);
367                 this.channelAllocator.cancel();
368             }
369 
370             this.channelAllocator = channelAllocator;
371         }
372     }
373 
374     /**
375      * Signals to this {@link Participant} that a specific
376      * {@link AbstractChannelAllocator} has completed its task and its thread
377      * is about to terminate.
378      * @param channelAllocator the {@link AbstractChannelAllocator} which has
379      * completed its task and its thread is about to terminate.
380      */
channelAllocatorCompleted( AbstractChannelAllocator channelAllocator)381     void channelAllocatorCompleted(
382         AbstractChannelAllocator channelAllocator)
383     {
384         synchronized (channelAllocatorSyncRoot)
385         {
386             if (this.channelAllocator == channelAllocator)
387             {
388                 this.channelAllocator = null;
389             }
390         }
391     }
392 
addSourcesAndGroups(MediaSourceMap addedSources, MediaSourceGroupMap addedGroups)393     public void addSourcesAndGroups(MediaSourceMap         addedSources,
394                                     MediaSourceGroupMap    addedGroups)
395     {
396         this.sources.add(addedSources);
397         this.sourceGroups.add(addedGroups);
398     }
399 
400     /**
401      * @return {@code true} if the session with this participant has already
402      * been established. Before the session is established, we are unable to
403      * update the remote state (e.g. the list of sources (SSRCs) of this
404      * participant).
405      */
isSessionEstablished()406     abstract public boolean isSessionEstablished();
407 }
408