RtpDescription.java

  1package eu.siacs.conversations.xmpp.jingle.stanzas;
  2
  3import com.google.common.base.Preconditions;
  4import com.google.common.collect.ImmutableList;
  5
  6import java.util.List;
  7import java.util.Locale;
  8
  9import eu.siacs.conversations.xml.Element;
 10import eu.siacs.conversations.xml.Namespace;
 11
 12public class RtpDescription extends GenericDescription {
 13
 14
 15    private RtpDescription(String name, String namespace) {
 16        super(name, namespace);
 17    }
 18
 19    public Media getMedia() {
 20        return Media.of(this.getAttribute("media"));
 21    }
 22
 23    public List<PayloadType> getPayloadTypes() {
 24        final ImmutableList.Builder<PayloadType> builder = new ImmutableList.Builder<>();
 25        for(Element child : getChildren()) {
 26            if ("payload-type".equals(child.getName())) {
 27                builder.add(PayloadType.of(child));
 28            }
 29        }
 30        return builder.build();
 31    }
 32
 33    public List<RtpHeaderExtension> getHeaderExtensions() {
 34        final ImmutableList.Builder<RtpHeaderExtension> builder = new ImmutableList.Builder<>();
 35        for(final Element child : getChildren()) {
 36            if ("rtp-hdrext".equals(child.getName()) && Namespace.JINGLE_RTP_HEADER_EXTENSIONS.equals(child.getNamespace())) {
 37                builder.add(RtpHeaderExtension.upgrade(child));
 38            }
 39        }
 40        return builder.build();
 41    }
 42
 43    public static RtpDescription upgrade(final Element element) {
 44        Preconditions.checkArgument("description".equals(element.getName()), "Name of provided element is not description");
 45        Preconditions.checkArgument(Namespace.JINGLE_APPS_RTP.equals(element.getNamespace()), "Element does not match the jingle rtp namespace");
 46        final RtpDescription description = new RtpDescription("description", Namespace.JINGLE_APPS_RTP);
 47        description.setAttributes(element.getAttributes());
 48        description.setChildren(element.getChildren());
 49        return description;
 50    }
 51
 52    //TODO: support for https://xmpp.org/extensions/xep-0293.html
 53
 54
 55    //XEP-0294: Jingle RTP Header Extensions Negotiation
 56    //maps to `extmap:$id $uri`
 57    public static class RtpHeaderExtension extends Element {
 58
 59        private RtpHeaderExtension() {
 60            super("rtp-hdrext", Namespace.JINGLE_RTP_HEADER_EXTENSIONS);
 61        }
 62
 63        public String getId() {
 64            return this.getAttribute("id");
 65        }
 66
 67        public String getUri() {
 68            return this.getAttribute("uri");
 69        }
 70
 71        public static RtpHeaderExtension upgrade(final Element element) {
 72            Preconditions.checkArgument("rtp-hdrext".equals(element.getName()));
 73            Preconditions.checkArgument(Namespace.JINGLE_RTP_HEADER_EXTENSIONS.equals(element.getNamespace()));
 74            final RtpHeaderExtension extension = new RtpHeaderExtension();
 75            extension.setAttributes(element.getAttributes());
 76            extension.setChildren(element.getChildren());
 77            return extension;
 78        }
 79    }
 80
 81    //maps to `rtpmap $id $name/$clockrate/$channels`
 82    public static class PayloadType extends Element {
 83
 84        private PayloadType(String name, String xmlns) {
 85            super(name, xmlns);
 86        }
 87        public String getId() {
 88            return this.getAttribute("id");
 89        }
 90
 91        public String getPayloadTypeName() {
 92            return this.getAttribute("name");
 93        }
 94
 95        public int getClockRate() {
 96            final String clockRate = this.getAttribute("clockrate");
 97            if (clockRate == null) {
 98                return 0;
 99            }
100            try {
101                return Integer.parseInt(clockRate);
102            } catch (NumberFormatException e) {
103                return 0;
104            }
105        }
106
107        public int getChannels() {
108            final String channels = this.getAttribute("channels");
109            if (channels == null) {
110                return 1; // The number of channels; if omitted, it MUST be assumed to contain one channel
111            }
112            try {
113                return Integer.parseInt(channels);
114            } catch (NumberFormatException e) {
115                return 1;
116            }
117        }
118
119        public List<Parameter> getParameters() {
120            final ImmutableList.Builder<Parameter> builder = new ImmutableList.Builder<>();
121            for (Element child : getChildren()) {
122                if ("parameter".equals(child.getName())) {
123                    builder.add(Parameter.of(child));
124                }
125            }
126            return builder.build();
127        }
128
129        public static PayloadType of(final Element element) {
130            Preconditions.checkArgument("payload-type".equals(element.getName()), "element name must be called payload-type");
131            PayloadType payloadType = new PayloadType("payload-type", Namespace.JINGLE_APPS_RTP);
132            payloadType.setAttributes(element.getAttributes());
133            payloadType.setChildren(element.getChildren());
134            return payloadType;
135        }
136    }
137
138    //map to `fmtp $id key=value;key=value
139    //where id is the id of the parent payload-type
140    public static class Parameter extends Element {
141
142        private Parameter() {
143            super("parameter", Namespace.JINGLE_APPS_RTP);
144        }
145
146        public Parameter(String name, String value) {
147            super("parameter", Namespace.JINGLE_APPS_RTP);
148            this.setAttribute("name", name);
149            this.setAttribute("value", value);
150        }
151
152        public String getParameterName() {
153            return this.getAttribute("name");
154        }
155
156        public String getParameterValue() {
157            return this.getAttribute("value");
158        }
159
160        public static Parameter of(final Element element) {
161            Preconditions.checkArgument("parameter".equals(element.getName()), "element name must be called parameter");
162            Parameter parameter = new Parameter();
163            parameter.setAttributes(element.getAttributes());
164            parameter.setChildren(element.getChildren());
165            return parameter;
166        }
167    }
168
169    public enum Media {
170        VIDEO, AUDIO, UNKNOWN;
171
172        @Override
173        public String toString() {
174            return super.toString().toLowerCase(Locale.ROOT);
175        }
176
177        public static Media of(String value) {
178            try {
179                return value == null ? UNKNOWN : Media.valueOf(value.toUpperCase(Locale.ROOT));
180            } catch (IllegalArgumentException e) {
181                return UNKNOWN;
182            }
183        }
184    }
185}