RtpContentMap.java

  1package eu.siacs.conversations.xmpp.jingle;
  2
  3import android.util.Log;
  4
  5import com.google.common.base.Function;
  6import com.google.common.base.Preconditions;
  7import com.google.common.collect.ImmutableMap;
  8import com.google.common.collect.Iterables;
  9import com.google.common.collect.Maps;
 10
 11import org.checkerframework.checker.nullness.compatqual.NullableDecl;
 12
 13import java.util.Map;
 14
 15import eu.siacs.conversations.Config;
 16import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
 17import eu.siacs.conversations.xmpp.jingle.stanzas.GenericDescription;
 18import eu.siacs.conversations.xmpp.jingle.stanzas.GenericTransportInfo;
 19import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
 20import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
 21import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
 22import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
 23
 24public class RtpContentMap {
 25
 26    public final Group group;
 27    public final Map<String, DescriptionTransport> contents;
 28
 29    private RtpContentMap(Group group, Map<String, DescriptionTransport> contents) {
 30        this.group = group;
 31        this.contents = contents;
 32    }
 33
 34    public static RtpContentMap of(final JinglePacket jinglePacket) {
 35        return new RtpContentMap(jinglePacket.getGroup(), DescriptionTransport.of(jinglePacket.getJingleContents()));
 36    }
 37
 38    public static RtpContentMap of(final SessionDescription sessionDescription) {
 39        final ImmutableMap.Builder<String, DescriptionTransport> contentMapBuilder = new ImmutableMap.Builder<>();
 40        for (SessionDescription.Media media : sessionDescription.media) {
 41            final String id = Iterables.getFirst(media.attributes.get("mid"), null);
 42            Preconditions.checkNotNull(id, "media has no mid");
 43            contentMapBuilder.put(id, DescriptionTransport.of(sessionDescription, media));
 44        }
 45        final String groupAttribute = Iterables.getFirst(sessionDescription.attributes.get("group"), null);
 46        final Group group = groupAttribute == null ? null : Group.ofSdpString(groupAttribute);
 47        return new RtpContentMap(group, contentMapBuilder.build());
 48    }
 49
 50    public void requireContentDescriptions() {
 51        if (this.contents.size() == 0) {
 52            throw new IllegalStateException("No contents available");
 53        }
 54        for(Map.Entry<String,DescriptionTransport> entry : this.contents.entrySet()) {
 55            if (entry.getValue().description == null) {
 56                throw new IllegalStateException(String.format("%s is lacking content description", entry.getKey()));
 57            }
 58        }
 59    }
 60
 61    public JinglePacket toJinglePacket(final JinglePacket.Action action, final String sessionId) {
 62        final JinglePacket jinglePacket = new JinglePacket(action, sessionId);
 63        if (this.group != null) {
 64            jinglePacket.addGroup(this.group);
 65        }
 66        for (Map.Entry<String, DescriptionTransport> entry : this.contents.entrySet()) {
 67            final Content content = new Content(Content.Creator.INITIATOR, entry.getKey());
 68            if (entry.getValue().description != null) {
 69                content.addChild(entry.getValue().description);
 70            }
 71            content.addChild(entry.getValue().transport);
 72            jinglePacket.addJingleContent(content);
 73        }
 74        return jinglePacket;
 75    }
 76
 77    public RtpContentMap transportInfo(final String contentName, final IceUdpTransportInfo.Candidate candidate) {
 78        final RtpContentMap.DescriptionTransport descriptionTransport =  contents.get(contentName);
 79        final IceUdpTransportInfo transportInfo = descriptionTransport == null ? null : descriptionTransport.transport;
 80        if (transportInfo == null) {
 81            throw new IllegalArgumentException("Unable to find transport info for content name "+contentName);
 82        }
 83        final IceUdpTransportInfo newTransportInfo = transportInfo.cloneWrapper();
 84        newTransportInfo.addChild(candidate);
 85        return new RtpContentMap(null, ImmutableMap.of(contentName, new DescriptionTransport(null,newTransportInfo)));
 86
 87    }
 88
 89    public static class DescriptionTransport {
 90        public final RtpDescription description;
 91        public final IceUdpTransportInfo transport;
 92
 93        public DescriptionTransport(final RtpDescription description, final IceUdpTransportInfo transport) {
 94            this.description = description;
 95            this.transport = transport;
 96        }
 97
 98        public static DescriptionTransport of(final Content content) {
 99            final GenericDescription description = content.getDescription();
100            final GenericTransportInfo transportInfo = content.getTransport();
101            final RtpDescription rtpDescription;
102            final IceUdpTransportInfo iceUdpTransportInfo;
103            if (description == null) {
104                rtpDescription = null;
105            } else if (description instanceof RtpDescription) {
106                rtpDescription = (RtpDescription) description;
107            } else {
108                Log.d(Config.LOGTAG, "description was " + description);
109                throw new IllegalArgumentException("Content does not contain RtpDescription");
110            }
111            if (transportInfo instanceof IceUdpTransportInfo) {
112                iceUdpTransportInfo = (IceUdpTransportInfo) transportInfo;
113            } else {
114                throw new IllegalArgumentException("Content does not contain ICE-UDP transport");
115            }
116            return new DescriptionTransport(rtpDescription, iceUdpTransportInfo);
117        }
118
119        public static DescriptionTransport of(final SessionDescription sessionDescription, final SessionDescription.Media media) {
120            final RtpDescription rtpDescription = RtpDescription.of(media);
121            final IceUdpTransportInfo transportInfo = IceUdpTransportInfo.of(sessionDescription, media);
122            return new DescriptionTransport(rtpDescription, transportInfo);
123        }
124
125        public static Map<String, DescriptionTransport> of(final Map<String, Content> contents) {
126            return ImmutableMap.copyOf(Maps.transformValues(contents, new Function<Content, DescriptionTransport>() {
127                @NullableDecl
128                @Override
129                public DescriptionTransport apply(@NullableDecl Content content) {
130                    return content == null ? null : of(content);
131                }
132            }));
133        }
134    }
135}