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;
20
21public class RtpCapability {
22
23 private static final List<String> BASIC_RTP_REQUIREMENTS = Arrays.asList(
24 Namespace.JINGLE,
25 Namespace.JINGLE_TRANSPORT_ICE_UDP,
26 Namespace.JINGLE_APPS_RTP,
27 Namespace.JINGLE_APPS_DTLS
28 );
29 private static final Collection<String> VIDEO_REQUIREMENTS = Arrays.asList(
30 Namespace.JINGLE_FEATURE_AUDIO,
31 Namespace.JINGLE_FEATURE_VIDEO
32 );
33
34 public static Capability check(final Presence presence) {
35 final ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
36 final Set<String> features = disco == null ? Collections.emptySet() : ImmutableSet.copyOf(disco.getFeatures());
37 if (features.containsAll(BASIC_RTP_REQUIREMENTS)) {
38 if (features.containsAll(VIDEO_REQUIREMENTS)) {
39 return Capability.VIDEO;
40 }
41 if (features.contains(Namespace.JINGLE_FEATURE_AUDIO)) {
42 return Capability.AUDIO;
43 }
44 }
45 return Capability.NONE;
46 }
47
48 public static String[] filterPresences(final Contact contact, Capability required) {
49 final Presences presences = contact.getPresences();
50 final ArrayList<String> resources = new ArrayList<>();
51 for (final Map.Entry<String, Presence> presence : presences.getPresencesMap().entrySet()) {
52 final Capability capability = check(presence.getValue());
53 if (capability == Capability.NONE) {
54 continue;
55 }
56 if (required == Capability.AUDIO || capability == required) {
57 resources.add(presence.getKey());
58 }
59 }
60 return resources.toArray(new String[0]);
61 }
62
63 public static Capability check(final Contact contact) {
64 return check(contact, true);
65 }
66
67 public static Capability check(final Contact contact, final boolean allowFallback) {
68 final Presences presences = contact.getPresences();
69 if (presences.isEmpty() && allowFallback && contact.getAccount().isEnabled()) {
70 return contact.getRtpCapability();
71 }
72 Capability result = Capability.NONE;
73 for (final Presence presence : presences.getPresences()) {
74 Capability capability = check(presence);
75 if (capability == Capability.VIDEO) {
76 result = capability;
77 } else if (capability == Capability.AUDIO && result == Capability.NONE) {
78 result = capability;
79 }
80 }
81 return result;
82 }
83
84 // do all devices that support Rtp Call also support JMI?
85 public static boolean jmiSupport(final Contact contact) {
86 return !Collections2.transform(
87 Collections2.filter(
88 contact.getPresences().getPresences(),
89 p -> RtpCapability.check(p) != RtpCapability.Capability.NONE),
90 p -> {
91 ServiceDiscoveryResult disco = p.getServiceDiscoveryResult();
92 return disco != null && disco.getFeatures().contains(Namespace.JINGLE_MESSAGE);
93 }).contains(false);
94 }
95
96 public enum Capability {
97 NONE, AUDIO, VIDEO;
98
99 public static Capability of(String value) {
100 if (Strings.isNullOrEmpty(value)) {
101 return NONE;
102 }
103 try {
104 return valueOf(value);
105 } catch (IllegalArgumentException e) {
106 return NONE;
107 }
108 }
109 }
110
111}