@@ -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
@@ -54,13 +54,27 @@ public class RtpContentMap {
}
for (Map.Entry<String, DescriptionTransport> 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;
@@ -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<String, String> 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;
+ }
}