add helper methods for content modification to RtpContentMap

This commit is contained in:
Daniel Gultsch 2022-11-22 10:13:48 +01:00
parent e2f98f6bbc
commit f4be142e4d
3 changed files with 137 additions and 22 deletions

View file

@ -1,7 +1,9 @@
package eu.siacs.conversations.xmpp.jingle; package eu.siacs.conversations.xmpp.jingle;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -236,6 +238,23 @@ public class RtpContentMap {
throw new IllegalStateException("Content map doesn't have distinct DTLS setup"); throw new IllegalStateException("Content map doesn't have distinct DTLS setup");
} }
private DTLS getDistinctDtls() {
final Set<DTLS> dtlsSet =
ImmutableSet.copyOf(
Collections2.transform(
contents.values(),
dt -> {
final IceUdpTransportInfo.Fingerprint fp =
dt.transport.getFingerprint();
return new DTLS(fp.getHash(), fp.getSetup(), fp.getContent());
}));
final DTLS dtls = Iterables.getFirst(dtlsSet, null);
if (dtlsSet.size() == 1 && dtls != null) {
return dtls;
}
throw new IllegalStateException("Content map doesn't have distinct DTLS setup");
}
public boolean emptyCandidates() { public boolean emptyCandidates() {
int count = 0; int count = 0;
for (DescriptionTransport descriptionTransport : contents.values()) { for (DescriptionTransport descriptionTransport : contents.values()) {
@ -262,12 +281,22 @@ public class RtpContentMap {
return new RtpContentMap(this.group, contentMapBuilder.build()); return new RtpContentMap(this.group, contentMapBuilder.build());
} }
public RtpContentMap toContentModification(final Collection<String> modifications) {
return new RtpContentMap(
this.group,
Maps.transformValues(
Maps.filterKeys(contents, Predicates.in(modifications)),
dt ->
new DescriptionTransport(
dt.senders, dt.description, IceUdpTransportInfo.STUB)));
}
public Diff diff(final RtpContentMap rtpContentMap) { public Diff diff(final RtpContentMap rtpContentMap) {
final Set<String> existingContentIds = this.contents.keySet(); final Set<String> existingContentIds = this.contents.keySet();
final Set<String> newContentIds = rtpContentMap.contents.keySet(); final Set<String> newContentIds = rtpContentMap.contents.keySet();
return new Diff( return new Diff(
Sets.difference(newContentIds, existingContentIds), ImmutableSet.copyOf(Sets.difference(newContentIds, existingContentIds)),
Sets.difference(existingContentIds, newContentIds)); ImmutableSet.copyOf(Sets.difference(existingContentIds, newContentIds)));
} }
public boolean iceRestart(final RtpContentMap rtpContentMap) { public boolean iceRestart(final RtpContentMap rtpContentMap) {
@ -278,6 +307,26 @@ public class RtpContentMap {
} }
} }
public RtpContentMap addContent(final RtpContentMap modification) {
final IceUdpTransportInfo.Credentials credentials = getDistinctCredentials();
final DTLS dtls = getDistinctDtls();
final IceUdpTransportInfo iceUdpTransportInfo =
IceUdpTransportInfo.of(credentials, dtls.setup, dtls.hash, dtls.fingerprint);
final Map<String, DescriptionTransport> combined =
new ImmutableMap.Builder<String, DescriptionTransport>()
.putAll(contents)
.putAll(
Maps.transformValues(
modification.contents,
dt ->
new DescriptionTransport(
dt.senders,
dt.description,
iceUdpTransportInfo)))
.build();
return new RtpContentMap(modification.group, combined);
}
public static class DescriptionTransport { public static class DescriptionTransport {
public final Content.Senders senders; public final Content.Senders senders;
public final RtpDescription description; public final RtpDescription description;
@ -370,4 +419,31 @@ public class RtpContentMap {
.toString(); .toString();
} }
} }
public static final class DTLS {
public final String hash;
public final IceUdpTransportInfo.Setup setup;
public final String fingerprint;
private DTLS(String hash, IceUdpTransportInfo.Setup setup, String fingerprint) {
this.hash = hash;
this.setup = setup;
this.fingerprint = fingerprint;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DTLS dtls = (DTLS) o;
return Objects.equal(hash, dtls.hash)
&& setup == dtls.setup
&& Objects.equal(fingerprint, dtls.fingerprint);
}
@Override
public int hashCode() {
return Objects.hashCode(hash, setup, fingerprint);
}
}
} }

View file

