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 final class Namespace {
|
||||||
public static final String DISCO_ITEMS = "http://jabber.org/protocol/disco#items";
|
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 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 BLOCKING = "urn:xmpp:blocking";
|
||||||
public static final String ROSTER = "jabber:iq:roster";
|
public static final String ROSTER = "jabber:iq:roster";
|
||||||
public static final String REGISTER = "jabber:iq:register";
|
public static final String REGISTER = "jabber:iq:register";
|
||||||
|
|
|
@ -1902,5 +1902,9 @@ public class XmppConnection implements Runnable {
|
||||||
public boolean bookmarks2() {
|
public boolean bookmarks2() {
|
||||||
return Config.USE_BOOKMARKS2 /* || hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT)*/;
|
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 java.util.Map;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.entities.Account;
|
||||||
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.OnIqPacketReceived;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
|
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.Reason;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
|
||||||
|
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
||||||
import rocks.xmpp.addr.Jid;
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
|
@ -51,6 +54,21 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
super(jingleConnectionManager, id, initiator);
|
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
|
@Override
|
||||||
void deliverPacket(final JinglePacket jinglePacket) {
|
void deliverPacket(final JinglePacket jinglePacket) {
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": packet delivered to JingleRtpConnection");
|
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);
|
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) {
|
private void receiveTransportInfo(final JinglePacket jinglePacket) {
|
||||||
if (isInState(State.SESSION_INITIALIZED, State.SESSION_INITIALIZED_PRE_APPROVED, State.SESSION_ACCEPTED)) {
|
if (isInState(State.SESSION_INITIALIZED, State.SESSION_INITIALIZED_PRE_APPROVED, State.SESSION_ACCEPTED)) {
|
||||||
final RtpContentMap contentMap;
|
final RtpContentMap contentMap;
|
||||||
|
@ -211,22 +214,24 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
if (rtpContentMap == null) {
|
if (rtpContentMap == null) {
|
||||||
throw new IllegalStateException("initiator RTP Content Map has not been set");
|
throw new IllegalStateException("initiator RTP Content Map has not been set");
|
||||||
}
|
}
|
||||||
setupWebRTC();
|
discoverIceServers(iceServers -> {
|
||||||
final org.webrtc.SessionDescription offer = new org.webrtc.SessionDescription(
|
setupWebRTC(iceServers);
|
||||||
org.webrtc.SessionDescription.Type.OFFER,
|
final org.webrtc.SessionDescription offer = new org.webrtc.SessionDescription(
|
||||||
SessionDescription.of(rtpContentMap).toString()
|
org.webrtc.SessionDescription.Type.OFFER,
|
||||||
);
|
SessionDescription.of(rtpContentMap).toString()
|
||||||
try {
|
);
|
||||||
this.webRTCWrapper.setRemoteDescription(offer).get();
|
try {
|
||||||
org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createAnswer().get();
|
this.webRTCWrapper.setRemoteDescription(offer).get();
|
||||||
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
|
org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createAnswer().get();
|
||||||
final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription);
|
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
|
||||||
sendSessionAccept(respondingRtpContentMap);
|
final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription);
|
||||||
this.webRTCWrapper.setLocalDescription(webRTCSessionDescription);
|
sendSessionAccept(respondingRtpContentMap);
|
||||||
} catch (Exception e) {
|
this.webRTCWrapper.setLocalDescription(webRTCSessionDescription);
|
||||||
Log.d(Config.LOGTAG, "unable to send session accept", e);
|
} catch (Exception e) {
|
||||||
|
Log.d(Config.LOGTAG, "unable to send session accept", e);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendSessionAccept(final RtpContentMap rtpContentMap) {
|
private void sendSessionAccept(final RtpContentMap rtpContentMap) {
|
||||||
|
@ -346,17 +351,19 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
|
|
||||||
private void sendSessionInitiate(final State targetState) {
|
private void sendSessionInitiate(final State targetState) {
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": prepare session-initiate");
|
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": prepare session-initiate");
|
||||||
setupWebRTC();
|
discoverIceServers(iceServers -> {
|
||||||
try {
|
setupWebRTC(iceServers);
|
||||||
org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createOffer().get();
|
try {
|
||||||
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
|
org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createOffer().get();
|
||||||
Log.d(Config.LOGTAG, "description: " + webRTCSessionDescription.description);
|
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
|
||||||
final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription);
|
Log.d(Config.LOGTAG, "description: " + webRTCSessionDescription.description);
|
||||||
sendSessionInitiate(rtpContentMap, targetState);
|
final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription);
|
||||||
this.webRTCWrapper.setLocalDescription(webRTCSessionDescription).get();
|
sendSessionInitiate(rtpContentMap, targetState);
|
||||||
} catch (Exception e) {
|
this.webRTCWrapper.setLocalDescription(webRTCSessionDescription).get();
|
||||||
Log.d(Config.LOGTAG, "unable to sendSessionInitiate", e);
|
} catch (Exception e) {
|
||||||
}
|
Log.d(Config.LOGTAG, "unable to sendSessionInitiate", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendSessionInitiate(RtpContentMap rtpContentMap, final State targetState) {
|
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.setup(this.xmppConnectionService);
|
||||||
this.webRTCWrapper.initializePeerConnection();
|
this.webRTCWrapper.initializePeerConnection(iceServers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void acceptCallFromProposed() {
|
private void acceptCallFromProposed() {
|
||||||
|
@ -559,4 +566,51 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
private void updateEndUserState() {
|
private void updateEndUserState() {
|
||||||
xmppConnectionService.notifyJingleRtpConnectionUpdate(id.account, id.with, id.sessionId, getEndUserState());
|
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();
|
PeerConnectionFactory peerConnectionFactory = PeerConnectionFactory.builder().createPeerConnectionFactory();
|
||||||
|
|
||||||
CameraVideoCapturer capturer = null;
|
CameraVideoCapturer capturer = null;
|
||||||
|
@ -193,10 +193,6 @@ public class WebRTCWrapper {
|
||||||
|
|
||||||
this.localVideoTrack = videoTrack;
|
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);
|
final PeerConnection peerConnection = peerConnectionFactory.createPeerConnection(iceServers, peerConnectionObserver);
|
||||||
if (peerConnection == null) {
|
if (peerConnection == null) {
|
||||||
throw new IllegalStateException("Unable to create PeerConnection");
|
throw new IllegalStateException("Unable to create PeerConnection");
|
||||||
|
|
Loading…
Reference in New Issue