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 c8a7159b..a6a1f20b 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 @@ -41,29 +41,42 @@ public class Connection { protected List subscribers = new ArrayList<>(); protected Connection(JsonObject json) { - JsonArray jsonArrayPublishers = json.get("publishers").getAsJsonArray(); - jsonArrayPublishers.forEach(publisher -> { - JsonObject pubJson = publisher.getAsJsonObject(); - JsonObject mediaOptions = pubJson.get("mediaOptions").getAsJsonObject(); - Publisher pub = new Publisher(pubJson.get("streamId").getAsString(), pubJson.get("createdAt").getAsLong(), - mediaOptions.get("hasAudio").getAsBoolean(), mediaOptions.get("hasVideo").getAsBoolean(), - mediaOptions.get("audioActive"), mediaOptions.get("videoActive"), mediaOptions.get("frameRate"), - mediaOptions.get("typeOfVideo"), mediaOptions.get("videoDimensions")); - this.publishers.put(pub.getStreamId(), pub); - }); + // These properties may be null + if (!json.get("publishers").isJsonNull()) { + JsonArray jsonArrayPublishers = json.get("publishers").getAsJsonArray(); + jsonArrayPublishers.forEach(publisher -> { + JsonObject pubJson = publisher.getAsJsonObject(); + JsonObject mediaOptions = pubJson.get("mediaOptions").getAsJsonObject(); + Publisher pub = new Publisher(pubJson.get("streamId").getAsString(), + pubJson.get("createdAt").getAsLong(), mediaOptions.get("hasAudio").getAsBoolean(), + mediaOptions.get("hasVideo").getAsBoolean(), mediaOptions.get("audioActive"), + mediaOptions.get("videoActive"), mediaOptions.get("frameRate"), mediaOptions.get("typeOfVideo"), + mediaOptions.get("videoDimensions")); + this.publishers.put(pub.getStreamId(), pub); + }); + } - JsonArray jsonArraySubscribers = json.get("subscribers").getAsJsonArray(); - jsonArraySubscribers.forEach(subscriber -> { - this.subscribers.add((subscriber.getAsJsonObject()).get("streamId").getAsString()); - }); + if (!json.get("subscribers").isJsonNull()) { + JsonArray jsonArraySubscribers = json.get("subscribers").getAsJsonArray(); + jsonArraySubscribers.forEach(subscriber -> { + this.subscribers.add((subscriber.getAsJsonObject()).get("streamId").getAsString()); + }); + } + if (!json.get("createdAt").isJsonNull()) { + this.createdAt = json.get("createdAt").getAsLong(); + } + if (!json.get("location").isJsonNull()) { + this.location = json.get("location").getAsString(); + } + if (!json.get("platform").isJsonNull()) { + this.platform = json.get("platform").getAsString(); + } + if (!json.get("clientData").isJsonNull()) { + this.clientData = json.get("clientData").getAsString(); + } + // These properties won't ever be null this.connectionId = json.get("connectionId").getAsString(); - this.createdAt = json.get("createdAt").getAsLong(); - - this.location = json.get("location").getAsString(); - this.platform = json.get("platform").getAsString(); - this.clientData = json.get("clientData").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(); @@ -179,12 +192,36 @@ public class Connection { return this.subscribers; } + protected JsonObject toJson() { + JsonObject json = new JsonObject(); + json.addProperty("id", this.getConnectionId()); + json.addProperty("createdAt", this.createdAt()); + 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()); + JsonArray pubs = new JsonArray(); + this.getPublishers().forEach(p -> { + pubs.add(p.toJson()); + }); + JsonArray subs = new JsonArray(); + this.getSubscribers().forEach(s -> { + subs.add(s); + }); + json.add("publishers", pubs); + json.add("subscribers", subs); + return json; + } + /** * For now only properties data, role and record can be updated */ - protected void overrideTokenOptions(TokenOptions tokenOptions) { - TokenOptions.Builder modifiableTokenOptions = new TokenOptions.Builder().role(tokenOptions.getRole()) - .record(tokenOptions.record()); + 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()); } 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 new file mode 100644 index 00000000..45b32efb --- /dev/null +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/ConnectionOptions.java @@ -0,0 +1,165 @@ +package io.openvidu.java.client; + +import com.google.gson.JsonArray; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; + +public class ConnectionOptions { + + private OpenViduRole role; + private String data; + private Boolean record; + private KurentoOptions kurentoOptions; + + /** + * + * Builder for {@link io.openvidu.java.client.ConnectionOptions} + * + */ + public static class Builder { + + private OpenViduRole role = OpenViduRole.PUBLISHER; + private String data; + private Boolean record = true; + private KurentoOptions kurentoOptions; + + /** + * Builder for {@link io.openvidu.java.client.ConnectionOptions}. + */ + public ConnectionOptions build() { + return new ConnectionOptions(this.role, this.data, this.record, this.kurentoOptions); + } + + /** + * Call this method to set the role assigned to this Connection. + */ + public Builder role(OpenViduRole role) { + this.role = role; + return this; + } + + /** + * Call this method to set the 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 in + * your clients. + * + */ + public Builder data(String data) { + this.data = data; + return this; + } + + /** + * Call this method to flag the streams published by this Connection to be + * recorded or not. This only affects INDIVIDUAL recording. If not set by default will be true. + */ + public Builder record(boolean record) { + this.record = record; + return this; + } + + /** + * Call this method to set a {@link io.openvidu.java.client.KurentoOptions} + * object for this Connection. + */ + public Builder kurentoOptions(KurentoOptions kurentoOptions) { + this.kurentoOptions = kurentoOptions; + return this; + } + + } + + ConnectionOptions(OpenViduRole role, String data, Boolean record, KurentoOptions kurentoOptions) { + this.role = role; + this.data = data; + this.record = record; + this.kurentoOptions = kurentoOptions; + } + + /** + * Returns the role assigned to this Connection. + */ + public OpenViduRole getRole() { + return this.role; + } + + /** + * Returns the secure (server-side) metadata assigned to this Connection. + */ + public String getData() { + return this.data; + } + + /** + * Whether the streams published by this Connection will be recorded or not. + * This only affects INDIVIDUAL recording. + */ + public Boolean record() { + return this.record; + } + + protected JsonObject toJsonObject(String sessionId) { + JsonObject json = new JsonObject(); + json.addProperty("session", sessionId); + if (getRole() != null) { + json.addProperty("role", getRole().name()); + } else { + json.add("role", JsonNull.INSTANCE); + } + if (getData() != null) { + json.addProperty("data", getData()); + } else { + json.add("data", JsonNull.INSTANCE); + } + if (record() != null) { + json.addProperty("record", record()); + } else { + 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); + } + return json; + } + +} 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 dc39bc28..b8256b14 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 @@ -268,7 +268,7 @@ public class Session { * @throws OpenViduJavaClientException */ public boolean fetch() throws OpenViduJavaClientException, OpenViduHttpException { - String beforeJSON = this.toJson(); + final String beforeJSON = this.toJson(); HttpGet request = new HttpGet(this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId); request.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded"); @@ -283,7 +283,7 @@ public class Session { int statusCode = response.getStatusLine().getStatusCode(); if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { this.resetSessionWithJson(httpResponseToJson(response)); - String afterJSON = this.toJson(); + final String afterJSON = this.toJson(); boolean hasChanged = !beforeJSON.equals(afterJSON); log.info("Session info fetched for session '{}'. Any change: {}", this.sessionId, hasChanged); return hasChanged; @@ -482,15 +482,14 @@ public class Session { } /** - * Updates the properties of a Connection. These properties are the ones defined - * by the {@link io.openvidu.java.client.TokenOptions} parameter when generating - * the token used to create the Connection. These are the properties that can be - * updated: + * Updates the properties of a Connection with a + * {@link io.openvidu.java.client.ConnectionOptions} object. Only these + * properties can be updated: * *
* @@ -508,9 +507,9 @@ public class Session { * changes consequence of the execution of this method applied in the local * objects. * - * @param connectionId The Connection (or a still not used Token) to modify - * @param tokenOptions A new TokenOptions object with the updated values to - * apply + * @param connectionId The Connection (or a still not used Token) to modify + * @param connectionOptions A ConnectionOptions object with the new values to + * apply * * @return The updated {@link io.openvidu.java.client.Connection Connection} * object @@ -518,7 +517,7 @@ public class Session { * @throws OpenViduJavaClientException * @throws OpenViduHttpException */ - public Connection updateConnection(String connectionId, TokenOptions tokenOptions) + public Connection updateConnection(String connectionId, ConnectionOptions connectionOptions) throws OpenViduJavaClientException, OpenViduHttpException { HttpPatch request = new HttpPatch( @@ -526,7 +525,7 @@ public class Session { StringEntity params; try { - params = new StringEntity(tokenOptions.toJsonObject(this.sessionId).toString()); + params = new StringEntity(connectionOptions.toJsonObject(this.sessionId).toString()); } catch (UnsupportedEncodingException e1) { throw new OpenViduJavaClientException(e1.getMessage(), e1.getCause()); } @@ -549,18 +548,19 @@ public class Session { } else { throw new OpenViduHttpException(statusCode); } + JsonObject json = httpResponseToJson(response); // Update the actual Connection object with the new options Connection existingConnection = this.activeConnections.get(connectionId); if (existingConnection == null) { // The updated Connection is not available in local map - Connection newConnection = new Connection(httpResponseToJson(response)); + Connection newConnection = new Connection(json); this.activeConnections.put(connectionId, newConnection); return newConnection; } else { // The updated Connection was available in local map - existingConnection.overrideTokenOptions(tokenOptions); + existingConnection.overrideConnectionOptions(connectionOptions); return existingConnection; } @@ -717,23 +717,7 @@ public class Session { connections.addProperty("numberOfElements", this.getActiveConnections().size()); JsonArray jsonArrayConnections = new JsonArray(); this.getActiveConnections().forEach(con -> { - JsonObject c = new JsonObject(); - c.addProperty("connectionId", con.getConnectionId()); - c.addProperty("role", con.getRole().name()); - c.addProperty("token", con.getToken()); - c.addProperty("clientData", con.getClientData()); - c.addProperty("serverData", con.getServerData()); - JsonArray pubs = new JsonArray(); - con.getPublishers().forEach(p -> { - pubs.add(p.toJson()); - }); - JsonArray subs = new JsonArray(); - con.getSubscribers().forEach(s -> { - subs.add(s); - }); - c.add("publishers", pubs); - c.add("subscribers", subs); - jsonArrayConnections.add(c); + jsonArrayConnections.add(con.toJson()); }); connections.add("content", jsonArrayConnections); json.add("connections", connections); diff --git a/openvidu-node-client/src/Connection.ts b/openvidu-node-client/src/Connection.ts index 9bb08874..8919edfe 100644 --- a/openvidu-node-client/src/Connection.ts +++ b/openvidu-node-client/src/Connection.ts @@ -17,7 +17,7 @@ import { OpenViduRole } from './OpenViduRole'; import { Publisher } from './Publisher'; -import { TokenOptions } from './TokenOptions'; +import { ConnectionOptions } from './ConnectionOptions'; /** * See [[Session.activeConnections]] @@ -88,6 +88,7 @@ export class Connection { * @hidden */ constructor(json) { + // These properties may be null if (json.publishers != null) { json.publishers.forEach(publisher => { this.publishers.push(new Publisher(publisher)); @@ -98,15 +99,17 @@ export class Connection { this.subscribers.push(subscriber.streamId); }); } - this.connectionId = json.connectionId; this.createdAt = json.createdAt; - this.role = json.role; - this.serverData = json.serverData; - this.record = json.record; - this.token = json.token; this.location = json.location; this.platform = json.platform; this.clientData = json.clientData; + + // These properties won't ever be null + this.connectionId = json.connectionId; + this.token = json.token; + this.role = json.role; + this.serverData = json.serverData; + this.record = json.record; } /** @@ -145,12 +148,12 @@ export class Connection { /** * @hidden */ - overrideTokenOptions(tokenOptions: TokenOptions): void { - if (tokenOptions.role != null) { - this.role = tokenOptions.role; + overrideConnectionOptions(connectionOptions: ConnectionOptions): void { + if (connectionOptions.role != null) { + this.role = connectionOptions.role; } - if (tokenOptions.record != null) { - this.record = tokenOptions.record + if (connectionOptions.record != null) { + this.record = connectionOptions.record } } diff --git a/openvidu-node-client/src/ConnectionOptions.ts b/openvidu-node-client/src/ConnectionOptions.ts new file mode 100644 index 00000000..bd10bbaf --- /dev/null +++ b/openvidu-node-client/src/ConnectionOptions.ts @@ -0,0 +1,72 @@ +/* + * (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 { OpenViduRole } from './OpenViduRole'; + +export interface ConnectionOptions { + + /** + * The role assigned to this Connection + * + * @default PUBLISHER + */ + role?: OpenViduRole; + + /** + * 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. + * - If you have provided no data in your clients when calling method `Session.connect(TOKEN, DATA)` (`DATA` not defined), then `Connection.data` will only have this [[ConnectionOptions.data]] property. + * - If you have provided some data when calling `Session.connect(TOKEN, DATA)` (`DATA` defined), then `Connection.data` will have the following structure: `"CLIENT_DATA%/%SERVER_DATA"`, being `CLIENT_DATA` the second + * parameter passed in OpenVidu Browser in method `Session.connect` and `SERVER_DATA` this [[ConnectionOptions.data]] property. + */ + data?: string; + + /** + * Whether to record the streams published by this Connection or not. This only affects [INDIVIDUAL recording](/en/stable/advanced-features/recording#selecting-streams-to-be-recorded) + * + * @default true + */ + record?: boolean; + + /** + * **WARNING**: experimental option. This interface may change in the near future + * + * Some advanced properties setting the configuration that the WebRTC streams of the Connection will have in Kurento Media Server. + * You can adjust: + * - `videoMaxRecvBandwidth`: maximum number of Kbps that the Connection 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](/en/stable/reference-docs/openvidu-config/) + * (parameter `OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH`) for every incoming stream of the Connection. + * _**WARNING**: the lower value set to this property limits every other bandwidth of the WebRTC pipeline this server-to-client stream belongs to. This includes the user publishing the stream and every other user subscribed to the stream_ + * - `videoMinRecvBandwidth`: minimum number of Kbps that the cConnection 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](/en/stable/reference-docs/openvidu-config/) + * (parameter `OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH`) for every incoming stream of the Connection. + * - `videoMaxSendBandwidth`: maximum number of Kbps that the Connection 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](/en/stable/reference-docs/openvidu-config/) + * (parameter `OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH`) for every outgoing stream of the Connection. + * _**WARNING**: this value limits every other bandwidth of the WebRTC pipeline this client-to-server stream belongs to. This includes every other user subscribed to the stream_ + * - `videoMinSendBandwidth`: minimum number of Kbps that the Connection 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](/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/) + */ + kurentoOptions?: { + videoMaxRecvBandwidth?: number, + videoMinRecvBandwidth?: number, + videoMaxSendBandwidth?: number, + videoMinSendBandwidth?: number, + allowedFilters?: string[] + }; +} \ No newline at end of file diff --git a/openvidu-node-client/src/Session.ts b/openvidu-node-client/src/Session.ts index cdb115be..79b8dfa9 100644 --- a/openvidu-node-client/src/Session.ts +++ b/openvidu-node-client/src/Session.ts @@ -17,6 +17,7 @@ import axios, { AxiosError } from 'axios'; import { Connection } from './Connection'; +import { ConnectionOptions } from './ConnectionOptions'; import { MediaMode } from './MediaMode'; import { OpenVidu } from './OpenVidu'; import { Publisher } from './Publisher'; @@ -359,12 +360,11 @@ export class Session { } /** - * Updates the properties of a Connection. These properties are the ones defined - * by the [[TokenOptions]] parameter when generating the token used to create the Connection. - * These are the properties that can be updated: + * Updates the properties of a Connection with a [[ConnectionOptions]] object. + * Only these properties can be updated: * - * - [[TokenOptions.role]] - * - [[TokenOptions.record]] + * - [[ConnectionOptions.role]] + * - [[ConnectionOptions.record]] * * The `connectionId` parameter can be obtained from a Connection object with * [[Connection.connectionId]], in which case the updated properties will @@ -375,17 +375,17 @@ export class Session { * [[Session.fetch]] to see the changes consequence of the execution of this method applied in the local objects. * * @param connectionId The [[Connection.connectionId]] property of the Connection object to modify, - * or the [[Token.connectionId]] property of a still not used token to modify - * @param tokenOptions A new [[TokenOptions]] object with the updated values to apply + * or the [[Token.connectionId]] property of a still not used token to modify + * @param connectionOptions A new [[ConnectionOptions]] object with the updated values to apply * * @returns A Promise that is resolved to the updated [[Connection]] object if the operation was - * successful and rejected with an Error object if not + * successful and rejected with an Error object if not */ - public updateConnection(connectionId: string, tokenOptions: TokenOptions): Promise { + public updateConnection(connectionId: string, connectionOptions: ConnectionOptions): Promise { return new Promise((resolve, reject) => { axios.patch( this.ov.host + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection/" + connectionId, - tokenOptions, + connectionOptions, { headers: { 'Authorization': this.ov.basicAuth, @@ -412,7 +412,7 @@ export class Session { resolve(newConnection); } else { // The updated Connection was available in local map - existingConnection.overrideTokenOptions(tokenOptions); + existingConnection.overrideConnectionOptions(connectionOptions); resolve(existingConnection); } }).catch(error => { diff --git a/openvidu-node-client/src/index.ts b/openvidu-node-client/src/index.ts index f08dbba0..e3b5a359 100644 --- a/openvidu-node-client/src/index.ts +++ b/openvidu-node-client/src/index.ts @@ -3,6 +3,7 @@ export * from './OpenViduRole'; export * from './Session'; export * from './SessionProperties'; export * from './TokenOptions'; +export * from './ConnectionOptions'; export * from './Token'; export * from './MediaMode'; export * from './RecordingLayout';