1package eu.siacs.conversations.xmpp.jingle;
2
3import com.google.common.base.Strings;
4import com.google.common.collect.Collections2;
5import com.google.common.collect.ImmutableSet;
6
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14
15import eu.siacs.conversations.entities.Contact;
16import eu.siacs.conversations.entities.Presence;
17import eu.siacs.conversations.entities.Presences;
18import eu.siacs.conversations.entities.ServiceDiscoveryResult;
19import eu.siacs.conversations.xml.Namespace;
20import eu.siacs.conversations.xmpp.Jid;
21
22public class RtpCapability {
23
24 private static final List<String> BASIC_RTP_REQUIREMENTS = Arrays.asList(
25 Namespace.JINGLE,
26 Namespace.JINGLE_TRANSPORT_ICE_UDP,
27 Namespace.JINGLE_APPS_RTP,
28 Namespace.JINGLE_APPS_DTLS
29 );
30 private static final Collection<String> VIDEO_REQUIREMENTS = Arrays.asList(
31 Namespace.JINGLE_FEATURE_AUDIO,
32 Namespace.JINGLE_FEATURE_VIDEO
33 );
34
35 public static Capability check(final Presence presence) {
36 final ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
37 final Set<String> features = disco == null ? Collections.emptySet() : ImmutableSet.copyOf(disco.getFeatures());
38 if (features.containsAll(BASIC_RTP_REQUIREMENTS)) {
39 if (features.containsAll(VIDEO_REQUIREMENTS)) {
40 return Capability.VIDEO;
41 }
42 if (features.contains(Namespace.JINGLE_FEATURE_AUDIO)) {
43 return Capability.AUDIO;
44 }
45 }
46 return Capability.NONE;
47 }
48
49 public static String[] filterPresences(final Contact contact, Capability required) {
50 final Presences presences = contact.getPresences();
51 final ArrayList<String> resources = new ArrayList<>();
52 for (final Map.Entry<String, Presence> presence : presences.getPresencesMap().entrySet()) {
53 final Capability capability = check(presence.getValue());
54 if (capability == Capability.NONE) {
55 continue;
56 }
57 if (required == Capability.AUDIO || capability == required) {
58 resources.add(presence.getKey());
59 }
60 }
61 return resources.toArray(new String[0]);
62 }
63
64 public static Capability check(final Contact contact) {
65 return check(contact, true);
66 }
67
68 public static Capability check(final Contact contact, final boolean allowFallback) {
69 final Presences presences = contact.getPresences();
70
71 if (presences.isEmpty() && allowFallback && contact.getAccount().isEnabled()) {
72 Contact gateway = contact.getAccount().getRoster().getContact(Jid.of(contact.getJid().getDomain()));
73 if (gateway.showInRoster() && gateway.getPresences().anyIdentity("gateway", "pstn")) {
74 return Capability.AUDIO;
75 }
76
77 return contact.getRtpCapability();
78 }
79 Capability result = Capability.NONE;
80 for (final Presence presence : presences.getPresences()) {
81 Capability capability = check(presence);
82 if (capability == Capability.VIDEO) {
83 result = capability;
84 } else if (capability == Capability.AUDIO && result == Capability.NONE) {
85 result = capability;
86 }
87 }
88 return result;
89 }
90
91 // do all devices that support Rtp Call also support JMI?
92 public static boolean jmiSupport(final Contact contact) {
93 return !Collections2.transform(
94 Collections2.filter(
95 contact.getPresences().getPresences(),
96 p -> RtpCapability.check(p) != RtpCapability.Capability.NONE),
97 p -> {
98 ServiceDiscoveryResult disco = p.getServiceDiscoveryResult();
99 return disco != null && disco.getFeatures().contains(Namespace.JINGLE_MESSAGE);
100 }).contains(false);
101 }
102
103 public enum Capability {
104 NONE, AUDIO, VIDEO;
105
106 public static Capability of(String value) {
107 if (Strings.isNullOrEmpty(value)) {
108 return NONE;
109 }
110 try {
111 return valueOf(value);
112 } catch (IllegalArgumentException e) {
113 return NONE;
114 }
115 }
116 }
117
118}