@ -186,15 +186,22 @@ public class WebRTCWrapper {
} }
@Override @Override
public void onTrack(RtpTransceiver transceiver) { public void onTrack(final RtpTransceiver transceiver) {
Log.d( Log.d(
EXTENDED_LOGGING_TAG, EXTENDED_LOGGING_TAG,
"onTrack(mid=" "onTrack(mid="
+ transceiver.getMid() + transceiver.getMid()
+ ",media=" + ",media="
+ transceiver.getMediaType() + transceiver.getMediaType()
+ ",direction="
+ transceiver.getDirection()
+ ")"); + ")");
} }
@Override
public void onRemoveTrack(final RtpReceiver receiver) {
Log.d(EXTENDED_LOGGING_TAG, "onRemoveTrack(" + receiver.id() + ")");
}
}; };
@Nullable private PeerConnectionFactory peerConnectionFactory = null; @Nullable private PeerConnectionFactory peerConnectionFactory = null;
@Nullable private PeerConnection peerConnection = null; @Nullable private PeerConnection peerConnection = null;

View file

@ -12,8 +12,6 @@ import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -28,23 +26,29 @@ import eu.siacs.conversations.xmpp.jingle.SessionDescription;
public class IceUdpTransportInfo extends GenericTransportInfo { public class IceUdpTransportInfo extends GenericTransportInfo {
public static final IceUdpTransportInfo STUB = new IceUdpTransportInfo();
public IceUdpTransportInfo() { public IceUdpTransportInfo() {
super("transport", Namespace.JINGLE_TRANSPORT_ICE_UDP); super("transport", Namespace.JINGLE_TRANSPORT_ICE_UDP);
} }
public static IceUdpTransportInfo upgrade(final Element element) { public static IceUdpTransportInfo upgrade(final Element element) {
Preconditions.checkArgument("transport".equals(element.getName()), "Name of provided element is not transport"); Preconditions.checkArgument(
Preconditions.checkArgument(Namespace.JINGLE_TRANSPORT_ICE_UDP.equals(element.getNamespace()), "Element does not match ice-udp transport namespace"); "transport".equals(element.getName()), "Name of provided element is not transport");
Preconditions.checkArgument(
Namespace.JINGLE_TRANSPORT_ICE_UDP.equals(element.getNamespace()),
"Element does not match ice-udp transport namespace");
final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo(); final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo();
transportInfo.setAttributes(element.getAttributes()); transportInfo.setAttributes(element.getAttributes());
transportInfo.setChildren(element.getChildren()); transportInfo.setChildren(element.getChildren());
return transportInfo; return transportInfo;
} }
public static IceUdpTransportInfo of(SessionDescription sessionDescription, SessionDescription.Media media) { public static IceUdpTransportInfo of(
SessionDescription sessionDescription, SessionDescription.Media media) {
final String ufrag = Iterables.getFirst(media.attributes.get("ice-ufrag"), null); final String ufrag = Iterables.getFirst(media.attributes.get("ice-ufrag"), null);
final String pwd = Iterables.getFirst(media.attributes.get("ice-pwd"), null); final String pwd = Iterables.getFirst(media.attributes.get("ice-pwd"), null);
IceUdpTransportInfo iceUdpTransportInfo = new IceUdpTransportInfo(); final IceUdpTransportInfo iceUdpTransportInfo = new IceUdpTransportInfo();
if (ufrag != null) { if (ufrag != null) {
iceUdpTransportInfo.setAttribute("ufrag", ufrag); iceUdpTransportInfo.setAttribute("ufrag", ufrag);
} }
@ -56,7 +60,15 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
iceUdpTransportInfo.addChild(fingerprint); iceUdpTransportInfo.addChild(fingerprint);
} }
return iceUdpTransportInfo; return iceUdpTransportInfo;
}
public static IceUdpTransportInfo of(
final Credentials credentials, final Setup setup, final String hash, final String fingerprint) {
final IceUdpTransportInfo iceUdpTransportInfo = new IceUdpTransportInfo();
iceUdpTransportInfo.addChild(Fingerprint.of(setup, hash, fingerprint));
iceUdpTransportInfo.setAttribute("ufrag", credentials.ufrag);
iceUdpTransportInfo.setAttribute("pwd", credentials.password);
return iceUdpTransportInfo;
} }
public Fingerprint getFingerprint() { public Fingerprint getFingerprint() {
@ -91,7 +103,8 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
transportInfo.setAttribute("ufrag", credentials.ufrag); transportInfo.setAttribute("ufrag", credentials.ufrag);
transportInfo.setAttribute("pwd", credentials.password); transportInfo.setAttribute("pwd", credentials.password);
for (final Element child : getChildren()) { for (final Element child : getChildren()) {
if (child.getName().equals("fingerprint") && Namespace.JINGLE_APPS_DTLS.equals(child.getNamespace())) { if (child.getName().equals("fingerprint")
&& Namespace.JINGLE_APPS_DTLS.equals(child.getNamespace())) {
final Fingerprint fingerprint = new Fingerprint(); final Fingerprint fingerprint = new Fingerprint();
fingerprint.setAttributes(new Hashtable<>(child.getAttributes())); fingerprint.setAttributes(new Hashtable<>(child.getAttributes()));
fingerprint.setContent(child.getContent()); fingerprint.setContent(child.getContent());
@ -231,7 +244,7 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
return getAttributeAsInt("rel-port"); return getAttributeAsInt("rel-port");
} }
public String getType() { //TODO might be converted to enum public String getType() { // TODO might be converted to enum
return getAttribute("type"); return getAttribute("type");
} }
@ -256,7 +269,8 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
checkNotNullNoWhitespace(protocol, "protocol"); checkNotNullNoWhitespace(protocol, "protocol");
final String transport = protocol.toLowerCase(Locale.ROOT); final String transport = protocol.toLowerCase(Locale.ROOT);
if (!"udp".equals(transport)) { if (!"udp".equals(transport)) {
throw new IllegalArgumentException(String.format("'%s' is not a supported protocol", transport)); throw new IllegalArgumentException(
String.format("'%s' is not a supported protocol", transport));
} }
final String priority = this.getAttribute("priority"); final String priority = this.getAttribute("priority");
checkNotNullNoWhitespace(priority, "priority"); checkNotNullNoWhitespace(priority, "priority");
@ -284,7 +298,15 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
if (ufrag != null) { if (ufrag != null) {
additionalParameter.put("ufrag", ufrag); additionalParameter.put("ufrag", ufrag);
} }
final String parametersString = Joiner.on(' ').join(Collections2.transform(additionalParameter.entrySet(), input -> String.format("%s %s", input.getKey(), input.getValue()))); final String parametersString =
Joiner.on(' ')
.join(
Collections2.transform(
additionalParameter.entrySet(),
input ->
String.format(
"%s %s",
input.getKey(), input.getValue())));
return String.format( return String.format(
"candidate:%s %s %s %s %s %s %s", "candidate:%s %s %s %s %s %s %s",
foundation, foundation,
@ -293,20 +315,19 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
priority, priority,
connectionAddress, connectionAddress,
port, port,
parametersString parametersString);
);
} }
} }
private static void checkNotNullNoWhitespace(final String value, final String name) { private static void checkNotNullNoWhitespace(final String value, final String name) {
if (Strings.isNullOrEmpty(value)) { if (Strings.isNullOrEmpty(value)) {
throw new IllegalArgumentException(String.format("Parameter %s is missing or empty", name)); throw new IllegalArgumentException(
String.format("Parameter %s is missing or empty", name));
} }
SessionDescription.checkNoWhitespace(value, String.format("Parameter %s contains white spaces", name)); SessionDescription.checkNoWhitespace(
value, String.format("Parameter %s contains white spaces", name));
} }
public static class Fingerprint extends Element { public static class Fingerprint extends Element {
private Fingerprint() { private Fingerprint() {
@ -340,11 +361,20 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
return null; return null;
} }
public static Fingerprint of(final SessionDescription sessionDescription, final SessionDescription.Media media) { public static Fingerprint of(
final SessionDescription sessionDescription, final SessionDescription.Media media) {
final Fingerprint fingerprint = of(media.attributes); final Fingerprint fingerprint = of(media.attributes);
return fingerprint == null ? of(sessionDescription.attributes) : fingerprint; return fingerprint == null ? of(sessionDescription.attributes) : fingerprint;
} }
private static Fingerprint of(final Setup setup, final String hash, final String content) {
final Fingerprint fingerprint = new Fingerprint();
fingerprint.setContent(content);
fingerprint.setAttribute("hash", hash);
fingerprint.setAttribute("setup", setup.toString().toLowerCase(Locale.ROOT));
return fingerprint;
}
public String getHash() { public String getHash() {
return this.getAttribute("hash"); return this.getAttribute("hash");
} }
@ -356,7 +386,9 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
} }
public enum Setup { public enum Setup {
ACTPASS, PASSIVE, ACTIVE; ACTPASS,
PASSIVE,
ACTIVE;
public static Setup of(String setup) { public static Setup of(String setup) {
try { try {
@ -373,7 +405,7 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
if (this == ACTIVE) { if (this == ACTIVE) {
return PASSIVE; return PASSIVE;
} }
throw new IllegalStateException(this.name()+" can not be flipped"); throw new IllegalStateException(this.name() + " can not be flipped");
} }
} }
} }