receive candidates/transport-info
This commit is contained in:
		
							parent
							
								
									885ec0febe
								
							
						
					
					
						commit
						ac9a1a773e
					
				@ -16,16 +16,19 @@ import org.webrtc.PeerConnectionFactory;
 | 
				
			|||||||
import org.webrtc.RtpReceiver;
 | 
					import org.webrtc.RtpReceiver;
 | 
				
			||||||
import org.webrtc.SdpObserver;
 | 
					import org.webrtc.SdpObserver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayDeque;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
import java.util.Collection;
 | 
					import java.util.Collection;
 | 
				
			||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import eu.siacs.conversations.Config;
 | 
					import eu.siacs.conversations.Config;
 | 
				
			||||||
import eu.siacs.conversations.xml.Element;
 | 
					import eu.siacs.conversations.xml.Element;
 | 
				
			||||||
import eu.siacs.conversations.xml.Namespace;
 | 
					import eu.siacs.conversations.xml.Namespace;
 | 
				
			||||||
 | 
					import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
 | 
				
			||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
 | 
					import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
 | 
				
			||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
 | 
					import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
 | 
				
			||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
 | 
					 | 
				
			||||||
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 | 
					import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 | 
				
			||||||
import rocks.xmpp.addr.Jid;
 | 
					import rocks.xmpp.addr.Jid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -45,6 +48,8 @@ public class JingleRtpConnection extends AbstractJingleConnection {
 | 
				
			|||||||
    private RtpContentMap initialRtpContentMap;
 | 
					    private RtpContentMap initialRtpContentMap;
 | 
				
			||||||
    private PeerConnection peerConnection;
 | 
					    private PeerConnection peerConnection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final ArrayDeque<IceCandidate> pendingIceCandidates = new ArrayDeque<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) {
 | 
					    public JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) {
 | 
				
			||||||
        super(jingleConnectionManager, id, initiator);
 | 
					        super(jingleConnectionManager, id, initiator);
 | 
				
			||||||
@ -57,14 +62,51 @@ public class JingleRtpConnection extends AbstractJingleConnection {
 | 
				
			|||||||
            case SESSION_INITIATE:
 | 
					            case SESSION_INITIATE:
 | 
				
			||||||
                receiveSessionInitiate(jinglePacket);
 | 
					                receiveSessionInitiate(jinglePacket);
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					            case TRANSPORT_INFO:
 | 
				
			||||||
 | 
					                receiveTransportInfo(jinglePacket);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                Log.d(Config.LOGTAG, String.format("%s: received unhandled jingle action %s", id.account.getJid().asBareJid(), jinglePacket.getAction()));
 | 
					                Log.d(Config.LOGTAG, String.format("%s: received unhandled jingle action %s", id.account.getJid().asBareJid(), jinglePacket.getAction()));
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void receiveTransportInfo(final JinglePacket jinglePacket) {
 | 
				
			||||||
 | 
					        if (isInState(State.SESSION_INITIALIZED, State.SESSION_ACCEPTED)) {
 | 
				
			||||||
 | 
					            final RtpContentMap contentMap;
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                contentMap = RtpContentMap.of(jinglePacket);
 | 
				
			||||||
 | 
					            } catch (IllegalArgumentException | NullPointerException e) {
 | 
				
			||||||
 | 
					                Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents", e);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            final Group originalGroup = this.initialRtpContentMap != null ? this.initialRtpContentMap.group : null;
 | 
				
			||||||
 | 
					            final List<String> identificationTags = originalGroup == null ? Collections.emptyList() : originalGroup.getIdentificationTags();
 | 
				
			||||||
 | 
					            if (identificationTags.size() == 0) {
 | 
				
			||||||
 | 
					                Log.w(Config.LOGTAG,id.account.getJid().asBareJid()+": no identification tags found in initial offer. we won't be able to calculate mLineIndices");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for(final Map.Entry<String, RtpContentMap.DescriptionTransport> content : contentMap.contents.entrySet()) {
 | 
				
			||||||
 | 
					                final String ufrag = content.getValue().transport.getAttribute("ufrag");
 | 
				
			||||||
 | 
					                for(final IceUdpTransportInfo.Candidate candidate : content.getValue().transport.getCandidates()) {
 | 
				
			||||||
 | 
					                    final String sdp = candidate.toSdpAttribute(ufrag);
 | 
				
			||||||
 | 
					                    final String sdpMid = content.getKey();
 | 
				
			||||||
 | 
					                    final int mLineIndex = identificationTags.indexOf(sdpMid);
 | 
				
			||||||
 | 
					                    final IceCandidate iceCandidate = new IceCandidate(sdpMid, mLineIndex, sdp);
 | 
				
			||||||
 | 
					                    Log.d(Config.LOGTAG,"received candidate: "+iceCandidate);
 | 
				
			||||||
 | 
					                    if (isInState(State.SESSION_ACCEPTED)) {
 | 
				
			||||||
 | 
					                        this.peerConnection.addIceCandidate(iceCandidate);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        this.pendingIceCandidates.push(iceCandidate);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received transport info while in state=" + this.state);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void receiveSessionInitiate(final JinglePacket jinglePacket) {
 | 
					    private void receiveSessionInitiate(final JinglePacket jinglePacket) {
 | 
				
			||||||
        Log.d(Config.LOGTAG,jinglePacket.toString());
 | 
					        Log.d(Config.LOGTAG, jinglePacket.toString());
 | 
				
			||||||
        if (isInitiator()) {
 | 
					        if (isInitiator()) {
 | 
				
			||||||
            Log.d(Config.LOGTAG, String.format("%s: received session-initiate even though we were initiating", id.account.getJid().asBareJid()));
 | 
					            Log.d(Config.LOGTAG, String.format("%s: received session-initiate even though we were initiating", id.account.getJid().asBareJid()));
 | 
				
			||||||
            //TODO respond with out-of-order
 | 
					            //TODO respond with out-of-order
 | 
				
			||||||
@ -73,13 +115,15 @@ public class JingleRtpConnection extends AbstractJingleConnection {
 | 
				
			|||||||
        final RtpContentMap contentMap;
 | 
					        final RtpContentMap contentMap;
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            contentMap = RtpContentMap.of(jinglePacket);
 | 
					            contentMap = RtpContentMap.of(jinglePacket);
 | 
				
			||||||
        } catch (IllegalArgumentException | NullPointerException e) {
 | 
					            contentMap.requireContentDescriptions();
 | 
				
			||||||
 | 
					        } catch (IllegalArgumentException | IllegalStateException | NullPointerException e) {
 | 
				
			||||||
            Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents", e);
 | 
					            Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents", e);
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Log.d(Config.LOGTAG, "processing session-init with " + contentMap.contents.size() + " contents");
 | 
					        Log.d(Config.LOGTAG, "processing session-init with " + contentMap.contents.size() + " contents");
 | 
				
			||||||
        final State oldState = this.state;
 | 
					        final State oldState = this.state;
 | 
				
			||||||
        if (transition(State.SESSION_INITIALIZED)) {
 | 
					        if (transition(State.SESSION_INITIALIZED)) {
 | 
				
			||||||
 | 
					            this.initialRtpContentMap = contentMap;
 | 
				
			||||||
            if (oldState == State.PROCEED) {
 | 
					            if (oldState == State.PROCEED) {
 | 
				
			||||||
                processContents(contentMap);
 | 
					                processContents(contentMap);
 | 
				
			||||||
                sendSessionAccept();
 | 
					                sendSessionAccept();
 | 
				
			||||||
@ -225,7 +269,10 @@ public class JingleRtpConnection extends AbstractJingleConnection {
 | 
				
			|||||||
        stream.addTrack(audioTrack);
 | 
					        stream.addTrack(audioTrack);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.peerConnection = peerConnectionFactory.createPeerConnection(Collections.emptyList(), new PeerConnection.Observer() {
 | 
					        final List<PeerConnection.IceServer> iceServers = ImmutableList.of(
 | 
				
			||||||
 | 
					                PeerConnection.IceServer.builder("stun:xmpp.conversations.im:3478").createIceServer()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        this.peerConnection = peerConnectionFactory.createPeerConnection(iceServers, new PeerConnection.Observer() {
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void onSignalingChange(PeerConnection.SignalingState signalingState) {
 | 
					            public void onSignalingChange(PeerConnection.SignalingState signalingState) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -249,7 +296,7 @@ public class JingleRtpConnection extends AbstractJingleConnection {
 | 
				
			|||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void onIceCandidate(IceCandidate iceCandidate) {
 | 
					            public void onIceCandidate(IceCandidate iceCandidate) {
 | 
				
			||||||
                IceUdpTransportInfo.Candidate candidate = IceUdpTransportInfo.Candidate.fromSdpAttribute(iceCandidate.sdp);
 | 
					                IceUdpTransportInfo.Candidate candidate = IceUdpTransportInfo.Candidate.fromSdpAttribute(iceCandidate.sdp);
 | 
				
			||||||
                Log.d(Config.LOGTAG, "onIceCandidate: " + iceCandidate.sdp);
 | 
					                Log.d(Config.LOGTAG, "onIceCandidate: " + iceCandidate.sdp + " mLineIndex=" + iceCandidate.sdpMLineIndex);
 | 
				
			||||||
                sendTransportInfo(iceCandidate.sdpMid, candidate);
 | 
					                sendTransportInfo(iceCandidate.sdpMid, candidate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -352,6 +399,10 @@ public class JingleRtpConnection extends AbstractJingleConnection {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private synchronized boolean isInState(State... state) {
 | 
				
			||||||
 | 
					        return Arrays.asList(state).contains(this.state);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private synchronized boolean transition(final State target) {
 | 
					    private synchronized boolean transition(final State target) {
 | 
				
			||||||
        final Collection<State> validTransitions = VALID_TRANSITIONS.get(this.state);
 | 
					        final Collection<State> validTransitions = VALID_TRANSITIONS.get(this.state);
 | 
				
			||||||
        if (validTransitions != null && validTransitions.contains(target)) {
 | 
					        if (validTransitions != null && validTransitions.contains(target)) {
 | 
				
			||||||
 | 
				
			|||||||
@ -47,6 +47,17 @@ public class RtpContentMap {
 | 
				
			|||||||
        return new RtpContentMap(group, contentMapBuilder.build());
 | 
					        return new RtpContentMap(group, contentMapBuilder.build());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void requireContentDescriptions() {
 | 
				
			||||||
 | 
					        if (this.contents.size() == 0) {
 | 
				
			||||||
 | 
					            throw new IllegalStateException("No contents available");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for(Map.Entry<String,DescriptionTransport> entry : this.contents.entrySet()) {
 | 
				
			||||||
 | 
					            if (entry.getValue().description == null) {
 | 
				
			||||||
 | 
					                throw new IllegalStateException(String.format("%s is lacking content description", entry.getKey()));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public JinglePacket toJinglePacket(final JinglePacket.Action action, final String sessionId) {
 | 
					    public JinglePacket toJinglePacket(final JinglePacket.Action action, final String sessionId) {
 | 
				
			||||||
        final JinglePacket jinglePacket = new JinglePacket(action, sessionId);
 | 
					        final JinglePacket jinglePacket = new JinglePacket(action, sessionId);
 | 
				
			||||||
        if (this.group != null) {
 | 
					        if (this.group != null) {
 | 
				
			||||||
@ -89,7 +100,9 @@ public class RtpContentMap {
 | 
				
			|||||||
            final GenericTransportInfo transportInfo = content.getTransport();
 | 
					            final GenericTransportInfo transportInfo = content.getTransport();
 | 
				
			||||||
            final RtpDescription rtpDescription;
 | 
					            final RtpDescription rtpDescription;
 | 
				
			||||||
            final IceUdpTransportInfo iceUdpTransportInfo;
 | 
					            final IceUdpTransportInfo iceUdpTransportInfo;
 | 
				
			||||||
            if (description instanceof RtpDescription) {
 | 
					            if (description == null) {
 | 
				
			||||||
 | 
					                rtpDescription = null;
 | 
				
			||||||
 | 
					            } else if (description instanceof RtpDescription) {
 | 
				
			||||||
                rtpDescription = (RtpDescription) description;
 | 
					                rtpDescription = (RtpDescription) description;
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                Log.d(Config.LOGTAG, "description was " + description);
 | 
					                Log.d(Config.LOGTAG, "description was " + description);
 | 
				
			||||||
 | 
				
			|||||||
@ -2,14 +2,22 @@ package eu.siacs.conversations.xmpp.jingle.stanzas;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import android.util.Log;
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.common.base.Function;
 | 
				
			||||||
 | 
					import com.google.common.base.Joiner;
 | 
				
			||||||
import com.google.common.base.Preconditions;
 | 
					import com.google.common.base.Preconditions;
 | 
				
			||||||
import com.google.common.collect.ArrayListMultimap;
 | 
					import com.google.common.collect.ArrayListMultimap;
 | 
				
			||||||
 | 
					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 com.google.common.collect.Lists;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.checkerframework.checker.nullness.compatqual.NullableDecl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Collection;
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.Hashtable;
 | 
					import java.util.Hashtable;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import eu.siacs.conversations.Config;
 | 
					import eu.siacs.conversations.Config;
 | 
				
			||||||
import eu.siacs.conversations.xml.Element;
 | 
					import eu.siacs.conversations.xml.Element;
 | 
				
			||||||
@ -144,6 +152,43 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
 | 
				
			|||||||
            return candidate;
 | 
					            return candidate;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public String toSdpAttribute(final String ufrag) {
 | 
				
			||||||
 | 
					            final String foundation = this.getAttribute("foundation");
 | 
				
			||||||
 | 
					            final String component = this.getAttribute("component");
 | 
				
			||||||
 | 
					            final String transport = this.getAttribute("protocol");
 | 
				
			||||||
 | 
					            final String priority = this.getAttribute("priority");
 | 
				
			||||||
 | 
					            final String connectionAddress = this.getAttribute("ip");
 | 
				
			||||||
 | 
					            final String port = this.getAttribute("port");
 | 
				
			||||||
 | 
					            final Map<String,String> additionalParameter = new HashMap<>();
 | 
				
			||||||
 | 
					            final String relAddr = this.getAttribute("rel-addr");
 | 
				
			||||||
 | 
					            if (relAddr != null) {
 | 
				
			||||||
 | 
					                additionalParameter.put("raddr",relAddr);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            final String relPort = this.getAttribute("rel-port");
 | 
				
			||||||
 | 
					            if (relPort != null) {
 | 
				
			||||||
 | 
					                additionalParameter.put("rport", relPort);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            final String generation = this.getAttribute("generation");
 | 
				
			||||||
 | 
					            if (generation != null) {
 | 
				
			||||||
 | 
					                additionalParameter.put("generation", generation);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (ufrag != null) {
 | 
				
			||||||
 | 
					                additionalParameter.put("ufrag", ufrag);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            final String parametersString = Joiner.on(' ').join(Collections2.transform(additionalParameter.entrySet(), input -> String.format("%s %s",input.getKey(),input.getValue())));
 | 
				
			||||||
 | 
					            return String.format(
 | 
				
			||||||
 | 
					                    "candidate:%s %s %s %s %s %s %s",
 | 
				
			||||||
 | 
					                    foundation,
 | 
				
			||||||
 | 
					                    component,
 | 
				
			||||||
 | 
					                    transport,
 | 
				
			||||||
 | 
					                    priority,
 | 
				
			||||||
 | 
					                    connectionAddress,
 | 
				
			||||||
 | 
					                    port,
 | 
				
			||||||
 | 
					                    parametersString
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // https://tools.ietf.org/html/draft-ietf-mmusic-ice-sip-sdp-39#section-5.1
 | 
					        // https://tools.ietf.org/html/draft-ietf-mmusic-ice-sip-sdp-39#section-5.1
 | 
				
			||||||
        public static Candidate fromSdpAttribute(final String attribute) {
 | 
					        public static Candidate fromSdpAttribute(final String attribute) {
 | 
				
			||||||
            final String[] pair = attribute.split(":", 2);
 | 
					            final String[] pair = attribute.split(":", 2);
 | 
				
			||||||
@ -164,6 +209,8 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
 | 
				
			|||||||
                    candidate.setAttribute("component", component);
 | 
					                    candidate.setAttribute("component", component);
 | 
				
			||||||
                    candidate.setAttribute("foundation", foundation);
 | 
					                    candidate.setAttribute("foundation", foundation);
 | 
				
			||||||
                    candidate.setAttribute("generation", additional.get("generation"));
 | 
					                    candidate.setAttribute("generation", additional.get("generation"));
 | 
				
			||||||
 | 
					                    candidate.setAttribute("rel-addr", additional.get("raddr"));
 | 
				
			||||||
 | 
					                    candidate.setAttribute("rel-port", additional.get("rport"));
 | 
				
			||||||
                    candidate.setAttribute("ip", connectionAddress);
 | 
					                    candidate.setAttribute("ip", connectionAddress);
 | 
				
			||||||
                    candidate.setAttribute("port", port);
 | 
					                    candidate.setAttribute("port", port);
 | 
				
			||||||
                    candidate.setAttribute("priority", priority);
 | 
					                    candidate.setAttribute("priority", priority);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user