openvidu-server: Add parameter MEDIA_NODES_PUBLIC_IPS to modify public ip on remote media server candidates

pull/648/head
cruizba 2021-07-06 21:22:30 +02:00
parent e66c5d1070
commit 879a88299d
6 changed files with 249 additions and 7 deletions

View File

@ -191,6 +191,12 @@ public class OpenviduConfig {
private String dotenvPath;
// Media Nodes private IPs and Public IPs
// If defined, they will be configured as public IPs of Kurento or Mediasoup
// Key: Private IP
// Value: Public IP
private Map<String, String> mediaNodesPublicIps = new HashMap<>();
// Derived properties
public static String finalUrl;
@ -365,6 +371,14 @@ public class OpenviduConfig {
this.isTurnadminAvailable = available;
}
public boolean areMediaNodesPublicIpsDefined() {
return !this.mediaNodesPublicIps.isEmpty();
}
public Map<String, String> getMediaNodesPublicIpsMap() {
return this.mediaNodesPublicIps;
}
public OpenViduRole[] getRolesFromRecordingNotification() {
OpenViduRole[] roles;
switch (this.openviduRecordingNotification) {
@ -538,6 +552,9 @@ public class OpenviduConfig {
openviduForcedCodec = asEnumValue("OPENVIDU_STREAMS_FORCED_VIDEO_CODEC", VideoCodec.class);
openviduAllowTranscoding = asBoolean("OPENVIDU_STREAMS_ALLOW_TRANSCODING");
// Load Public IPS
mediaNodesPublicIps = loadMediaNodePublicIps("MEDIA_NODES_PUBLIC_IPS");
kmsUrisList = checkKmsUris();
checkCoturnIp();
@ -1005,4 +1022,45 @@ public class OpenviduConfig {
return null;
}
private Map<String, String> loadMediaNodePublicIps(String propertyName) {
String mediaNodesPublicIpsRaw = this.asOptionalString(propertyName);
final Map<String, String> mediaNodesPublicIps = new HashMap<>();
if (mediaNodesPublicIpsRaw == null || mediaNodesPublicIpsRaw.isEmpty()) {
return mediaNodesPublicIps;
}
List<String> mediaNodesPublicIpsList = asJsonStringsArray(propertyName);
for(String ipPairStr: mediaNodesPublicIpsList) {
String[] ipPair = ipPairStr.trim().split(":");
if (ipPair.length != 2) {
addError(propertyName, "Not valid ip pair in " + propertyName + ": " + ipPairStr);
break;
}
String privateIp = ipPair[0];
String publicIp = ipPair[1];
isValidIp(propertyName, privateIp);
isValidIp(propertyName, publicIp);
mediaNodesPublicIps.put(privateIp, publicIp);
}
return mediaNodesPublicIps;
}
private void isValidIp(String propertyName, String ip) {
if (ip != null && !ip.isEmpty()) {
boolean isIP;
try {
final InetAddress inet = InetAddress.getByName(ip);
isIP = inet instanceof Inet4Address || inet instanceof Inet6Address;
if (isIP) {
ip = inet.getHostAddress();
}
} catch (final UnknownHostException e) {
isIP = false;
}
if (!isIP) {
addError(propertyName, "Is not a valid IP Address (IPv4 or IPv6): " + ip);
}
}
}
}

View File

@ -17,6 +17,7 @@
package io.openvidu.server.kurento.core;
import io.openvidu.server.config.OpenviduConfig;
import org.kurento.client.BaseRtpEndpoint;
import org.kurento.client.Endpoint;
import org.kurento.client.PlayerEndpoint;
@ -41,6 +42,9 @@ public class KurentoParticipantEndpointConfig {
@Autowired
protected CallDetailRecord CDR;
@Autowired
protected OpenviduConfig openviduConfig;
public void addEndpointListeners(MediaEndpoint endpoint, String typeOfEndpoint) {
// WebRtcEndpoint events

View File

@ -17,6 +17,8 @@
package io.openvidu.server.kurento.endpoint;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@ -28,6 +30,8 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import io.openvidu.server.utils.ice.IceCandidateDataParser;
import io.openvidu.server.utils.ice.IceCandidateType;
import org.kurento.client.BaseRtpEndpoint;
import org.kurento.client.Continuation;
import org.kurento.client.Endpoint;
@ -103,6 +107,7 @@ public abstract class MediaEndpoint {
private final List<IceCandidate> gatheredCandidateList = Collections.synchronizedList(new ArrayList<>());
private LinkedList<IceCandidate> candidates = new LinkedList<IceCandidate>();
public String selectedLocalIceCandidate;
public String selectedRemoteIceCandidate;
public Queue<KmsEvent> kmsEvents = new ConcurrentLinkedQueue<>();
@ -568,15 +573,64 @@ public abstract class MediaEndpoint {
webEndpoint.addIceCandidateFoundListener(event -> {
final IceCandidate candidate = event.getCandidate();
if (this.openviduConfig.areMediaNodesPublicIpsDefined()) {
sendCandidatesWithConfiguredIp(senderPublicId, candidate);
} else {
gatheredCandidateList.add(candidate);
this.owner.logIceCandidate(new WebrtcDebugEvent(this.owner, this.streamId, WebrtcDebugEventIssuer.server,
this.getWebrtcDebugOperation(), WebrtcDebugEventType.iceCandidate,
gson.toJsonTree(candidate).toString()));
owner.sendIceCandidate(senderPublicId, endpointName, candidate);
}
});
}
private void sendCandidatesWithConfiguredIp(String senderPublicId, IceCandidate candidate) {
// Get media node private IP
String kurentoPrivateIp = this.owner.getSession().getKms().getIp();
// Get Ip to be replaced
String ipToReplace = this.openviduConfig.getMediaNodesPublicIpsMap().get(kurentoPrivateIp);
// If Ip is configured
if (ipToReplace != null && !ipToReplace.isEmpty()) {
// Candidate which will have the public IP
IceCandidate candidatePublicIp = new IceCandidate(candidate.getCandidate(), candidate.getSdpMid(),
candidate.getSdpMLineIndex());
IceCandidateDataParser candidatePublicIpParser = new IceCandidateDataParser(candidatePublicIp);
// Only create host candidates to increase priority
if (candidatePublicIpParser.getType() == IceCandidateType.host) {
candidatePublicIpParser.setIp(ipToReplace);
// Max priority for public IP
candidatePublicIpParser.setMaxPriority();
candidatePublicIp.setCandidate(candidatePublicIpParser.toString());
gatheredCandidateList.add(candidatePublicIp);
this.owner.logIceCandidate(new WebrtcDebugEvent(this.owner, this.streamId, WebrtcDebugEventIssuer.server,
this.getWebrtcDebugOperation(), WebrtcDebugEventType.iceCandidate,
gson.toJsonTree(candidatePublicIp).toString()));
owner.sendIceCandidate(senderPublicId, endpointName, candidatePublicIp);
// Candidate which will have the private IP exposed of the media node
IceCandidate candidatePrivateIp = new IceCandidate(candidate.getCandidate(), candidate.getSdpMid(),
candidate.getSdpMLineIndex());
IceCandidateDataParser candidatePrivateIpParser = new IceCandidateDataParser(candidatePrivateIp);
// Min priority for private IP
candidatePrivateIpParser.setMinPriority();
candidatePrivateIpParser.setIp(kurentoPrivateIp);
candidatePrivateIp.setCandidate(candidatePrivateIpParser.toString());
gatheredCandidateList.add(candidatePrivateIp);
this.owner.logIceCandidate(new WebrtcDebugEvent(this.owner, this.streamId, WebrtcDebugEventIssuer.server,
this.getWebrtcDebugOperation(), WebrtcDebugEventType.iceCandidate,
gson.toJsonTree(candidatePrivateIp).toString()));
owner.sendIceCandidate(senderPublicId, endpointName, candidatePrivateIp);
}
}
}
/**
* If supported, it instructs the internal endpoint to start gathering
* {@link IceCandidate}s.

View File

@ -0,0 +1,120 @@
package io.openvidu.server.utils.ice;
import org.kurento.client.IceCandidate;
import java.security.SecureRandom;
import java.util.Objects;
/**
* Ice candidate data following rfc5245, section-15.1 (only necessary data for OpenVidu)
*/
public class IceCandidateDataParser {
/**
* Max priority and Min priority possible defined in rfc5245 15.1
* "<priority>: is a positive integer between 1 and (2**31 - 1)"
* MAX_PRIORITY = (2^24)*126 + (2^8)*65535 + 255
* MIN_PRIORITY = (2^24)*126 + (2^8)*1 + 255
*/
private final int MAX_PRIORITY = 2130706431;
private final int MIN_PRIORITY = 511;
/**
* Full string with the candidate
*/
private String[] candidate;
public IceCandidateDataParser(IceCandidate iceCandidate) {
this.candidate = iceCandidate.getCandidate().split(" ");
}
public IceCandidateDataParser(String iceCandidate) {
this.candidate = iceCandidate.split(" ");
}
/**
* Following rfc5245, section-15.1, the candidate foundation id is the 1 th element
* @return
*/
public void setRandomFoundation() {
String prefix = candidate[0].split(":")[0];
candidate[0] = prefix + ":" + new SecureRandom().nextInt(Integer.MAX_VALUE - 1);
}
/**
* Following rfc5245, section-15.1, the priority is the 4th element
* @return The priority of the candidate
*/
public String getPriority() {
return candidate[3];
}
/**
* Following rfc5245, section-15.1, the priority to replace is the 4th element
* @param priority
*/
public void setPriority(String priority) {
candidate[3] = priority;
}
/**
* Following rfc5245, section-15.1, set max priority value
*/
public void setMaxPriority() {
this.setPriority(Long.toString(MAX_PRIORITY));
}
/**
* Following rfc5245, section-15.1, set min priority value
*/
public void setMinPriority() {
this.setPriority(Long.toString(MIN_PRIORITY));
}
/**
* Following rfc5245, section-15.1, the ip to replace is the 4th element
* @return ip of the candidate
*/
public String getIp() {
return candidate[4];
}
/**
* Following rfc5245, section-15.1, the ip to replace is the 4th element
* @param ip New ip for the ICE candidate
*/
public void setIp(String ip) {
this.candidate[4] = ip;
}
/**
* Following rfc5245, section-15.1, the typ to get is the 7th element
* @return typ of the candidate
*/
public IceCandidateType getType() {
return IceCandidateType.valueOf(candidate[7]);
}
/**
* Following rfc5245, section-15.1, the typ to replace is the 7th element
* @return typ of the candidate
*/
public void setType(IceCandidateType type) {
candidate[7] = type.toString();
}
/** Check if Ice candidate type is of the passed argument
* @param iceCandidateType Ice candidate type
* @return
*/
public boolean isType(IceCandidateType iceCandidateType) {
return Objects.equals(iceCandidateType, this.getType());
}
@Override
public String toString() {
return String.join(" ", candidate);
}
}

View File

@ -0,0 +1,5 @@
package io.openvidu.server.utils.ice;
public enum IceCandidateType {
host, srflx, prflx, relay
}

View File

@ -52,3 +52,4 @@ COTURN_REDIS_IP=127.0.0.1
COTURN_REDIS_DBNAME=0
COTURN_REDIS_PASSWORD=turn
COTURN_REDIS_CONNECT_TIMEOUT=30
MEDIA_NODES_PUBLIC_IPS=[]