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