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