From 32fd093cf35c5cc590256ca6a857b399453c9dac Mon Sep 17 00:00:00 2001 From: Juan Navarro Date: Wed, 10 Nov 2021 15:50:21 +0100 Subject: [PATCH 1/5] openvidu-server: add MEDIA_SERVER_PREFERRED as default for ForcedVideoCodec MEDIA_SERVER_PREFERRED: A recommended choice is done for you, based on the media server that is currently in use. This is the default setting, and is equivalent to these values: - For *mediasoup*, `NONE` is selected. - For *Kurento*, `VP8` is selected. --- .../java/client/SessionProperties.java | 23 +++++++++++-------- .../io/openvidu/java/client/VideoCodec.java | 4 ++-- openvidu-node-client/src/SessionProperties.ts | 21 ++++++++++------- openvidu-node-client/src/VideoCodec.ts | 7 +++--- .../deployments/ce/docker-compose/.env | 6 ++--- .../deployments/enterprise/master-node/.env | 6 ++--- .../docker-compose/openvidu-server-pro/.env | 6 ++--- .../kurento/core/KurentoSessionManager.java | 16 +++++++++++++ ...itional-spring-configuration-metadata.json | 2 +- .../src/main/resources/application.properties | 2 +- .../resources/integration-test.properties | 2 +- 11 files changed, 60 insertions(+), 35 deletions(-) diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java index 22bfbb21..27d85638 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java @@ -42,7 +42,7 @@ public class SessionProperties { private RecordingProperties defaultRecordingProperties = new RecordingProperties.Builder().build(); private String customSessionId = ""; private String mediaNode; - private VideoCodec forcedVideoCodec = VideoCodec.VP8; + private VideoCodec forcedVideoCodec = VideoCodec.MEDIA_SERVER_PREFERRED; private Boolean allowTranscoding = false; /** @@ -114,15 +114,20 @@ public class SessionProperties { } /** - * Call this method to define which video codec do you want to be forcibly used - * for this session. This allows browsers/clients to use the same codec avoiding - * transcoding in the media server. If the browser/client is not compatible with - * the specified codec and {@link #allowTranscoding(Boolean)} is - * false and exception will occur. If forcedVideoCodec is set to - * NONE, no codec will be forced.
+ * Define which video codec will be forcibly used for this session. + * This forces all browsers/clients to use the same codec, which would + * avoid transcoding in the media server (Kurento only). If + * forcedVideoCodec is set to NONE, no codec will be forced. + * + * If the browser/client is not compatible with the specified codec, and + * {@link #allowTranscoding(Boolean)} is false, an + * exception will occur. + * * If defined here, this parameter has prevalence over - * OPENVIDU_STREAMS_FORCED_VIDEO_CODEC. OPENVIDU_STREAMS_FORCED_VIDEO_CODEC - * default is {@link VideoCodec#VP8} + * OPENVIDU_STREAMS_FORCED_VIDEO_CODEC. + * + * Default is {@link VideoCodec#VP8} for Kurento, and + * {@link VideoCodec#NONE} for mediasoup. */ public SessionProperties.Builder forcedVideoCodec(VideoCodec forcedVideoCodec) { this.forcedVideoCodec = forcedVideoCodec; diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/VideoCodec.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/VideoCodec.java index 9db0e73c..24c8f30c 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/VideoCodec.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/VideoCodec.java @@ -22,5 +22,5 @@ package io.openvidu.java.client; * {@link io.openvidu.java.client.SessionProperties.Builder#forcedVideoCodec(VideoCodec)} */ public enum VideoCodec { - VP8, VP9, H264, NONE -} \ No newline at end of file + MEDIA_SERVER_PREFERRED, NONE, VP8, VP9, H264 +} diff --git a/openvidu-node-client/src/SessionProperties.ts b/openvidu-node-client/src/SessionProperties.ts index fed08292..adb4b145 100644 --- a/openvidu-node-client/src/SessionProperties.ts +++ b/openvidu-node-client/src/SessionProperties.ts @@ -56,7 +56,7 @@ export interface SessionProperties { customSessionId?: string; /** - * **This feature is part of OpenVidu Pro tier** PRO + * **This feature is part of OpenVidu Pro tier** PRO * * The Media Node where to host the session. The default option if this property is not defined is the less loaded * Media Node at the moment the first user joins the session. This object defines the following properties as Media Node selector: @@ -67,13 +67,19 @@ export interface SessionProperties { } /** - * It defines which video codec do you want to be forcibly used for this session. - * This allows browsers/clients to use the same codec avoiding transcoding in the media server. - * If the browser/client is not compatible with the specified codec and [[allowTranscoding]] is false - * and exception will occur. If forcedVideoCodec is set to [[VideoCodec.NONE]], no codec will be forced. + * Define which video codec will be forcibly used for this session. + * This forces all browsers/clients to use the same codec, which would + * avoid transcoding in the media server (Kurento only). If + * forcedVideoCodec is set to NONE, no codec will be forced. * - * If defined here, this parameter has prevalence over OPENVIDU_STREAMS_FORCED_VIDEO_CODEC. - * OPENVIDU_STREAMS_FORCED_VIDEO_CODEC default is [[VideoCodec.VP8]] + * If the browser/client is not compatible with the specified codec, and + * [[allowTranscoding]] is false, an exception will occur. + * + * If defined here, this parameter has prevalence over + * OPENVIDU_STREAMS_FORCED_VIDEO_CODEC. + * + * Default is [[VideoCodec.VP8]] for Kurento, and + * [[VideoCodec.NONE]] for mediasoup. */ forcedVideoCodec?: VideoCodec; @@ -85,5 +91,4 @@ export interface SessionProperties { * OPENVIDU_STREAMS_ALLOW_TRANSCODING default is 'false' */ allowTranscoding?: boolean; - } diff --git a/openvidu-node-client/src/VideoCodec.ts b/openvidu-node-client/src/VideoCodec.ts index 464bef34..bab98d28 100644 --- a/openvidu-node-client/src/VideoCodec.ts +++ b/openvidu-node-client/src/VideoCodec.ts @@ -2,10 +2,9 @@ * See [[SessionProperties.forcedVideoCodec]] */ export enum VideoCodec { - + MEDIA_SERVER_PREFERRED = 'MEDIA_SERVER_PREFERRED', + NONE = 'NONE', VP8 = 'VP8', VP9 = 'VP9', H264 = 'H264', - NONE = 'NONE' - -} \ No newline at end of file +} diff --git a/openvidu-server/deployments/ce/docker-compose/.env b/openvidu-server/deployments/ce/docker-compose/.env index ea16f6ee..83e14381 100644 --- a/openvidu-server/deployments/ce/docker-compose/.env +++ b/openvidu-server/deployments/ce/docker-compose/.env @@ -128,9 +128,9 @@ OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300 # All sessions of OpenVidu will try to force this codec. If OPENVIDU_STREAMS_ALLOW_TRANSCODING=true # when a codec can not be forced, transcoding will be allowed -# Values: VP8, VP9, H264, NONE -# Default value is VP8 -# OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8 +# Values: MEDIA_SERVER_PREFERRED, NONE, VP8, VP9, H264 +# Default value is MEDIA_SERVER_PREFERRED +# OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=MEDIA_SERVER_PREFERRED # Allow transcoding if codec specified in OPENVIDU_STREAMS_FORCED_VIDEO_CODEC can not be applied # Values: true | false diff --git a/openvidu-server/deployments/enterprise/master-node/.env b/openvidu-server/deployments/enterprise/master-node/.env index 1db77389..6af79b5a 100644 --- a/openvidu-server/deployments/enterprise/master-node/.env +++ b/openvidu-server/deployments/enterprise/master-node/.env @@ -259,9 +259,9 @@ OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300 # All sessions of OpenVidu will try to force this codec. If OPENVIDU_STREAMS_ALLOW_TRANSCODING=true # when a codec can not be forced, transcoding will be allowed -# Values: VP8, VP9, H264, NONE -# Default value is VP8 -# OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8 +# Values: MEDIA_SERVER_PREFERRED, NONE, VP8, VP9, H264 +# Default value is MEDIA_SERVER_PREFERRED +# OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=MEDIA_SERVER_PREFERRED # Allow transcoding if codec specified in OPENVIDU_STREAMS_FORCED_VIDEO_CODEC can not be applied # Values: true | false diff --git a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env index fea37985..caba683d 100644 --- a/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env +++ b/openvidu-server/deployments/pro/docker-compose/openvidu-server-pro/.env @@ -246,9 +246,9 @@ OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300 # All sessions of OpenVidu will try to force this codec. If OPENVIDU_STREAMS_ALLOW_TRANSCODING=true # when a codec can not be forced, transcoding will be allowed -# Values: VP8, VP9, H264, NONE -# Default value is VP8 -# OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8 +# Values: MEDIA_SERVER_PREFERRED, NONE, VP8, VP9, H264 +# Default value is MEDIA_SERVER_PREFERRED +# OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=MEDIA_SERVER_PREFERRED # Allow transcoding if codec specified in OPENVIDU_STREAMS_FORCED_VIDEO_CODEC can not be applied # Values: true | false 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 fb551f02..e2fa865d 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 @@ -392,6 +392,22 @@ public class KurentoSessionManager extends SessionManager { log.warn("AllowTranscoding has no effect if the Media Server is not Kurento"); } + // Set appropriate value for the ForcedVideoCodec feature. + if (forcedVideoCodec == VideoCodec.MEDIA_SERVER_PREFERRED) { + final MediaServer mediaServer = openviduConfig.getMediaServer(); + switch (mediaServer) { + case mediasoup: + forcedVideoCodec = VideoCodec.NONE; + break; + case kurento: + default: + forcedVideoCodec = VideoCodec.VP8; + break; + } + + log.info("Media Server: {}, selected ForcedVideoCodec value: {}", mediaServer, forcedVideoCodec); + } + // Modify sdp if forced codec is defined if (forcedVideoCodec != VideoCodec.NONE && !participant.isIpcam()) { kurentoOptions.sdpOffer = sdpMunging.forceCodec(kurentoOptions.sdpOffer, participant, true, false, diff --git a/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json index e34d6a5e..fa97d8c5 100644 --- a/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -157,7 +157,7 @@ "name": "OPENVIDU_STREAMS_FORCED_VIDEO_CODEC", "type": "java.lang.String", "description": "Defines which video codec is being forced to be used in the browser/client", - "defaultValue": "VP8" + "defaultValue": "MEDIA_SERVER_PREFERRED" }, { "name": "OPENVIDU_STREAMS_ALLOW_TRANSCODING", diff --git a/openvidu-server/src/main/resources/application.properties b/openvidu-server/src/main/resources/application.properties index 6311b88a..c4c0c5cf 100644 --- a/openvidu-server/src/main/resources/application.properties +++ b/openvidu-server/src/main/resources/application.properties @@ -43,7 +43,7 @@ OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH=300 OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000 OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300 OPENVIDU_STREAMS_VIDEO_SIMULCAST=true -OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8 +OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=MEDIA_SERVER_PREFERRED OPENVIDU_STREAMS_ALLOW_TRANSCODING=false OPENVIDU_SESSIONS_GARBAGE_INTERVAL=900 diff --git a/openvidu-server/src/test/resources/integration-test.properties b/openvidu-server/src/test/resources/integration-test.properties index 3c7d9ba3..1a1fd70d 100644 --- a/openvidu-server/src/test/resources/integration-test.properties +++ b/openvidu-server/src/test/resources/integration-test.properties @@ -36,7 +36,7 @@ OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH=1000 OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH=300 OPENVIDU_STREAMS_VIDEO_MAX_SEND_BANDWIDTH=1000 OPENVIDU_STREAMS_VIDEO_MIN_SEND_BANDWIDTH=300 -OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=VP8 +OPENVIDU_STREAMS_FORCED_VIDEO_CODEC=MEDIA_SERVER_PREFERRED OPENVIDU_STREAMS_ALLOW_TRANSCODING=false OPENVIDU_SESSIONS_GARBAGE_INTERVAL=900 From 0262c85ac0bc33a2ef94d511c88349d6d2a05088 Mon Sep 17 00:00:00 2001 From: Juan Navarro Date: Fri, 26 Nov 2021 12:27:24 +0100 Subject: [PATCH 2/5] openvidu-server: remove duplicated serialization of SessionProperties Let SessionProperties serialize itself, instead of doing it externally on the Session classes. --- .../java/io/openvidu/java/client/Session.java | 19 +++++++---------- .../java/client/SessionProperties.java | 4 ++-- .../java/io/openvidu/server/core/Session.java | 21 ++++++++++--------- 3 files changed, 21 insertions(+), 23 deletions(-) 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 1fb2d396..6bd50536 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 @@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory; import com.google.gson.Gson; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonSyntaxException; @@ -756,19 +757,15 @@ public class Session { JsonObject json = new JsonObject(); json.addProperty("sessionId", this.sessionId); json.addProperty("createdAt", this.createdAt); - json.addProperty("customSessionId", this.properties.customSessionId()); json.addProperty("recording", this.recording); - json.addProperty("mediaMode", this.properties.mediaMode().name()); - json.addProperty("recordingMode", this.properties.recordingMode().name()); - if (this.properties.defaultRecordingProperties() != null) { - json.add("defaultRecordingProperties", this.properties.defaultRecordingProperties().toJson()); - } - if (this.properties.forcedVideoCodec() != null) { - json.addProperty("forcedVideoCodec", this.properties.forcedVideoCodec().name()); - } - if (this.properties.isTranscodingAllowed() != null) { - json.addProperty("allowTranscoding", this.properties.isTranscodingAllowed()); + + // Add keys from SessionProperties + JsonObject sessionPropertiesJson = this.properties.toJson(); + for (Map.Entry entry : sessionPropertiesJson.entrySet()) { + json.add(entry.getKey(), entry.getValue().deepCopy()); } + + // Add "connections" object JsonObject connections = new JsonObject(); connections.addProperty("numberOfElements", this.getConnections().size()); JsonArray jsonArrayConnections = new JsonArray(); diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java index 27d85638..10392c77 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java @@ -236,13 +236,13 @@ public class SessionProperties { return this.allowTranscoding; } - protected JsonObject toJson() { + public JsonObject toJson() { JsonObject json = new JsonObject(); json.addProperty("mediaMode", mediaMode().name()); json.addProperty("recordingMode", recordingMode().name()); json.addProperty("customSessionId", customSessionId()); json.add("defaultRecordingProperties", defaultRecordingProperties.toJson()); - if (mediaNode() != null) { + if (mediaNode() != null && !mediaNode().isEmpty()) { JsonObject mediaNodeJson = new JsonObject(); mediaNodeJson.addProperty("id", mediaNode()); json.add("mediaNode", mediaNodeJson); diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Session.java b/openvidu-server/src/main/java/io/openvidu/server/core/Session.java index f5548b77..d19147ec 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/Session.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/Session.java @@ -19,6 +19,7 @@ package io.openvidu.server.core; import java.util.HashSet; import java.util.Iterator; +import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -32,6 +33,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.openvidu.client.OpenViduException; @@ -234,22 +236,21 @@ public class Session implements SessionInterface { json.addProperty("object", "session"); json.addProperty("sessionId", this.sessionId); // TODO: deprecated. Better use only "id" json.addProperty("createdAt", this.startTime); - json.addProperty("mediaMode", this.sessionProperties.mediaMode().name()); - json.addProperty("recordingMode", this.sessionProperties.recordingMode().name()); - json.add("defaultRecordingProperties", this.sessionProperties.defaultRecordingProperties().toJson()); - if (this.sessionProperties.customSessionId() != null) { - json.addProperty("customSessionId", this.sessionProperties.customSessionId()); + json.addProperty("recording", this.recordingManager.sessionIsBeingRecorded(this.sessionId)); + + // Add keys from SessionProperties + JsonObject sessionPropertiesJson = sessionProperties.toJson(); + for (Map.Entry entry : sessionPropertiesJson.entrySet()) { + json.add(entry.getKey(), entry.getValue().deepCopy()); } + + // Add "connections" object JsonObject connections = new JsonObject(); JsonArray participants = this.getSnapshotOfConnectionsAsJsonArray(withPendingConnections, withWebrtcStats); connections.addProperty("numberOfElements", participants.size()); connections.add("content", participants); json.add("connections", connections); - json.addProperty("recording", this.recordingManager.sessionIsBeingRecorded(this.sessionId)); - if (this.sessionProperties.forcedVideoCodec() != null) { - json.addProperty("forcedVideoCodec", this.sessionProperties.forcedVideoCodec().name()); - } - json.addProperty("allowTranscoding", this.sessionProperties.isTranscodingAllowed()); + return json; } From 0cb9180ec5d9103c9cbf668b336d07fd711dad42 Mon Sep 17 00:00:00 2001 From: Juan Navarro Date: Fri, 26 Nov 2021 15:25:25 +0100 Subject: [PATCH 3/5] openvidu-server+clients: add new forcedVideoCodecResolved session property Fill a new SessionProperties member "forcedVideoCodecResolved" to contain the resolved value of "forcedVideoCodec", once the new MEDIA_SERVER_PREFERRED has been taken into account and translated into the appropriate option for each media server. The logic to decide how to translate MEDIA_SERVER_PREFERRED into a concrete forcedVideoCodec value is placed once in the REST entry point of session creation (SessionRestController.java). Afterwards, SessionProperties is just used as a simple storage for all session features, and serialized / passed around between server and client. --- .../java/io/openvidu/java/client/Session.java | 14 +++-- .../java/client/SessionProperties.java | 60 ++++++++++++++----- openvidu-node-client/src/Session.ts | 14 ++++- openvidu-node-client/src/SessionProperties.ts | 14 ++++- .../kurento/core/KurentoSessionManager.java | 24 ++------ .../server/rest/SessionRestController.java | 37 ++++++++++-- .../io/openvidu/server/utils/SDPMunging.java | 2 +- 7 files changed, 113 insertions(+), 52 deletions(-) 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 6bd50536..451d3862 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 @@ -662,16 +662,17 @@ public class Session { this.sessionId = responseJson.get("id").getAsString(); this.createdAt = responseJson.get("createdAt").getAsLong(); - // forcedVideoCodec and allowTranscoding values are configured in OpenVidu - // Server via configuration or session + // Values that get filled by OpenVidu Server from its global or per-session configuration VideoCodec forcedVideoCodec = VideoCodec.valueOf(responseJson.get("forcedVideoCodec").getAsString()); + VideoCodec forcedVideoCodecResolved = VideoCodec + .valueOf(responseJson.get("forcedVideoCodecResolved").getAsString()); Boolean allowTranscoding = responseJson.get("allowTranscoding").getAsBoolean(); - SessionProperties responseProperties = new SessionProperties.Builder() - .customSessionId(properties.customSessionId()).mediaMode(properties.mediaMode()) + SessionProperties responseProperties = new SessionProperties.Builder().mediaMode(properties.mediaMode()) .recordingMode(properties.recordingMode()) .defaultRecordingProperties(properties.defaultRecordingProperties()) - .mediaNode(properties.mediaNode()).forcedVideoCodec(forcedVideoCodec) + .customSessionId(properties.customSessionId()).mediaNode(properties.mediaNode()) + .forcedVideoCodec(forcedVideoCodec).forcedVideoCodecResolved(forcedVideoCodecResolved) .allowTranscoding(allowTranscoding).build(); this.properties = responseProperties; @@ -719,6 +720,9 @@ public class Session { if (json.has("forcedVideoCodec")) { builder.forcedVideoCodec(VideoCodec.valueOf(json.get("forcedVideoCodec").getAsString())); } + if (json.has("forcedVideoCodecResolved")) { + builder.forcedVideoCodecResolved(VideoCodec.valueOf(json.get("forcedVideoCodecResolved").getAsString())); + } if (json.has("allowTranscoding")) { builder.allowTranscoding(json.get("allowTranscoding").getAsBoolean()); } diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java index 10392c77..4ca54a22 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/SessionProperties.java @@ -30,6 +30,7 @@ public class SessionProperties { private String customSessionId; private String mediaNode; private VideoCodec forcedVideoCodec; + private VideoCodec forcedVideoCodecResolved; private Boolean allowTranscoding; /** @@ -43,6 +44,7 @@ public class SessionProperties { private String customSessionId = ""; private String mediaNode; private VideoCodec forcedVideoCodec = VideoCodec.MEDIA_SERVER_PREFERRED; + private VideoCodec forcedVideoCodecResolved = VideoCodec.NONE; private Boolean allowTranscoding = false; /** @@ -51,7 +53,8 @@ public class SessionProperties { */ public SessionProperties build() { return new SessionProperties(this.mediaMode, this.recordingMode, this.defaultRecordingProperties, - this.customSessionId, this.mediaNode, this.forcedVideoCodec, this.allowTranscoding); + this.customSessionId, this.mediaNode, this.forcedVideoCodec, this.forcedVideoCodecResolved, + this.allowTranscoding); } /** @@ -126,14 +129,25 @@ public class SessionProperties { * If defined here, this parameter has prevalence over * OPENVIDU_STREAMS_FORCED_VIDEO_CODEC. * - * Default is {@link VideoCodec#VP8} for Kurento, and - * {@link VideoCodec#NONE} for mediasoup. + * Default is {@link VideoCodec#MEDIA_SERVER_PREFERRED}. */ public SessionProperties.Builder forcedVideoCodec(VideoCodec forcedVideoCodec) { this.forcedVideoCodec = forcedVideoCodec; return this; } + /** + * Actual video codec that will be forcibly used for this session. + * This is the same as forcedVideoCodec, except when its + * value is {@link VideoCodec#MEDIA_SERVER_PREFERRED}: in that case, + * OpenVidu Server will fill this property with a resolved value, + * depending on what is the configured media server. + */ + public SessionProperties.Builder forcedVideoCodecResolved(VideoCodec forcedVideoCodec) { + this.forcedVideoCodecResolved = forcedVideoCodec; + return this; + } + /** * Call this method to define if you want to allow transcoding in the media * server or not when {@link #forcedVideoCodec(VideoCodec)} is not compatible @@ -159,13 +173,14 @@ public class SessionProperties { private SessionProperties(MediaMode mediaMode, RecordingMode recordingMode, RecordingProperties defaultRecordingProperties, String customSessionId, String mediaNode, - VideoCodec forcedVideoCodec, Boolean allowTranscoding) { + VideoCodec forcedVideoCodec, VideoCodec forcedVideoCodecResolved, Boolean allowTranscoding) { this.mediaMode = mediaMode; this.recordingMode = recordingMode; this.defaultRecordingProperties = defaultRecordingProperties; this.customSessionId = customSessionId; this.mediaNode = mediaNode; this.forcedVideoCodec = forcedVideoCodec; + this.forcedVideoCodecResolved = forcedVideoCodecResolved; this.allowTranscoding = allowTranscoding; } @@ -222,12 +237,24 @@ public class SessionProperties { } /** - * Defines which video codec is being forced to be used in the browser/client + * Defines which video codec is being forced to be used in the browser/client. + * This is the raw value that was configured. It might get resolved into a + * different one for actual usage in the server. */ public VideoCodec forcedVideoCodec() { return this.forcedVideoCodec; } + /** + * Defines which video codec is being forced to be used in the browser/client. + * This is the resolved value, for actual usage in the server. + * + * @hidden + */ + public VideoCodec forcedVideoCodecResolved() { + return this.forcedVideoCodecResolved; + } + /** * Defines if transcoding is allowed or not when {@link #forcedVideoCodec} is * not a compatible codec with the browser/client. @@ -238,20 +265,23 @@ public class SessionProperties { public JsonObject toJson() { JsonObject json = new JsonObject(); - json.addProperty("mediaMode", mediaMode().name()); - json.addProperty("recordingMode", recordingMode().name()); - json.addProperty("customSessionId", customSessionId()); - json.add("defaultRecordingProperties", defaultRecordingProperties.toJson()); - if (mediaNode() != null && !mediaNode().isEmpty()) { + json.addProperty("mediaMode", this.mediaMode.name()); + json.addProperty("recordingMode", this.recordingMode.name()); + json.add("defaultRecordingProperties", this.defaultRecordingProperties.toJson()); + json.addProperty("customSessionId", this.customSessionId); + if (this.mediaNode != null && !this.mediaNode.isEmpty()) { JsonObject mediaNodeJson = new JsonObject(); - mediaNodeJson.addProperty("id", mediaNode()); + mediaNodeJson.addProperty("id", this.mediaNode); json.add("mediaNode", mediaNodeJson); } - if (forcedVideoCodec() != null) { - json.addProperty("forcedVideoCodec", forcedVideoCodec().name()); + if (this.forcedVideoCodec != null) { + json.addProperty("forcedVideoCodec", this.forcedVideoCodec.name()); } - if (isTranscodingAllowed() != null) { - json.addProperty("allowTranscoding", isTranscodingAllowed()); + if (this.forcedVideoCodecResolved != null) { + json.addProperty("forcedVideoCodecResolved", this.forcedVideoCodecResolved.name()); + } + if (this.allowTranscoding != null) { + json.addProperty("allowTranscoding", this.allowTranscoding); } return json; } diff --git a/openvidu-node-client/src/Session.ts b/openvidu-node-client/src/Session.ts index 718709b5..9ca089a2 100644 --- a/openvidu-node-client/src/Session.ts +++ b/openvidu-node-client/src/Session.ts @@ -490,6 +490,7 @@ export class Session { this.properties.defaultRecordingProperties = res.data.defaultRecordingProperties; this.properties.mediaNode = res.data.mediaNode; this.properties.forcedVideoCodec = res.data.forcedVideoCodec; + this.properties.forcedVideoCodecResolved = res.data.forcedVideoCodecResolved; this.properties.allowTranscoding = res.data.allowTranscoding; this.sanitizeDefaultSessionProperties(this.properties); resolve(this.sessionId); @@ -533,6 +534,7 @@ export class Session { recordingMode: json.recordingMode, defaultRecordingProperties: json.defaultRecordingProperties, forcedVideoCodec: json.forcedVideoCodec, + forcedVideoCodecResolved: json.forcedVideoCodecResolved, allowTranscoding: json.allowTranscoding }; this.sanitizeDefaultSessionProperties(this.properties); @@ -548,6 +550,9 @@ export class Session { if (json.forcedVideoCodec == null) { delete this.properties.forcedVideoCodec; } + if (json.forcedVideoCodecResolved == null) { + delete this.properties.forcedVideoCodecResolved; + } if (json.allowTranscoding == null) { delete this.properties.allowTranscoding; } @@ -655,9 +660,12 @@ export class Session { props.mediaMode = (props.mediaMode != null) ? props.mediaMode : MediaMode.ROUTED; props.recordingMode = (props.recordingMode != null) ? props.recordingMode : RecordingMode.MANUAL; props.customSessionId = (props.customSessionId != null) ? props.customSessionId : ''; - props.mediaNode = (props.mediaNode != null) ? props.mediaNode : undefined; - props.forcedVideoCodec = props.forcedVideoCodec; - props.allowTranscoding = props.allowTranscoding; + + // Remove null values: either set, or undefined + props.mediaNode = props.mediaNode ?? undefined; + props.forcedVideoCodec = props.forcedVideoCodec ?? undefined; + props.forcedVideoCodecResolved = props.forcedVideoCodecResolved ?? undefined; + props.allowTranscoding = props.allowTranscoding ?? undefined; if (!props.defaultRecordingProperties) { props.defaultRecordingProperties = {}; diff --git a/openvidu-node-client/src/SessionProperties.ts b/openvidu-node-client/src/SessionProperties.ts index adb4b145..9b23203a 100644 --- a/openvidu-node-client/src/SessionProperties.ts +++ b/openvidu-node-client/src/SessionProperties.ts @@ -78,11 +78,21 @@ export interface SessionProperties { * If defined here, this parameter has prevalence over * OPENVIDU_STREAMS_FORCED_VIDEO_CODEC. * - * Default is [[VideoCodec.VP8]] for Kurento, and - * [[VideoCodec.NONE]] for mediasoup. + * Default is [[VideoCodec.MEDIA_SERVER_PREFERRED]]. */ forcedVideoCodec?: VideoCodec; + /** + * Actual video codec that will be forcibly used for this session. + * This is the same as forcedVideoCodec, except when its value + * is [[VideoCodec.MEDIA_SERVER_PREFERRED]]: in that case, OpenVidu Server + * will fill this property with a resolved value, depending on what is the + * configured media server. + * + * @hidden + */ + forcedVideoCodecResolved?: VideoCodec; + /** * It defines if you want to allow transcoding in the media server or not * when [[forcedVideoCodec]] is not compatible with the browser/client. 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 e2fa865d..da0bd0fa 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 @@ -380,7 +380,7 @@ public class KurentoSessionManager extends SessionManager { KurentoParticipant kParticipant = (KurentoParticipant) participant; KurentoSession kSession = kParticipant.getSession(); boolean isTranscodingAllowed = kSession.getSessionProperties().isTranscodingAllowed(); - VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodec(); + VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodecResolved(); final String streamId = kParticipant.generateStreamId(kurentoOptions); @@ -392,22 +392,6 @@ public class KurentoSessionManager extends SessionManager { log.warn("AllowTranscoding has no effect if the Media Server is not Kurento"); } - // Set appropriate value for the ForcedVideoCodec feature. - if (forcedVideoCodec == VideoCodec.MEDIA_SERVER_PREFERRED) { - final MediaServer mediaServer = openviduConfig.getMediaServer(); - switch (mediaServer) { - case mediasoup: - forcedVideoCodec = VideoCodec.NONE; - break; - case kurento: - default: - forcedVideoCodec = VideoCodec.VP8; - break; - } - - log.info("Media Server: {}, selected ForcedVideoCodec value: {}", mediaServer, forcedVideoCodec); - } - // Modify sdp if forced codec is defined if (forcedVideoCodec != VideoCodec.NONE && !participant.isIpcam()) { kurentoOptions.sdpOffer = sdpMunging.forceCodec(kurentoOptions.sdpOffer, participant, true, false, @@ -613,7 +597,7 @@ public class KurentoSessionManager extends SessionManager { WebrtcDebugEventType.sdpOffer, sdpOffer)); boolean isTranscodingAllowed = session.getSessionProperties().isTranscodingAllowed(); - VideoCodec forcedVideoCodec = session.getSessionProperties().forcedVideoCodec(); + VideoCodec forcedVideoCodec = session.getSessionProperties().forcedVideoCodecResolved(); // Modify server's SDPOffer if forced codec is defined if (forcedVideoCodec != VideoCodec.NONE && !participant.isIpcam()) { @@ -679,7 +663,7 @@ public class KurentoSessionManager extends SessionManager { // Client initiated negotiation. sdpString is the SDP Offer of the client boolean isTranscodingAllowed = session.getSessionProperties().isTranscodingAllowed(); - VideoCodec forcedVideoCodec = session.getSessionProperties().forcedVideoCodec(); + VideoCodec forcedVideoCodec = session.getSessionProperties().forcedVideoCodecResolved(); String sdpOffer = sdpString; // Modify sdp if forced codec is defined @@ -1236,7 +1220,7 @@ public class KurentoSessionManager extends SessionManager { private String mungeSdpOffer(Session kSession, Participant participant, String sdpOffer, boolean isPublisher) { boolean isTranscodingAllowed = kSession.getSessionProperties().isTranscodingAllowed(); - VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodec(); + VideoCodec forcedVideoCodec = kSession.getSessionProperties().forcedVideoCodecResolved(); // Modify sdp if forced codec is defined if (forcedVideoCodec != VideoCodec.NONE && !participant.isIpcam()) { return sdpMunging.forceCodec(sdpOffer, participant, isPublisher, true, isTranscodingAllowed, 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 6e9e9889..cc7d40e1 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 @@ -726,20 +726,31 @@ public class SessionRestController { if (params != null) { + // Obtain primitive values from the params map String mediaModeString; String recordingModeString; - String forcedVideoCodec; + String forcedVideoCodecStr; Boolean allowTranscoding; try { mediaModeString = (String) params.get("mediaMode"); recordingModeString = (String) params.get("recordingMode"); customSessionId = (String) params.get("customSessionId"); - forcedVideoCodec = (String) params.get("forcedVideoCodec"); + forcedVideoCodecStr = (String) params.get("forcedVideoCodec"); allowTranscoding = (Boolean) params.get("allowTranscoding"); } catch (ClassCastException e) { throw new Exception("Type error in some parameter: " + e.getMessage()); } + // Parse obtained values into actual types + VideoCodec forcedVideoCodec = null; + try { + forcedVideoCodec = VideoCodec.valueOf(forcedVideoCodecStr); + } catch (NullPointerException e) { + // Not an error: "forcedVideoCodec" was not provided in params. + } catch (IllegalArgumentException e) { + throw new Exception("Invalid value for parameter 'forcedVideoCodec': " + e.getMessage()); + } + try { // Safe parameter retrieval. Default values if not defined if (recordingModeString != null) { @@ -761,11 +772,25 @@ public class SessionRestController { } builder = builder.customSessionId(customSessionId); } - if (forcedVideoCodec != null) { - builder = builder.forcedVideoCodec(VideoCodec.valueOf(forcedVideoCodec)); - } else { - builder = builder.forcedVideoCodec(openviduConfig.getOpenviduForcedCodec()); + + if (forcedVideoCodec == null) { + forcedVideoCodec = openviduConfig.getOpenviduForcedCodec(); } + builder = builder.forcedVideoCodec(forcedVideoCodec); + if (forcedVideoCodec == VideoCodec.MEDIA_SERVER_PREFERRED) { + switch (openviduConfig.getMediaServer()) { + case mediasoup: + builder = builder.forcedVideoCodecResolved(VideoCodec.NONE); + break; + case kurento: + default: + builder = builder.forcedVideoCodecResolved(VideoCodec.VP8); + break; + } + } else { + builder = builder.forcedVideoCodecResolved(forcedVideoCodec); + } + if (allowTranscoding != null) { builder = builder.allowTranscoding(allowTranscoding); } else { diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/SDPMunging.java b/openvidu-server/src/main/java/io/openvidu/server/utils/SDPMunging.java index c30febce..b4a4803f 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/utils/SDPMunging.java +++ b/openvidu-server/src/main/java/io/openvidu/server/utils/SDPMunging.java @@ -187,7 +187,7 @@ public class SDPMunging { return mungedSdpOffer; } else { throw new OpenViduException(Code.FORCED_CODEC_NOT_FOUND_IN_SDPOFFER, - "Codec not supported by Media Server"); + "Codec not supported by Media Server: " + forcedVideoCodec); } } catch (OpenViduException e) { From 727a872d9f5a6ec4f589b2ea0f7231e8ac6021dd Mon Sep 17 00:00:00 2001 From: Juan Navarro Date: Mon, 27 Dec 2021 11:27:47 +0100 Subject: [PATCH 4/5] openvidu-test-e2e: Add new key 'forcedVideoCodecResolved' to API tests --- .../src/main/java/io/openvidu/test/e2e/OpenViduTestE2e.java | 2 +- .../test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openvidu-test-e2e/src/main/java/io/openvidu/test/e2e/OpenViduTestE2e.java b/openvidu-test-e2e/src/main/java/io/openvidu/test/e2e/OpenViduTestE2e.java index 01a85386..0328c138 100644 --- a/openvidu-test-e2e/src/main/java/io/openvidu/test/e2e/OpenViduTestE2e.java +++ b/openvidu-test-e2e/src/main/java/io/openvidu/test/e2e/OpenViduTestE2e.java @@ -67,7 +67,7 @@ public class OpenViduTestE2e { final protected static String MEDIASOUP_IMAGE = "openvidu/mediasoup-controller"; protected static String MEDIA_SERVER_IMAGE = KURENTO_IMAGE + ":6.16.0"; - final protected String DEFAULT_JSON_SESSION = "{'id':'STR','object':'session','sessionId':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultRecordingProperties':{'hasVideo':true,'frameRate':25,'hasAudio':true,'shmSize':536870912,'name':'','outputMode':'COMPOSED','resolution':'1280x720','recordingLayout':'BEST_FIT'},'customSessionId':'STR','connections':{'numberOfElements':0,'content':[]},'recording':false,'forcedVideoCodec':'STR','allowTranscoding':false}"; + final protected String DEFAULT_JSON_SESSION = "{'id':'STR','object':'session','sessionId':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultRecordingProperties':{'hasVideo':true,'frameRate':25,'hasAudio':true,'shmSize':536870912,'name':'','outputMode':'COMPOSED','resolution':'1280x720','recordingLayout':'BEST_FIT'},'customSessionId':'STR','connections':{'numberOfElements':0,'content':[]},'recording':false,'forcedVideoCodec':'STR','forcedVideoCodecResolved':'STR','allowTranscoding':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,'ip':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','ip':'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','ip':'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':[]}"; 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 4fe9c3d2..85bcc905 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 @@ -3213,7 +3213,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { + "'videoDimensions':'STR','filter':{}}}],'subscribers':[{'createdAt':0,'streamId':'STR','publisher':'STR'}]},{'connectionId':'STR','createdAt':0,'location':'STR','ip':'STR'," + "'platform':'STR','token':'STR','role':'STR','serverData':'STR','clientData':'STR','publishers':[{'createdAt':0,'streamId':'STR','mediaOptions':{'hasAudio':false," + "'audioActive':false,'hasVideo':false,'videoActive':false,'typeOfVideo':'STR','frameRate':0,'videoDimensions':'STR','filter':{}}}],'subscribers':[{'createdAt':0,'streamId':'STR','publisher':'STR'}]}]}," - + "'recording':false,'forcedVideoCodec':'STR','allowTranscoding':false}"); + + "'recording':false,'forcedVideoCodec':'STR','forcedVideoCodecResolved':'STR','allowTranscoding':false}"); String streamId = res.get("connections").getAsJsonObject().get("content").getAsJsonArray().get(0) .getAsJsonObject().get("publishers").getAsJsonArray().get(0).getAsJsonObject().get("streamId") .getAsString(); From bfa2ad8c6521dd4638cf4fc11874d1a232f519c4 Mon Sep 17 00:00:00 2001 From: Juan Navarro Date: Mon, 27 Dec 2021 17:11:28 +0100 Subject: [PATCH 5/5] openvidu-test-e2e: Support MEDIA_SERVER_PREFERRED and NONE as test codecs Because this test is always run only for Kurento and Chrome, we know what to select here in each case. --- .../openvidu/test/e2e/OpenViduTestAppE2eTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 85bcc905..d50372f3 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 @@ -4673,6 +4673,20 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { // Check browser codecs VideoCodec codecToCheck = (codec != null) ? codec : defaultForcedVideoCodec; + + // Validate the codec to check for special cases: + // * MEDIA_SERVER_PREFERRED means to use the codec that is preferred by the media server. + // * NONE means to use the codec that is preferred by the web browser. + // Because this test is always run only for Kurento and Chrome, we know what to select here. + if (codecToCheck == VideoCodec.MEDIA_SERVER_PREFERRED) { + // Kurento preferred video codec is VP8. + codecToCheck = VideoCodec.VP8; + } + else if (codecToCheck == VideoCodec.NONE) { + // Chrome preferred video codec is VP8. + codecToCheck = VideoCodec.VP8; + } + List statsButtons = user.getDriver().findElements(By.className("stats-button")); for (WebElement statButton : statsButtons) { statButton.click();