From f989b0c2ee930a6483c1532d49431044794cda6d Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Tue, 20 Oct 2020 22:09:06 +0200 Subject: [PATCH] POST Connection API --- .../io/openvidu/java/client/Connection.java | 190 +++++++++-- .../java/client/ConnectionOptions.java | 308 +++++++++++++++--- .../openvidu/java/client/KurentoOptions.java | 82 +++-- .../io/openvidu/java/client/OpenVidu.java | 4 + .../java/io/openvidu/java/client/Session.java | 275 ++++++++-------- .../java/io/openvidu/java/client/Token.java | 127 -------- .../io/openvidu/java/client/TokenOptions.java | 31 +- openvidu-node-client/src/Connection.ts | 117 ++++--- openvidu-node-client/src/ConnectionOptions.ts | 62 +++- openvidu-node-client/src/ConnectionType.ts | 34 ++ openvidu-node-client/src/OpenVidu.ts | 1 + openvidu-node-client/src/Session.ts | 92 ++++-- openvidu-node-client/src/Token.ts | 81 ----- openvidu-node-client/src/TokenOptions.ts | 2 +- openvidu-node-client/src/index.ts | 2 +- .../io/openvidu/server/core/Participant.java | 43 +-- .../openvidu/server/core/SessionManager.java | 35 +- .../java/io/openvidu/server/core/Token.java | 151 ++++++--- .../openvidu/server/core/TokenGenerator.java | 12 +- .../kurento/core/KurentoParticipant.java | 2 +- .../kurento/core/KurentoSessionManager.java | 14 +- .../kurento/core/KurentoTokenOptions.java | 107 ------ .../kurento/endpoint/MediaEndpoint.java | 4 +- .../recording/RecorderEndpointWrapper.java | 4 +- .../server/rest/SessionRestController.java | 268 ++++++++------- .../io/openvidu/server/rpc/RpcHandler.java | 5 +- ...essionGarbageCollectorIntegrationTest.java | 5 +- .../test/browsers/utils/CustomHttpClient.java | 5 +- .../e2e/AbstractOpenViduTestAppE2eTest.java | 20 ++ .../test/e2e/OpenViduProTestAppE2eTest.java | 70 ++-- .../test/e2e/OpenViduTestAppE2eTest.java | 153 ++++++--- 31 files changed, 1350 insertions(+), 956 deletions(-) delete mode 100644 openvidu-java-client/src/main/java/io/openvidu/java/client/Token.java create mode 100644 openvidu-node-client/src/ConnectionType.ts delete mode 100644 openvidu-node-client/src/Token.ts delete mode 100644 openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoTokenOptions.java diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java index 6ba1a9e6..600ef600 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Connection.java @@ -39,7 +39,8 @@ public class Connection { private String location; private String platform; private String clientData; - private Token token; + private ConnectionOptions connectionOptions; + private String token; protected Map publishers = new ConcurrentHashMap<>(); protected List subscribers = new ArrayList<>(); @@ -49,7 +50,7 @@ public class Connection { } /** - * Returns the identifier of the connection. You can call methods + * Returns the identifier of the Connection. You can call methods * {@link io.openvidu.java.client.Session#forceDisconnect(String)} or * {@link io.openvidu.java.client.Session#updateConnection(String, TokenOptions)} * passing this property as parameter @@ -59,7 +60,7 @@ public class Connection { } /** - * Returns the status of the connection. Can be: + * Returns the status of the Connection. Can be: *
    *
  • pending: if the Connection is waiting for any user to use * its internal token to connect to the session, calling method INDIVIDUAL recording. */ public boolean record() { - return this.token.record(); + return this.connectionOptions.record(); } /** - * Returns the token string associated to the connection + * Returns the role of the Connection. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#WEBRTC} + */ + public OpenViduRole getRole() { + return this.connectionOptions.getRole(); + } + + /** + * Returns the RTSP URI of the Connection. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#IPCAM} + */ + public String getRtspUri() { + return this.connectionOptions.getRtspUri(); + } + + /** + * Whether the Connection uses adaptative bitrate (and therefore adaptative + * quality) or not. For local network connections that do not require media + * transcoding this can be disabled to save CPU power. If you are not sure if + * transcoding might be necessary, setting this property to false may + * result in media connections not being established. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#IPCAM} + */ + public boolean adaptativeBitrate() { + return this.connectionOptions.adaptativeBitrate(); + } + + /** + * Whether the IP camera stream of this Connection will only be enabled when + * some user is subscribed to it, or not. This allows you to reduce power + * consumption and network bandwidth in your server while nobody is asking to + * receive the camera's video. On the counterpart, first user subscribing to the + * IP camera stream will take a little longer to receive its video. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#IPCAM} + */ + public boolean onlyPlayWithSubscribers() { + return this.connectionOptions.onlyPlayWithSubscribers(); + } + + /** + * Returns the size of the buffer of the endpoint receiving the IP camera's + * stream, in milliseconds. The smaller it is, the less delay the signal will + * have, but more problematic will be in unstable networks. Use short buffers + * only if there is a quality connection between the IP camera and OpenVidu + * Server. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#IPCAM} + */ + public int getNetworkCache() { + return this.connectionOptions.getNetworkCache(); + } + + /** + * Returns the token string associated to the Connection */ public String getToken() { - return this.token.getToken(); + return this.token; } /** @@ -180,17 +253,23 @@ public class Connection { protected JsonObject toJson() { JsonObject json = new JsonObject(); - json.addProperty("id", this.getConnectionId()); + json.addProperty("connectionId", this.getConnectionId()); json.addProperty("status", this.getStatus()); json.addProperty("createdAt", this.createdAt()); json.addProperty("activeAt", this.activeAt()); json.addProperty("location", this.getLocation()); json.addProperty("platform", this.getPlatform()); - json.addProperty("token", this.getToken()); - json.addProperty("role", this.getRole().name()); - json.addProperty("serverData", this.getServerData()); - json.addProperty("record", this.record()); json.addProperty("clientData", this.getClientData()); + json.addProperty("token", this.getToken()); + + JsonObject jsonConnectionOptions = this.connectionOptions.toJson(""); + jsonConnectionOptions.remove("session"); + json.addProperty("serverData", jsonConnectionOptions.get("data").getAsString()); + jsonConnectionOptions.remove("data"); + jsonConnectionOptions.entrySet().forEach(entry -> { + json.add(entry.getKey(), entry.getValue()); + }); + JsonArray pubs = new JsonArray(); this.getPublishers().forEach(p -> { pubs.add(p.toJson()); @@ -204,14 +283,33 @@ public class Connection { return json; } - /** - * For now only properties data, role and record can be updated - */ - protected void overrideConnectionOptions(ConnectionOptions connectionOptions) { - TokenOptions.Builder modifiableTokenOptions = new TokenOptions.Builder().role(connectionOptions.getRole()) - .record(connectionOptions.record()); - modifiableTokenOptions.data(this.token.getData()); - this.token.overrideTokenOptions(modifiableTokenOptions.build()); + protected void overrideConnectionOptions(ConnectionOptions newConnectionOptions) { + ConnectionOptions.Builder builder = new ConnectionOptions.Builder(); + // For now only properties role and record can be updated + if (newConnectionOptions.getRole() != null) { + builder.role(newConnectionOptions.getRole()); + } else { + builder.role(this.connectionOptions.getRole()); + } + if (newConnectionOptions.record() != null) { + builder.record(newConnectionOptions.record()); + } else { + builder.record(this.connectionOptions.record()); + } + // Keep old configuration in the rest of properties + builder.type(this.connectionOptions.getType()).data(this.connectionOptions.getData()) + .kurentoOptions(this.connectionOptions.getKurentoOptions()) + .rtspUri(this.connectionOptions.getRtspUri()); + if (this.connectionOptions.adaptativeBitrate() != null) { + builder.adaptativeBitrate(this.connectionOptions.adaptativeBitrate()); + } + if (this.connectionOptions.onlyPlayWithSubscribers() != null) { + builder.onlyPlayWithSubscribers(this.connectionOptions.onlyPlayWithSubscribers()); + } + if (this.connectionOptions.getNetworkCache() != null) { + builder.networkCache(this.connectionOptions.getNetworkCache()); + } + this.connectionOptions = builder.build(); } protected void setSubscribers(List subscribers) { @@ -220,6 +318,10 @@ public class Connection { protected Connection resetWithJson(JsonObject json) { + this.connectionId = json.get("connectionId").getAsString(); + this.status = json.get("status").getAsString(); + this.token = !json.get("token").isJsonNull() ? json.get("token").getAsString() : null; + if (!json.get("publishers").isJsonNull()) { JsonArray jsonArrayPublishers = json.get("publishers").getAsJsonArray(); @@ -284,16 +386,34 @@ public class Connection { this.clientData = json.get("clientData").getAsString(); } - // These properties won't ever be null - this.connectionId = json.get("connectionId").getAsString(); - this.status = json.get("status").getAsString(); - String token = json.has("token") ? json.get("token").getAsString() : null; - OpenViduRole role = OpenViduRole.valueOf(json.get("role").getAsString()); - String data = json.get("serverData").getAsString(); - Boolean record = json.get("record").getAsBoolean(); + // COMMON + ConnectionType type = ConnectionType.valueOf(json.get("type").getAsString()); + String data = (json.has("serverData") && !json.get("serverData").isJsonNull()) + ? json.get("serverData").getAsString() + : null; + Boolean record = (json.has("record") && !json.get("record").isJsonNull()) ? json.get("record").getAsBoolean() + : null; - TokenOptions tokenOptions = new TokenOptions(role, data, record, null); - this.token = new Token(token, this.connectionId, tokenOptions); + // WEBRTC + OpenViduRole role = (json.has("role") && !json.get("role").isJsonNull()) + ? OpenViduRole.valueOf(json.get("role").getAsString()) + : null; + + // IPCAM + String rtspUri = (json.has("rtspUri") && !json.get("rtspUri").isJsonNull()) ? json.get("rtspUri").getAsString() + : null; + Boolean adaptativeBitrate = (json.has("adaptativeBitrate") && !json.get("adaptativeBitrate").isJsonNull()) + ? json.get("adaptativeBitrate").getAsBoolean() + : null; + Boolean onlyPlayWithSubscribers = (json.has("onlyPlayWithSubscribers") + && !json.get("onlyPlayWithSubscribers").isJsonNull()) + ? json.get("onlyPlayWithSubscribers").getAsBoolean() + : null; + Integer networkCache = (json.has("networkCache") && !json.get("networkCache").isJsonNull()) + ? json.get("networkCache").getAsInt() + : null; + this.connectionOptions = new ConnectionOptions(type, data, record, role, null, rtspUri, adaptativeBitrate, + onlyPlayWithSubscribers, networkCache); return this; } diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionOptions.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionOptions.java index 45b32efb..8fbaea86 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionOptions.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionOptions.java @@ -1,15 +1,26 @@ package io.openvidu.java.client; -import com.google.gson.JsonArray; import com.google.gson.JsonNull; import com.google.gson.JsonObject; +/** + * See + * {@link io.openvidu.java.client.Session#createConnection(ConnectionOptions)} + */ public class ConnectionOptions { - private OpenViduRole role; + private ConnectionType type; + // COMMON private String data; private Boolean record; + // WEBRTC + private OpenViduRole role; private KurentoOptions kurentoOptions; + // IPCAM + private String rtspUri; + private Boolean adaptativeBitrate; + private Boolean onlyPlayWithSubscribers; + private Integer networkCache; /** * @@ -18,23 +29,58 @@ public class ConnectionOptions { */ public static class Builder { - private OpenViduRole role = OpenViduRole.PUBLISHER; + private ConnectionType type; + // COMMON private String data; - private Boolean record = true; + private Boolean record; + // WEBRTC + private OpenViduRole role; private KurentoOptions kurentoOptions; + // IPCAM + private String rtspUri; + private Boolean adaptativeBitrate; + private Boolean onlyPlayWithSubscribers; + private Integer networkCache; /** * Builder for {@link io.openvidu.java.client.ConnectionOptions}. */ public ConnectionOptions build() { - return new ConnectionOptions(this.role, this.data, this.record, this.kurentoOptions); + return new ConnectionOptions(this.type, this.data, this.record, this.role, this.kurentoOptions, + this.rtspUri, this.adaptativeBitrate, this.onlyPlayWithSubscribers, this.networkCache); } /** - * Call this method to set the role assigned to this Connection. + * Call this method to set the type of Connection. The + * {@link io.openvidu.java.client.ConnectionType} dictates what properties will + * have effect: + *
      + *
    • {@link io.openvidu.java.client.ConnectionType#WEBRTC}: + * {@link io.openvidu.java.client.ConnectionOptions.Builder#data(String) data}, + * {@link io.openvidu.java.client.ConnectionOptions.Builder#record(boolean) + * record}, + * {@link io.openvidu.java.client.ConnectionOptions.Builder#role(OpenViduRole) + * role}, + * {@link io.openvidu.java.client.ConnectionOptions.Builder#kurentoOptions(KurentoOptions) + * kurentoOptions}
    • + *
    • {@link io.openvidu.java.client.ConnectionType#IPCAM}: + * {@link io.openvidu.java.client.ConnectionOptions.Builder#data(String) data}, + * {@link io.openvidu.java.client.ConnectionOptions.Builder#record(boolean) + * record}, + * {@link io.openvidu.java.client.ConnectionOptions.Builder#rtspUri(String) + * rtspUri}, + * {@link io.openvidu.java.client.ConnectionOptions.Builder#adaptativeBitrate(boolean) + * adaptativeBitrate}, + * {@link io.openvidu.java.client.ConnectionOptions.Builder#onlyPlayWithSubscribers(boolean) + * onlyPlayWithSubscribers}, + * {@link io.openvidu.java.client.ConnectionOptions.Builder#networkCache(int) + * networkCache}
    • + *
    + * If not set by default will be @link + * io.openvidu.java.client.ConnectionType#WEBRTC}. */ - public Builder role(OpenViduRole role) { - this.role = role; + public Builder type(ConnectionType type) { + this.type = type; return this; } @@ -76,29 +122,122 @@ public class ConnectionOptions { return this; } + /** + * Call this method to set the role assigned to this Connection. If not set by + * default will be {@link io.openvidu.java.client.OpenViduRole#PUBLISHER + * PUBLISHER}. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#WEBRTC} + */ + public Builder role(OpenViduRole role) { + this.role = role; + return this; + } + /** * Call this method to set a {@link io.openvidu.java.client.KurentoOptions} * object for this Connection. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#WEBRTC} */ public Builder kurentoOptions(KurentoOptions kurentoOptions) { this.kurentoOptions = kurentoOptions; return this; } + /** + * Call this method to set the RTSP URI of an IP camera. For example: + * rtsp://your.camera.ip:7777/path + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#IPCAM} + */ + public Builder rtspUri(String rtspUri) { + this.rtspUri = rtspUri; + return this; + } + + /** + * Call this method to set whether to use adaptative bitrate (and therefore + * adaptative quality) or not. For local network connections that do not require + * media transcoding this can be disabled to save CPU power. If you are not sure + * if transcoding might be necessary, setting this property to false may + * result in media connections not being established. Default to + * true. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#IPCAM} + */ + public Builder adaptativeBitrate(boolean adaptativeBitrate) { + this.adaptativeBitrate = adaptativeBitrate; + return this; + } + + /** + * Call this method to set whether to enable the IP camera stream only when some + * user is subscribed to it, or not. This allows you to reduce power consumption + * and network bandwidth in your server while nobody is asking to receive the + * camera's video. On the counterpart, first user subscribing to the IP camera + * stream will take a little longer to receive its video. Default to + * true. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#IPCAM} + */ + public Builder onlyPlayWithSubscribers(boolean onlyPlayWithSubscribers) { + this.onlyPlayWithSubscribers = onlyPlayWithSubscribers; + return this; + } + + /** + * Call this method to set the size of the buffer of the endpoint receiving the + * IP camera's stream, in milliseconds. The smaller it is, the less delay the + * signal will have, but more problematic will be in unstable networks. Use + * short buffers only if there is a quality connection between the IP camera and + * OpenVidu Server. Default to 2000. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#IPCAM} + */ + public Builder networkCache(int networkCache) { + this.networkCache = networkCache; + return this; + } } - ConnectionOptions(OpenViduRole role, String data, Boolean record, KurentoOptions kurentoOptions) { - this.role = role; + ConnectionOptions(ConnectionType type, String data, Boolean record, OpenViduRole role, + KurentoOptions kurentoOptions, String rtspUri, Boolean adaptativeBitrate, Boolean onlyPlayWithSubscribers, + Integer networkCache) { + this.type = type; this.data = data; this.record = record; + this.role = role; this.kurentoOptions = kurentoOptions; + this.rtspUri = rtspUri; + this.adaptativeBitrate = adaptativeBitrate; + this.onlyPlayWithSubscribers = onlyPlayWithSubscribers; + this.networkCache = networkCache; } /** - * Returns the role assigned to this Connection. + * Returns the type of Connection. */ - public OpenViduRole getRole() { - return this.role; + public ConnectionType getType() { + return this.type; } /** @@ -118,13 +257,97 @@ public class ConnectionOptions { return this.record; } - protected JsonObject toJsonObject(String sessionId) { + /** + * Returns the role assigned to this Connection. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#WEBRTC} + */ + public OpenViduRole getRole() { + return this.role; + } + + /** + * Returns the KurentoOptions assigned to this Connection. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#WEBRTC} + */ + public KurentoOptions getKurentoOptions() { + return this.kurentoOptions; + } + + /** + * Returns the RTSP URI of this Connection. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#IPCAM} + */ + public String getRtspUri() { + return this.rtspUri; + } + + /** + * Whether this Connection uses adaptative bitrate (and therefore adaptative + * quality) or not. For local network connections that do not require media + * transcoding this can be disabled to save CPU power. If you are not sure if + * transcoding might be necessary, setting this property to false may + * result in media connections not being established. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#IPCAM} + */ + public Boolean adaptativeBitrate() { + return this.adaptativeBitrate; + } + + /** + * Whether to enable the IP camera stream only when some user is subscribed to + * it. This allows you to reduce power consumption and network bandwidth in your + * server while nobody is asking to receive the camera's video. On the + * counterpart, first user subscribing to the IP camera stream will take a + * little longer to receive its video. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#IPCAM} + */ + public Boolean onlyPlayWithSubscribers() { + return this.onlyPlayWithSubscribers; + } + + /** + * Size of the buffer of the endpoint receiving the IP camera's stream, in + * milliseconds. The smaller it is, the less delay the signal will have, but + * more problematic will be in unstable networks. Use short buffers only if + * there is a quality connection between the IP camera and OpenVidu Server. + * + *
    + *
    + * Only for + * {@link io.openvidu.java.client.ConnectionType#IPCAM} + */ + public Integer getNetworkCache() { + return this.networkCache; + } + + public JsonObject toJson(String sessionId) { JsonObject json = new JsonObject(); json.addProperty("session", sessionId); - if (getRole() != null) { - json.addProperty("role", getRole().name()); + // COMMON + if (getType() != null) { + json.addProperty("type", getType().name()); } else { - json.add("role", JsonNull.INSTANCE); + json.add("type", JsonNull.INSTANCE); } if (getData() != null) { json.addProperty("data", getData()); @@ -136,28 +359,37 @@ public class ConnectionOptions { } else { json.add("record", JsonNull.INSTANCE); } + // WEBRTC + if (getRole() != null) { + json.addProperty("role", getRole().name()); + } else { + json.add("role", JsonNull.INSTANCE); + } if (this.kurentoOptions != null) { - JsonObject kurentoOptions = new JsonObject(); - if (this.kurentoOptions.getVideoMaxRecvBandwidth() != null) { - kurentoOptions.addProperty("videoMaxRecvBandwidth", this.kurentoOptions.getVideoMaxRecvBandwidth()); - } - if (this.kurentoOptions.getVideoMinRecvBandwidth() != null) { - kurentoOptions.addProperty("videoMinRecvBandwidth", this.kurentoOptions.getVideoMinRecvBandwidth()); - } - if (this.kurentoOptions.getVideoMaxSendBandwidth() != null) { - kurentoOptions.addProperty("videoMaxSendBandwidth", this.kurentoOptions.getVideoMaxSendBandwidth()); - } - if (this.kurentoOptions.getVideoMinSendBandwidth() != null) { - kurentoOptions.addProperty("videoMinSendBandwidth", this.kurentoOptions.getVideoMinSendBandwidth()); - } - if (this.kurentoOptions.getAllowedFilters().length > 0) { - JsonArray allowedFilters = new JsonArray(); - for (String filter : this.kurentoOptions.getAllowedFilters()) { - allowedFilters.add(filter); - } - kurentoOptions.add("allowedFilters", allowedFilters); - } - json.add("kurentoOptions", kurentoOptions); + json.add("kurentoOptions", kurentoOptions.toJson()); + } else { + json.add("kurentoOptions", JsonNull.INSTANCE); + } + // IPCAM + if (getRtspUri() != null) { + json.addProperty("rtspUri", getRtspUri()); + } else { + json.add("rtspUri", JsonNull.INSTANCE); + } + if (adaptativeBitrate() != null) { + json.addProperty("adaptativeBitrate", adaptativeBitrate()); + } else { + json.add("adaptativeBitrate", JsonNull.INSTANCE); + } + if (onlyPlayWithSubscribers() != null) { + json.addProperty("onlyPlayWithSubscribers", onlyPlayWithSubscribers()); + } else { + json.add("onlyPlayWithSubscribers", JsonNull.INSTANCE); + } + if (getNetworkCache() != null) { + json.addProperty("networkCache", getNetworkCache()); + } else { + json.add("networkCache", JsonNull.INSTANCE); } return json; } diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/KurentoOptions.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/KurentoOptions.java index c29730ac..0f5f5fa3 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/KurentoOptions.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/KurentoOptions.java @@ -17,6 +17,11 @@ package io.openvidu.java.client; +import java.util.Arrays; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + /** * See {@link io.openvidu.java.client.TokenOptions#getKurentoOptions()} */ @@ -105,10 +110,10 @@ public class KurentoOptions { } /** - * Defines the maximum number of Kbps that the client owning the token will be - * able to receive from Kurento Media Server. 0 means unconstrained. Giving a - * value to this property will override the global configuration set in OpenVidu Server configuration (parameter * OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH) for every incoming * stream of the user owning the token.
    @@ -122,10 +127,10 @@ public class KurentoOptions { } /** - * Defines the minimum number of Kbps that the client owning the token will try - * to receive from Kurento Media Server. 0 means unconstrained. Giving a value - * to this property will override the global configuration set in OpenVidu Server configuration (parameter * OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH) for every incoming * stream of the user owning the token. @@ -135,10 +140,10 @@ public class KurentoOptions { } /** - * Defines the maximum number of Kbps that the client owning the token will be - * able to send to Kurento Media Server. 0 means unconstrained. Giving a value - * to this property will override the global configuration set in OpenVidu Server configuration (parameter * OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH) for every outgoing * stream of the user owning the token.
    @@ -151,10 +156,10 @@ public class KurentoOptions { } /** - * Defines the minimum number of Kbps that the client owning the token will try - * to send to Kurento Media Server. 0 means unconstrained. Giving a value to - * this property will override the global configuration set in OpenVidu Server configuration (parameter * OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH) for every outgoing * stream of the user owning the token. @@ -164,13 +169,50 @@ public class KurentoOptions { } /** - * Defines the names of the filters the user owning the token will be able to - * apply. See - * Voice and - * video filters + * Defines the names of the filters the Connection will be able to apply to its + * published streams. See + * Voice and video filters. */ public String[] getAllowedFilters() { return allowedFilters; } + /** + * See if the Connection can apply certain filter. See + * Voice and video filters. + */ + public boolean isFilterAllowed(String filterType) { + if (filterType == null) { + return false; + } + return Arrays.stream(allowedFilters).anyMatch(filterType::equals); + } + + public JsonObject toJson() { + JsonObject json = new JsonObject(); + if (this.getVideoMaxRecvBandwidth() != null) { + json.addProperty("videoMaxRecvBandwidth", this.getVideoMaxRecvBandwidth()); + } + if (this.getVideoMinRecvBandwidth() != null) { + json.addProperty("videoMinRecvBandwidth", this.getVideoMinRecvBandwidth()); + } + if (this.getVideoMaxSendBandwidth() != null) { + json.addProperty("videoMaxSendBandwidth", this.getVideoMaxSendBandwidth()); + } + if (this.getVideoMinSendBandwidth() != null) { + json.addProperty("videoMinSendBandwidth", this.getVideoMinSendBandwidth()); + } + if (this.getAllowedFilters().length > 0) { + JsonArray filtersJson = new JsonArray(); + String[] filters = this.getAllowedFilters(); + for (String filter : filters) { + filtersJson.add(filter); + } + json.add("allowedFilters", filtersJson); + } + return json; + } + } diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java index 0e1a660f..d657dbec 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java @@ -497,6 +497,10 @@ public class OpenVidu { * since the last time method {@link io.openvidu.java.client.OpenVidu#fetch()} * was called. Exceptions to this rule are: *
      + *
    • Calling + * {@link io.openvidu.java.client.OpenVidu#createSession(SessionProperties) + * OpenVidu.createSession} automatically adds the new Session object to the + * local collection.
    • *
    • Calling {@link io.openvidu.java.client.Session#fetch()} updates that * specific Session status
    • *
    • Calling {@link io.openvidu.java.client.Session#close()} automatically diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java index e1ef12b4..9c54f6a7 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Session.java @@ -89,8 +89,9 @@ public class Session { } /** - * @deprecated Use {@link Session#createToken() Session.createToken()} instead - * to get a {@link io.openvidu.java.client.Token} object. + * @deprecated Use {@link Session#createConnection() Session.createConnection()} + * instead to get a {@link io.openvidu.java.client.Connection} + * object. * * @return The generated token String * @@ -99,14 +100,14 @@ public class Session { */ @Deprecated public String generateToken() throws OpenViduJavaClientException, OpenViduHttpException { - return createToken().getToken(); + return generateToken(new TokenOptions.Builder().data("").role(OpenViduRole.PUBLISHER).record(true).build()); } /** * @deprecated Use - * {@link Session#createToken(io.openvidu.java.client.TokenOptions) - * Session.createToken(TokenOptions)} instead to get a - * {@link io.openvidu.java.client.Token} object. + * {@link Session#createConnection(io.openvidu.java.client.ConnectionOptions) + * Session.createConnection(ConnectionOptions)} instead to get a + * {@link io.openvidu.java.client.Connection} object. * * @return The generated token String * @@ -115,70 +116,6 @@ public class Session { */ @Deprecated public String generateToken(TokenOptions tokenOptions) throws OpenViduJavaClientException, OpenViduHttpException { - return createToken(tokenOptions).getToken(); - } - - /** - * Gets a new token object associated to Session object with default values for - * {@link io.openvidu.java.client.TokenOptions}. The token string value to send - * to the client side can be retrieved with - * {@link io.openvidu.java.client.Token#getToken() Token.getToken()}.
      - *
      - * You can use method {@link io.openvidu.java.client.Token#getConnectionId() - * Token.getConnectionId()} to get the connection identifier that will be given - * to the user consuming the token. With connectionId you can call - * the following methods without having to fetch and search for the actual - * {@link io.openvidu.java.client.Connection Connection} object: - *
        - *
      • Call {@link io.openvidu.java.client.Session#forceDisconnect(String) - * Session.forceDisconnect()} to invalidate the token if no client has used it - * yet or force the connected client to leave the session if it has.
      • - *
      • Call - * {@link io.openvidu.java.client.Session#updateConnection(String, TokenOptions) - * Session.updateConnection()} to update the - * {@link io.openvidu.java.client.Connection Connection} options. And this is - * valid for unused tokens, but also for already used tokens, so you can - * dynamically change the connection options on the fly.
      • - *
      - * - * @return The generated {@link io.openvidu.java.client.Token Token} object. - * - * @throws OpenViduJavaClientException - * @throws OpenViduHttpException - */ - public Token createToken() throws OpenViduJavaClientException, OpenViduHttpException { - return createToken(new TokenOptions.Builder().data("").role(OpenViduRole.PUBLISHER).record(true).build()); - } - - /** - * Gets a new token object associated to Session object configured with - * tokenOptions. The token string value to send to the client side - * can be retrieved with {@link io.openvidu.java.client.Token#getToken() - * Token.getToken()}.
      - *
      - * You can use method {@link io.openvidu.java.client.Token#getConnectionId() - * Token.getConnectionId()} to get the connection identifier that will be given - * to the user consuming the token. With connectionId you can call - * the following methods without having to fetch and search for the actual - * {@link io.openvidu.java.client.Connection Connection} object: - *
        - *
      • Call {@link io.openvidu.java.client.Session#forceDisconnect(String) - * Session.forceDisconnect()} to invalidate the token if no client has used it - * yet or force the connected client to leave the session if it has.
      • - *
      • Call - * {@link io.openvidu.java.client.Session#updateConnection(String, ConnectionOptions) - * Session.updateConnection()} to update the - * {@link io.openvidu.java.client.Connection Connection} options. And this is - * valid for unused tokens, but also for already used tokens, so you can - * dynamically change the user connection options on the fly.
      • - *
      - * - * @return The generated {@link io.openvidu.java.client.Token Token} object. - * - * @throws OpenViduJavaClientException - * @throws OpenViduHttpException - */ - public Token createToken(TokenOptions tokenOptions) throws OpenViduJavaClientException, OpenViduHttpException { if (!this.hasSessionId()) { this.getSessionId(); } @@ -205,8 +142,8 @@ public class Session { try { int statusCode = response.getStatusLine().getStatusCode(); if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { - Token token = new Token(httpResponseToJson(response)); - log.info("Returning a TOKEN: {}", token.getToken()); + String token = httpResponseToJson(response).get("id").getAsString(); + log.info("Returning a TOKEN: {}", token); return token; } else { throw new OpenViduHttpException(statusCode); @@ -216,6 +153,75 @@ public class Session { } } + /** + * Same as + * {@link io.openvidu.java.client.Session#createConnection(ConnectionOptions) + * but with default ConnectionOptions values. + * + * @return The generated {@link io.openvidu.java.client.Connection Connection} + * object. + * + * @throws OpenViduJavaClientException + * @throws OpenViduHttpException + */ + public Connection createConnection() throws OpenViduJavaClientException, OpenViduHttpException { + return createConnection( + new ConnectionOptions.Builder().data("").role(OpenViduRole.PUBLISHER).record(true).build()); + } + + /** + * Creates a new Connection object associated to Session object and configured + * with connectionOptions. Each user connecting to the Session + * requires a Connection. The token string value to send to the client side can + * be retrieved with {@link io.openvidu.java.client.Connection#getToken() + * Connection.getToken()}. + * + * @return The generated {@link io.openvidu.java.client.Connection Connection} + * object. + * + * @throws OpenViduJavaClientException + * @throws OpenViduHttpException + */ + public Connection createConnection(ConnectionOptions connectionOptions) + throws OpenViduJavaClientException, OpenViduHttpException { + if (!this.hasSessionId()) { + this.getSessionId(); + } + + HttpPost request = new HttpPost( + this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection"); + + StringEntity params; + try { + params = new StringEntity(connectionOptions.toJson(sessionId).toString()); + } catch (UnsupportedEncodingException e1) { + throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause()); + } + + request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); + request.setEntity(params); + + HttpResponse response; + try { + response = this.openVidu.httpClient.execute(request); + } catch (IOException e2) { + throw new OpenViduJavaClientException(e2.getMessage(), e2.getCause()); + } + + try { + int statusCode = response.getStatusLine().getStatusCode(); + if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { + Connection connection = new Connection(httpResponseToJson(response)); + this.connections.put(connection.getConnectionId(), connection); + return connection; + } else { + throw new OpenViduHttpException(statusCode); + } + } finally { + EntityUtils.consumeQuietly(response.getEntity()); + } + } + /** * Gracefully closes the Session: unpublishes all streams and evicts every * participant. @@ -298,24 +304,31 @@ public class Session { } /** - * Forces the user represented by connection to leave the session. - * OpenVidu Browser will trigger the proper events on the client-side - * (streamDestroyed, connectionDestroyed, + * Removes the Connection from the Session. This can translate into a forced + * eviction of a user from the Session if the Connection had status + * active, or into a token invalidation if no user had taken the + * Connection yet (status pending).
      + *
      + * + * In the first case, OpenVidu Browser will trigger the proper events on the + * client-side (streamDestroyed, connectionDestroyed, * sessionDisconnected) with reason set to * "forceDisconnectByServer".
      *
      * - * You can get connection parameter with - * {@link io.openvidu.java.client.Session#fetch()} and then - * {@link io.openvidu.java.client.Session#getActiveConnections()}.
      + * In the second case, the token of the Connection will be invalidated and no + * user will be able to connect to the session with it.
      *
      * * This method automatically updates the properties of the local affected * objects. This means that there is no need to call - * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} to see the + * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or + * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} to see the * changes consequence of the execution of this method applied in the local * objects. * + * @param connection The Connection to remove + * * @throws OpenViduJavaClientException * @throws OpenViduHttpException */ @@ -324,35 +337,12 @@ public class Session { } /** - * Forces the user with Connection connectionId to leave the - * session, or invalidates the {@link Token} associated with that - * connectionId if no user has used it yet.
      - *
      + * Same as {@link io.openvidu.java.client.Session#forceDisconnect(Connection) + * forceDisconnect(ConnectionOptions)} but providing the + * {@link io.openvidu.java.client.Connection#getConnectionId() connectionId} + * instead of the Connection object. * - * In the first case you can get connectionId parameter from - * {@link io.openvidu.java.client.Connection#getConnectionId() - * Connection.getConnectionId()}. Connection objects can be listed with - * {@link io.openvidu.java.client.Session#getActiveConnections() - * Session.getActiveConnections()} (remember to use first - * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} to fetch the - * current active connections from OpenVidu Server). As a result, OpenVidu - * Browser will trigger the proper events on the client-side - * (streamDestroyed, connectionDestroyed, - * sessionDisconnected) with reason set to - * "forceDisconnectByServer".
      - *
      - * - * In the second case you can get connectionId parameter from a - * {@link Token} with {@link Token#getConnectionId()}. As a result, the token - * will be invalidated and no user will be able to connect to the session with - * it.
      - *
      - * - * This method automatically updates the properties of the local affected - * objects. This means that there is no need to call - * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} to see the - * changes consequence of the execution of this method applied in the local - * objects. + * @param connectionId The identifier of the Connection object to remove * * @throws OpenViduJavaClientException * @throws OpenViduHttpException @@ -400,9 +390,9 @@ public class Session { } /** - * Forces some user to unpublish a Stream. OpenVidu Browser will trigger the - * proper events on the client-side (streamDestroyed) with reason - * set to "forceUnpublishByServer".
      + * Forces some Connection to unpublish a Stream. OpenVidu Browser will trigger + * the proper events in the client-side (streamDestroyed) with + * reason set to "forceUnpublishByServer".
      *
      * * You can get publisher parameter with @@ -415,10 +405,13 @@ public class Session { * * This method automatically updates the properties of the local affected * objects. This means that there is no need to call - * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} to see the + * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or + * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} to see the * changes consequence of the execution of this method applied in the local * objects. * + * @param publisher The Publisher object to unpublish + * * @throws OpenViduJavaClientException * @throws OpenViduHttpException */ @@ -427,26 +420,12 @@ public class Session { } /** - * Forces some user to unpublish a Stream. OpenVidu Browser will trigger the - * proper events on the client-side (streamDestroyed) with reason - * set to "forceUnpublishByServer".
      - *
      + * Same as {@link io.openvidu.java.client.Session#forceUnpublish(Publisher) + * forceUnpublish(Publisher)} but providing the + * {@link io.openvidu.java.client.Publisher#getStreamId() streamId} instead of + * the Publisher object. * - * You can get streamId parameter with - * {@link io.openvidu.java.client.Session#getActiveConnections()} and then for - * each Connection you can call - * {@link io.openvidu.java.client.Connection#getPublishers()}. Finally - * {@link io.openvidu.java.client.Publisher#getStreamId()}) will give you the - * streamId. Remember to call - * {@link io.openvidu.java.client.Session#fetch()} before to fetch the current - * actual properties of the Session from OpenVidu Server.
      - *
      - * - * This method automatically updates the properties of the local affected - * objects. This means that there is no need to call - * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} to see the - * changes consequence of the execution of this method applied in the local - * objects. + * @param streamId The identifier of the Publisher object to remove * * @throws OpenViduJavaClientException * @throws OpenViduHttpException @@ -520,7 +499,7 @@ public class Session { StringEntity params; try { - params = new StringEntity(connectionOptions.toJsonObject(this.sessionId).toString()); + params = new StringEntity(connectionOptions.toJson(this.sessionId).toString()); } catch (UnsupportedEncodingException e1) { throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause()); } @@ -570,6 +549,11 @@ public class Session { * actual value you must call first * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()}. + * + * @param id The Connection to get + * + * @return The {@link io.openvidu.java.client.Connection Connection} object, or + * null if no Connection is found for param id */ public Connection getConnection(String id) { return this.connections.get(id); @@ -581,6 +565,29 @@ public class Session { * current actual value you must call first * {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()}. + * + * The list of Connections will remain unchanged since the last time + * method {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or + * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} was + * called. Exceptions to this rule are: + *
        + *
      • Calling + * {@link io.openvidu.java.client.Session#createConnection(ConnectionOptions) + * createConnection(ConnectionOptions)} automatically adds the new Connection + * object to the local collection.
      • + *
      • Calling {@link io.openvidu.java.client.Session#forceUnpublish(String)} + * automatically updates each affected local Connection object.
      • + *
      • Calling {@link io.openvidu.java.client.Session#forceDisconnect(String)} + * automatically updates each affected local Connection object.
      • + *
      • Calling + * {@link io.openvidu.java.client.Session#updateConnection(String, ConnectionOptions)} + * automatically updates the attributes of the affected local Connection + * object.
      • + *
      + *
      + * To get the list of connections with their current actual value, you must call + * first {@link io.openvidu.java.client.Session#fetch() Session.fetch()} or + * {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()}. */ public List getConnections() { return this.connections.values().stream().collect(Collectors.toList()); @@ -596,6 +603,10 @@ public class Session { * or {@link io.openvidu.java.client.OpenVidu#fetch() OpenVidu.fetch()} was * called. Exceptions to this rule are: *
        + *
      • Calling + * {@link io.openvidu.java.client.Session#createConnection(ConnectionOptions) + * createConnection(ConnectionOptions)} automatically adds the new Connection + * object to the local collection.
      • *
      • Calling {@link io.openvidu.java.client.Session#forceUnpublish(String)} * automatically updates each affected local Connection object.
      • *
      • Calling {@link io.openvidu.java.client.Session#forceDisconnect(String)} @@ -609,7 +620,7 @@ public class Session { * To get the list of active connections with their current actual value, you * must call first {@link io.openvidu.java.client.Session#fetch() * Session.fetch()} or {@link io.openvidu.java.client.OpenVidu#fetch() - * OpenVidu.fetch()}. + * OpenVidu.fetch()} OpenVidu.fetch()}. */ public List getActiveConnections() { return this.connections.values().stream().filter(con -> "active".equals(con.getStatus())) diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Token.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Token.java deleted file mode 100644 index b629d323..00000000 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Token.java +++ /dev/null @@ -1,127 +0,0 @@ -package io.openvidu.java.client; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -public class Token { - - private String token; - private String connectionId; - private TokenOptions tokenOptions; - - protected Token(String token, String connectionId, TokenOptions tokenOptions) { - this.token = token; - this.connectionId = connectionId; - this.tokenOptions = tokenOptions; - } - - protected Token(JsonObject json) { - - this.token = json.get("token").getAsString(); - this.connectionId = json.get("connectionId").getAsString(); - - OpenViduRole role = OpenViduRole.valueOf(json.get("role").getAsString()); - String data = json.get("data").getAsString(); - Boolean record = json.get("record").getAsBoolean(); - - KurentoOptions kurentoOptions = null; - if (json.has("kurentoOptions")) { - JsonObject kurentoOptionsJson = json.get("kurentoOptions").getAsJsonObject(); - - Integer videoMaxRecvBandwidth = null; - Integer videoMinRecvBandwidth = null; - Integer videoMaxSendBandwidth = null; - Integer videoMinSendBandwidth = null; - String[] allowedFilters = null; - - if (kurentoOptionsJson.has("videoMaxRecvBandwidth")) { - videoMaxRecvBandwidth = kurentoOptionsJson.get("videoMaxRecvBandwidth").getAsInt(); - } - if (kurentoOptionsJson.has("videoMinRecvBandwidth")) { - videoMinRecvBandwidth = kurentoOptionsJson.get("videoMinRecvBandwidth").getAsInt(); - } - if (kurentoOptionsJson.has("videoMaxSendBandwidth")) { - videoMaxSendBandwidth = kurentoOptionsJson.get("videoMaxSendBandwidth").getAsInt(); - } - if (kurentoOptionsJson.has("videoMinSendBandwidth")) { - videoMinSendBandwidth = kurentoOptionsJson.get("videoMinSendBandwidth").getAsInt(); - } - if (kurentoOptionsJson.has("allowedFilters")) { - JsonArray filters = kurentoOptionsJson.get("allowedFilters").getAsJsonArray(); - allowedFilters = new String[filters.size()]; - for (int i = 0; i < filters.size(); i++) { - allowedFilters[i] = filters.get(i).getAsString(); - } - } - kurentoOptions = new KurentoOptions(videoMaxRecvBandwidth, videoMinRecvBandwidth, videoMaxSendBandwidth, - videoMinSendBandwidth, allowedFilters); - } - - this.tokenOptions = new TokenOptions(role, data, record, kurentoOptions); - } - - /** - * Returns the token string value that must be sent to clients. They need to use - * it to connect to the session. - */ - public String getToken() { - return this.token; - } - - /** - * Returns the connection identifier that will be associated to the user - * consuming this token. This means that the future - * {@link io.openvidu.java.client.Connection Connection} object created with - * this token will have as connectionId this string. In other - * words, method {@link io.openvidu.java.client.Connection#getConnectionId() - * Connection.getConnectionId()} will return this same value. - * - * With connectionId you can call the following methods without - * having to fetch and search for the actual - * {@link io.openvidu.java.client.Connection Connection} object: - *
          - *
        • Call {@link io.openvidu.java.client.Session#forceDisconnect(String) - * Session.forceDisconnect()} to invalidate the token if no client has used it - * yet or force the connected client to leave the session if it has.
        • - *
        • Call - * {@link io.openvidu.java.client.Session#updateConnection(String, TokenOptions) - * Session.updateConnection()} to update the - * {@link io.openvidu.java.client.Connection Connection} options. And this is - * valid for unused tokens, but also for already used tokens, so you can - * dynamically change the connection options on the fly.
        • - *
        - * - */ - public String getConnectionId() { - return this.connectionId; - } - - /** - * Returns the role assigned to this token. - */ - public OpenViduRole getRole() { - return this.tokenOptions.getRole(); - } - - /** - * Returns the secure (server-side) metadata assigned to this token. - */ - public String getData() { - return this.tokenOptions.getData(); - } - - /** - * Whether the streams published by the participant owning this token will be - * recorded or not. This only affects INDIVIDUAL recording. - */ - public Boolean record() { - return this.tokenOptions.record(); - } - - protected void overrideTokenOptions(TokenOptions tokenOptions) { - this.tokenOptions = tokenOptions; - } - -} diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java index f9338064..f450399b 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/TokenOptions.java @@ -17,12 +17,12 @@ package io.openvidu.java.client; -import com.google.gson.JsonArray; import com.google.gson.JsonNull; import com.google.gson.JsonObject; /** - * See {@link io.openvidu.java.client.Session#generateToken(TokenOptions)} + * @deprecated Use {@link io.openvidu.java.client.ConnectionOptions + * ConnectionOptions} instead */ public class TokenOptions { @@ -32,9 +32,8 @@ public class TokenOptions { private KurentoOptions kurentoOptions; /** - * - * Builder for {@link io.openvidu.java.client.TokenOptions} - * + * @deprecated Use {@link io.openvidu.java.client.ConnectionOptions.Builder + * ConnectionOptions.Builder} instead */ public static class Builder { @@ -157,27 +156,7 @@ public class TokenOptions { json.add("record", JsonNull.INSTANCE); } if (this.kurentoOptions != null) { - JsonObject kurentoOptions = new JsonObject(); - if (this.kurentoOptions.getVideoMaxRecvBandwidth() != null) { - kurentoOptions.addProperty("videoMaxRecvBandwidth", this.kurentoOptions.getVideoMaxRecvBandwidth()); - } - if (this.kurentoOptions.getVideoMinRecvBandwidth() != null) { - kurentoOptions.addProperty("videoMinRecvBandwidth", this.kurentoOptions.getVideoMinRecvBandwidth()); - } - if (this.kurentoOptions.getVideoMaxSendBandwidth() != null) { - kurentoOptions.addProperty("videoMaxSendBandwidth", this.kurentoOptions.getVideoMaxSendBandwidth()); - } - if (this.kurentoOptions.getVideoMinSendBandwidth() != null) { - kurentoOptions.addProperty("videoMinSendBandwidth", this.kurentoOptions.getVideoMinSendBandwidth()); - } - if (this.kurentoOptions.getAllowedFilters().length > 0) { - JsonArray allowedFilters = new JsonArray(); - for (String filter : this.kurentoOptions.getAllowedFilters()) { - allowedFilters.add(filter); - } - kurentoOptions.add("allowedFilters", allowedFilters); - } - json.add("kurentoOptions", kurentoOptions); + json.add("kurentoOptions", kurentoOptions.toJson()); } return json; } diff --git a/openvidu-node-client/src/Connection.ts b/openvidu-node-client/src/Connection.ts index 0f724f71..44be496b 100644 --- a/openvidu-node-client/src/Connection.ts +++ b/openvidu-node-client/src/Connection.ts @@ -15,9 +15,9 @@ * */ -import { OpenViduRole } from './OpenViduRole'; import { Publisher } from './Publisher'; import { ConnectionOptions } from './ConnectionOptions'; +import { OpenViduRole } from 'OpenViduRole'; /** * See [[Session.connections]] @@ -25,13 +25,13 @@ import { ConnectionOptions } from './ConnectionOptions'; export class Connection { /** - * Identifier of the connection. You can call methods [[Session.forceDisconnect]] + * Identifier of the Connection. You can call methods [[Session.forceDisconnect]] * or [[Session.updateConnection]] passing this property as parameter */ connectionId: string; /** - * Returns the status of the connection. Can be: + * Returns the status of the Connection. Can be: * - `pending`: if the Connection is waiting for any user to use * its internal token to connect to the session, calling method * [Session.connect](https://docs.openvidu.io/en/stable/api/openvidu-browser/classes/session.html#connect) @@ -43,39 +43,19 @@ export class Connection { status: string; /** - * Timestamp when this connection was created, in UTC milliseconds (ms since Jan 1, 1970, 00:00:00 UTC) + * Timestamp when the Connection was created, in UTC milliseconds (ms since Jan 1, 1970, 00:00:00 UTC) */ createdAt: number; /** - * Timestamp when this connection was taken by a user (passing from status "pending" to "active") + * Timestamp when the Connection was taken by a user (passing from status "pending" to "active") * in UTC milliseconds (ms since Jan 1, 1970, 00:00:00 UTC) */ activeAt: number; - /** - * Role of the connection - */ - role: OpenViduRole; - - /** - * Data associated to the connection on the server-side. This value is set with property [[TokenOptions.data]] when calling [[Session.generateToken]] - */ - serverData: string; - - /** - * Whether to record the streams published by the participant owning this token or not. This only affects [INDIVIDUAL recording](/en/stable/advanced-features/recording#selecting-streams-to-be-recorded) - */ - record: boolean; - - /** - * Token associated to the connection - */ - token: string; - /** * PRO - * Geo location of the connection, with the following format: `"CITY, COUNTRY"` (`"unknown"` if it wasn't possible to locate it) + * Geo location of the Connection, with the following format: `"CITY, COUNTRY"` (`"unknown"` if it wasn't possible to locate it) */ location: string; @@ -85,11 +65,21 @@ export class Connection { platform: string; /** - * Data associated to the connection on the client-side. This value is set with second parameter of method + * Data associated to the Connection on the client-side. This value is set with second parameter of method * [Session.connect](/en/stable/api/openvidu-browser/classes/session.html#connect) in OpenVidu Browser */ clientData: string; + /** + * The [[ConnectionOptions]] assigned to the Connection + */ + connectionOptions: ConnectionOptions; + + /** + * Token associated to the Connection + */ + token: string; + /** * Array of Publisher objects this particular Connection is publishing to the Session (each Publisher object has one Stream, uniquely * identified by its `streamId`). You can call [[Session.forceUnpublish]] passing any of this values as parameter @@ -102,6 +92,15 @@ export class Connection { */ subscribers: string[] = []; + /** + * @hidden deprecated. Inside ConnectionOptions + */ + role?: OpenViduRole; + /** + * @hidden deprecated. Inside ConnectionOptions + */ + serverData?: string; + /** * @hidden */ @@ -114,15 +113,41 @@ export class Connection { */ resetWithJson(json): Connection { - // These properties won't ever be null this.connectionId = json.connectionId; this.status = json.status; + this.createdAt = json.createdAt; + this.activeAt = json.activeAt; + this.location = json.location; + this.platform = json.platform; + this.clientData = json.clientData; this.token = json.token; + if (this.connectionOptions != null) { + this.connectionOptions.type = json.type; + this.connectionOptions.data = json.data; + this.connectionOptions.record = json.record; + this.connectionOptions.role = json.role; + this.connectionOptions.kurentoOptions = json.kurentoOptions; + this.connectionOptions.rtspUri = json.rtspUri; + this.connectionOptions.adaptativeBitrate = json.adaptativeBitrate; + this.connectionOptions.onlyPlayWithSubscribers = json.onlyPlayWithSubscribers; + this.connectionOptions.networkCache = json.networkCache; + } else { + this.connectionOptions = { + type: json.type, + data: json.data, + record: json.record, + role: json.role, + kurentoOptions: json.kurentoOptions, + rtspUri: json.rtspUri, + adaptativeBitrate: json.adaptativeBitrate, + onlyPlayWithSubscribers: json.onlyPlayWithSubscribers, + networkCache: json.networkCache + } + } this.role = json.role; - this.serverData = json.serverData; - this.record = json.record; + this.serverData = json.data; - // These properties may be null + // publishers may be null if (json.publishers != null) { // 1. Array to store fetched Publishers and later remove closed ones @@ -151,6 +176,7 @@ export class Connection { } + // subscribers may be null if (json.subscribers != null) { // 1. Array to store fetched Subscribers and later remove closed ones @@ -171,12 +197,6 @@ export class Connection { } } - this.createdAt = json.createdAt; - this.activeAt = json.activeAt; - this.location = json.location; - this.platform = json.platform; - this.clientData = json.clientData; - return this; } @@ -189,9 +209,15 @@ export class Connection { this.status === other.status && this.createdAt === other.createdAt && this.activeAt === other.activeAt && - this.role === other.role && - this.serverData === other.serverData && - this.record === other.record && + this.connectionOptions.type === other.connectionOptions.type && + this.connectionOptions.data === other.connectionOptions.data && + this.connectionOptions.record === other.connectionOptions.record && + this.connectionOptions.role === other.connectionOptions.role && + this.connectionOptions.kurentoOptions === other.connectionOptions.kurentoOptions && + this.connectionOptions.rtspUri === other.connectionOptions.rtspUri && + this.connectionOptions.adaptativeBitrate === other.connectionOptions.adaptativeBitrate && + this.connectionOptions.onlyPlayWithSubscribers === other.connectionOptions.onlyPlayWithSubscribers && + this.connectionOptions.networkCache === other.connectionOptions.networkCache && this.token === other.token && this.location === other.location && this.platform === other.platform && @@ -218,12 +244,13 @@ export class Connection { /** * @hidden */ - overrideConnectionOptions(connectionOptions: ConnectionOptions): void { - if (connectionOptions.role != null) { - this.role = connectionOptions.role; + overrideConnectionOptions(newConnectionOptions: ConnectionOptions): void { + // For now only properties record and role + if (newConnectionOptions.record != null) { + this.connectionOptions.record = newConnectionOptions.record; } - if (connectionOptions.record != null) { - this.record = connectionOptions.record + if (newConnectionOptions.role != null) { + this.connectionOptions.role = newConnectionOptions.role; } } diff --git a/openvidu-node-client/src/ConnectionOptions.ts b/openvidu-node-client/src/ConnectionOptions.ts index bd10bbaf..222bc2ec 100644 --- a/openvidu-node-client/src/ConnectionOptions.ts +++ b/openvidu-node-client/src/ConnectionOptions.ts @@ -15,16 +15,20 @@ * */ +import { ConnectionType } from 'ConnectionType'; import { OpenViduRole } from './OpenViduRole'; export interface ConnectionOptions { /** - * The role assigned to this Connection + * Type of Connection. The [[ConnectionType]] dictates what properties will have effect: * - * @default PUBLISHER + * - **[[ConnectionType.WEBRTC]]**: [[data]], [[record]], [[role]], [[kurentoOptions]] + * - **[[ConnectionType.IPCAM]]**: [[data]], [[record]], [[rtspUri]], [[adaptativeBitrate]], [[onlyPlayWithSubscribers]], [[networkCache]] + * + * @default WEBRTC */ - role?: OpenViduRole; + type?: ConnectionType; /** * Secure (server-side) data associated to this Connection. Every client will receive this data in property `Connection.data`. Object `Connection` can be retrieved by subscribing to event `connectionCreated` of Session object. @@ -41,6 +45,15 @@ export interface ConnectionOptions { */ record?: boolean; + /** + * The role assigned to this Connection + * + * **Only for [[ConnectionType.WEBRTC]]** + * + * @default PUBLISHER + */ + role?: OpenViduRole; + /** * **WARNING**: experimental option. This interface may change in the near future * @@ -61,6 +74,8 @@ export interface ConnectionOptions { * the global configuration set in [OpenVidu Server configuration](/en/stable/reference-docs/openvidu-config/) * (parameter `OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH`) for every outgoing stream of the Connection. * - `allowedFilters`: names of the filters the Connection will be able to apply. See [Voice and video filters](/en/stable/advanced-features/filters/) + * + * **Only for [[ConnectionType.WEBRTC]]** */ kurentoOptions?: { videoMaxRecvBandwidth?: number, @@ -69,4 +84,45 @@ export interface ConnectionOptions { videoMinSendBandwidth?: number, allowedFilters?: string[] }; + + /** + * RTSP URI of an IP camera. For example: `rtsp://your.camera.ip:7777/path` + * + * **Only for [[ConnectionType.IPCAM]]** + */ + rtspUri?: string; + + /** + * Whether to use adaptative bitrate (and therefore adaptative quality) or not. For local network connections + * that do not require media transcoding this can be disabled to save CPU power. If you are not sure if transcoding + * might be necessary, setting this property to false **may result in media connections not being established**. + * + * **Only for [[ConnectionType.IPCAM]]** + * + * @default true + */ + adaptativeBitrate?: boolean; + + /** + * Whether to enable the IP camera stream only when some user is subscribed to it, or not. This allows you to reduce + * power consumption and network bandwidth in your server while nobody is asking to receive the camera's video. + * On the counterpart, first user subscribing to the IP camera stream will take a little longer to receive its video. + * + * **Only for [[ConnectionType.IPCAM]]** + * + * @default true + */ + onlyPlayWithSubscribers?: boolean; + + /** + * Size of the buffer of the endpoint receiving the IP camera's stream, in milliseconds. The smaller it is, the less + * delay the signal will have, but more problematic will be in unstable networks. Use short buffers only if there is + * a quality connection between the IP camera and OpenVidu Server. + * + * **Only for [[ConnectionType.IPCAM]]** + * + * @default 2000 + */ + networkCache?: number; + } \ No newline at end of file diff --git a/openvidu-node-client/src/ConnectionType.ts b/openvidu-node-client/src/ConnectionType.ts new file mode 100644 index 00000000..29268f4b --- /dev/null +++ b/openvidu-node-client/src/ConnectionType.ts @@ -0,0 +1,34 @@ +/* + * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * See [[Session.createConnection]] + */ +export enum ConnectionType { + + /** + * WebRTC connection. This is the normal type of Connection for a regular user + * connecting to a session from an application. + */ + WEBRTC = 'WEBRTC', + + /** + * IP camera connection. This is the type of Connection used by IP cameras to + * connect to a session. + */ + IPCAM = 'IPCAM' +} \ No newline at end of file diff --git a/openvidu-node-client/src/OpenVidu.ts b/openvidu-node-client/src/OpenVidu.ts index 6979352b..b98a2ee1 100644 --- a/openvidu-node-client/src/OpenVidu.ts +++ b/openvidu-node-client/src/OpenVidu.ts @@ -76,6 +76,7 @@ export class OpenVidu { * Array of active sessions. **This value will remain unchanged since the last time method [[OpenVidu.fetch]] * was called**. Exceptions to this rule are: * + * - Calling [[OpenVidu.createSession]] automatically adds the new Session object to the local collection. * - Calling [[Session.fetch]] updates that specific Session status * - Calling [[Session.close]] automatically removes the Session from the list of active Sessions * - Calling [[Session.forceDisconnect]] automatically updates the inner affected connections for that specific Session diff --git a/openvidu-node-client/src/Session.ts b/openvidu-node-client/src/Session.ts index 10711654..9cb41354 100644 --- a/openvidu-node-client/src/Session.ts +++ b/openvidu-node-client/src/Session.ts @@ -25,10 +25,8 @@ import { Recording } from './Recording'; import { RecordingLayout } from './RecordingLayout'; import { RecordingMode } from './RecordingMode'; import { SessionProperties } from './SessionProperties'; -import { Token } from './Token'; import { TokenOptions } from './TokenOptions'; - export class Session { /** @@ -51,6 +49,7 @@ export class Session { * **will remain unchanged since the last time method [[Session.fetch]] or [[OpenVidu.fetch]] was called**. * Exceptions to this rule are: * + * - Calling [[Session.createConnection]] automatically adds the new Connection object to the local collection. * - Calling [[Session.forceUnpublish]] automatically updates each affected local Connection object. * - Calling [[Session.forceDisconnect]] automatically updates each affected local Connection object. * - Calling [[Session.updateConnection]] automatically updates the attributes of the affected local Connection object. @@ -105,37 +104,12 @@ export class Session { } /** - * @deprecated Use [[Session.createToken]] instead to get a [[Token]] object. + * @deprecated Use [[Session.createConnection]] instead to get a [[Connection]] object. * * @returns A Promise that is resolved to the generated _token_ string if success and rejected with an Error object if not */ public generateToken(tokenOptions?: TokenOptions): Promise { return new Promise((resolve, reject) => { - this.createToken(tokenOptions).then(token => resolve(token.token)).catch(error => reject(error)); - }); - } - - /** - * Gets a new token object associated to Session object configured with - * `tokenOptions`. The token string value to send to the client side - * is available at [[Token.token]] property. - * - * Property [[Token.connectionId]] provides the connection identifier that will be given - * to the user consuming the token. With `connectionId` you can call - * the following methods without having to fetch and search for the actual - * [[Connection]] object: - * - * - Call [[Session.forceDisconnect]] to invalidate the token if no client has used it - * yet or force the connected client to leave the session if it has. - * - Call [[Session.updateConnection]] to update the [[Connection]] options. And this is - * valid for unused tokens, but also for already used tokens, so you can - * dynamically change the user connection options on the fly. - * - * @returns A Promise that is resolved to the generated [[Token]] object if success and rejected with an Error object if not - */ - public createToken(tokenOptions?: TokenOptions): Promise { - return new Promise((resolve, reject) => { - const data = JSON.stringify({ session: this.sessionId, role: (!!tokenOptions && !!tokenOptions.role) ? tokenOptions.role : null, @@ -156,7 +130,51 @@ export class Session { .then(res => { if (res.status === 200) { // SUCCESS response from openvidu-server. Resolve token - resolve(new Token(res.data)); + resolve(res.data.token); + } else { + // ERROR response from openvidu-server. Resolve HTTP status + reject(new Error(res.status.toString())); + } + }).catch(error => { + this.handleError(error, reject); + }); + }); + } + + /** + * Creates a new Connection object associated to Session object and configured with + * `connectionOptions`. Each user connecting to the Session requires a Connection. + * The token string value to send to the client side is available at [[Connection.token]]. + * + * @returns A Promise that is resolved to the generated [[Connection]] object if success and rejected with an Error object if not + */ + public createConnection(connectionOptions?: ConnectionOptions): Promise { + return new Promise((resolve, reject) => { + const data = JSON.stringify({ + role: (!!connectionOptions && !!connectionOptions.role) ? connectionOptions.role : null, + data: (!!connectionOptions && !!connectionOptions.data) ? connectionOptions.data : null, + record: !!connectionOptions ? connectionOptions.record : null, + kurentoOptions: (!!connectionOptions && !!connectionOptions.kurentoOptions) ? connectionOptions.kurentoOptions : null + }); + axios.post( + this.ov.host + OpenVidu.API_SESSIONS + '/' + this.sessionId + '/connection', + data, + { + headers: { + 'Authorization': this.ov.basicAuth, + 'Content-Type': 'application/json' + } + } + ) + .then(res => { + if (res.status === 200) { + // SUCCESS response from openvidu-server. Store and resolve Connection + const connection = new Connection(res.data); + this.connections.push(connection); + if (connection.status === 'active') { + this.activeConnections.push(connection); + } + resolve(new Connection(res.data)); } else { // ERROR response from openvidu-server. Resolve HTTP status reject(new Error(res.status.toString())); @@ -240,12 +258,18 @@ export class Session { } /** - * Removes a Connection from the Session. + * Removes the Connection from the Session. This can translate into a forced eviction of a user from the Session if the + * Connection had status `active` or into a token invalidation if no user had taken the Connection yet (status `pending`). + * + * In the first case, OpenVidu Browser will trigger the proper events in the client-side (`streamDestroyed`, `connectionDestroyed`, + * `sessionDisconnected`) with reason set to `"forceDisconnectByServer"`. + * + * In the second case, the token of the Connection will be invalidated and no user will be able to connect to the session with it. * * This method automatically updates the properties of the local affected objects. This means that there is no need to call - * [[Session.fetch]] or [[OpenVidu.fetch]] to see the changes consequence of the execution of this method applied in the local objects. + * [[Session.fetch]] or [[OpenVidu.fetch]]] to see the changes consequence of the execution of this method applied in the local objects. * - * @param connection The Connection object to disconnect from the session, or its `connectionId` property + * @param connection The Connection object to remove from the session, or its `connectionId` property * * @returns A Promise that is resolved if the Connection was successfully removed from the Session and rejected with an Error object if not */ @@ -308,7 +332,7 @@ export class Session { } /** - * Forces some user to unpublish a Stream (identified by its `streamId` or the corresponding [[Publisher]] object owning it). + * Forces some Connection to unpublish a Stream (identified by its `streamId` or the corresponding [[Publisher]] object owning it). * OpenVidu Browser will trigger the proper events on the client-side (`streamDestroyed`) with reason set to `"forceUnpublishByServer"`. * * You can get `publisher` parameter from [[Connection.publishers]] array ([[Publisher.streamId]] for getting each `streamId` property). @@ -317,6 +341,8 @@ export class Session { * This method automatically updates the properties of the local affected objects. This means that there is no need to call * [[Session.fetch]] or [[OpenVidu.fetch]] to see the changes consequence of the execution of this method applied in the local objects. * + * @param publisher The Publisher object to unpublish, or its `streamId` property + * * @returns A Promise that is resolved if the stream was successfully unpublished and rejected with an Error object if not */ public forceUnpublish(publisher: string | Publisher): Promise { diff --git a/openvidu-node-client/src/Token.ts b/openvidu-node-client/src/Token.ts deleted file mode 100644 index bd9119d1..00000000 --- a/openvidu-node-client/src/Token.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { TokenOptions } from './TokenOptions'; - -export class Token { - - /** - * The token string value that must be sent to clients. They need to use it to connect to the session. - */ - token: string; - - /** - * The connection identifier that will be associated to the user - * consuming this token. This means that the future [[Connection]] object created with - * this token will have as [[Connection.connectionId]] this same value. - * - * With `connectionId` you can call the following methods without - * having to fetch and search for the actual [[Connection]] object: - * - * - Call [[Session.forceDisconnect]] to invalidate the token if no client has used it - * yet or force the connected client to leave the session if it has. - * - Call [[Session.updateConnection]] to update the [[Connection]] options. And this is - * valid for unused tokens, but also for already used tokens, so you can - * dynamically change the connection options on the fly. - */ - connectionId: string; - - /** - * The [[TokenOptions]] assigned to this token. - */ - tokenOptions: TokenOptions; - - /** - * @hidden - */ - constructor(json) { - this.token = json.token; - this.connectionId = json.connectionId; - let possibleKurentoOptions; - if (!!json.kurentoOptions) { - possibleKurentoOptions = {}; - if (json.kurentoOptions.videoMaxRecvBandwidth != null) { - possibleKurentoOptions['videoMaxRecvBandwidth'] = json.kurentoOptions.videoMaxRecvBandwidth; - } - if (json.kurentoOptions.videoMinRecvBandwidth != null) { - possibleKurentoOptions['videoMinRecvBandwidth'] = json.kurentoOptions.videoMinRecvBandwidth; - } - if (json.kurentoOptions.videoMaxSendBandwidth != null) { - possibleKurentoOptions['videoMaxSendBandwidth'] = json.kurentoOptions.videoMaxSendBandwidth; - } - if (json.kurentoOptions.videoMinSendBandwidth != null) { - possibleKurentoOptions['videoMinSendBandwidth'] = json.kurentoOptions.videoMinSendBandwidth; - } - if (json.kurentoOptions.allowedFilters != null) { - possibleKurentoOptions['allowedFilters'] = json.kurentoOptions.allowedFilters; - } - } - this.tokenOptions = { - role: json.role, - data: json.data, - record: json.record, - kurentoOptions: possibleKurentoOptions - }; - } - -} \ No newline at end of file diff --git a/openvidu-node-client/src/TokenOptions.ts b/openvidu-node-client/src/TokenOptions.ts index 0dacc6ed..cd4d947a 100644 --- a/openvidu-node-client/src/TokenOptions.ts +++ b/openvidu-node-client/src/TokenOptions.ts @@ -18,7 +18,7 @@ import { OpenViduRole } from './OpenViduRole'; /** - * See [[Session.generateToken]] + * @deprecated Use [[ConnectionOptions]] instead */ export interface TokenOptions { diff --git a/openvidu-node-client/src/index.ts b/openvidu-node-client/src/index.ts index e3b5a359..a0887a7c 100644 --- a/openvidu-node-client/src/index.ts +++ b/openvidu-node-client/src/index.ts @@ -4,7 +4,7 @@ export * from './Session'; export * from './SessionProperties'; export * from './TokenOptions'; export * from './ConnectionOptions'; -export * from './Token'; +export * from './ConnectionType'; export * from './MediaMode'; export * from './RecordingLayout'; export * from './RecordingMode'; diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java b/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java index 4acc2671..20c62e00 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java @@ -22,7 +22,6 @@ import java.util.concurrent.locks.ReentrantLock; import com.google.gson.JsonObject; -import io.openvidu.java.client.ConnectionType; import io.openvidu.server.kurento.endpoint.EndpointType; import io.openvidu.server.utils.GeoLocation; @@ -44,7 +43,6 @@ public class Participant { active } - protected ConnectionType type; // WEBRTC, IPCAM protected String finalUserId; // ID to match this connection with a final user (HttpSession id) protected String participantPrivatetId; // ID to identify the user on server (org.kurento.jsonrpc.Session.id) protected String participantPublicId; // ID to identify the user on clients @@ -52,7 +50,6 @@ public class Participant { protected ParticipantStatus status; // Status of the connection protected Long activeAt; // Timestamp when this connection entered status "active" protected String clientMetadata = ""; // Metadata provided on client side - protected String serverMetadata = ""; // Metadata provided on server side protected Token token; // Token associated to this participant protected GeoLocation location; // Location of the participant protected String platform; // Platform used by the participant to connect to the session @@ -78,10 +75,9 @@ public class Participant { */ public Lock singleRecordingLock = new ReentrantLock(); - public Participant(ConnectionType type, String finalUserId, String participantPrivatetId, - String participantPublicId, String sessionId, Token token, String clientMetadata, GeoLocation location, - String platform, EndpointType endpointType, Long activeAt) { - this.type = type; + public Participant(String finalUserId, String participantPrivatetId, String participantPublicId, String sessionId, + Token token, String clientMetadata, GeoLocation location, String platform, EndpointType endpointType, + Long activeAt) { this.finalUserId = finalUserId; this.participantPrivatetId = participantPrivatetId; this.participantPublicId = participantPublicId; @@ -96,18 +92,11 @@ public class Participant { if (clientMetadata != null) { this.clientMetadata = clientMetadata; } - if (!token.getServerMetadata().isEmpty()) { - this.serverMetadata = token.getServerMetadata(); - } this.location = location; this.platform = platform; this.endpointType = endpointType; } - public ConnectionType getType() { - return type; - } - public String getFinalUserId() { return finalUserId; } @@ -145,11 +134,7 @@ public class Participant { } public String getServerMetadata() { - return serverMetadata; - } - - public void setServerMetadata(String serverMetadata) { - this.serverMetadata = serverMetadata; + return this.token.getServerMetadata(); } public Token getToken() { @@ -234,16 +219,16 @@ public class Participant { public String getFullMetadata() { String fullMetadata; - if ((!this.clientMetadata.isEmpty()) && (!this.serverMetadata.isEmpty())) { - fullMetadata = this.clientMetadata + METADATA_SEPARATOR + this.serverMetadata; + if ((!this.clientMetadata.isEmpty()) && (!this.token.getServerMetadata().isEmpty())) { + fullMetadata = this.clientMetadata + METADATA_SEPARATOR + this.token.getServerMetadata(); } else { - fullMetadata = this.clientMetadata + this.serverMetadata; + fullMetadata = this.clientMetadata + this.token.getServerMetadata(); } return fullMetadata; } public void deleteIpcamProperties() { - this.clientMetadata = ""; + this.clientMetadata = null; this.token.setToken(null); } @@ -305,9 +290,9 @@ public class Participant { public JsonObject toJson() { JsonObject json = new JsonObject(); + // COMMON json.addProperty("id", this.participantPublicId); json.addProperty("object", "connection"); - json.addProperty("type", this.type.name()); json.addProperty("status", this.status.name()); json.addProperty("connectionId", this.participantPublicId); // TODO: deprecated. Better use only "id" json.addProperty("sessionId", this.sessionId); @@ -317,10 +302,14 @@ public class Participant { json.addProperty("platform", this.platform); if (this.token.getToken() != null) { json.addProperty("token", this.token.getToken()); + } else { + json.add("token", null); } - json.addProperty("role", this.token.getRole().name()); - json.addProperty("serverData", this.serverMetadata); - json.addProperty("record", this.token.record()); + // Add all ConnectionOptions + JsonObject connectionOptionsJson = this.token.getConnectionOptionsWithFinalJsonFormat(); + connectionOptionsJson.entrySet().forEach(entry -> { + json.add(entry.getKey(), entry.getValue()); + }); json.addProperty("clientData", this.clientMetadata); return json; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java index e51dd190..9ac14df5 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java @@ -45,14 +45,15 @@ import com.google.gson.JsonSyntaxException; import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; import io.openvidu.client.internal.ProtocolElements; +import io.openvidu.java.client.ConnectionOptions; import io.openvidu.java.client.ConnectionType; +import io.openvidu.java.client.KurentoOptions; import io.openvidu.java.client.OpenViduRole; import io.openvidu.java.client.Recording; import io.openvidu.java.client.SessionProperties; import io.openvidu.server.cdr.CDREventRecording; import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.coturn.CoturnCredentialsService; -import io.openvidu.server.kurento.core.KurentoTokenOptions; import io.openvidu.server.kurento.endpoint.EndpointType; import io.openvidu.server.recording.service.RecordingManager; import io.openvidu.server.utils.FormatChecker; @@ -160,8 +161,8 @@ public abstract class SessionManager { public abstract void removeFilterEventListener(Session session, Participant subscriber, String streamId, String eventType); - public abstract Participant publishIpcam(Session session, MediaOptions mediaOptions, String serverMetadata) - throws Exception; + public abstract Participant publishIpcam(Session session, MediaOptions mediaOptions, + ConnectionOptions connectionOptions) throws Exception; public abstract void reconnectStream(Participant participant, String streamId, String sdpOffer, Integer transactionId); @@ -301,22 +302,22 @@ public abstract class SessionManager { } public Token newToken(Session session, OpenViduRole role, String serverMetadata, boolean record, - KurentoTokenOptions kurentoTokenOptions) throws Exception { + KurentoOptions kurentoOptions) throws Exception { if (!formatChecker.isServerMetadataFormatCorrect(serverMetadata)) { log.error("Data invalid format"); throw new OpenViduException(Code.GENERIC_ERROR_CODE, "Data invalid format"); } - Token tokenObj = tokenGenerator.generateToken(session.getSessionId(), role, serverMetadata, record, - kurentoTokenOptions); + Token tokenObj = tokenGenerator.generateToken(session.getSessionId(), serverMetadata, + record, role, kurentoOptions); session.storeToken(tokenObj); session.showTokens("Token created"); return tokenObj; } - public Token newTokenForInsecureUser(Session session, String token, String serverMetadata) throws Exception { - Token tokenObj = new Token(token, session.getSessionId(), OpenViduRole.PUBLISHER, - serverMetadata != null ? serverMetadata : "", true, - this.openviduConfig.isTurnadminAvailable() ? this.coturnCredentialsService.createUser() : null, null); + public Token newTokenForInsecureUser(Session session, String token, ConnectionOptions connectionOptions) + throws Exception { + Token tokenObj = new Token(token, session.getSessionId(), connectionOptions, + this.openviduConfig.isTurnadminAvailable() ? this.coturnCredentialsService.createUser() : null); session.storeToken(tokenObj); session.showTokens("Token created for insecure user"); return tokenObj; @@ -364,9 +365,8 @@ public abstract class SessionManager { if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null) { - Participant p = new Participant(ConnectionType.WEBRTC, finalUserId, participantPrivatetId, - token.getConnectionId(), sessionId, token, clientMetadata, location, platform, - EndpointType.WEBRTC_ENDPOINT, null); + Participant p = new Participant(finalUserId, participantPrivatetId, token.getConnectionId(), sessionId, + token, clientMetadata, location, platform, EndpointType.WEBRTC_ENDPOINT, null); this.sessionidParticipantpublicidParticipant.get(sessionId).put(p.getParticipantPublicId(), p); @@ -386,9 +386,8 @@ public abstract class SessionManager { public Participant newRecorderParticipant(String sessionId, String participantPrivatetId, Token token, String clientMetadata) { if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null) { - Participant p = new Participant(ConnectionType.WEBRTC, null, participantPrivatetId, - ProtocolElements.RECORDER_PARTICIPANT_PUBLICID, sessionId, token, clientMetadata, null, null, - EndpointType.WEBRTC_ENDPOINT, null); + Participant p = new Participant(null, participantPrivatetId, ProtocolElements.RECORDER_PARTICIPANT_PUBLICID, + sessionId, token, clientMetadata, null, null, EndpointType.WEBRTC_ENDPOINT, null); this.sessionidParticipantpublicidParticipant.get(sessionId) .put(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID, p); return p; @@ -400,8 +399,8 @@ public abstract class SessionManager { public Participant newIpcamParticipant(String sessionId, String ipcamId, Token token, GeoLocation location, String platform) { if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null) { - Participant p = new Participant(ConnectionType.IPCAM, ipcamId, ipcamId, ipcamId, sessionId, token, null, - location, platform, EndpointType.PLAYER_ENDPOINT, null); + Participant p = new Participant(ipcamId, ipcamId, ipcamId, sessionId, token, null, location, platform, + EndpointType.PLAYER_ENDPOINT, null); this.sessionidParticipantpublicidParticipant.get(sessionId).put(ipcamId, p); return p; } else { diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Token.java b/openvidu-server/src/main/java/io/openvidu/server/core/Token.java index b0fe7d30..ebb19202 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/Token.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/Token.java @@ -21,95 +21,113 @@ import org.apache.commons.lang3.RandomStringUtils; import com.google.gson.JsonObject; +import io.openvidu.java.client.ConnectionOptions; import io.openvidu.java.client.ConnectionType; +import io.openvidu.java.client.KurentoOptions; import io.openvidu.java.client.OpenViduRole; import io.openvidu.server.core.Participant.ParticipantStatus; import io.openvidu.server.coturn.TurnCredentials; -import io.openvidu.server.kurento.core.KurentoTokenOptions; public class Token { private String token; private String sessionId; private Long createdAt; - private OpenViduRole role; - private String serverMetadata = ""; - private boolean record; + private ConnectionOptions connectionOptions; private TurnCredentials turnCredentials; - private KurentoTokenOptions kurentoTokenOptions; private final String connectionId = IdentifierPrefixes.PARTICIPANT_PUBLIC_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase() + RandomStringUtils.randomAlphanumeric(9); - public Token(String token, String sessionId, OpenViduRole role, String serverMetadata, boolean record, - TurnCredentials turnCredentials, KurentoTokenOptions kurentoTokenOptions) { + public Token(String token, String sessionId, ConnectionOptions connectionOptions, TurnCredentials turnCredentials) { this.token = token; this.sessionId = sessionId; this.createdAt = System.currentTimeMillis(); - this.role = role; - this.serverMetadata = serverMetadata; - this.record = record; + this.connectionOptions = connectionOptions; this.turnCredentials = turnCredentials; - this.kurentoTokenOptions = kurentoTokenOptions; + } + + public ConnectionType getType() { + return this.connectionOptions.getType(); } public String getToken() { return token; } - public void setToken(String token) { - this.token = token; + public void setToken(String newToken) { + this.token = newToken; } public Long getCreatedAt() { return this.createdAt; } - public OpenViduRole getRole() { - return role; - } - public String getServerMetadata() { - return serverMetadata; + return this.connectionOptions.getData(); } public boolean record() { - return record; + return this.connectionOptions.record(); + } + + public void setRecord(boolean newRecord) { + this.updateConnectionOptions(connectionOptions.getType(), connectionOptions.getData(), newRecord, + connectionOptions.getRole(), connectionOptions.getKurentoOptions(), connectionOptions.getRtspUri(), + connectionOptions.adaptativeBitrate(), connectionOptions.onlyPlayWithSubscribers(), + connectionOptions.getNetworkCache()); + } + + public OpenViduRole getRole() { + return this.connectionOptions.getRole(); + } + + public void setRole(OpenViduRole newRole) { + this.updateConnectionOptions(connectionOptions.getType(), connectionOptions.getData(), + connectionOptions.record(), newRole, connectionOptions.getKurentoOptions(), + connectionOptions.getRtspUri(), connectionOptions.adaptativeBitrate(), + connectionOptions.onlyPlayWithSubscribers(), connectionOptions.getNetworkCache()); + } + + public KurentoOptions getKurentoOptions() { + return this.connectionOptions.getKurentoOptions(); + } + + public String getRtspUri() { + return this.connectionOptions.getRtspUri(); + } + + public Boolean adaptativeBitrate() { + return this.connectionOptions.adaptativeBitrate(); + } + + public Boolean onlyPlayWithSubscribers() { + return this.connectionOptions.onlyPlayWithSubscribers(); + } + + public Integer getNetworkCache() { + return this.connectionOptions.getNetworkCache(); } public TurnCredentials getTurnCredentials() { return turnCredentials; } - public KurentoTokenOptions getKurentoTokenOptions() { - return kurentoTokenOptions; - } - public String getConnectionId() { return connectionId; } - public void setRole(OpenViduRole role) { - this.role = role; - } - - public void setRecord(boolean record) { - this.record = record; - } - public JsonObject toJson() { JsonObject json = new JsonObject(); json.addProperty("id", this.getToken()); - json.addProperty("object", "token"); json.addProperty("token", this.getToken()); + json.addProperty("createdAt", this.getCreatedAt()); json.addProperty("connectionId", this.getConnectionId()); - json.addProperty("createdAt", this.createdAt); json.addProperty("session", this.sessionId); - json.addProperty("role", this.getRole().toString()); json.addProperty("data", this.getServerMetadata()); - json.addProperty("record", this.record()); - if (this.getKurentoTokenOptions() != null) { - json.add("kurentoOptions", this.getKurentoTokenOptions().toJson()); + json.addProperty("role", this.getRole().toString()); + if (this.getKurentoOptions() != null) { + json.add("kurentoOptions", this.getKurentoOptions().toJson()); } return json; } @@ -118,28 +136,73 @@ public class Token { JsonObject json = new JsonObject(); json.addProperty("id", this.getConnectionId()); json.addProperty("object", "connection"); - json.addProperty("type", ConnectionType.WEBRTC.name()); json.addProperty("status", ParticipantStatus.pending.name()); json.addProperty("connectionId", this.getConnectionId()); // DEPRECATED: better use id json.addProperty("sessionId", this.sessionId); json.addProperty("createdAt", this.createdAt); + + // Add all ConnectionOptions + JsonObject connectionOptionsJson = this.getConnectionOptionsWithFinalJsonFormat(); + connectionOptionsJson.entrySet().forEach(entry -> { + json.add(entry.getKey(), entry.getValue()); + }); + + json.addProperty("token", this.getToken()); json.add("activeAt", null); json.add("location", null); json.add("platform", null); - json.addProperty("token", this.getToken()); - json.addProperty("role", this.getRole().toString()); - json.addProperty("serverData", this.getServerMetadata()); - json.addProperty("record", this.record()); json.add("clientData", null); json.add("publishers", null); json.add("subscribers", null); return json; } + protected JsonObject getConnectionOptionsWithFinalJsonFormat() { + JsonObject json = this.connectionOptions.toJson(this.sessionId); + json.remove("session"); + json.addProperty("serverData", json.get("data").getAsString()); + json.remove("data"); + return json; + } + + private void updateConnectionOptions(ConnectionType type, String data, Boolean record, OpenViduRole role, + KurentoOptions kurentoOptions, String rtspUri, Boolean adaptativeBitrate, Boolean onlyPlayWithSubscribers, + Integer networkCache) { + ConnectionOptions.Builder builder = new ConnectionOptions.Builder(); + if (type != null) { + builder.type(type); + } + if (data != null) { + builder.data(data); + } + if (record != null) { + builder.record(record); + } + if (role != null) { + builder.role(role); + } + if (kurentoOptions != null) { + builder.kurentoOptions(kurentoOptions); + } + if (rtspUri != null) { + builder.rtspUri(rtspUri); + } + if (adaptativeBitrate != null) { + builder.adaptativeBitrate(adaptativeBitrate); + } + if (onlyPlayWithSubscribers != null) { + builder.onlyPlayWithSubscribers(onlyPlayWithSubscribers); + } + if (networkCache != null) { + builder.networkCache(networkCache); + } + this.connectionOptions = builder.build(); + } + @Override public String toString() { - if (this.role != null) - return this.role.name(); + if (this.connectionOptions.getRole() != null) + return this.connectionOptions.getRole().name(); else return this.token; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java b/openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java index 0c5f9b84..ed5b6966 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java @@ -20,13 +20,15 @@ package io.openvidu.server.core; import org.apache.commons.lang3.RandomStringUtils; import org.springframework.beans.factory.annotation.Autowired; +import io.openvidu.java.client.ConnectionOptions; +import io.openvidu.java.client.ConnectionType; +import io.openvidu.java.client.KurentoOptions; import io.openvidu.java.client.OpenViduRole; import io.openvidu.server.OpenViduServer; import io.openvidu.server.config.OpenviduBuildInfo; import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.coturn.CoturnCredentialsService; import io.openvidu.server.coturn.TurnCredentials; -import io.openvidu.server.kurento.core.KurentoTokenOptions; public class TokenGenerator { @@ -39,8 +41,8 @@ public class TokenGenerator { @Autowired protected OpenviduBuildInfo openviduBuildConfig; - public Token generateToken(String sessionId, OpenViduRole role, String serverMetadata, boolean record, - KurentoTokenOptions kurentoTokenOptions) throws Exception { + public Token generateToken(String sessionId, String serverMetadata, boolean record, OpenViduRole role, + KurentoOptions kurentoOptions) throws Exception { String token = OpenViduServer.wsUrl; token += "?sessionId=" + sessionId; token += "&token=" + IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase() @@ -56,6 +58,8 @@ public class TokenGenerator { token += "&turnCredential=" + turnCredentials.getCredential(); } } - return new Token(token, sessionId, role, serverMetadata, record, turnCredentials, kurentoTokenOptions); + ConnectionOptions connectionOptions = new ConnectionOptions.Builder().type(ConnectionType.WEBRTC) + .data(serverMetadata).record(record).role(role).kurentoOptions(kurentoOptions).build(); + return new Token(token, sessionId, connectionOptions, turnCredentials); } } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java index 6af8375d..306e5034 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java @@ -77,7 +77,7 @@ public class KurentoParticipant extends Participant { public KurentoParticipant(Participant participant, KurentoSession kurentoSession, KurentoParticipantEndpointConfig endpointConfig, OpenviduConfig openviduConfig, RecordingManager recordingManager) { - super(participant.getType(), participant.getFinalUserId(), participant.getParticipantPrivateId(), participant.getParticipantPublicId(), + super(participant.getFinalUserId(), participant.getParticipantPrivateId(), participant.getParticipantPublicId(), kurentoSession.getSessionId(), participant.getToken(), participant.getClientMetadata(), participant.getLocation(), participant.getPlatform(), participant.getEndpointType(), participant.getActiveAt()); diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java index 8823a2db..c9ba06a5 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java @@ -48,6 +48,8 @@ import com.google.gson.JsonObject; import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; import io.openvidu.client.internal.ProtocolElements; +import io.openvidu.java.client.ConnectionOptions; +import io.openvidu.java.client.KurentoOptions; import io.openvidu.java.client.MediaMode; import io.openvidu.java.client.Recording; import io.openvidu.java.client.RecordingLayout; @@ -387,7 +389,7 @@ public class KurentoSessionManager extends SessionManager { * kurentoParticipant.getPublisher().apply(elem); } */ - KurentoTokenOptions kurentoTokenOptions = participant.getToken().getKurentoTokenOptions(); + KurentoOptions kurentoTokenOptions = participant.getToken().getKurentoOptions(); if (kurentoOptions.getFilter() != null && kurentoTokenOptions != null) { if (kurentoTokenOptions.isFilterAllowed(kurentoOptions.getFilter().getType())) { this.applyFilterInPublisher(kParticipant, kurentoOptions.getFilter()); @@ -990,7 +992,7 @@ public class KurentoSessionManager extends SessionManager { @Override /* Protected by Session.closingLock.readLock */ - public Participant publishIpcam(Session session, MediaOptions mediaOptions, String serverMetadata) + public Participant publishIpcam(Session session, MediaOptions mediaOptions, ConnectionOptions connectionOptions) throws Exception { final String sessionId = session.getSessionId(); final KurentoMediaOptions kMediaOptions = (KurentoMediaOptions) mediaOptions; @@ -1032,7 +1034,8 @@ public class KurentoSessionManager extends SessionManager { this.newInsecureParticipant(rtspConnectionId); String token = IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase() + RandomStringUtils.randomAlphanumeric(15); - this.newTokenForInsecureUser(session, token, serverMetadata); + + this.newTokenForInsecureUser(session, token, connectionOptions); final Token tokenObj = session.consumeToken(token); Participant ipcamParticipant = this.newIpcamParticipant(sessionId, rtspConnectionId, tokenObj, location, @@ -1109,9 +1112,10 @@ public class KurentoSessionManager extends SessionManager { Session session = this.getSession(sessionId); return ((KurentoSession) session).getParticipantPrivateIdFromStreamId(streamId); } - + @Override - public void onVideoData(Participant participant, Integer transactionId, Integer height, Integer width, Boolean videoActive, Boolean audioActive) { + public void onVideoData(Participant participant, Integer transactionId, Integer height, Integer width, + Boolean videoActive, Boolean audioActive) { sessionEventsHandler.onVideoData(participant, transactionId, height, width, videoActive, audioActive); } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoTokenOptions.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoTokenOptions.java deleted file mode 100644 index a7b21da8..00000000 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoTokenOptions.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package io.openvidu.server.kurento.core; - -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -public class KurentoTokenOptions { - - private Integer videoMaxRecvBandwidth; - private Integer videoMinRecvBandwidth; - private Integer videoMaxSendBandwidth; - private Integer videoMinSendBandwidth; - private Map allowedFilters = new ConcurrentHashMap<>(); - - public KurentoTokenOptions(JsonObject options) { - if (options.has("videoMaxRecvBandwidth")) { - this.videoMaxRecvBandwidth = options.get("videoMaxRecvBandwidth").getAsInt(); - } - if (options.has("videoMinRecvBandwidth")) { - this.videoMinRecvBandwidth = options.get("videoMinRecvBandwidth").getAsInt(); - } - if (options.has("videoMaxSendBandwidth")) { - this.videoMaxSendBandwidth = options.get("videoMaxSendBandwidth").getAsInt(); - } - if (options.has("videoMinSendBandwidth")) { - this.videoMinSendBandwidth = options.get("videoMinSendBandwidth").getAsInt(); - } - if (options.has("allowedFilters")) { - JsonArray filters = options.get("allowedFilters").getAsJsonArray(); - Iterator it = filters.iterator(); - while (it.hasNext()) { - this.allowedFilters.put(it.next().getAsString(), true); - } - } - } - - public Integer getVideoMaxRecvBandwidth() { - return videoMaxRecvBandwidth; - } - - public Integer getVideoMinRecvBandwidth() { - return videoMinRecvBandwidth; - } - - public Integer getVideoMaxSendBandwidth() { - return videoMaxSendBandwidth; - } - - public Integer getVideoMinSendBandwidth() { - return videoMinSendBandwidth; - } - - public String[] getAllowedFilters() { - return allowedFilters.keySet().stream().toArray(String[]::new); - } - - public boolean isFilterAllowed(String filterType) { - return this.allowedFilters.containsKey(filterType); - } - - public JsonObject toJson() { - JsonObject json = new JsonObject(); - if (this.getVideoMaxRecvBandwidth() != null) { - json.addProperty("videoMaxRecvBandwidth", this.getVideoMaxRecvBandwidth()); - } - if (this.getVideoMinRecvBandwidth() != null) { - json.addProperty("videoMinRecvBandwidth", this.getVideoMinRecvBandwidth()); - } - if (this.getVideoMaxSendBandwidth() != null) { - json.addProperty("videoMaxSendBandwidth", this.getVideoMaxSendBandwidth()); - } - if (this.getVideoMinSendBandwidth() != null) { - json.addProperty("videoMinSendBandwidth", this.getVideoMinSendBandwidth()); - } - if (this.getAllowedFilters().length > 0) { - JsonArray filtersJson = new JsonArray(); - String[] filters = this.getAllowedFilters(); - for (String filter : filters) { - filtersJson.add(filter); - } - json.add("allowedFilters", filtersJson); - } - return json; - } - -} diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java index 77b216f2..8c68e41a 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java @@ -53,11 +53,11 @@ import com.google.gson.JsonObject; import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; +import io.openvidu.java.client.KurentoOptions; import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.core.Participant; import io.openvidu.server.kurento.core.KurentoMediaOptions; import io.openvidu.server.kurento.core.KurentoParticipant; -import io.openvidu.server.kurento.core.KurentoTokenOptions; /** * {@link Endpoint} wrapper. Can be based on WebRtcEndpoint (that supports @@ -128,7 +128,7 @@ public abstract class MediaEndpoint { this.openviduConfig = openviduConfig; - KurentoTokenOptions kurentoTokenOptions = this.owner.getToken().getKurentoTokenOptions(); + KurentoOptions kurentoTokenOptions = this.owner.getToken().getKurentoOptions(); if (kurentoTokenOptions != null) { this.maxRecvKbps = kurentoTokenOptions.getVideoMaxRecvBandwidth() != null ? kurentoTokenOptions.getVideoMaxRecvBandwidth() diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/RecorderEndpointWrapper.java b/openvidu-server/src/main/java/io/openvidu/server/recording/RecorderEndpointWrapper.java index 655ba4a9..fe552fad 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/RecorderEndpointWrapper.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/RecorderEndpointWrapper.java @@ -64,7 +64,9 @@ public class RecorderEndpointWrapper { this.name = StringUtils.removeEnd(nameAux, SingleStreamRecordingService.INDIVIDUAL_RECORDING_EXTENSION); this.connectionId = json.get("connectionId").getAsString(); this.streamId = json.get("streamId").getAsString(); - this.clientData = json.get("clientData").getAsString(); + this.clientData = (json.has("clientData") && !json.get("clientData").isJsonNull()) + ? json.get("clientData").getAsString() + : null; this.serverData = json.get("serverData").getAsString(); this.startTime = json.get("startTime").getAsLong(); this.endTime = json.get("endTime").getAsLong(); diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java index 4b7dea45..7f981f57 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java @@ -20,7 +20,6 @@ package io.openvidu.server.rest; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; @@ -45,12 +44,15 @@ import org.springframework.web.bind.annotation.RestController; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import io.openvidu.client.OpenViduException; import io.openvidu.client.internal.ProtocolElements; +import io.openvidu.java.client.ConnectionOptions; import io.openvidu.java.client.ConnectionType; +import io.openvidu.java.client.KurentoOptions; import io.openvidu.java.client.MediaMode; import io.openvidu.java.client.OpenViduRole; import io.openvidu.java.client.Recording.OutputMode; @@ -66,7 +68,6 @@ import io.openvidu.server.core.Session; import io.openvidu.server.core.SessionManager; import io.openvidu.server.core.Token; import io.openvidu.server.kurento.core.KurentoMediaOptions; -import io.openvidu.server.kurento.core.KurentoTokenOptions; import io.openvidu.server.recording.Recording; import io.openvidu.server.recording.service.RecordingManager; import io.openvidu.server.utils.RecordingUtils; @@ -273,45 +274,29 @@ public class SessionRestController { public ResponseEntity initializeConnection(@PathVariable("sessionId") String sessionId, @RequestBody Map params) { - log.info("REST API: POST {}/sessions/{}/connection {}", RequestMappings.API, sessionId, params.toString()); + log.info("REST API: POST {} {}", RequestMappings.API + "/sessions/" + sessionId + "/connection", + params.toString()); Session session = this.sessionManager.getSessionWithNotActive(sessionId); if (session == null) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - String typeString; - String data; - Boolean record; + ConnectionOptions connectionOptions; try { - typeString = (String) params.get("type"); - data = (String) params.get("data"); - record = (Boolean) params.get("record"); - } catch (ClassCastException e) { - return this.generateErrorResponse("Type error in parameter \"type\"", - "/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST); + connectionOptions = getConnectionOptionsFromParams(params); + } catch (Exception e) { + return this.generateErrorResponse(e.getMessage(), "/sessions/" + sessionId + "/connection", + HttpStatus.BAD_REQUEST); } - - ConnectionType type; - try { - if (typeString != null) { - type = ConnectionType.valueOf(typeString); - } else { - type = ConnectionType.WEBRTC; - } - } catch (IllegalArgumentException e) { - return this.generateErrorResponse("Parameter type " + params.get("typeString") + " is not defined", - RequestMappings.API + "/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST); - } - - switch (type) { + switch (connectionOptions.getType()) { case WEBRTC: - return this.newWebrtcConnection(session, data, record, params); + return this.newWebrtcConnection(session, connectionOptions); case IPCAM: - return this.newIpcamConnection(session, data, record, params); + return this.newIpcamConnection(session, connectionOptions); default: - return this.generateErrorResponse("Wrong type " + typeString, - RequestMappings.API + "/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST); + return this.generateErrorResponse("Wrong type parameter", "/sessions/" + sessionId + "/connection", + HttpStatus.BAD_REQUEST); } } @@ -654,12 +639,8 @@ public class SessionRestController { log.info("REST API: POST {}/tokens {}", RequestMappings.API, params.toString()); String sessionId; - String metadata; - Boolean record; try { sessionId = (String) params.get("session"); - metadata = (String) params.get("data"); - record = (Boolean) params.get("record"); } catch (ClassCastException e) { return this.generateErrorResponse("Type error in some parameter", "/tokens", HttpStatus.BAD_REQUEST); } @@ -675,10 +656,15 @@ public class SessionRestController { if (session == null) { return this.generateErrorResponse("Session " + sessionId + " not found", "/tokens", HttpStatus.NOT_FOUND); } - Map map = new HashMap<>(); - params.entrySet().forEach(entry -> map.put((String) entry.getKey(), entry.getValue())); - map.put("type", "WEBRTC"); - ResponseEntity entity = this.newWebrtcConnection(session, metadata, record, params); + + ConnectionOptions connectionOptions; + try { + connectionOptions = getConnectionOptionsFromParams(params); + } catch (Exception e) { + return this.generateErrorResponse(e.getMessage(), "/sessions/" + sessionId + "/connection", + HttpStatus.BAD_REQUEST); + } + ResponseEntity entity = this.newWebrtcConnection(session, connectionOptions); JsonObject jsonResponse = JsonParser.parseString(entity.getBody().toString()).getAsJsonObject(); if (jsonResponse.has("error")) { @@ -793,57 +779,15 @@ public class SessionRestController { return new ResponseEntity<>(HttpStatus.OK); } - protected ResponseEntity newWebrtcConnection(Session session, String serverData, Boolean record, - Map params) { + protected ResponseEntity newWebrtcConnection(Session session, ConnectionOptions connectionOptions) { - final String REQUEST_PATH = RequestMappings.API + "/sessions/" + session.getSessionId() + "/connection"; - - String roleString = null; - try { - roleString = (String) params.get("role"); - } catch (ClassCastException e) { - return this.generateErrorResponse("Type error in some parameter", REQUEST_PATH, HttpStatus.BAD_REQUEST); - } - - OpenViduRole role = null; - try { - if (roleString != null) { - role = OpenViduRole.valueOf(roleString); - } else { - role = OpenViduRole.PUBLISHER; - } - } catch (IllegalArgumentException e) { - return this.generateErrorResponse("Parameter role " + params.get("role") + " is not defined", REQUEST_PATH, - HttpStatus.BAD_REQUEST); - } - - JsonObject kurentoOptions = null; - if (params.get("kurentoOptions") != null) { - try { - kurentoOptions = JsonParser.parseString(params.get("kurentoOptions").toString()).getAsJsonObject(); - } catch (Exception e) { - return this.generateErrorResponse("Error in parameter 'kurentoOptions'. It is not a valid JSON object", - REQUEST_PATH, HttpStatus.BAD_REQUEST); - } - } - - KurentoTokenOptions kurentoTokenOptions = null; - if (kurentoOptions != null) { - try { - kurentoTokenOptions = new KurentoTokenOptions(kurentoOptions); - } catch (Exception e) { - return this.generateErrorResponse("Type error in some parameter of 'kurentoOptions'", REQUEST_PATH, - HttpStatus.BAD_REQUEST); - } - } - - serverData = (serverData != null) ? serverData : ""; - record = (record != null) ? record : true; + final String REQUEST_PATH = "/sessions/" + session.getSessionId() + "/connection"; // While closing a session tokens can't be generated if (session.closingLock.readLock().tryLock()) { try { - Token token = sessionManager.newToken(session, role, serverData, record, kurentoTokenOptions); + Token token = sessionManager.newToken(session, connectionOptions.getRole(), connectionOptions.getData(), + connectionOptions.record(), connectionOptions.getKurentoOptions()); return new ResponseEntity<>(token.toJsonAsParticipant().toString(), RestUtils.getResponseHeaders(), HttpStatus.OK); } catch (Exception e) { @@ -861,34 +805,9 @@ public class SessionRestController { } } - protected ResponseEntity newIpcamConnection(Session session, String serverData, Boolean record, - Map params) { + protected ResponseEntity newIpcamConnection(Session session, ConnectionOptions connectionOptions) { - final String REQUEST_PATH = RequestMappings.API + "/sessions/" + session.getSessionId() + "/connection"; - - String rtspUri; - Boolean adaptativeBitrate; - Boolean onlyPlayWithSubscribers; - Integer networkCache; - try { - rtspUri = (String) params.get("rtspUri"); - adaptativeBitrate = (Boolean) params.get("adaptativeBitrate"); - onlyPlayWithSubscribers = (Boolean) params.get("onlyPlayWithSubscribers"); - networkCache = (Integer) params.get("networkCache"); - } catch (ClassCastException e) { - return this.generateErrorResponse("Type error in some parameter", REQUEST_PATH, HttpStatus.BAD_REQUEST); - } - - if (rtspUri == null) { - return this.generateErrorResponse("\"rtspUri\" parameter is mandatory", REQUEST_PATH, - HttpStatus.BAD_REQUEST); - } - - adaptativeBitrate = adaptativeBitrate != null ? adaptativeBitrate : true; - onlyPlayWithSubscribers = onlyPlayWithSubscribers != null ? onlyPlayWithSubscribers : true; - networkCache = networkCache != null ? networkCache : 2000; - serverData = serverData != null ? serverData : ""; - record = (record != null) ? record : true; + final String REQUEST_PATH = "/sessions/" + session.getSessionId() + "/connection"; boolean hasAudio = true; boolean hasVideo = true; @@ -898,8 +817,9 @@ public class SessionRestController { Integer frameRate = null; String videoDimensions = null; KurentoMediaOptions mediaOptions = new KurentoMediaOptions(true, null, hasAudio, hasVideo, audioActive, - videoActive, typeOfVideo, frameRate, videoDimensions, null, false, rtspUri, adaptativeBitrate, - onlyPlayWithSubscribers, networkCache); + videoActive, typeOfVideo, frameRate, videoDimensions, null, false, connectionOptions.getRtspUri(), + connectionOptions.adaptativeBitrate(), connectionOptions.onlyPlayWithSubscribers(), + connectionOptions.getNetworkCache()); // While closing a session IP cameras can't be published if (session.closingLock.readLock().tryLock()) { @@ -907,7 +827,8 @@ public class SessionRestController { if (session.isClosed()) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - Participant ipcamParticipant = this.sessionManager.publishIpcam(session, mediaOptions, serverData); + Participant ipcamParticipant = this.sessionManager.publishIpcam(session, mediaOptions, + connectionOptions); return new ResponseEntity<>(ipcamParticipant.toJson().toString(), RestUtils.getResponseHeaders(), HttpStatus.OK); } catch (MalformedURLException e) { @@ -936,6 +857,125 @@ public class SessionRestController { return token; } + protected ConnectionOptions getConnectionOptionsFromParams(Map params) throws Exception { + + ConnectionOptions.Builder builder = new ConnectionOptions.Builder(); + + String typeString; + String data; + Boolean record; + try { + typeString = (String) params.get("type"); + data = (String) params.get("data"); + record = (Boolean) params.get("record"); + } catch (ClassCastException e) { + throw new Exception("Type error in some parameter: " + e.getMessage()); + } + + ConnectionType type; + try { + if (typeString != null) { + type = ConnectionType.valueOf(typeString); + } else { + type = ConnectionType.WEBRTC; + } + } catch (IllegalArgumentException e) { + throw new Exception("Parameter 'type' " + typeString + " is not defined"); + } + data = data != null ? data : ""; + record = record != null ? record : true; + + // Build COMMON options + builder.type(type).data(data).record(record); + + OpenViduRole role = null; + KurentoOptions kurentoOptions = null; + + if (ConnectionType.WEBRTC.equals(type)) { + String roleString; + try { + roleString = (String) params.get("role"); + } catch (ClassCastException e) { + throw new Exception("Type error in parameter 'role': " + e.getMessage()); + } + try { + if (roleString != null) { + role = OpenViduRole.valueOf(roleString); + } else { + role = OpenViduRole.PUBLISHER; + } + } catch (IllegalArgumentException e) { + throw new Exception("Parameter role " + params.get("role") + " is not defined"); + } + JsonObject kurentoOptionsJson = null; + if (params.get("kurentoOptions") != null) { + try { + kurentoOptionsJson = JsonParser.parseString(params.get("kurentoOptions").toString()) + .getAsJsonObject(); + } catch (Exception e) { + throw new Exception("Error in parameter 'kurentoOptions'. It is not a valid JSON object"); + } + } + if (kurentoOptionsJson != null) { + try { + KurentoOptions.Builder builder2 = new KurentoOptions.Builder(); + if (kurentoOptionsJson.has("videoMaxRecvBandwidth")) { + builder2.videoMaxRecvBandwidth(kurentoOptionsJson.get("videoMaxRecvBandwidth").getAsInt()); + } + if (kurentoOptionsJson.has("videoMinRecvBandwidth")) { + builder2.videoMinRecvBandwidth(kurentoOptionsJson.get("videoMinRecvBandwidth").getAsInt()); + } + if (kurentoOptionsJson.has("videoMaxSendBandwidth")) { + builder2.videoMaxSendBandwidth(kurentoOptionsJson.get("videoMaxSendBandwidth").getAsInt()); + } + if (kurentoOptionsJson.has("videoMinSendBandwidth")) { + builder2.videoMinSendBandwidth(kurentoOptionsJson.get("videoMinSendBandwidth").getAsInt()); + } + if (kurentoOptionsJson.has("allowedFilters")) { + JsonArray filters = kurentoOptionsJson.get("allowedFilters").getAsJsonArray(); + String[] arrayOfFilters = new String[filters.size()]; + Iterator it = filters.iterator(); + int index = 0; + while (it.hasNext()) { + arrayOfFilters[index] = it.next().getAsString(); + index++; + } + builder2.allowedFilters(arrayOfFilters); + } + kurentoOptions = builder2.build(); + } catch (Exception e) { + throw new Exception("Type error in some parameter of 'kurentoOptions': " + e.getMessage()); + } + } + + // Build WEBRTC options + builder.role(role).kurentoOptions(kurentoOptions); + + } else if (ConnectionType.IPCAM.equals(type)) { + String rtspUri; + Boolean adaptativeBitrate; + Boolean onlyPlayWithSubscribers; + Integer networkCache; + try { + rtspUri = (String) params.get("rtspUri"); + adaptativeBitrate = (Boolean) params.get("adaptativeBitrate"); + onlyPlayWithSubscribers = (Boolean) params.get("onlyPlayWithSubscribers"); + networkCache = (Integer) params.get("networkCache"); + } catch (ClassCastException e) { + throw new Exception("Type error in some parameter: " + e.getMessage()); + } + adaptativeBitrate = adaptativeBitrate != null ? adaptativeBitrate : true; + onlyPlayWithSubscribers = onlyPlayWithSubscribers != null ? onlyPlayWithSubscribers : true; + networkCache = networkCache != null ? networkCache : 2000; + + // Build IPCAM options + builder.rtspUri(rtspUri).adaptativeBitrate(adaptativeBitrate) + .onlyPlayWithSubscribers(onlyPlayWithSubscribers).networkCache(networkCache).build(); + } + + return builder.build(); + } + protected ResponseEntity generateErrorResponse(String errorMessage, String path, HttpStatus status) { JsonObject responseJson = new JsonObject(); responseJson.addProperty("timestamp", System.currentTimeMillis()); diff --git a/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java b/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java index 3d627340..c50ca993 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java @@ -45,6 +45,7 @@ import com.google.gson.JsonSyntaxException; import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; import io.openvidu.client.internal.ProtocolElements; +import io.openvidu.java.client.ConnectionOptions; import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.core.EndReason; import io.openvidu.server.core.IdentifierPrefixes; @@ -248,7 +249,7 @@ public class RpcHandler extends DefaultJsonRpcHandler { token = IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase() + RandomStringUtils.randomAlphanumeric(15); try { - sessionManager.newTokenForInsecureUser(session, token, null); + sessionManager.newTokenForInsecureUser(session, token, new ConnectionOptions.Builder().build()); } catch (Exception e) { throw new OpenViduException(Code.TOKEN_CANNOT_BE_CREATED_ERROR_CODE, "Unable to create token for session " + sessionId + ": " + e.getMessage()); @@ -492,7 +493,7 @@ public class RpcHandler extends DefaultJsonRpcHandler { // user's stream) or if the user is the owner of the stream and has a token // configured with this specific filter if (isModerator || (this.userIsStreamOwner(rpcConnection.getSessionId(), participant, streamId) - && participant.getToken().getKurentoTokenOptions().isFilterAllowed(filterType))) { + && participant.getToken().getKurentoOptions().isFilterAllowed(filterType))) { JsonObject filterOptions; try { filterOptions = JsonParser.parseString(getStringParam(request, ProtocolElements.FILTER_OPTIONS_PARAM)) diff --git a/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java b/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java index 125c1ea3..ddb56819 100644 --- a/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java +++ b/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java @@ -36,7 +36,7 @@ import org.springframework.test.context.web.WebAppConfiguration; import com.google.gson.Gson; import com.google.gson.JsonObject; -import io.openvidu.java.client.OpenViduRole; +import io.openvidu.java.client.ConnectionOptions; import io.openvidu.server.core.Participant; import io.openvidu.server.core.SessionManager; import io.openvidu.server.core.Token; @@ -115,7 +115,8 @@ public class SessionGarbageCollectorIntegrationTest { } private void joinParticipant(String sessionId, String token) { - Token t = new Token(token, sessionId, OpenViduRole.PUBLISHER, "SERVER_METADATA", true, null, null); + ConnectionOptions connectionOptions = new ConnectionOptions.Builder().data("SERVER_METADATA").build(); + Token t = new Token(token, sessionId, connectionOptions, null); String uuid = UUID.randomUUID().toString(); String participantPrivateId = "PARTICIPANT_PRIVATE_ID_" + uuid; String finalUserId = "FINAL_USER_ID_" + uuid; diff --git a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomHttpClient.java b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomHttpClient.java index 4c09af54..6daa0339 100644 --- a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomHttpClient.java +++ b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomHttpClient.java @@ -110,7 +110,7 @@ public class CustomHttpClient { public JsonObject rest(HttpMethod method, String path, String body, int status, boolean matchKeys, boolean matchValues, boolean matchArrays, String jsonReturnedValue) throws Exception { JsonObject jsonExpected = null; - jsonReturnedValue.replaceAll("'", "\""); + jsonReturnedValue = jsonReturnedValue.replaceAll("'", "\""); try { jsonExpected = JsonParser.parseString(jsonReturnedValue).getAsJsonObject(); } catch (JsonSyntaxException e1) { @@ -171,7 +171,8 @@ public class CustomHttpClient { public static void checkSameType(JsonElement expected, JsonElement actual, String key, boolean checkAlsoSameValue) throws Exception { if (!expected.getClass().equals(actual.getClass())) { - throw new Exception("Expected JSON element has not the same class as the actual JSON element. Expected: " + throw new Exception("Expected JSON element \"" + key + + "\" has not the same class as the actual JSON element. Expected: " + expected.getClass().getSimpleName() + ". Actual: " + actual.getClass().getSimpleName()); } if (expected.isJsonNull()) { diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/AbstractOpenViduTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/AbstractOpenViduTestAppE2eTest.java index 028209be..0127d9d5 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/AbstractOpenViduTestAppE2eTest.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/AbstractOpenViduTestAppE2eTest.java @@ -45,6 +45,7 @@ import org.slf4j.LoggerFactory; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.google.gson.stream.JsonReader; import io.github.bonigarcia.wdm.WebDriverManager; @@ -63,6 +64,12 @@ import io.openvidu.test.browsers.utils.Unzipper; public class AbstractOpenViduTestAppE2eTest { + final protected String DEFAULT_JSON_SESSION = "{'id':'STR','object':'session','sessionId':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultOutputMode':'STR','defaultRecordingLayout':'STR','customSessionId':'STR','connections':{'numberOfElements':0,'content':[]},'recording':false}"; + final protected String DEFAULT_JSON_PENDING_CONNECTION = "{'id':'STR','object':'connection','type':'WEBRTC','status':'pending','connectionId':'STR','sessionId':'STR','createdAt':0,'activeAt':null,'location':null,'platform':null,'token':'STR','serverData':'STR','record':true,'role':'STR','kurentoOptions':null,'rtspUri':null,'adaptativeBitrate':null,'onlyPlayWithSubscribers':null,'networkCache':null,'clientData':null,'publishers':null,'subscribers':null}"; + final protected String DEFAULT_JSON_ACTIVE_CONNECTION = "{'id':'STR','object':'connection','type':'WEBRTC','status':'active','connectionId':'STR','sessionId':'STR','createdAt':0,'activeAt':0,'location':'STR','platform':'STR','token':'STR','serverData':'STR','record':true,'role':'STR','kurentoOptions':null,'rtspUri':null,'adaptativeBitrate':null,'onlyPlayWithSubscribers':null,'networkCache':null,'clientData':'STR','publishers':[],'subscribers':[]}"; + final protected String DEFAULT_JSON_IPCAM_CONNECTION = "{'id':'STR','object':'connection','type':'IPCAM','status':'active','connectionId':'STR','sessionId':'STR','createdAt':0,'activeAt':0,'location':'STR','platform':'IPCAM','token':null,'serverData':'STR','record':true,'role':null,'kurentoOptions':null,'rtspUri':'STR','adaptativeBitrate':true,'onlyPlayWithSubscribers':true,'networkCache':2000,'clientData':null,'publishers':[],'subscribers':[]}"; + final protected String DEFAULT_JSON_TOKEN = "{'id':'STR','token':'STR','connectionId':'STR','createdAt':0,'session':'STR','role':'STR','data':'STR','kurentoOptions':{}}"; + protected static String OPENVIDU_SECRET = "MY_SECRET"; protected static String OPENVIDU_URL = "https://localhost:4443/"; protected static String APP_URL = "http://localhost:4200/"; @@ -546,4 +553,17 @@ public class AbstractOpenViduTestAppE2eTest { + " | awk '{print $1 }' | xargs -I {} docker rm -f {}"); } + protected String mergeJson(String json, String newProperties, String[] removeProperties) { + JsonObject jsonObj = JsonParser.parseString(json.replaceAll("'", "\"")).getAsJsonObject(); + JsonObject newJsonObj = JsonParser.parseString(newProperties.replaceAll("'", "\"")).getAsJsonObject(); + newJsonObj.entrySet().forEach(entry -> { + jsonObj.remove(entry.getKey()); + jsonObj.add(entry.getKey(), entry.getValue()); + }); + for (String prop : removeProperties) { + jsonObj.remove(prop); + } + return jsonObj.toString().replaceAll("\"", "'"); + } + } diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduProTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduProTestAppE2eTest.java index f075ec1b..43b4da49 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduProTestAppE2eTest.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduProTestAppE2eTest.java @@ -31,7 +31,6 @@ import io.openvidu.java.client.OpenViduHttpException; import io.openvidu.java.client.OpenViduRole; import io.openvidu.java.client.Recording; import io.openvidu.java.client.Session; -import io.openvidu.java.client.Token; import io.openvidu.test.browsers.utils.CustomHttpClient; import io.openvidu.test.browsers.utils.Unzipper; @@ -212,19 +211,19 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { // Updating only role should let record value untouched restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenConnectionId, "{'role':'MODERATOR'}", HttpStatus.SC_OK, true, true, true, - "{'id':'" + tokenConnectionId - + "','object':'connection','type':'WEBRTC','status':'pending','connectionId':'" - + tokenConnectionId + "','role':'MODERATOR','record':false,'token':'" + token - + "','sessionId':'CUSTOM_SESSION_ID','serverData':'','publishers':null,'subscribers':null,'createdAt':" - + createdAt + ",'activeAt':null,'platform':null,'location':null,'clientData':null}"); + mergeJson(DEFAULT_JSON_PENDING_CONNECTION, + "{'id':'" + tokenConnectionId + "','connectionId':'" + tokenConnectionId + + "','role':'MODERATOR','serverData':'','record':false,'token':'" + token + + "','sessionId':'CUSTOM_SESSION_ID','createdAt':" + createdAt + "}", + new String[0])); // Updating only record should let role value untouched restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenConnectionId, "{'record':true}", HttpStatus.SC_OK, true, true, true, - "{'id':'" + tokenConnectionId - + "','object':'connection','type':'WEBRTC','status':'pending','connectionId':'" - + tokenConnectionId + "','role':'MODERATOR','record':true,'token':'" + token - + "','sessionId':'CUSTOM_SESSION_ID','serverData':'','publishers':null,'subscribers':null,'createdAt':" - + createdAt + ",'activeAt':null,'platform':null,'location':null,'clientData':null}"); + mergeJson(DEFAULT_JSON_PENDING_CONNECTION, + "{'id':'" + tokenConnectionId + "','connectionId':'" + tokenConnectionId + + "','role':'MODERATOR','serverData':'','token':'" + token + + "','sessionId':'CUSTOM_SESSION_ID','createdAt':" + createdAt + "}", + new String[0])); // Test with openvidu-java-client OpenVidu OV = new OpenVidu(OpenViduTestAppE2eTest.OPENVIDU_URL, OpenViduTestAppE2eTest.OPENVIDU_SECRET); @@ -275,6 +274,8 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { Assert.assertTrue("Session object should have changed", session.fetch()); connection = session.getActiveConnections().get(0); + final Long activeAt = connection.activeAt(); + Assert.assertTrue("activeAt should be greater than createdAt in Connection object", activeAt > createdAt); Assert.assertEquals("Wrong role in Connection object", OpenViduRole.SUBSCRIBER, connection.getRole()); Assert.assertFalse("Wrong record in Connection object", connection.record()); @@ -284,29 +285,37 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { // Updating only role should let record value untouched restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenConnectionId, "{'role':'MODERATOR'}", HttpStatus.SC_OK, false, true, true, - "{'id':'" + tokenConnectionId - + "','object':'connection','type':'WEBRTC','status':'active','connectionId':'" - + tokenConnectionId + "','role':'MODERATOR','record':false,'token':'" + token - + "','sessionId':'CUSTOM_SESSION_ID','serverData':'','publishers':[],'subscribers':[]}"); + mergeJson(DEFAULT_JSON_ACTIVE_CONNECTION, + "{'id':'" + tokenConnectionId + "','connectionId':'" + tokenConnectionId + + "','role':'MODERATOR','record':false,'token':'" + token + + "','sessionId':'CUSTOM_SESSION_ID','createdAt':" + createdAt + ",'activeAt':" + + activeAt + ",'serverData':''}", + new String[] { "location", "platform", "clientData" })); // Updating only record should let role value untouched restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenConnectionId, "{'record':true}", HttpStatus.SC_OK, false, true, true, - "{'id':'" + tokenConnectionId - + "','object':'connection','type':'WEBRTC','status':'active','connectionId':'" - + tokenConnectionId + "','role':'MODERATOR','record':true,'token':'" + token - + "','sessionId':'CUSTOM_SESSION_ID','serverData':'','publishers':[],'subscribers':[]}"); + mergeJson(DEFAULT_JSON_ACTIVE_CONNECTION, + "{'id':'" + tokenConnectionId + "','connectionId':'" + tokenConnectionId + + "','role':'MODERATOR','record':true,'token':'" + token + + "','sessionId':'CUSTOM_SESSION_ID','createdAt':" + createdAt + ",'activeAt':" + + activeAt + ",'serverData':''}", + new String[] { "location", "platform", "clientData" })); restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenConnectionId, "{'role':'SUBSCRIBER','record':true,'data':'OTHER DATA'}", HttpStatus.SC_OK, false, true, true, - "{'id':'" + tokenConnectionId - + "','object':'connection','type':'WEBRTC','status':'active','connectionId':'" - + tokenConnectionId + "','role':'SUBSCRIBER','record':true,'token':'" + token - + "','sessionId':'CUSTOM_SESSION_ID','serverData':'','publishers':[],'subscribers':[]}"); + mergeJson(DEFAULT_JSON_ACTIVE_CONNECTION, + "{'id':'" + tokenConnectionId + "','connectionId':'" + tokenConnectionId + + "','role':'SUBSCRIBER','record':true,'token':'" + token + + "','sessionId':'CUSTOM_SESSION_ID','createdAt':" + createdAt + ",'activeAt':" + + activeAt + ",'serverData':''}", + new String[] { "location", "platform", "clientData" })); restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenConnectionId, "{'role':'PUBLISHER'}", HttpStatus.SC_OK, false, true, true, - "{'id':'" + tokenConnectionId - + "','object':'connection','type':'WEBRTC','status':'active','connectionId':'" - + tokenConnectionId + "','role':'PUBLISHER','record':true,'token':'" + token - + "','sessionId':'CUSTOM_SESSION_ID','serverData':'','publishers':[],'subscribers':[]}"); + mergeJson(DEFAULT_JSON_ACTIVE_CONNECTION, + "{'id':'" + tokenConnectionId + "','connectionId':'" + tokenConnectionId + + "','role':'PUBLISHER','record':true,'token':'" + token + + "','sessionId':'CUSTOM_SESSION_ID','createdAt':" + createdAt + ",'activeAt':" + + activeAt + ",'serverData':''}", + new String[] { "location", "platform", "clientData" })); // Test with openvidu-node-client user.getDriver().findElement(By.id("session-api-btn-0")).click(); @@ -341,7 +350,7 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { Assert.assertFalse("Session object should not have changed", session.fetch()); Assert.assertEquals("Wrong connectionId in Connection object", tokenConnectionId, connection.getConnectionId()); Assert.assertEquals("Wrong role in Connection object", OpenViduRole.PUBLISHER, connection.getRole()); - Assert.assertTrue("Wrong record in Connection object", connection.record()); + Assert.assertFalse("Wrong record in Connection object", connection.record()); Assert.assertEquals("Wrong status in Connection object", "active", connection.getStatus()); connection = session.updateConnection(tokenConnectionId, new ConnectionOptions.Builder().role(OpenViduRole.SUBSCRIBER).build()); @@ -397,9 +406,8 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { void openViduJavaClientProTest() throws Exception { Session session = OV.createSession(); Assert.assertFalse(session.fetch()); - Token token = session.createToken(); - Assert.assertTrue(session.fetch()); - Connection connection = session.getConnection(token.getConnectionId()); + Connection connection = session.createConnection(); + Assert.assertFalse(session.fetch()); Assert.assertEquals("Wrong role property", OpenViduRole.PUBLISHER, connection.getRole()); Assert.assertTrue("Wrong record property", connection.record()); session.updateConnection(connection.getConnectionId(), diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java index 7316d1f6..c5cf2af8 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java @@ -48,11 +48,14 @@ import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.ExpectedConditions; import org.springframework.test.context.junit.jupiter.SpringExtension; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.mashape.unirest.http.HttpMethod; import io.openvidu.java.client.Connection; +import io.openvidu.java.client.ConnectionOptions; +import io.openvidu.java.client.ConnectionType; import io.openvidu.java.client.KurentoOptions; import io.openvidu.java.client.MediaMode; import io.openvidu.java.client.OpenVidu; @@ -66,8 +69,6 @@ import io.openvidu.java.client.RecordingMode; import io.openvidu.java.client.RecordingProperties; import io.openvidu.java.client.Session; import io.openvidu.java.client.SessionProperties; -import io.openvidu.java.client.Token; -import io.openvidu.java.client.TokenOptions; import io.openvidu.test.browsers.FirefoxUser; import io.openvidu.test.browsers.utils.CustomHttpClient; import io.openvidu.test.browsers.utils.layout.CustomLayoutHandler; @@ -84,10 +85,6 @@ import io.openvidu.test.browsers.utils.webhook.CustomWebhook; @ExtendWith(SpringExtension.class) public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { - final String DEFAULT_JSON_SESSION = "{'id':'STR','object':'STR','sessionId':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultOutputMode':'STR','defaultRecordingLayout':'STR','customSessionId':'STR','connections':{'numberOfElements':0,'content':[]},'recording':false}"; - final String DEFAULT_JSON_TOKEN = "{'id':'STR','object':'STR','token':'STR','connectionId':0,'session':'STR','createdAt':0,'role':'STR','data':'STR','record':true}"; - final String DEFAULT_JSON_CONNECTION = "{'id':'STR','object':'STR','type':'STR','status':'STR','connectionId':'STR','sessionId':'STR','createdAt':0,'activeAt':0,'location':'STR','platform':'STR','role':'STR','record':true,'serverData':'STR','clientData':'STR','publishers':[],'subscribers':[]}"; - @BeforeAll() protected static void setupAll() { checkFfmpegInstallation(); @@ -2141,27 +2138,19 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { KurentoOptions kurentoOptions = new KurentoOptions.Builder().videoMaxRecvBandwidth(250) .allowedFilters(new String[] { "GStreamerFilter" }).build(); - TokenOptions tokenOptionsModerator = new TokenOptions.Builder().role(OpenViduRole.MODERATOR) + ConnectionOptions moderatorConnectionOptions = new ConnectionOptions.Builder().role(OpenViduRole.MODERATOR) .data(serverDataModerator).kurentoOptions(kurentoOptions).build(); - Token tokenModerator = session.createToken(tokenOptionsModerator); - String tokenModeratorString = tokenModerator.getToken(); - String connectionIdModerator = tokenModerator.getConnectionId(); + Connection connectionModerator = session.createConnection(moderatorConnectionOptions); - TokenOptions tokenOptionsSubscriber = new TokenOptions.Builder().role(OpenViduRole.SUBSCRIBER) - .data(serverDataSubscriber).build(); - Token tokenSubscriber = session.createToken(tokenOptionsSubscriber); - String tokenSubscriberString = tokenSubscriber.getToken(); - String connectionIdSubscriber = tokenSubscriber.getConnectionId(); + ConnectionOptions subscriberConnectionOptions = new ConnectionOptions.Builder().type(ConnectionType.WEBRTC) + .role(OpenViduRole.SUBSCRIBER).data(serverDataSubscriber).build(); + Connection connectionSubscriber = session.createConnection(subscriberConnectionOptions); - Assert.assertTrue("Session.fetch() should return true if new pending connections", session.fetch()); + Assert.assertFalse("Session.fetch() should return false after Session.createConnection", session.fetch()); Assert.assertFalse("OpenVidu.fetch() should return false after Session.fetch()", OV.fetch()); Assert.assertEquals("Wrong number of active connections", 0, session.getActiveConnections().size()); Assert.assertEquals("Wrong number of connections", 2, session.getConnections().size()); - Connection connectionModerator = session.getConnection(connectionIdModerator); - Connection connectionSubscriber = session.getConnection(connectionIdSubscriber); - Assert.assertEquals("Wrong connectionId property", connectionIdModerator, - connectionModerator.getConnectionId()); Assert.assertEquals("Wrong status property", "pending", connectionModerator.getStatus()); Assert.assertEquals("Wrong role property", OpenViduRole.MODERATOR, connectionModerator.getRole()); Assert.assertTrue("Wrong record property", connectionModerator.record()); @@ -2183,7 +2172,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { Thread.sleep(1000); WebElement tokeInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input")); tokeInput.clear(); - tokeInput.sendKeys(tokenModeratorString); + tokeInput.sendKeys(connectionModerator.getToken()); user.getDriver().findElement(By.id("save-btn")).click(); Thread.sleep(1000); @@ -2198,7 +2187,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { Thread.sleep(1000); tokeInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input")); tokeInput.clear(); - tokeInput.sendKeys(tokenSubscriberString); + tokeInput.sendKeys(connectionSubscriber.getToken()); user.getDriver().findElement(By.id("save-btn")).click(); Thread.sleep(1000); @@ -2514,8 +2503,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { } pub = connectionModerator.getPublishers().get(0); - // TODO: test delete unused Token, update unused Token and update ongoing - // Connection + // TODO: test delete unused Connection session.forceUnpublish(pub); user.getEventManager().waitUntilEventReaches("streamDestroyed", 6); @@ -2528,6 +2516,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { Assert.assertEquals("Wrong number of Subscribers", 0, con.getSubscribers().size()); }); + // Delete active Connection session.forceDisconnect(connectionModerator); user.getEventManager().waitUntilEventReaches("sessionDisconnected", 1); user.getEventManager().waitUntilEventReaches("connectionDestroyed", 1); @@ -2541,6 +2530,33 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { Assert.assertFalse("Session.fetch() should return true", OV.fetch()); + // Delete pending Connection + session = OV.createSession(); + Connection con = session.createConnection(); + Assert.assertEquals("Wrong number of Connections", 1, session.getConnections().size()); + Assert.assertEquals("Wrong number of active Connections", 0, session.getActiveConnections().size()); + Assert.assertFalse(session.fetch()); + session.forceDisconnect(con); + Assert.assertEquals("Wrong number of Connections", 0, session.getConnections().size()); + Assert.assertEquals("Wrong number of active Connections", 0, session.getActiveConnections().size()); + Assert.assertFalse(session.fetch()); + + // Test IPCAM + final String rtsp = "rtsp://dummyurl.com"; + Connection ipcamera = session.createConnection(new ConnectionOptions.Builder().type(ConnectionType.IPCAM) + .rtspUri(rtsp).adaptativeBitrate(false).onlyPlayWithSubscribers(false).networkCache(50).build()); + Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch()); + Assert.assertFalse("Session.fetch() should return false", session.fetch()); + Assert.assertEquals("Wrong number of active connections", 1, session.getActiveConnections().size()); + Assert.assertEquals("Wrong number of connections", 1, session.getConnections().size()); + ipcamera = session.getConnection(ipcamera.getConnectionId()); + Assert.assertEquals("Wrong type property of Connection object", "IPCAM", ipcamera.getType().name()); + Assert.assertNull("Property role of an IPCAM connection should be null", ipcamera.getRole()); + Assert.assertEquals("Wrong property rtspUri", rtsp, ipcamera.getRtspUri()); + Assert.assertFalse("Wrong property adaptativeBitrate", ipcamera.adaptativeBitrate()); + Assert.assertFalse("Wrong property onlyPlayWithSubscribers", ipcamera.onlyPlayWithSubscribers()); + Assert.assertEquals("Wrong property networkCache", 50, ipcamera.getNetworkCache()); + gracefullyLeaveParticipants(2); } @@ -2626,31 +2642,61 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { // 200 body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'MODERATOR', 'data': 'SERVER_DATA', 'kurentoOptions': {'videoMaxSendBandwidth':777,'allowedFilters': ['GStreamerFilter']}}"; res = restClient.rest(HttpMethod.POST, "/openvidu/api/tokens", body, HttpStatus.SC_OK, true, false, true, - "{'id':'STR','object':'STR','connectionId':'STR','session':'STR','createdAt':0,'role':'STR','data':'STR','record':true,'token':'STR','kurentoOptions':{'videoMaxSendBandwidth':777,'allowedFilters':['STR']}}"); + mergeJson(DEFAULT_JSON_TOKEN, + "{'kurentoOptions':{'videoMaxSendBandwidth':777,'allowedFilters':['STR']}}", new String[0])); final String token1 = res.get("token").getAsString(); - final String connectionId1 = res.get("connectionId").getAsString(); - final long createdAt1 = res.get("createdAt").getAsLong(); Assert.assertEquals("JSON return value from /openvidu/api/tokens should have equal srtings in 'id' and 'token'", res.get("id").getAsString(), token1); Assert.assertEquals("Wrong session parameter", "CUSTOM_SESSION_ID", res.get("session").getAsString()); + res = restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", null, + HttpStatus.SC_OK, true, true, false, "{'numberOfElements':1,'content':[]}"); + JsonObject connection1 = res.getAsJsonObject().get("content").getAsJsonArray().get(0).getAsJsonObject(); + final String connectionId1 = connection1.get("id").getAsString(); + final long createdAt1 = connection1.get("createdAt").getAsLong(); + /** POST /openvidu/api/sessions/CUSTOM_SESSION_ID/connection **/ + // 400 + body = "{'type':false}"; + restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body, + HttpStatus.SC_BAD_REQUEST); + body = "{'type':'NOT_EXISTS'}"; + restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body, + HttpStatus.SC_BAD_REQUEST); + body = "{'type':'WEBRTC','role':123}"; + restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body, + HttpStatus.SC_BAD_REQUEST); + body = "{'type':'WEBRTC','role':123}"; + restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body, + HttpStatus.SC_BAD_REQUEST); + body = "{'type':'WEBRTC','role':'MODERATOR','data':true}"; + restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body, + HttpStatus.SC_BAD_REQUEST); + // 200 + String kurentoOpts = "'kurentoOptions':{'videoMaxSendBandwidth':777,'allowedFilters':['GStreamerFilter']}"; + body = "{'type':'WEBRTC','role':'MODERATOR','data':'SERVER_DATA'," + kurentoOpts + "}"; + res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body, + HttpStatus.SC_OK, true, false, true, + mergeJson(DEFAULT_JSON_PENDING_CONNECTION, "{" + kurentoOpts + "}", new String[0])); + restClient.rest(HttpMethod.DELETE, + "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + res.get("id").getAsString(), + HttpStatus.SC_NO_CONTENT); // Default values - body = "{'session': 'CUSTOM_SESSION_ID'}"; - res = restClient.rest(HttpMethod.POST, "/openvidu/api/tokens", body, HttpStatus.SC_OK, true, false, true, - DEFAULT_JSON_TOKEN); - final String token2 = res.get("id").getAsString(); - final String connectionId2 = res.get("connectionId").getAsString(); + res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", "{}", + HttpStatus.SC_OK); + final String token2 = res.get("token").getAsString(); + final String connectionId2 = res.get("id").getAsString(); /** GET /openvidu/api/sessions/ID/connection (with pending connections) **/ restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", null, HttpStatus.SC_OK, true, true, false, "{'numberOfElements':2,'content':[]}"); restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId1, null, HttpStatus.SC_OK, true, true, true, - "{'id':'" + connectionId1 + "','connectionId':'" + connectionId1 - + "','object':'connection','type':'WEBRTC','status':'pending','sessionId':'CUSTOM_SESSION_ID','token':'" - + token1 + "','role':'MODERATOR','serverData':'SERVER_DATA','record':true,'createdAt':" - + createdAt1 - + ",'activeAt':null,'platform':null,'location':null,'clientData':null,'publishers':null,'subscribers':null}"); + mergeJson(DEFAULT_JSON_PENDING_CONNECTION, + "{'id':'" + connectionId1 + "','connectionId':'" + connectionId1 + + "','sessionId':'CUSTOM_SESSION_ID','token':'" + token1 + + "','serverData':'SERVER_DATA','role':'MODERATOR'," + kurentoOpts + ",'createdAt':" + + createdAt1 + "}", + new String[0])); /** POST /openvidu/api/signal (NOT ACTIVE SESSION) **/ body = "{}"; @@ -2864,14 +2910,14 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { **/ body = "{'customSessionId': 'CUSTOM_SESSION_ID'}"; restClient.rest(HttpMethod.POST, "/openvidu/api/sessions", body, HttpStatus.SC_OK); - body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'SUBSCRIBER'}"; - res = restClient.rest(HttpMethod.POST, "/openvidu/api/tokens", body, HttpStatus.SC_OK, true, false, true, - DEFAULT_JSON_TOKEN); - final String tokenAConnectionId = res.get("connectionId").getAsString(); + body = "{'type': 'WEBRTC', 'role': 'SUBSCRIBER'}"; + res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body, + HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_PENDING_CONNECTION); + final String connectionIdA = res.get("id").getAsString(); final String tokenA = res.get("token").getAsString(); - res = restClient.rest(HttpMethod.POST, "/openvidu/api/tokens", body, HttpStatus.SC_OK, true, false, true, - DEFAULT_JSON_TOKEN); - final String tokenBConnectionId = res.get("connectionId").getAsString(); + res = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection", body, + HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_PENDING_CONNECTION); + final String connectionIdB = res.get("connectionId").getAsString(); final String tokenB = res.get("token").getAsString(); user.getDriver().findElement(By.id("one2one-btn")).click(); @@ -2895,7 +2941,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { Thread.sleep(1000); // Invalidate token - restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenAConnectionId, + restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionIdA, HttpStatus.SC_NO_CONTENT); // User should pop up invalid token @@ -2918,7 +2964,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { user.getEventManager().waitUntilEventReaches("connectionCreated", 1); // connectionId should be equal to the one brought by the token - Assert.assertEquals("Wrong connectionId", tokenBConnectionId, + Assert.assertEquals("Wrong connectionId", connectionIdB, restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID", HttpStatus.SC_OK) .get("connections").getAsJsonObject().get("content").getAsJsonArray().get(0).getAsJsonObject() .get("connectionId").getAsString()); @@ -3286,7 +3332,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { // Publish IP camera. Dummy URL because no user will subscribe to it [200] String ipCamBody = "{'type':'IPCAM','rtspUri':'rtsp://dummyurl.com','adaptativeBitrate':true,'onlyPlayWithSubscribers':true,'networkCache':1000,'data':'MY_IP_CAMERA'}"; JsonObject response = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/IP_CAM_SESSION/connection", - ipCamBody, HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_CONNECTION); + ipCamBody, HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_IPCAM_CONNECTION); CustomWebhook.waitForEvent("sessionCreated", 1); CustomWebhook.waitForEvent("participantJoined", 1); @@ -3294,7 +3340,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { Assert.assertEquals("Wrong serverData property", "MY_IP_CAMERA", response.get("serverData").getAsString()); Assert.assertEquals("Wrong platform property", "IPCAM", response.get("platform").getAsString()); - Assert.assertEquals("Wrong role property", "PUBLISHER", response.get("role").getAsString()); + Assert.assertEquals("Wrong role property", JsonNull.INSTANCE, response.get("role")); Assert.assertEquals("Wrong type property", "IPCAM", response.get("type").getAsString()); Assert.assertEquals("Wrong number of publishers in IPCAM participant", 1, @@ -3384,7 +3430,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { + "','adaptativeBitrate':true,'onlyPlayWithSubscribers':true,'networkCache':1000,'data':'MY_IP_CAMERA'}"; restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/TestSession/connection", ipCamBody, - HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_CONNECTION); + HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_IPCAM_CONNECTION); user.getEventManager().waitUntilEventReaches("connectionCreated", 2); user.getEventManager().waitUntilEventReaches("streamCreated", 2); @@ -3411,7 +3457,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { // Publish again the IPCAM response = restClient.rest(HttpMethod.POST, "/openvidu/api/sessions/TestSession/connection", ipCamBody, - HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_CONNECTION); + HttpStatus.SC_OK, true, false, true, DEFAULT_JSON_IPCAM_CONNECTION); user.getEventManager().waitUntilEventReaches("connectionCreated", 3); user.getEventManager().waitUntilEventReaches("streamCreated", 3); user.getEventManager().waitUntilEventReaches("streamPlaying", 3); @@ -3523,9 +3569,8 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { checkNodeFetchChanged(true, true); checkNodeFetchChanged(true, false); - Token token = session.createToken(); - // TODO: when using createConnection this below should be false! - Assert.assertTrue("Java fetch should be true", session.fetch()); + Connection connection = session.createConnection(); + Assert.assertFalse("Java fetch should be false", session.fetch()); Assert.assertFalse("Java fetch should be false", OV.fetch()); checkNodeFetchChanged(true, true); checkNodeFetchChanged(true, false); @@ -3550,7 +3595,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { Thread.sleep(1000); WebElement tokeInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input")); tokeInput.clear(); - tokeInput.sendKeys(token.getToken()); + tokeInput.sendKeys(connection.getToken()); user.getDriver().findElement(By.id("save-btn")).click(); Thread.sleep(1000); user.getDriver().findElement(By.className("join-btn")).click();