mirror of https://github.com/OpenVidu/openvidu.git
openvidu-server: Add parameter MEDIA_NODES_PUBLIC_IPS to modify public ip on remote media server candidates
parent
e66c5d1070
commit
879a88299d
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
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);
|
||||
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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package io.openvidu.server.utils.ice;
|
||||
|
||||
public enum IceCandidateType {
|
||||
host, srflx, prflx, relay
|
||||
}
|
|
@ -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=[]
|
||||
|
|
Loading…
Reference in New Issue