RtpDescription.java

  1package eu.siacs.conversations.xmpp.jingle.stanzas;
  2
  3import android.util.Log;
  4
  5import com.google.common.base.Preconditions;
  6import com.google.common.collect.ImmutableList;
  7
  8import java.util.List;
  9import java.util.Locale;
 10
 11import eu.siacs.conversations.Config;
 12import eu.siacs.conversations.xml.Element;
 13import eu.siacs.conversations.xml.Namespace;
 14import eu.siacs.conversations.xmpp.jingle.SessionDescription;
 15
 16public class RtpDescription extends GenericDescription {
 17
 18
 19    private RtpDescription() {
 20        super("description", Namespace.JINGLE_APPS_RTP);
 21    }
 22
 23    public Media getMedia() {
 24        return Media.of(this.getAttribute("media"));
 25    }
 26
 27    public List<PayloadType> getPayloadTypes() {
 28        final ImmutableList.Builder<PayloadType> builder = new ImmutableList.Builder<>();
 29        for (Element child : getChildren()) {
 30            if ("payload-type".equals(child.getName())) {
 31                builder.add(PayloadType.of(child));
 32            }
 33        }
 34        return builder.build();
 35    }
 36
 37    public List<FeedbackNegotiation> getFeedbackNegotiations() {
 38        return FeedbackNegotiation.fromChildren(this.getChildren());
 39    }
 40
 41    public List<FeedbackNegotiationTrrInt> feedbackNegotiationTrrInts() {
 42        return FeedbackNegotiationTrrInt.fromChildren(this.getChildren());
 43    }
 44
 45    public List<RtpHeaderExtension> getHeaderExtensions() {
 46        final ImmutableList.Builder<RtpHeaderExtension> builder = new ImmutableList.Builder<>();
 47        for (final Element child : getChildren()) {
 48            if ("rtp-hdrext".equals(child.getName()) && Namespace.JINGLE_RTP_HEADER_EXTENSIONS.equals(child.getNamespace())) {
 49                builder.add(RtpHeaderExtension.upgrade(child));
 50            }
 51        }
 52        return builder.build();
 53    }
 54
 55    public static RtpDescription upgrade(final Element element) {
 56        Preconditions.checkArgument("description".equals(element.getName()), "Name of provided element is not description");
 57        Preconditions.checkArgument(Namespace.JINGLE_APPS_RTP.equals(element.getNamespace()), "Element does not match the jingle rtp namespace");
 58        final RtpDescription description = new RtpDescription();
 59        description.setAttributes(element.getAttributes());
 60        description.setChildren(element.getChildren());
 61        return description;
 62    }
 63
 64    public static class FeedbackNegotiation extends Element {
 65        private FeedbackNegotiation() {
 66            super("rtcp-fb", Namespace.JINGLE_RTP_FEEDBACK_NEGOTIATION);
 67        }
 68
 69        public String getType() {
 70            return this.getAttribute("type");
 71        }
 72
 73        public String getSubType() {
 74            return this.getAttribute("subtype");
 75        }
 76
 77        private static FeedbackNegotiation upgrade(final Element element) {
 78            Preconditions.checkArgument("rtcp-fb".equals(element.getName()));
 79            Preconditions.checkArgument(Namespace.JINGLE_RTP_FEEDBACK_NEGOTIATION.equals(element.getNamespace()));
 80            final FeedbackNegotiation feedback = new FeedbackNegotiation();
 81            feedback.setAttributes(element.getAttributes());
 82            feedback.setChildren(element.getChildren());
 83            return feedback;
 84        }
 85
 86        public static List<FeedbackNegotiation> fromChildren(final List<Element> children) {
 87            ImmutableList.Builder<FeedbackNegotiation> builder = new ImmutableList.Builder<>();
 88            for (final Element child : children) {
 89                if ("rtcp-fb".equals(child.getName()) && Namespace.JINGLE_RTP_FEEDBACK_NEGOTIATION.equals(child.getNamespace())) {
 90                    builder.add(upgrade(child));
 91                }
 92            }
 93            return builder.build();
 94        }
 95
 96    }
 97
 98    public static class FeedbackNegotiationTrrInt extends Element {
 99        private FeedbackNegotiationTrrInt() {
100            super("rtcp-fb-trr-int", Namespace.JINGLE_RTP_FEEDBACK_NEGOTIATION);
101        }
102
103        public int getValue() {
104            final String value = getAttribute("value");
105            if (value == null) {
106                return 0;
107            }
108            try {
109                return Integer.parseInt(value);
110            } catch (NumberFormatException e) {
111                return 0;
112            }
113        }
114
115        private static FeedbackNegotiationTrrInt upgrade(final Element element) {
116            Preconditions.checkArgument("rtcp-fb-trr-int".equals(element.getName()));
117            Preconditions.checkArgument(Namespace.JINGLE_RTP_FEEDBACK_NEGOTIATION.equals(element.getNamespace()));
118            final FeedbackNegotiationTrrInt trr = new FeedbackNegotiationTrrInt();
119            trr.setAttributes(element.getAttributes());
120            trr.setChildren(element.getChildren());
121            return trr;
122        }
123
124        public static List<FeedbackNegotiationTrrInt> fromChildren(final List<Element> children) {
125            ImmutableList.Builder<FeedbackNegotiationTrrInt> builder = new ImmutableList.Builder<>();
126            for (final Element child : children) {
127                if ("rtcp-fb-trr-int".equals(child.getName()) && Namespace.JINGLE_RTP_FEEDBACK_NEGOTIATION.equals(child.getNamespace())) {
128                    builder.add(upgrade(child));
129                }
130            }
131            return builder.build();
132        }
133    }
134
135
136    //XEP-0294: Jingle RTP Header Extensions Negotiation
137    //maps to `extmap:$id $uri`
138    public static class RtpHeaderExtension extends Element {
139
140        private RtpHeaderExtension() {
141            super("rtp-hdrext", Namespace.JINGLE_RTP_HEADER_EXTENSIONS);
142        }
143
144        public RtpHeaderExtension(String id, String uri) {
145            super("rtp-hdrext", Namespace.JINGLE_RTP_HEADER_EXTENSIONS);
146            this.setAttribute("id", id);
147            this.setAttribute("uri", uri);
148        }
149
150        public String getId() {
151            return this.getAttribute("id");
152        }
153
154        public String getUri() {
155            return this.getAttribute("uri");
156        }
157
158        public static RtpHeaderExtension upgrade(final Element element) {
159            Preconditions.checkArgument("rtp-hdrext".equals(element.getName()));
160            Preconditions.checkArgument(Namespace.JINGLE_RTP_HEADER_EXTENSIONS.equals(element.getNamespace()));
161            final RtpHeaderExtension extension = new RtpHeaderExtension();
162            extension.setAttributes(element.getAttributes());
163            extension.setChildren(element.getChildren());
164            return extension;
165        }
166
167        public static RtpHeaderExtension ofSdpString(final String sdp) {
168            final String[] pair = sdp.split(" ", 2);
169            if (pair.length == 2) {
170                final String id = pair[0];
171                final String uri = pair[1];
172                return new RtpHeaderExtension(id,uri);
173            } else {
174                return null;
175            }
176        }
177    }
178
179    //maps to `rtpmap $id $name/$clockrate/$channels`
180    public static class PayloadType extends Element {
181
182        private PayloadType() {
183            super("payload-type", Namespace.JINGLE_APPS_RTP);
184        }
185
186        public PayloadType(String id, String name, int clockRate, int channels) {
187            super("payload-type", Namespace.JINGLE_APPS_RTP);
188            this.setAttribute("id",id);
189            this.setAttribute("name", name);
190            this.setAttribute("clockrate", clockRate);
191            if (channels != 1) {
192                this.setAttribute("channels", channels);
193            }
194        }
195
196        public String getId() {
197            return this.getAttribute("id");
198        }
199
200        public String getPayloadTypeName() {
201            return this.getAttribute("name");
202        }
203
204        public int getClockRate() {
205            final String clockRate = this.getAttribute("clockrate");
206            if (clockRate == null) {
207                return 0;
208            }
209            try {
210                return Integer.parseInt(clockRate);
211            } catch (NumberFormatException e) {
212                return 0;
213            }
214        }
215
216        public int getChannels() {
217            final String channels = this.getAttribute("channels");
218            if (channels == null) {
219                return 1; // The number of channels; if omitted, it MUST be assumed to contain one channel
220            }
221            try {
222                return Integer.parseInt(channels);
223            } catch (NumberFormatException e) {
224                return 1;
225            }
226        }
227
228        public List<Parameter> getParameters() {
229            final ImmutableList.Builder<Parameter> builder = new ImmutableList.Builder<>();
230            for (Element child : getChildren()) {
231                if ("parameter".equals(child.getName())) {
232                    builder.add(Parameter.of(child));
233                }
234            }
235            return builder.build();
236        }
237
238        public List<FeedbackNegotiation> getFeedbackNegotiations() {
239            return FeedbackNegotiation.fromChildren(this.getChildren());
240        }
241
242        public List<FeedbackNegotiationTrrInt> feedbackNegotiationTrrInts() {
243            return FeedbackNegotiationTrrInt.fromChildren(this.getChildren());
244        }
245
246        public static PayloadType of(final Element element) {
247            Preconditions.checkArgument("payload-type".equals(element.getName()), "element name must be called payload-type");
248            PayloadType payloadType = new PayloadType();
249            payloadType.setAttributes(element.getAttributes());
250            payloadType.setChildren(element.getChildren());
251            return payloadType;
252        }
253
254        public static PayloadType ofSdpString(final String sdp) {
255            final String[] pair = sdp.split(" ",2);
256            if (pair.length == 2) {
257                final String id = pair[0];
258                final String[] parts = pair[1].split("/");
259                if (parts.length >= 2) {
260                    final String name = parts[0];
261                    final int clockRate = SessionDescription.ignorantIntParser(parts[1]);
262                    final int channels;
263                    if (parts.length >= 3) {
264                        channels = SessionDescription.ignorantIntParser(parts[2]);
265                    } else {
266                        channels =1;
267                    }
268                    return new PayloadType(id,name,clockRate,channels);
269                }
270            }
271            return null;
272        }
273    }
274
275    //map to `fmtp $id key=value;key=value
276    //where id is the id of the parent payload-type
277    public static class Parameter extends Element {
278
279        private Parameter() {
280            super("parameter", Namespace.JINGLE_APPS_RTP);
281        }
282
283        public Parameter(String name, String value) {
284            super("parameter", Namespace.JINGLE_APPS_RTP);
285            this.setAttribute("name", name);
286            this.setAttribute("value", value);
287        }
288
289        public String getParameterName() {
290            return this.getAttribute("name");
291        }
292
293        public String getParameterValue() {
294            return this.getAttribute("value");
295        }
296
297        public static Parameter of(final Element element) {
298            Preconditions.checkArgument("parameter".equals(element.getName()), "element name must be called parameter");
299            Parameter parameter = new Parameter();
300            parameter.setAttributes(element.getAttributes());
301            parameter.setChildren(element.getChildren());
302            return parameter;
303        }
304    }
305
306    public enum Media {
307        VIDEO, AUDIO, UNKNOWN;
308
309        @Override
310        public String toString() {
311            return super.toString().toLowerCase(Locale.ROOT);
312        }
313
314        public static Media of(String value) {
315            try {
316                return value == null ? UNKNOWN : Media.valueOf(value.toUpperCase(Locale.ROOT));
317            } catch (IllegalArgumentException e) {
318                return UNKNOWN;
319            }
320        }
321    }
322
323    public static RtpDescription of(final SessionDescription.Media media) {
324        final RtpDescription rtpDescription = new RtpDescription();
325        for(final String rtpmap : media.attributes.get("rtpmap")) {
326            final PayloadType payloadType = PayloadType.ofSdpString(rtpmap);
327            if (payloadType != null) {
328                rtpDescription.addChild(payloadType);
329            }
330        }
331        for(final String extmap : media.attributes.get("extmap")) {
332            final RtpHeaderExtension extension = RtpHeaderExtension.ofSdpString(extmap);
333            if (extension != null) {
334                rtpDescription.addChild(extension);
335            }
336        }
337        return rtpDescription;
338    }
339}