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 JinglePacket toJinglePacket(final JinglePacket.Action action, final String sessionId) {
51 final JinglePacket jinglePacket = new JinglePacket(action, sessionId);
52 if (this.group != null) {
53 jinglePacket.addGroup(this.group);
54 }
55 for (Map.Entry<String, DescriptionTransport> entry : this.contents.entrySet()) {
56 final Content content = new Content(Content.Creator.INITIATOR, entry.getKey());
57 if (entry.getValue().description != null) {
58 content.addChild(entry.getValue().description);
59 }
60 content.addChild(entry.getValue().transport);
61 jinglePacket.addJingleContent(content);
62 }
63 return jinglePacket;
64 }
65
66 public RtpContentMap transportInfo(final String contentName, final IceUdpTransportInfo.Candidate candidate) {
67 final RtpContentMap.DescriptionTransport descriptionTransport = contents.get(contentName);
68 final IceUdpTransportInfo transportInfo = descriptionTransport == null ? null : descriptionTransport.transport;
69 if (transportInfo == null) {
70 throw new IllegalArgumentException("Unable to find transport info for content name "+contentName);
71 }
72 final IceUdpTransportInfo newTransportInfo = transportInfo.cloneWrapper();
73 newTransportInfo.addChild(candidate);
74 return new RtpContentMap(null, ImmutableMap.of(contentName, new DescriptionTransport(null,newTransportInfo)));
75
76 }
77
78 public static class DescriptionTransport {
79 public final RtpDescription description;
80 public final IceUdpTransportInfo transport;
81
82 public DescriptionTransport(final RtpDescription description, final IceUdpTransportInfo transport) {
83 this.description = description;
84 this.transport = transport;
85 }
86
87 public static DescriptionTransport of(final Content content) {
88 final GenericDescription description = content.getDescription();
89 final GenericTransportInfo transportInfo = content.getTransport();
90 final RtpDescription rtpDescription;
91 final IceUdpTransportInfo iceUdpTransportInfo;
92 if (description instanceof RtpDescription) {
93 rtpDescription = (RtpDescription) description;
94 } else {
95 Log.d(Config.LOGTAG, "description was " + description);
96 throw new IllegalArgumentException("Content does not contain RtpDescription");
97 }
98 if (transportInfo instanceof IceUdpTransportInfo) {
99 iceUdpTransportInfo = (IceUdpTransportInfo) transportInfo;
100 } else {
101 throw new IllegalArgumentException("Content does not contain ICE-UDP transport");
102 }
103 return new DescriptionTransport(rtpDescription, iceUdpTransportInfo);
104 }
105
106 public static DescriptionTransport of(final SessionDescription sessionDescription, final SessionDescription.Media media) {
107 final RtpDescription rtpDescription = RtpDescription.of(media);
108 final IceUdpTransportInfo transportInfo = IceUdpTransportInfo.of(sessionDescription, media);
109 return new DescriptionTransport(rtpDescription, transportInfo);
110 }
111
112 public static Map<String, DescriptionTransport> of(final Map<String, Content> contents) {
113 return ImmutableMap.copyOf(Maps.transformValues(contents, new Function<Content, DescriptionTransport>() {
114 @NullableDecl
115 @Override
116 public DescriptionTransport apply(@NullableDecl Content content) {
117 return content == null ? null : of(content);
118 }
119 }));
120 }
121 }
122}