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