From 2591a96945edf67af5593ceaa822db4c0df5092e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 5 Apr 2020 13:58:05 +0200 Subject: [PATCH] sdp candidate to transport-info --- .../xmpp/jingle/JingleRtpConnection.java | 60 +++++++++++++++++-- .../xmpp/jingle/RtpContentMap.java | 16 ++++- .../jingle/stanzas/IceUdpTransportInfo.java | 42 +++++++++++++ 3 files changed, 112 insertions(+), 6 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index 6165fde34..2d0e953ad 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -42,6 +42,7 @@ public class JingleRtpConnection extends AbstractJingleConnection { } private State state = State.NULL; + private RtpContentMap initialRtpContentMap; public JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) { @@ -152,9 +153,32 @@ public class JingleRtpConnection extends AbstractJingleConnection { } private void sendSessionInitiate(RtpContentMap rtpContentMap) { - Log.d(Config.LOGTAG, rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_INITIATE, id.sessionId).toString()); + this.initialRtpContentMap = rtpContentMap; + final JinglePacket sessionInitiate = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_INITIATE, id.sessionId); + Log.d(Config.LOGTAG, sessionInitiate.toString()); + send(sessionInitiate); } + private void sendTransportInfo(final String contentName, IceUdpTransportInfo.Candidate candidate) { + final RtpContentMap transportInfo; + try { + transportInfo = this.initialRtpContentMap.transportInfo(contentName, candidate); + } catch (Exception e) { + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to prepare transport-info from candidate for content=" + contentName); + return; + } + final JinglePacket jinglePacket = transportInfo.toJinglePacket(JinglePacket.Action.TRANSPORT_INFO, id.sessionId); + Log.d(Config.LOGTAG, jinglePacket.toString()); + send(jinglePacket); + } + + private void send(final JinglePacket jinglePacket) { + jinglePacket.setTo(id.with); + //TODO track errors + xmppConnectionService.sendIqPacket(id.account, jinglePacket, null); + } + + private void sendSessionAccept() { Log.d(Config.LOGTAG, "sending session-accept"); } @@ -186,7 +210,7 @@ public class JingleRtpConnection extends AbstractJingleConnection { stream.addTrack(audioTrack); - PeerConnection peer = peerConnectionFactory.createPeerConnection(Collections.emptyList(), new PeerConnection.Observer() { + PeerConnection peerConnection = peerConnectionFactory.createPeerConnection(Collections.emptyList(), new PeerConnection.Observer() { @Override public void onSignalingChange(PeerConnection.SignalingState signalingState) { @@ -204,11 +228,16 @@ public class JingleRtpConnection extends AbstractJingleConnection { @Override public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) { - + Log.d(Config.LOGTAG, "onIceGatheringChange() " + iceGatheringState); } @Override public void onIceCandidate(IceCandidate iceCandidate) { + IceUdpTransportInfo.Candidate candidate = IceUdpTransportInfo.Candidate.fromSdpAttribute(iceCandidate.sdp); + Log.d(Config.LOGTAG, "onIceCandidate: " + iceCandidate.sdp); + Log.d(Config.LOGTAG, "xml: " + candidate.toString()); + Log.d(Config.LOGTAG, "mid: " + iceCandidate.sdpMid); + sendTransportInfo(iceCandidate.sdpMid, candidate); } @@ -243,9 +272,9 @@ public class JingleRtpConnection extends AbstractJingleConnection { } }); - peer.addStream(stream); + peerConnection.addStream(stream); - peer.createOffer(new SdpObserver() { + peerConnection.createOffer(new SdpObserver() { @Override public void onCreateSuccess(org.webrtc.SessionDescription description) { @@ -253,6 +282,27 @@ public class JingleRtpConnection extends AbstractJingleConnection { Log.d(Config.LOGTAG, "description: " + description.description); final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription); sendSessionInitiate(rtpContentMap); + peerConnection.setLocalDescription(new SdpObserver() { + @Override + public void onCreateSuccess(org.webrtc.SessionDescription sessionDescription) { + + } + + @Override + public void onSetSuccess() { + Log.d(Config.LOGTAG, "onSetSuccess()"); + } + + @Override + public void onCreateFailure(String s) { + + } + + @Override + public void onSetFailure(String s) { + + } + }, description); } @Override diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java index f1dd58aba..445d34dad 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java @@ -54,13 +54,27 @@ public class RtpContentMap { } for (Map.Entry entry : this.contents.entrySet()) { final Content content = new Content(Content.Creator.INITIATOR, entry.getKey()); - content.addChild(entry.getValue().description); + if (entry.getValue().description != null) { + content.addChild(entry.getValue().description); + } content.addChild(entry.getValue().transport); jinglePacket.addJingleContent(content); } return jinglePacket; } + public RtpContentMap transportInfo(final String contentName, final IceUdpTransportInfo.Candidate candidate) { + final RtpContentMap.DescriptionTransport descriptionTransport = contents.get(contentName); + final IceUdpTransportInfo transportInfo = descriptionTransport == null ? null : descriptionTransport.transport; + if (transportInfo == null) { + throw new IllegalArgumentException("Unable to find transport info for content name "+contentName); + } + final IceUdpTransportInfo newTransportInfo = transportInfo.cloneWrapper(); + newTransportInfo.addChild(candidate); + return new RtpContentMap(null, ImmutableMap.of(contentName, new DescriptionTransport(null,newTransportInfo))); + + } + public static class DescriptionTransport { public final RtpDescription description; public final IceUdpTransportInfo transport; diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java index 275f6cd2b..e4d953c83 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java @@ -1,12 +1,17 @@ package eu.siacs.conversations.xmpp.jingle.stanzas; +import android.util.Log; + import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import java.util.HashMap; +import java.util.Hashtable; import java.util.List; +import eu.siacs.conversations.Config; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xmpp.jingle.SessionDescription; @@ -41,6 +46,12 @@ public class IceUdpTransportInfo extends GenericTransportInfo { return transportInfo; } + public IceUdpTransportInfo cloneWrapper() { + final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo(); + transportInfo.setAttributes(new Hashtable<>(getAttributes())); + return transportInfo; + } + public static IceUdpTransportInfo of(SessionDescription sessionDescription, SessionDescription.Media media) { final String ufrag = Iterables.getFirst(media.attributes.get("ice-ufrag"), null); final String pwd = Iterables.getFirst(media.attributes.get("ice-pwd"), null); @@ -132,6 +143,37 @@ public class IceUdpTransportInfo extends GenericTransportInfo { candidate.setChildren(element.getChildren()); return candidate; } + + // https://tools.ietf.org/html/draft-ietf-mmusic-ice-sip-sdp-39#section-5.1 + public static Candidate fromSdpAttribute(final String attribute) { + final String[] pair = attribute.split(":", 2); + if (pair.length == 2 && "candidate".equals(pair[0])) { + final String[] segments = pair[1].split(" "); + if (segments.length >= 6) { + final String foundation = segments[0]; + final String component = segments[1]; + final String transport = segments[2]; + final String priority = segments[3]; + final String connectionAddress = segments[4]; + final String port = segments[5]; + final HashMap additional = new HashMap<>(); + for (int i = 6; i < segments.length - 1; i = i + 2) { + additional.put(segments[i], segments[i + 1]); + } + final Candidate candidate = new Candidate(); + candidate.setAttribute("component", component); + candidate.setAttribute("foundation", foundation); + candidate.setAttribute("generation", additional.get("generation")); + candidate.setAttribute("ip", connectionAddress); + candidate.setAttribute("port", port); + candidate.setAttribute("priority", priority); + candidate.setAttribute("protocol", transport); + candidate.setAttribute("type", additional.get("typ")); + return candidate; + } + } + return null; + } }