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.protocol.xmpp.util; 19 20 import org.jitsi.xmpp.extensions.colibri.*; 21 import org.jitsi.xmpp.extensions.jingle.*; 22 23 import org.jitsi.jicofo.*; 24 import org.jitsi.utils.*; 25 26 import java.util.*; 27 28 /** 29 * Wrapper for <tt>SourceGroupPacketExtension</tt>. 30 * 31 * @author Pawel Domas 32 */ 33 public class SourceGroup 34 { 35 /** 36 * Underlying source group packet extension. 37 */ 38 private final SourceGroupPacketExtension group; 39 40 /** 41 * Extracts source groups from Jingle content packet extension. 42 * @param content the <tt>ContentPacketExtension</tt> that contains(or not) 43 * the description of source groups. 44 * @return the list of <tt>SourceGroup</tt>s described by given 45 * <tt>ContentPacketExtension</tt>. 46 */ getSourceGroupsForContent( ContentPacketExtension content)47 public static List<SourceGroup> getSourceGroupsForContent( 48 ContentPacketExtension content) 49 { 50 List<SourceGroup> groups = new ArrayList<SourceGroup>(); 51 52 RtpDescriptionPacketExtension rtpDescPe 53 = JingleUtils.getRtpDescription(content); 54 55 if (rtpDescPe == null) 56 { 57 return groups; 58 } 59 60 List<SourceGroupPacketExtension> groupExtensions 61 = rtpDescPe.getChildExtensionsOfType( 62 SourceGroupPacketExtension.class); 63 64 for (SourceGroupPacketExtension groupPe : groupExtensions) 65 { 66 groups.add(new SourceGroup(groupPe)); 67 } 68 69 return groups; 70 } 71 72 /** 73 * Creates new instance of <tt>SourceGroup</tt>. 74 * @param group the packet extension that described source group to be wrapped 75 * by new object. 76 * @throws NullPointerException if <tt>group</tt> is <tt>null</tt>. 77 */ SourceGroup(SourceGroupPacketExtension group)78 public SourceGroup(SourceGroupPacketExtension group) 79 { 80 this.group = Objects.requireNonNull(group, "group"); 81 } 82 83 /** 84 * Creates new instance of <tt>SourceGroup</tt>. 85 * @param semantics the group's semantics 86 * @param sources a {@link List} of the group's 87 * {@link SourcePacketExtension}s 88 */ SourceGroup(String semantics, List<SourcePacketExtension> sources)89 public SourceGroup(String semantics, List<SourcePacketExtension> sources) 90 { 91 group = new SourceGroupPacketExtension(); 92 group.setSemantics(semantics); 93 group.addSources(sources); 94 } 95 96 /** 97 * Adds source to this group. 98 * 99 * @param source the <tt>SourcePacketExtension</tt> to be added to this 100 * group. 101 */ addSource(SourcePacketExtension source)102 public void addSource(SourcePacketExtension source) 103 { 104 group.addChildExtension(source); 105 } 106 107 /** 108 * Adds the list of sources to this group. 109 * 110 * @param video the list of <tt>SourcePacketExtension</tt> which will be 111 * added to this group. 112 */ addSources(List<SourcePacketExtension> video)113 public void addSources(List<SourcePacketExtension> video) 114 { 115 group.addSources(video); 116 } 117 118 /** 119 * Checks if given {@link SourcePacketExtension} is part of this group. 120 * 121 * @param source {@link SourcePacketExtension} to be checked. 122 * 123 * @return <tt>true</tt> or <tt>false</tt> 124 */ belongsToGroup(SourcePacketExtension source)125 public boolean belongsToGroup(SourcePacketExtension source) 126 { 127 for (SourcePacketExtension groupSrcs : this.getSources()) 128 { 129 if (groupSrcs.sourceEquals(source)) 130 { 131 return true; 132 } 133 } 134 135 return false; 136 } 137 138 /** 139 * Obtains group's MSID. 140 * 141 * NOTE it makes sense only once the attributes have been copied from 142 * the media section {@link SourcePacketExtension}s, as normally 143 * {@link SourcePacketExtension}s signalled inside of 144 * {@link SourceGroupPacketExtension} do not contain any parameters 145 * including MSID. See {@link SSRCValidator#copySourceParamsToGroups()}. 146 * 147 * @return a {@link String} 148 */ getGroupMsid()149 public String getGroupMsid() 150 { 151 List<SourcePacketExtension> sources = this.group.getSources(); 152 153 return sources.size() > 0 154 ? SSRCSignaling.getMsid(sources.get(0)) 155 : null; 156 } 157 158 /** 159 * Returns the sources contained in this group. 160 * @return the internal list that stores <tt>SourcePacketExtension</tt> 161 */ getSources()162 public List<SourcePacketExtension> getSources() 163 { 164 return group.getSources(); 165 } 166 167 /** 168 * Returns deep copy of underlying <tt>SourceGroupPacketExtension</tt>. 169 */ getExtensionCopy()170 public SourceGroupPacketExtension getExtensionCopy() 171 { 172 return group.copy(); 173 } 174 175 /** 176 * Returns the underlying <tt>SourceGroupPacketExtension</tt> wrapped by 177 * this <tt>SourceGroup</tt> instance. 178 */ getPacketExtension()179 public SourceGroupPacketExtension getPacketExtension() 180 { 181 return group; 182 } 183 184 /** 185 * Returns full copy of this <tt>SourceGroup</tt>. 186 */ copy()187 public SourceGroup copy() 188 { 189 return new SourceGroup(getExtensionCopy()); 190 } 191 192 /** 193 * Overrides {@link Object#equals(Object)}. Two {@link SourceGroup}s are 194 * considered equal if: 195 * 1. They have the same semantics 196 * 2. They have the same sources (according to 197 * {@link SourcePacketExtension#equals(Object)}), and in the same order. 198 * 199 * @param obj the other source group. 200 */ 201 @Override equals(Object obj)202 public boolean equals(Object obj) 203 { 204 if (!(obj instanceof SourceGroup)) 205 { 206 return false; 207 } 208 209 SourceGroup other = (SourceGroup) obj; 210 String semantics = other.getSemantics(); 211 if (StringUtils.isNullOrEmpty(semantics) 212 && !StringUtils.isNullOrEmpty(getSemantics())) 213 { 214 return false; 215 } 216 217 if (!getSemantics().equals(semantics)) 218 { 219 return false; 220 } 221 222 List<SourcePacketExtension> sources = getSources(); 223 List<SourcePacketExtension> otherSources = other.getSources(); 224 225 if (sources.size() != otherSources.size()) 226 { 227 return false; 228 } 229 230 for (int i = 0; i < sources.size(); i++) 231 { 232 if (!sources.get(i).sourceEquals(otherSources.get(i))) 233 { 234 return false; 235 } 236 } 237 238 return true; 239 } 240 241 /** 242 * Check if this <tt>SourceGroup</tt> contains any 243 * <tt>SourceGroupPacketExtension</tt>s. 244 * 245 * @return <tt>true</tt> if this <tt>SourceGroup</tt> is empty or 246 * <tt>false</tt> otherwise. 247 */ isEmpty()248 public boolean isEmpty() 249 { 250 return this.group.getSources().isEmpty(); 251 } 252 253 @Override toString()254 public String toString() 255 { 256 StringBuilder sources = new StringBuilder(); 257 for (SourcePacketExtension source : this.group.getSources()) 258 { 259 // FIXME do not print for the last element 260 sources.append(source.toString()).append(", "); 261 } 262 return "SourceGroup[" + this.group.getSemantics() + ", " + sources 263 + "]@" + Integer.toHexString(hashCode()); 264 } 265 getSemantics()266 public String getSemantics() 267 { 268 return group.getSemantics(); 269 } 270 } 271