discover stun server
This commit is contained in:
		
							parent
							
								
									859bc0bef3
								
							
						
					
					
						commit
						ca9b95fc9c
					
				| 
						 | 
				
			
			@ -3,6 +3,7 @@ package eu.siacs.conversations.xml;
 | 
			
		|||
public final class Namespace {
 | 
			
		||||
    public static final String DISCO_ITEMS = "http://jabber.org/protocol/disco#items";
 | 
			
		||||
    public static final String DISCO_INFO = "http://jabber.org/protocol/disco#info";
 | 
			
		||||
    public static final String EXTERNAL_SERVICE_DISCOVERY = "urn:xmpp:extdisco:2";
 | 
			
		||||
    public static final String BLOCKING = "urn:xmpp:blocking";
 | 
			
		||||
    public static final String ROSTER = "jabber:iq:roster";
 | 
			
		||||
    public static final String REGISTER = "jabber:iq:register";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1902,5 +1902,9 @@ public class XmppConnection implements Runnable {
 | 
			
		|||
        public boolean bookmarks2() {
 | 
			
		||||
            return Config.USE_BOOKMARKS2 /* || hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT)*/;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public boolean extendedServiceDiscovery() {
 | 
			
		||||
            return hasDiscoFeature(Jid.of(account.getServer()),Namespace.EXTERNAL_SERVICE_DISCOVERY);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,12 +16,15 @@ import java.util.List;
 | 
			
		|||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import eu.siacs.conversations.Config;
 | 
			
		||||
import eu.siacs.conversations.entities.Account;
 | 
			
		||||
import eu.siacs.conversations.xml.Element;
 | 
			
		||||
import eu.siacs.conversations.xml.Namespace;
 | 
			
		||||
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 | 
			
		||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
 | 
			
		||||
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
 | 
			
		||||
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
 | 
			
		||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
 | 
			
		||||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 | 
			
		||||
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 | 
			
		||||
import rocks.xmpp.addr.Jid;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -51,6 +54,21 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
 | 
			
		|||
        super(jingleConnectionManager, id, initiator);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static State reasonToState(Reason reason) {
 | 
			
		||||
        switch (reason) {
 | 
			
		||||
            case SUCCESS:
 | 
			
		||||
                return State.TERMINATED_SUCCESS;
 | 
			
		||||
            case DECLINE:
 | 
			
		||||
            case BUSY:
 | 
			
		||||
                return State.TERMINATED_DECLINED_OR_BUSY;
 | 
			
		||||
            case CANCEL:
 | 
			
		||||
            case TIMEOUT:
 | 
			
		||||
                return State.TERMINATED_CANCEL_OR_TIMEOUT;
 | 
			
		||||
            default:
 | 
			
		||||
                return State.TERMINATED_CONNECTIVITY_ERROR;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    void deliverPacket(final JinglePacket jinglePacket) {
 | 
			
		||||
        Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": packet delivered to JingleRtpConnection");
 | 
			
		||||
| 
						 | 
				
			
			@ -85,21 +103,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
 | 
			
		|||
        jingleConnectionManager.finishConnection(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static State reasonToState(Reason reason) {
 | 
			
		||||
        switch (reason) {
 | 
			
		||||
            case SUCCESS:
 | 
			
		||||
                return State.TERMINATED_SUCCESS;
 | 
			
		||||
            case DECLINE:
 | 
			
		||||
            case BUSY:
 | 
			
		||||
                return State.TERMINATED_DECLINED_OR_BUSY;
 | 
			
		||||
            case CANCEL:
 | 
			
		||||
            case TIMEOUT:
 | 
			
		||||
                return State.TERMINATED_CANCEL_OR_TIMEOUT;
 | 
			
		||||
            default:
 | 
			
		||||
                return State.TERMINATED_CONNECTIVITY_ERROR;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void receiveTransportInfo(final JinglePacket jinglePacket) {
 | 
			
		||||
        if (isInState(State.SESSION_INITIALIZED, State.SESSION_INITIALIZED_PRE_APPROVED, State.SESSION_ACCEPTED)) {
 | 
			
		||||
            final RtpContentMap contentMap;
 | 
			
		||||
| 
						 | 
				
			
			@ -211,22 +214,24 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
 | 
			
		|||
        if (rtpContentMap == null) {
 | 
			
		||||
            throw new IllegalStateException("initiator RTP Content Map has not been set");
 | 
			
		||||
        }
 | 
			
		||||
        setupWebRTC();
 | 
			
		||||
        final org.webrtc.SessionDescription offer = new org.webrtc.SessionDescription(
 | 
			
		||||
                org.webrtc.SessionDescription.Type.OFFER,
 | 
			
		||||
                SessionDescription.of(rtpContentMap).toString()
 | 
			
		||||
        );
 | 
			
		||||
        try {
 | 
			
		||||
            this.webRTCWrapper.setRemoteDescription(offer).get();
 | 
			
		||||
            org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createAnswer().get();
 | 
			
		||||
            final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
 | 
			
		||||
            final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription);
 | 
			
		||||
            sendSessionAccept(respondingRtpContentMap);
 | 
			
		||||
            this.webRTCWrapper.setLocalDescription(webRTCSessionDescription);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            Log.d(Config.LOGTAG, "unable to send session accept", e);
 | 
			
		||||
        discoverIceServers(iceServers -> {
 | 
			
		||||
            setupWebRTC(iceServers);
 | 
			
		||||
            final org.webrtc.SessionDescription offer = new org.webrtc.SessionDescription(
 | 
			
		||||
                    org.webrtc.SessionDescription.Type.OFFER,
 | 
			
		||||
                    SessionDescription.of(rtpContentMap).toString()
 | 
			
		||||
            );
 | 
			
		||||
            try {
 | 
			
		||||
                this.webRTCWrapper.setRemoteDescription(offer).get();
 | 
			
		||||
                org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createAnswer().get();
 | 
			
		||||
                final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
 | 
			
		||||
                final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription);
 | 
			
		||||
                sendSessionAccept(respondingRtpContentMap);
 | 
			
		||||
                this.webRTCWrapper.setLocalDescription(webRTCSessionDescription);
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                Log.d(Config.LOGTAG, "unable to send session accept", e);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void sendSessionAccept(final RtpContentMap rtpContentMap) {
 | 
			
		||||
| 
						 | 
				
			
			@ -346,17 +351,19 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
 | 
			
		|||
 | 
			
		||||
    private void sendSessionInitiate(final State targetState) {
 | 
			
		||||
        Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": prepare session-initiate");
 | 
			
		||||
        setupWebRTC();
 | 
			
		||||
        try {
 | 
			
		||||
            org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createOffer().get();
 | 
			
		||||
            final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
 | 
			
		||||
            Log.d(Config.LOGTAG, "description: " + webRTCSessionDescription.description);
 | 
			
		||||
            final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription);
 | 
			
		||||
            sendSessionInitiate(rtpContentMap, targetState);
 | 
			
		||||
            this.webRTCWrapper.setLocalDescription(webRTCSessionDescription).get();
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            Log.d(Config.LOGTAG, "unable to sendSessionInitiate", e);
 | 
			
		||||
        }
 | 
			
		||||
        discoverIceServers(iceServers -> {
 | 
			
		||||
            setupWebRTC(iceServers);
 | 
			
		||||
            try {
 | 
			
		||||
                org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createOffer().get();
 | 
			
		||||
                final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
 | 
			
		||||
                Log.d(Config.LOGTAG, "description: " + webRTCSessionDescription.description);
 | 
			
		||||
                final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription);
 | 
			
		||||
                sendSessionInitiate(rtpContentMap, targetState);
 | 
			
		||||
                this.webRTCWrapper.setLocalDescription(webRTCSessionDescription).get();
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                Log.d(Config.LOGTAG, "unable to sendSessionInitiate", e);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void sendSessionInitiate(RtpContentMap rtpContentMap, final State targetState) {
 | 
			
		||||
| 
						 | 
				
			
			@ -481,9 +488,9 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setupWebRTC() {
 | 
			
		||||
    private void setupWebRTC(final List<PeerConnection.IceServer> iceServers) {
 | 
			
		||||
        this.webRTCWrapper.setup(this.xmppConnectionService);
 | 
			
		||||
        this.webRTCWrapper.initializePeerConnection();
 | 
			
		||||
        this.webRTCWrapper.initializePeerConnection(iceServers);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void acceptCallFromProposed() {
 | 
			
		||||
| 
						 | 
				
			
			@ -559,4 +566,51 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
 | 
			
		|||
    private void updateEndUserState() {
 | 
			
		||||
        xmppConnectionService.notifyJingleRtpConnectionUpdate(id.account, id.with, id.sessionId, getEndUserState());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void discoverIceServers(final OnIceServersDiscovered onIceServersDiscovered) {
 | 
			
		||||
        if (id.account.getXmppConnection().getFeatures().extendedServiceDiscovery()) {
 | 
			
		||||
            final IqPacket request = new IqPacket(IqPacket.TYPE.GET);
 | 
			
		||||
            request.setTo(Jid.of(id.account.getJid().getDomain()));
 | 
			
		||||
            request.addChild("services", Namespace.EXTERNAL_SERVICE_DISCOVERY);
 | 
			
		||||
            xmppConnectionService.sendIqPacket(id.account, request, (account, response) -> {
 | 
			
		||||
                ImmutableList.Builder<PeerConnection.IceServer> listBuilder = new ImmutableList.Builder<>();
 | 
			
		||||
                if (response.getType() == IqPacket.TYPE.RESULT) {
 | 
			
		||||
                    final Element services = response.findChild("services", Namespace.EXTERNAL_SERVICE_DISCOVERY);
 | 
			
		||||
                    final List<Element> children = services == null ? Collections.emptyList() : services.getChildren();
 | 
			
		||||
                    for (final Element child : children) {
 | 
			
		||||
                        if ("service".equals(child.getName())) {
 | 
			
		||||
                            final String type = child.getAttribute("type");
 | 
			
		||||
                            final String host = child.getAttribute("host");
 | 
			
		||||
                            final String port = child.getAttribute("port");
 | 
			
		||||
                            final String transport = child.getAttribute("transport");
 | 
			
		||||
                            final String username = child.getAttribute("username");
 | 
			
		||||
                            final String password = child.getAttribute("password");
 | 
			
		||||
                            if (Arrays.asList("stun", "type").contains(type) && host != null && port != null && "udp".equals(transport)) {
 | 
			
		||||
                                PeerConnection.IceServer.Builder iceServerBuilder = PeerConnection.IceServer.builder(String.format("%s:%s:%s", type, host, port));
 | 
			
		||||
                                if (username != null && password != null) {
 | 
			
		||||
                                    iceServerBuilder.setUsername(username);
 | 
			
		||||
                                    iceServerBuilder.setPassword(password);
 | 
			
		||||
                                }
 | 
			
		||||
                                final PeerConnection.IceServer iceServer = iceServerBuilder.createIceServer();
 | 
			
		||||
                                Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": discovered ICE Server: " + iceServer);
 | 
			
		||||
                                listBuilder.add(iceServer);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                List<PeerConnection.IceServer> iceServers = listBuilder.build();
 | 
			
		||||
                if (iceServers.size() == 0) {
 | 
			
		||||
                    Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": no ICE server found " + response);
 | 
			
		||||
                }
 | 
			
		||||
                onIceServersDiscovered.onIceServersDiscovered(iceServers);
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": has no external service discovery");
 | 
			
		||||
            onIceServersDiscovered.onIceServersDiscovered(Collections.emptyList());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private interface OnIceServersDiscovered {
 | 
			
		||||
        void onIceServersDiscovered(List<PeerConnection.IceServer> iceServers);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -132,7 +132,7 @@ public class WebRTCWrapper {
 | 
			
		|||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void initializePeerConnection() {
 | 
			
		||||
    public void initializePeerConnection(final List<PeerConnection.IceServer> iceServers) {
 | 
			
		||||
        PeerConnectionFactory peerConnectionFactory = PeerConnectionFactory.builder().createPeerConnectionFactory();
 | 
			
		||||
 | 
			
		||||
        CameraVideoCapturer capturer = null;
 | 
			
		||||
| 
						 | 
				
			
			@ -193,10 +193,6 @@ public class WebRTCWrapper {
 | 
			
		|||
 | 
			
		||||
        this.localVideoTrack = videoTrack;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        final List<PeerConnection.IceServer> iceServers = ImmutableList.of(
 | 
			
		||||
                PeerConnection.IceServer.builder("stun:xmpp.conversations.im:3478").createIceServer()
 | 
			
		||||
        );
 | 
			
		||||
        final PeerConnection peerConnection = peerConnectionFactory.createPeerConnection(iceServers, peerConnectionObserver);
 | 
			
		||||
        if (peerConnection == null) {
 | 
			
		||||
            throw new IllegalStateException("Unable to create PeerConnection");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue