create stub objects for most of what’s in description and transport

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/xml/Namespace.java                           |   1 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java         |  25 
src/main/java/eu/siacs/conversations/xmpp/jingle/SdpUtils.java                    |  11 
src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java             |   6 
src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java | 118 
src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java        |   2 
src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/RtpDescription.java      | 157 
7 files changed, 314 insertions(+), 6 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/xml/Namespace.java πŸ”—

@@ -38,6 +38,7 @@ public final class Namespace {
     public static final String JINGLE_APPS_DTLS = "urn:xmpp:jingle:apps:dtls:0";
     public static final String JINGLE_FEATURE_AUDIO = "urn:xmpp:jingle:apps:rtp:audio";
     public static final String JINGLE_FEATURE_VIDEO = "urn:xmpp:jingle:apps:rtp:video";
+    public static final String JINGLE_RTP_HEADER_EXTENSIONS = "urn:xmpp:jingle:apps:rtp:rtp-hdrext:0";
     public static final String IBB = "http://jabber.org/protocol/ibb";
     public static final String PING = "urn:xmpp:ping";
     public static final String PUSH = "urn:xmpp:push:0";

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java πŸ”—

@@ -73,6 +73,7 @@ public class JingleRtpConnection extends AbstractJingleConnection {
         final State oldState = this.state;
         if (transition(State.SESSION_INITIALIZED)) {
             if (oldState == State.PROCEED) {
+                processContents(contents);
                 sendSessionAccept();
             } else {
                 //TODO start ringing
@@ -82,6 +83,23 @@ public class JingleRtpConnection extends AbstractJingleConnection {
         }
     }
 
+    private void processContents(final Map<String,DescriptionTransport> contents) {
+        for(Map.Entry<String,DescriptionTransport> content : contents.entrySet()) {
+            final DescriptionTransport descriptionTransport = content.getValue();
+            final RtpDescription rtpDescription = descriptionTransport.description;
+            Log.d(Config.LOGTAG,"receive content with name "+content.getKey()+" and media="+rtpDescription.getMedia());
+            for(RtpDescription.PayloadType payloadType : rtpDescription.getPayloadTypes()) {
+                Log.d(Config.LOGTAG,"payload type: "+payloadType.toString());
+            }
+            for(RtpDescription.RtpHeaderExtension extension : rtpDescription.getHeaderExtensions()) {
+                Log.d(Config.LOGTAG,"extension: "+extension.toString());
+            }
+            final IceUdpTransportInfo iceUdpTransportInfo = descriptionTransport.transport;
+            Log.d(Config.LOGTAG,"transport: "+descriptionTransport.transport);
+            Log.d(Config.LOGTAG,"fingerprint "+iceUdpTransportInfo.getFingerprint());
+        }
+    }
+
     void deliveryMessage(final Jid from, final Element message) {
         Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": delivered message to JingleRtpConnection " + message);
         switch (message.getName()) {
@@ -175,7 +193,7 @@ public class JingleRtpConnection extends AbstractJingleConnection {
         }
     }
 
-    private static class DescriptionTransport {
+    public static class DescriptionTransport {
         private final RtpDescription description;
         private final IceUdpTransportInfo transport;
 
@@ -192,6 +210,7 @@ public class JingleRtpConnection extends AbstractJingleConnection {
             if (description instanceof RtpDescription) {
                 rtpDescription = (RtpDescription) description;
             } else {
+                Log.d(Config.LOGTAG,"description was "+description);
                 throw new IllegalArgumentException("Content does not contain RtpDescription");
             }
             if (transportInfo instanceof IceUdpTransportInfo) {
@@ -203,13 +222,13 @@ public class JingleRtpConnection extends AbstractJingleConnection {
         }
 
         public static Map<String, DescriptionTransport> of(final Map<String,Content> contents) {
-            return Maps.transformValues(contents, new Function<Content, DescriptionTransport>() {
+            return ImmutableMap.copyOf(Maps.transformValues(contents, new Function<Content, DescriptionTransport>() {
                 @NullableDecl
                 @Override
                 public DescriptionTransport apply(@NullableDecl Content content) {
                     return content == null ? null : of(content);
                 }
-            });
+            }));
         }
     }
 

src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java πŸ”—

@@ -51,9 +51,11 @@ public class Content extends Element {
         if (description == null) {
             return null;
         }
-        final String xmlns = description.getNamespace();
-        if (FileTransferDescription.NAMESPACES.contains(xmlns)) {
+        final String namespace = description.getNamespace();
+        if (FileTransferDescription.NAMESPACES.contains(namespace)) {
             return FileTransferDescription.upgrade(description);
+        } else if (Namespace.JINGLE_APPS_RTP.equals(namespace)) {
+            return RtpDescription.upgrade(description);
         } else {
             return GenericDescription.upgrade(description);
         }

src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java πŸ”—

@@ -1,6 +1,9 @@
 package eu.siacs.conversations.xmpp.jingle.stanzas;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
 
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xml.Namespace;
@@ -11,6 +14,21 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
         super(name, xmlns);
     }
 
+    public Fingerprint getFingerprint() {
+        final Element fingerprint = this.findChild("fingerprint", Namespace.JINGLE_APPS_DTLS);
+        return fingerprint == null ? null : Fingerprint.upgrade(fingerprint);
+    }
+
+    public List<Candidate> getCandidates() {
+        final ImmutableList.Builder<Candidate> builder = new ImmutableList.Builder<>();
+        for(final Element child : getChildren()) {
+            if ("candidate".equals(child.getName())) {
+                builder.add(Candidate.upgrade(child));
+            }
+        }
+        return builder.build();
+    }
+
     public static IceUdpTransportInfo upgrade(final Element element) {
         Preconditions.checkArgument("transport".equals(element.getName()), "Name of provided element is not transport");
         Preconditions.checkArgument(Namespace.JINGLE_TRANSPORT_ICE_UDP.equals(element.getNamespace()), "Element does not match ice-udp transport namespace");
@@ -19,4 +37,104 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
         transportInfo.setChildren(element.getChildren());
         return transportInfo;
     }
+
+    public static class Candidate extends Element {
+
+        private Candidate() {
+            super("candidate");
+        }
+
+        public int getComponent() {
+            return getAttributeAsInt("component");
+        }
+
+        public int getFoundation() {
+            return getAttributeAsInt("foundation");
+        }
+
+        public int getGeneration() {
+            return getAttributeAsInt("generation");
+        }
+
+        public String getId() {
+            return getAttribute("id");
+        }
+
+        public String getIp() {
+            return getAttribute("ip");
+        }
+
+        public int getNetwork() {
+            return getAttributeAsInt("network");
+        }
+
+        public int getPort() {
+            return getAttributeAsInt("port");
+        }
+
+        public int getPriority() {
+            return getAttributeAsInt("priority");
+        }
+
+        public String getProtocol() {
+            return getAttribute("protocol");
+        }
+
+        public String getRelAddr() {
+            return getAttribute("rel-addr");
+        }
+
+        public int getRelPort() {
+            return getAttributeAsInt("rel-port");
+        }
+
+        public String getType() { //TODO might be converted to enum
+            return getAttribute("type");
+        }
+
+        private int getAttributeAsInt(final String name) {
+            final String value = this.getAttribute(name);
+            if (value == null) {
+                return 0;
+            }
+            try {
+                return Integer.parseInt(value);
+            } catch (NumberFormatException e) {
+                return 0;
+            }
+        }
+
+        public static Candidate upgrade(final Element element) {
+            Preconditions.checkArgument("candidate".equals(element.getName()));
+            final Candidate candidate = new Candidate();
+            candidate.setAttributes(element.getAttributes());
+            candidate.setChildren(element.getChildren());
+            return candidate;
+        }
+    }
+
+
+    public static class Fingerprint extends Element {
+
+        public String getHash() {
+            return this.getAttribute("hash");
+        }
+
+        public String getSetup() {
+            return this.getAttribute("setup");
+        }
+
+        private Fingerprint() {
+            super("fingerprint", Namespace.JINGLE_APPS_DTLS);
+        }
+
+        public static Fingerprint upgrade(final Element element) {
+            Preconditions.checkArgument("fingerprint".equals(element.getName()));
+            Preconditions.checkArgument(Namespace.JINGLE_APPS_DTLS.equals(element.getNamespace()));
+            final Fingerprint fingerprint = new Fingerprint();
+            fingerprint.setAttributes(element.getAttributes());
+            fingerprint.setContent(element.getContent());
+            return fingerprint;
+        }
+    }
 }

src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java πŸ”—

@@ -38,7 +38,7 @@ public class JinglePacket extends IqPacket {
         return jinglePacket;
     }
 
-    //TODO can have multiple contents
+    //TODO deprecate this somehow and make file transfer fail if there are multiple (or something)
     public Content getJingleContent() {
         final Element content = getJingleChild("content");
         return content == null ? null : Content.upgrade(content);

src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/RtpDescription.java πŸ”—

@@ -1,6 +1,10 @@
 package eu.siacs.conversations.xmpp.jingle.stanzas;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Locale;
 
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xml.Namespace;
@@ -12,6 +16,30 @@ public class RtpDescription extends GenericDescription {
         super(name, namespace);
     }
 
+    public Media getMedia() {
+        return Media.of(this.getAttribute("media"));
+    }
+
+    public List<PayloadType> getPayloadTypes() {
+        final ImmutableList.Builder<PayloadType> builder = new ImmutableList.Builder<>();
+        for(Element child : getChildren()) {
+            if ("payload-type".equals(child.getName())) {
+                builder.add(PayloadType.of(child));
+            }
+        }
+        return builder.build();
+    }
+
+    public List<RtpHeaderExtension> getHeaderExtensions() {
+        final ImmutableList.Builder<RtpHeaderExtension> builder = new ImmutableList.Builder<>();
+        for(final Element child : getChildren()) {
+            if ("rtp-hdrext".equals(child.getName()) && Namespace.JINGLE_RTP_HEADER_EXTENSIONS.equals(child.getNamespace())) {
+                builder.add(RtpHeaderExtension.upgrade(child));
+            }
+        }
+        return builder.build();
+    }
+
     public static RtpDescription upgrade(final Element element) {
         Preconditions.checkArgument("description".equals(element.getName()), "Name of provided element is not description");
         Preconditions.checkArgument(Namespace.JINGLE_APPS_RTP.equals(element.getNamespace()), "Element does not match the jingle rtp namespace");
@@ -20,4 +48,133 @@ public class RtpDescription extends GenericDescription {
         description.setChildren(element.getChildren());
         return description;
     }
+
+    //TODO: support for https://xmpp.org/extensions/xep-0293.html
+
+
+    public static class RtpHeaderExtension extends Element {
+
+        private RtpHeaderExtension() {
+            super("rtp-hdrext", Namespace.JINGLE_RTP_HEADER_EXTENSIONS);
+        }
+
+        public String getId() {
+            return this.getAttribute("id");
+        }
+
+        public String getUri() {
+            return this.getAttribute("uri");
+        }
+
+        public static RtpHeaderExtension upgrade(final Element element) {
+            Preconditions.checkArgument("rtp-hdrext".equals(element.getName()));
+            Preconditions.checkArgument(Namespace.JINGLE_RTP_HEADER_EXTENSIONS.equals(element.getNamespace()));
+            final RtpHeaderExtension extension = new RtpHeaderExtension();
+            extension.setAttributes(element.getAttributes());
+            extension.setChildren(element.getChildren());
+            return extension;
+        }
+    }
+
+    public static class PayloadType extends Element {
+
+        private PayloadType(String name, String xmlns) {
+            super(name, xmlns);
+        }
+        public String getId() {
+            return this.getAttribute("id");
+        }
+
+        public String getPayloadTypeName() {
+            return this.getAttribute("name");
+        }
+
+        public int getClockRate() {
+            final String clockRate = this.getAttribute("clockrate");
+            if (clockRate == null) {
+                return 0;
+            }
+            try {
+                return Integer.parseInt(clockRate);
+            } catch (NumberFormatException e) {
+                return 0;
+            }
+        }
+
+        public int getChannels() {
+            final String channels = this.getAttribute("channels");
+            if (channels == null) {
+                return 1; // The number of channels; if omitted, it MUST be assumed to contain one channel
+            }
+            try {
+                return Integer.parseInt(channels);
+            } catch (NumberFormatException e) {
+                return 1;
+            }
+        }
+
+        public List<Parameter> getParameters() {
+            final ImmutableList.Builder<Parameter> builder = new ImmutableList.Builder<>();
+            for (Element child : getChildren()) {
+                if ("parameter".equals(child.getName())) {
+                    builder.add(Parameter.of(child));
+                }
+            }
+            return builder.build();
+        }
+
+        public static PayloadType of(final Element element) {
+            Preconditions.checkArgument("payload-type".equals(element.getName()), "element name must be called payload-type");
+            PayloadType payloadType = new PayloadType("payload-type", Namespace.JINGLE_APPS_RTP);
+            payloadType.setAttributes(element.getAttributes());
+            payloadType.setChildren(element.getChildren());
+            return payloadType;
+        }
+    }
+
+    public static class Parameter extends Element {
+
+        private Parameter() {
+            super("parameter", Namespace.JINGLE_APPS_RTP);
+        }
+
+        public Parameter(String name, String value) {
+            super("parameter", Namespace.JINGLE_APPS_RTP);
+            this.setAttribute("name", name);
+            this.setAttribute("value", value);
+        }
+
+        public String getParameterName() {
+            return this.getAttribute("name");
+        }
+
+        public String getParameterValue() {
+            return this.getAttribute("value");
+        }
+
+        public static Parameter of(final Element element) {
+            Preconditions.checkArgument("parameter".equals(element.getName()), "element name must be called parameter");
+            Parameter parameter = new Parameter();
+            parameter.setAttributes(element.getAttributes());
+            parameter.setChildren(element.getChildren());
+            return parameter;
+        }
+    }
+
+    public enum Media {
+        VIDEO, AUDIO, UNKNOWN;
+
+        @Override
+        public String toString() {
+            return super.toString().toLowerCase(Locale.ROOT);
+        }
+
+        public static Media of(String value) {
+            try {
+                return value == null ? UNKNOWN : Media.valueOf(value.toUpperCase(Locale.ROOT));
+            } catch (IllegalArgumentException e) {
+                return UNKNOWN;
+            }
+        }
+    }
 }