diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java index 8b3680dc..d709b442 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/Recording.java @@ -17,6 +17,9 @@ package io.openvidu.java.client; +import java.util.Map; + +import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -120,7 +123,9 @@ public class Recording { } this.status = Recording.Status.valueOf(json.get("status").getAsString()); - this.recordingProperties = RecordingProperties.fromJson(json); + RecordingProperties.Builder builder = RecordingProperties + .fromJson(new Gson().fromJson(json.toString(), Map.class), null); + this.recordingProperties = builder.build(); } /** @@ -233,9 +238,9 @@ public class Recording { * URL of the recording. You can access the file from there. It is * null until recording reaches "ready" or "failed" status. If * - * OpenVidu Server configuration - * property OPENVIDU_RECORDING_PUBLIC_ACCESS is false, - * this path will be secured with OpenVidu credentials + * OpenVidu Server configuration property + * OPENVIDU_RECORDING_PUBLIC_ACCESS is false, this path will be + * secured with OpenVidu credentials */ public String getUrl() { return url; diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java index 47598431..f99028c6 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/RecordingProperties.java @@ -17,9 +17,12 @@ package io.openvidu.java.client; +import java.util.Map; + import com.google.gson.JsonObject; import io.openvidu.java.client.Recording.OutputMode; +import io.openvidu.java.client.utils.FormatChecker; /** * See @@ -28,6 +31,7 @@ import io.openvidu.java.client.Recording.OutputMode; public class RecordingProperties { public static class DefaultValues { + public static final String name = ""; public static final Boolean hasAudio = true; public static final Boolean hasVideo = true; public static final Recording.OutputMode outputMode = Recording.OutputMode.COMPOSED; @@ -35,14 +39,15 @@ public class RecordingProperties { public static final String resolution = "1280x720"; public static final Integer frameRate = 25; public static final Long shmSize = 536870912L; + public static final String customLayout = ""; public static final Boolean ignoreFailedStreams = false; } // For all - private String name = ""; - private Boolean hasAudio = true; - private Boolean hasVideo = true; - private Recording.OutputMode outputMode = Recording.OutputMode.COMPOSED; + private String name = DefaultValues.name; + private Boolean hasAudio = DefaultValues.hasAudio; + private Boolean hasVideo = DefaultValues.hasVideo; + private Recording.OutputMode outputMode = DefaultValues.outputMode; // For COMPOSED/COMPOSED_QUICK_START + hasVideo private RecordingLayout recordingLayout; private String resolution; @@ -60,15 +65,15 @@ public class RecordingProperties { */ public static class Builder { - private String name = ""; + private String name = DefaultValues.name; private Boolean hasAudio = DefaultValues.hasAudio; private Boolean hasVideo = DefaultValues.hasVideo; private Recording.OutputMode outputMode = DefaultValues.outputMode; - private RecordingLayout recordingLayout; - private String resolution; - private Integer frameRate; - private Long shmSize; - private String customLayout; + private RecordingLayout recordingLayout = DefaultValues.recordingLayout; + private String resolution = DefaultValues.resolution; + private Integer frameRate = DefaultValues.frameRate; + private Long shmSize = DefaultValues.shmSize; + private String customLayout = DefaultValues.customLayout; private Boolean ignoreFailedStreams = DefaultValues.ignoreFailedStreams; private String mediaNode; @@ -202,9 +207,9 @@ public class RecordingProperties { * to {@link io.openvidu.java.client.RecordingLayout#CUSTOM} you can call this * method to set the relative path to the specific custom layout you want to * use.
- * See - * Custom recording layouts - * to learn more + * See + * Custom recording layouts to learn more */ public RecordingProperties.Builder customLayout(String path) { this.customLayout = path; @@ -234,13 +239,13 @@ public class RecordingProperties { } /** - * PRO Call this method to force the recording to be hosted in - * the Media Node with identifier mediaNodeId. This property only - * applies to {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} or + * PRO Call + * this method to force the recording to be hosted in the Media Node with + * identifier mediaNodeId. This property only applies to + * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} or * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED_QUICK_START} * recordings with {@link RecordingProperties#hasVideo()} to true and is ignored * for {@link io.openvidu.java.client.Recording.OutputMode#INDIVIDUAL} @@ -257,7 +262,7 @@ public class RecordingProperties { protected RecordingProperties(String name, Boolean hasAudio, Boolean hasVideo, Recording.OutputMode outputMode, RecordingLayout layout, String resolution, Integer frameRate, Long shmSize, String customLayout, Boolean ignoreFailedStreams, String mediaNode) { - this.name = name != null ? name : ""; + this.name = name != null ? name : DefaultValues.name; this.hasAudio = hasAudio != null ? hasAudio : DefaultValues.hasAudio; this.hasVideo = hasVideo != null ? hasVideo : DefaultValues.hasVideo; this.outputMode = outputMode != null ? outputMode : DefaultValues.outputMode; @@ -390,9 +395,9 @@ public class RecordingProperties { * If {@link io.openvidu.java.client.RecordingProperties#recordingLayout()} is * set to {@link io.openvidu.java.client.RecordingLayout#CUSTOM}, this property * defines the relative path to the specific custom layout you want to use.
- * See - * Custom recording layouts - * to learn more + * See + * Custom recording layouts to learn more */ public String customLayout() { return this.customLayout; @@ -423,15 +428,15 @@ public class RecordingProperties { } /** - * PRO The Media Node where to host the recording. The default - * option if this property is not defined is the same Media Node hosting the - * Session to record. This property only applies to COMPOSED or - * COMPOSED_QUICK_START recordings with {@link RecordingProperties#hasVideo()} - * to true and is ignored for INDIVIDUAL recordings and audio-only recordings + * PRO The + * Media Node where to host the recording. The default option if this property + * is not defined is the same Media Node hosting the Session to record. This + * property only applies to COMPOSED or COMPOSED_QUICK_START recordings with + * {@link RecordingProperties#hasVideo()} to true and is ignored for INDIVIDUAL + * recordings and audio-only recordings */ public String mediaNode() { return this.mediaNode; @@ -442,7 +447,7 @@ public class RecordingProperties { */ public JsonObject toJson() { JsonObject json = new JsonObject(); - json.addProperty("name", name); + json.addProperty("name", name != null ? name : DefaultValues.name); json.addProperty("hasAudio", hasAudio != null ? hasAudio : DefaultValues.hasAudio); json.addProperty("hasVideo", hasVideo != null ? hasVideo : DefaultValues.hasVideo); json.addProperty("outputMode", outputMode != null ? outputMode.name() : DefaultValues.outputMode.name()); @@ -454,7 +459,7 @@ public class RecordingProperties { json.addProperty("frameRate", frameRate != null ? frameRate : DefaultValues.frameRate); json.addProperty("shmSize", shmSize != null ? shmSize : DefaultValues.shmSize); if (RecordingLayout.CUSTOM.equals(recordingLayout)) { - json.addProperty("customLayout", customLayout != null ? customLayout : ""); + json.addProperty("customLayout", customLayout != null ? customLayout : DefaultValues.customLayout); } } if (OutputMode.INDIVIDUAL.equals(outputMode)) { @@ -467,69 +472,226 @@ public class RecordingProperties { return json; } + public static RecordingProperties.Builder fromJson(Map params, RecordingProperties defaultProps) + throws RuntimeException { + + // Final properties being used + String nameFinal = null; + Boolean hasAudioFinal = null; + Boolean hasVideoFinal = null; + OutputMode outputModeFinal = null; + RecordingLayout recordingLayoutFinal = null; + String resolutionFinal = null; + Integer frameRateFinal = null; + Long shmSizeFinal = null; + String customLayoutFinal = null; + Boolean ignoreFailedStreamsFinal = null; + + // Defaults properties + String nameDefault = null; + Boolean hasAudioDefault = null; + Boolean hasVideoDefault = null; + OutputMode outputModeDefault = null; + RecordingLayout recordingLayoutDefault = null; + String resolutionDefault = null; + Integer frameRateDefault = null; + Long shmSizeDefault = null; + String customLayoutDefault = null; + Boolean ignoreFailedStreamsDefault = null; + String mediaNodeDefault = null; + + if (defaultProps != null) { + nameDefault = defaultProps.name(); + hasAudioDefault = defaultProps.hasAudio(); + hasVideoDefault = defaultProps.hasVideo(); + outputModeDefault = defaultProps.outputMode(); + recordingLayoutDefault = defaultProps.recordingLayout(); + resolutionDefault = defaultProps.resolution(); + frameRateDefault = defaultProps.frameRate(); + shmSizeDefault = defaultProps.shmSize(); + customLayoutDefault = defaultProps.customLayout(); + ignoreFailedStreamsDefault = defaultProps.ignoreFailedStreams(); + mediaNodeDefault = defaultProps.mediaNode(); + } + + nameDefault = nameDefault != null ? nameDefault : DefaultValues.name; + hasAudioDefault = hasAudioDefault != null ? hasAudioDefault : DefaultValues.hasAudio; + hasVideoDefault = hasVideoDefault != null ? hasVideoDefault : DefaultValues.hasVideo; + outputModeDefault = outputModeDefault != null ? outputModeDefault : DefaultValues.outputMode; + recordingLayoutDefault = recordingLayoutDefault != null ? recordingLayoutDefault + : DefaultValues.recordingLayout; + resolutionDefault = resolutionDefault != null ? resolutionDefault : DefaultValues.resolution; + frameRateDefault = frameRateDefault != null ? frameRateDefault : DefaultValues.frameRate; + shmSizeDefault = shmSizeDefault != null ? shmSizeDefault : DefaultValues.shmSize; + customLayoutDefault = customLayoutDefault != null ? customLayoutDefault : DefaultValues.customLayout; + ignoreFailedStreamsDefault = ignoreFailedStreamsDefault != null ? ignoreFailedStreamsDefault + : DefaultValues.ignoreFailedStreams; + + // Provided properties through params + String nameParam; + Boolean hasAudioParam; + Boolean hasVideoParam; + String outputModeStringParam; + String recordingLayoutStringParam; + String resolutionParam; + Number frameRateParam; + Long shmSizeParam = null; + String customLayoutParam; + Boolean ignoreFailedStreamsParam; + + try { + nameParam = (String) params.get("name"); + hasAudioParam = (Boolean) params.get("hasAudio"); + hasVideoParam = (Boolean) params.get("hasVideo"); + outputModeStringParam = (String) params.get("outputMode"); + recordingLayoutStringParam = (String) params.get("recordingLayout"); + resolutionParam = (String) params.get("resolution"); + frameRateParam = (Number) params.get("frameRate"); + if (params.get("shmSize") != null) { + Number shmSize = (Number) params.get("shmSize"); + shmSizeParam = shmSize.longValue(); + } + customLayoutParam = (String) params.get("customLayout"); + ignoreFailedStreamsParam = (Boolean) params.get("ignoreFailedStreams"); + } catch (ClassCastException | NumberFormatException e) { + throw new IllegalArgumentException("Type error in some parameter: " + e.getMessage()); + } + + if (nameParam != null && !nameParam.isEmpty()) { + if (!FormatChecker.isValidRecordingName(nameParam)) { + throw new IllegalArgumentException( + "Parameter 'name' is wrong. Must be an alphanumeric string [a-zA-Z0-9_-~]+"); + } + nameFinal = nameParam; + } else { + nameFinal = nameDefault; + } + + if (hasAudioParam != null) { + hasAudioFinal = hasAudioParam; + } else { + hasAudioFinal = hasAudioDefault; + } + + if (hasVideoParam != null) { + hasVideoFinal = hasVideoParam; + } else { + hasVideoFinal = hasVideoDefault; + } + + if (!hasAudioFinal && !hasVideoFinal) { + // Cannot start a recording with both "hasAudio" and "hasVideo" to false + throw new IllegalStateException( + "Cannot start a recording with both \"hasAudio\" and \"hasVideo\" set to false"); + } + + if (outputModeStringParam != null) { + try { + outputModeFinal = OutputMode.valueOf(outputModeStringParam); + + // If param outputMode is COMPOSED when default is COMPOSED_QUICK_START, + // change outputMode to COMPOSED_QUICK_START (and vice versa) + if (defaultProps != null && OutputMode.COMPOSED_QUICK_START.equals(defaultProps.outputMode()) + && OutputMode.COMPOSED.equals(outputModeFinal)) { + outputModeFinal = OutputMode.COMPOSED_QUICK_START; + } else if (defaultProps != null && OutputMode.COMPOSED.equals(defaultProps.outputMode()) + && OutputMode.COMPOSED_QUICK_START.equals(outputModeFinal)) { + outputModeFinal = OutputMode.COMPOSED; + } + + } catch (Exception e) { + throw new IllegalArgumentException("Type error in parameter 'outputMode'"); + } + } else { + outputModeFinal = outputModeDefault; + } + + if (IS_COMPOSED(outputModeFinal)) { + + if (recordingLayoutStringParam != null) { + try { + recordingLayoutFinal = RecordingLayout.valueOf(recordingLayoutStringParam); + } catch (Exception e) { + throw new IllegalArgumentException("Type error in parameter 'recordingLayout'"); + } + } else { + recordingLayoutFinal = recordingLayoutDefault; + } + + if (resolutionParam != null) { + if (!FormatChecker.isAcceptableRecordingResolution(resolutionParam)) { + throw new IllegalStateException( + "Wrong 'resolution' parameter. Acceptable values from 100 to 1999 for both width and height"); + } + resolutionFinal = resolutionParam; + } else { + resolutionFinal = resolutionDefault; + } + + if (frameRateParam != null) { + if (!FormatChecker.isAcceptableRecordingFrameRate(frameRateParam.intValue())) { + throw new IllegalStateException( + "Wrong 'resolution' parameter. Acceptable values from 100 to 1999 for both width and height"); + } + frameRateFinal = frameRateParam.intValue(); + } else { + frameRateFinal = frameRateDefault; + } + + if (shmSizeParam != null) { + if (!FormatChecker.isAcceptableRecordingShmSize(shmSizeParam)) { + throw new IllegalStateException("Wrong \"shmSize\" parameter. Must be 134217728 (128 MB) minimum"); + } + shmSizeFinal = shmSizeParam; + } else { + shmSizeFinal = shmSizeDefault; + } + + if (RecordingLayout.CUSTOM.equals(recordingLayoutFinal)) { + if (customLayoutParam != null) { + customLayoutFinal = customLayoutParam; + } else { + customLayoutFinal = customLayoutDefault; + } + } + } else if (OutputMode.INDIVIDUAL.equals(outputModeFinal)) { + if (ignoreFailedStreamsParam != null) { + ignoreFailedStreamsFinal = ignoreFailedStreamsParam; + } else { + ignoreFailedStreamsFinal = ignoreFailedStreamsDefault; + } + } + + RecordingProperties.Builder builder = new RecordingProperties.Builder(); + builder.name(nameFinal).hasAudio(hasAudioFinal).hasVideo(hasVideoFinal).outputMode(outputModeFinal); + if (IS_COMPOSED(outputModeFinal) && hasVideoFinal) { + builder.recordingLayout(recordingLayoutFinal); + builder.resolution(resolutionFinal); + builder.frameRate(frameRateFinal); + builder.shmSize(shmSizeFinal); + if (RecordingLayout.CUSTOM.equals(recordingLayoutFinal)) { + builder.customLayout(customLayoutFinal); + } + } + if (OutputMode.INDIVIDUAL.equals(outputModeFinal)) { + builder.ignoreFailedStreams(ignoreFailedStreamsFinal); + } + + if (mediaNodeDefault == null) { + mediaNodeDefault = SessionProperties.getMediaNodeProperty(params); + } + if (mediaNodeDefault != null && !mediaNodeDefault.isEmpty()) { + builder.mediaNode = mediaNodeDefault; + } + + return builder; + } + /** * @hidden */ - public static RecordingProperties fromJson(JsonObject json) { - - Boolean hasVideoAux = true; - Recording.OutputMode outputModeAux = null; - RecordingLayout recordingLayoutAux = null; - - Builder builder = new RecordingProperties.Builder(); - if (json.has("name")) { - builder.name(json.get("name").getAsString()); - } - if (json.has("hasAudio")) { - builder.hasAudio(json.get("hasAudio").getAsBoolean()); - } - if (json.has("hasVideo")) { - hasVideoAux = json.get("hasVideo").getAsBoolean(); - builder.hasVideo(hasVideoAux); - } - if (json.has("outputMode")) { - outputModeAux = OutputMode.valueOf(json.get("outputMode").getAsString()); - } else { - outputModeAux = DefaultValues.outputMode; - } - builder.outputMode(outputModeAux); - if ((OutputMode.COMPOSED.equals(outputModeAux) || OutputMode.COMPOSED_QUICK_START.equals(outputModeAux)) - && hasVideoAux) { - if (json.has("recordingLayout")) { - recordingLayoutAux = RecordingLayout.valueOf(json.get("recordingLayout").getAsString()); - builder.recordingLayout(recordingLayoutAux); - } - if (json.has("resolution")) { - builder.resolution(json.get("resolution").getAsString()); - } - if (json.has("frameRate")) { - builder.frameRate(json.get("frameRate").getAsInt()); - } - if (json.has("shmSize")) { - builder.shmSize(json.get("shmSize").getAsLong()); - } - if (RecordingLayout.CUSTOM.equals(recordingLayoutAux)) { - if (json.has("customLayout")) { - builder.customLayout(json.get("customLayout").getAsString()); - } - } - } - if (json.has("ignoreFailedStreams") && OutputMode.INDIVIDUAL.equals(outputModeAux)) { - builder.ignoreFailedStreams(json.get("ignoreFailedStreams").getAsBoolean()); - } - if (json.has("mediaNode")) { - String mediaNodeId = null; - if (json.get("mediaNode").isJsonObject()) { - mediaNodeId = json.get("mediaNode").getAsJsonObject().get("id").getAsString(); - - } else if (json.get("mediaNode").isJsonPrimitive()) { - mediaNodeId = json.get("mediaNode").getAsString(); - } - if (mediaNodeId != null && !mediaNodeId.isEmpty()) { - builder.mediaNode(mediaNodeId); - } - } - return builder.build(); + public final static boolean IS_COMPOSED(OutputMode outputMode) { + return (OutputMode.COMPOSED.equals(outputMode) || OutputMode.COMPOSED_QUICK_START.equals(outputMode)); } } 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 eb35b11d..106e7755 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 @@ -671,8 +671,7 @@ public class Session { .recordingMode(properties.recordingMode()) .defaultRecordingProperties(properties.defaultRecordingProperties()) .customSessionId(properties.customSessionId()).mediaNode(properties.mediaNode()) - .forcedVideoCodec(forcedVideoCodec) - .allowTranscoding(allowTranscoding).build(); + .forcedVideoCodec(forcedVideoCodec).allowTranscoding(allowTranscoding).build(); this.properties = responseProperties; log.info("Session '{}' created", this.sessionId); @@ -715,8 +714,10 @@ public class Session { .mediaMode(MediaMode.valueOf(json.get("mediaMode").getAsString())) .recordingMode(RecordingMode.valueOf(json.get("recordingMode").getAsString())); if (json.has("defaultRecordingProperties")) { - builder.defaultRecordingProperties( - RecordingProperties.fromJson(json.get("defaultRecordingProperties").getAsJsonObject())); + String jsonString = json.get("defaultRecordingProperties").getAsJsonObject().toString(); + RecordingProperties.Builder recBuilder = RecordingProperties + .fromJson(new Gson().fromJson(jsonString, Map.class), null); + builder.defaultRecordingProperties(recBuilder.build()); } if (json.has("customSessionId")) { builder.customSessionId(json.get("customSessionId").getAsString()); 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 f38f122a..0e2ecf48 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 @@ -17,11 +17,14 @@ package io.openvidu.java.client; +import java.lang.reflect.Type; import java.util.Map; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; /** * See {@link io.openvidu.java.client.OpenVidu#createSession(SessionProperties)} @@ -108,12 +111,12 @@ public class SessionProperties { } /** - * PRO Call this method to force the session to be hosted in the - * Media Node with identifier mediaNodeId + * PRO Call + * this method to force the session to be hosted in the Media Node with + * identifier mediaNodeId */ public SessionProperties.Builder mediaNode(String mediaNodeId) { this.mediaNode = mediaNodeId; @@ -121,14 +124,14 @@ public class SessionProperties { } /** - * 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. + * 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. + * {@link #allowTranscoding(Boolean)} is false, an exception will + * occur. * * If defined here, this parameter has prevalence over * OPENVIDU_STREAMS_FORCED_VIDEO_CODEC. @@ -141,11 +144,11 @@ public class SessionProperties { } /** - * 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. + * 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; @@ -228,13 +231,13 @@ public class SessionProperties { } /** - * 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. + * 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. */ public String mediaNode() { return this.mediaNode; @@ -253,12 +256,12 @@ public class SessionProperties { * 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. * - * This is a server-only property, and as such, it doesn't need to be transmitted - * over the wire between server and client. Thus it doesn't get serialized in - * the `toJson()` method. + * This is a server-only property, and as such, it doesn't need to be + * transmitted over the wire between server and client. Thus it doesn't get + * serialized in the `toJson()` method. * - * If more server-only properties start to appear here, maybe a good idea - * would be to refactor them all into a server-specific Properties class. + * If more server-only properties start to appear here, maybe a good idea would + * be to refactor them all into a server-specific Properties class. * * @hidden */ @@ -377,8 +380,11 @@ public class SessionProperties { } if (defaultRecordingPropertiesJson != null) { try { - RecordingProperties defaultRecordingProperties = RecordingProperties - .fromJson(defaultRecordingPropertiesJson); + + String jsonString = defaultRecordingPropertiesJson.toString(); + RecordingProperties.Builder recBuilder = RecordingProperties + .fromJson(new Gson().fromJson(jsonString, Map.class), null); + RecordingProperties defaultRecordingProperties = recBuilder.build(); builder = builder.defaultRecordingProperties(defaultRecordingProperties); } catch (Exception e) { throw new IllegalArgumentException( @@ -408,8 +414,17 @@ public class SessionProperties { JsonObject mediaNodeJson; try { mediaNodeJson = JsonParser.parseString(params.get("mediaNode").toString()).getAsJsonObject(); - } catch (Exception e) { - throw new IllegalArgumentException("Error in parameter 'mediaNode'. It is not a valid JSON object"); + } catch (JsonSyntaxException e) { + try { + Gson gson = new Gson(); + Type gsonType = new TypeToken() { + }.getType(); + String gsonString = gson.toJson(params.get("mediaNode"), gsonType); + mediaNodeJson = JsonParser.parseString(gsonString).getAsJsonObject(); + } catch (Exception e2) { + throw new IllegalArgumentException("Error in parameter 'mediaNode'. It is not a valid JSON object"); + + } } if (!mediaNodeJson.has("id")) { throw new IllegalArgumentException("Error in parameter 'mediaNode'. Property 'id' not found"); diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/utils/FormatChecker.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/utils/FormatChecker.java new file mode 100644 index 00000000..7a417b1b --- /dev/null +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/utils/FormatChecker.java @@ -0,0 +1,36 @@ +package io.openvidu.java.client.utils; + +/** + * @hidden + */ +public final class FormatChecker { + + public static boolean isAcceptableRecordingResolution(String stringResolution) { + // Matches every string with format "AxB", being A and B any number not starting + // with 0 and 3 digits long or 4 digits long if they start with 1 + return stringResolution.matches("^(?!(0))(([0-9]{3})|1([0-9]{3}))x(?!0)(([0-9]{3})|1([0-9]{3}))$"); + } + + public static boolean isAcceptableRecordingFrameRate(Integer frameRate) { + // Integer greater than 0 and below 120 + return (frameRate > 0 && frameRate <= 120); + } + + public static boolean isAcceptableRecordingShmSize(Long shmSize) { + // Long grater than 134217728 (128 MB) + return (shmSize >= 134217728L); + } + + public static boolean isServerMetadataFormatCorrect(String metadata) { + return true; + } + + public static boolean isValidCustomSessionId(String customSessionId) { + return customSessionId.matches("[a-zA-Z0-9_\\-]+"); + } + + public static boolean isValidRecordingName(String recodingName) { + return recodingName.matches("[a-zA-Z0-9_\\-~]+"); + } + +} diff --git a/openvidu-server/src/test/java/io/openvidu/server/test/unit/FormatCheckerTest.java b/openvidu-java-client/src/test/java/io/openvidu/java/client/test/FormatCheckerTest.java similarity index 59% rename from openvidu-server/src/test/java/io/openvidu/server/test/unit/FormatCheckerTest.java rename to openvidu-java-client/src/test/java/io/openvidu/java/client/test/FormatCheckerTest.java index edc5e113..c780f045 100644 --- a/openvidu-server/src/test/java/io/openvidu/server/test/unit/FormatCheckerTest.java +++ b/openvidu-java-client/src/test/java/io/openvidu/java/client/test/FormatCheckerTest.java @@ -1,19 +1,24 @@ -package io.openvidu.server.test.unit; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +package io.openvidu.java.client.test; import java.util.Arrays; import java.util.List; -import org.junit.jupiter.api.Test; +import io.openvidu.java.client.utils.FormatChecker; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; -import io.openvidu.server.utils.FormatChecker; +public class FormatCheckerTest extends TestCase { -public class FormatCheckerTest { + public FormatCheckerTest(String testName) { + super(testName); + } - @Test - void customSessionIdFormatTest() { + public static Test suite() { + return new TestSuite(FormatCheckerTest.class); + } + + public void testCustomSessionIdFormat() { List invalidCustomSessionIds = Arrays.asList("", "session#", "session!", "session*", "'session", "\"session", "sess(ion", "sess_ion)", "session:session", ";session;", "session@session", "$", @@ -24,40 +29,35 @@ public class FormatCheckerTest { "0session10", "-session", "session-", "-session-", "_session", "session_", "_session_", "_-session", "session-_", "123_session-1"); - FormatChecker formatChecker = new FormatChecker(); for (String id : invalidCustomSessionIds) - assertFalse(formatChecker.isValidCustomSessionId(id)); + assertFalse(FormatChecker.isValidCustomSessionId(id)); for (String id : validCustomSessionIds) - assertTrue(formatChecker.isValidCustomSessionId(id)); + assertTrue(FormatChecker.isValidCustomSessionId(id)); } - @Test - void acceptableRecordingResolutionTest() { + public void testAcceptableRecordingResolution() { List invalidResolutions = Arrays.asList("", "a", "123", "true", "AXB", "AxB", "12x", "x12", "0920x1080", "1080x0720", "720x2000", "99x720", "1920X1080"); List validResolutions = Arrays.asList("1920x1080", "1280x720", "100x1999"); - FormatChecker formatChecker = new FormatChecker(); for (String resolution : invalidResolutions) - assertFalse(formatChecker.isAcceptableRecordingResolution(resolution)); + assertFalse(FormatChecker.isAcceptableRecordingResolution(resolution)); for (String resolution : validResolutions) - assertTrue(formatChecker.isAcceptableRecordingResolution(resolution)); + assertTrue(FormatChecker.isAcceptableRecordingResolution(resolution)); } - @Test - void acceptableRecordingFrameRateTest() { + public void testAcceptableRecordingFrameRate() { List invalidFrameRates = Arrays.asList(-1, 0, 121, 9999); List validFramerates = Arrays.asList(1, 2, 30, 60, 119, 120); - FormatChecker formatChecker = new FormatChecker(); for (int framerate : invalidFrameRates) - assertFalse(formatChecker.isAcceptableRecordingFrameRate(framerate)); + assertFalse(FormatChecker.isAcceptableRecordingFrameRate(framerate)); for (int framerate : validFramerates) - assertTrue(formatChecker.isAcceptableRecordingFrameRate(framerate)); + assertTrue(FormatChecker.isAcceptableRecordingFrameRate(framerate)); } } \ No newline at end of file diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventRecordingStatusChanged.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventRecordingStatusChanged.java index 7f3c5b36..41f5e34e 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventRecordingStatusChanged.java +++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventRecordingStatusChanged.java @@ -21,9 +21,9 @@ import com.google.gson.JsonObject; import io.openvidu.java.client.Recording.Status; import io.openvidu.java.client.RecordingLayout; +import io.openvidu.java.client.RecordingProperties; import io.openvidu.server.core.EndReason; import io.openvidu.server.recording.Recording; -import io.openvidu.server.utils.RecordingUtils; public class CDREventRecordingStatusChanged extends CDREventEnd { @@ -44,7 +44,7 @@ public class CDREventRecordingStatusChanged extends CDREventEnd { json.addProperty("id", this.recording.getId()); json.addProperty("name", this.recording.getName()); json.addProperty("outputMode", this.recording.getOutputMode().name()); - if (RecordingUtils.IS_COMPOSED(this.recording.getOutputMode()) && this.recording.hasVideo()) { + if (RecordingProperties.IS_COMPOSED(this.recording.getOutputMode()) && this.recording.hasVideo()) { json.addProperty("resolution", this.recording.getResolution()); json.addProperty("frameRate", this.recording.getFrameRate()); json.addProperty("recordingLayout", this.recording.getRecordingLayout().name()); 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 8520e311..df495b6f 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 @@ -53,13 +53,13 @@ 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.java.client.utils.FormatChecker; import io.openvidu.server.cdr.CDREventRecordingStatusChanged; import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.coturn.CoturnCredentialsService; import io.openvidu.server.kurento.endpoint.EndpointType; import io.openvidu.server.kurento.kms.Kms; import io.openvidu.server.recording.service.RecordingManager; -import io.openvidu.server.utils.FormatChecker; import io.openvidu.server.utils.GeoLocation; import io.openvidu.server.utils.GeoLocationByIp; import io.openvidu.server.utils.MediaNodeManager; @@ -93,8 +93,6 @@ public abstract class SessionManager { @Autowired protected GeoLocationByIp geoLocationByIp; - public FormatChecker formatChecker = new FormatChecker(); - private UpdatableTimerTask sessionGarbageCollectorTimer; final protected ConcurrentMap sessions = new ConcurrentHashMap<>(); @@ -332,7 +330,7 @@ public abstract class SessionManager { public Token newToken(Session session, OpenViduRole role, String serverMetadata, boolean record, KurentoOptions kurentoOptions, List customIceServers) throws Exception { - if (!formatChecker.isServerMetadataFormatCorrect(serverMetadata)) { + if (!FormatChecker.isServerMetadataFormatCorrect(serverMetadata)) { log.error("Data invalid format"); throw new OpenViduException(Code.GENERIC_ERROR_CODE, "Data invalid format"); } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/Kms.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/Kms.java index c02051bc..e251d4f9 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/Kms.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/kms/Kms.java @@ -39,7 +39,6 @@ import com.google.gson.JsonObject; import io.openvidu.java.client.RecordingProperties; import io.openvidu.server.core.MediaServer; import io.openvidu.server.kurento.core.KurentoSession; -import io.openvidu.server.utils.RecordingUtils; import io.openvidu.server.utils.UpdatableTimerTask; /** @@ -192,14 +191,14 @@ public class Kms { public synchronized void incrementActiveRecordings(String sessionId, String recordingId, RecordingProperties properties) { this.activeRecordings.put(recordingId, sessionId); - if (RecordingUtils.IS_COMPOSED(properties.outputMode())) { + if (RecordingProperties.IS_COMPOSED(properties.outputMode())) { this.activeComposedRecordings.incrementAndGet(); } } public synchronized void decrementActiveRecordings(String recordingId, RecordingProperties properties) { this.activeRecordings.remove(recordingId); - if (RecordingUtils.IS_COMPOSED(properties.outputMode())) { + if (RecordingProperties.IS_COMPOSED(properties.outputMode())) { this.activeComposedRecordings.decrementAndGet(); } kmsManager.getMediaNodeManager().dropIdleMediaNode(this.id); diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java b/openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java index c9be43c5..d86c8199 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java @@ -23,7 +23,6 @@ import com.google.gson.JsonObject; import io.openvidu.java.client.RecordingLayout; import io.openvidu.java.client.RecordingProperties; -import io.openvidu.server.utils.RecordingUtils; public class Recording { @@ -77,7 +76,7 @@ public class Recording { .valueOf(json.get("outputMode").getAsString()); RecordingProperties.Builder builder = new RecordingProperties.Builder().name(json.get("name").getAsString()) .outputMode(outputMode).hasAudio(hasAudio).hasVideo(hasVideo); - if (RecordingUtils.IS_COMPOSED(outputMode) && hasVideo) { + if (RecordingProperties.IS_COMPOSED(outputMode) && hasVideo) { if (json.has("resolution")) { builder.resolution(json.get("resolution").getAsString()); } @@ -193,7 +192,8 @@ public class Recording { json.addProperty("object", "recording"); json.addProperty("name", this.recordingProperties.name()); json.addProperty("outputMode", this.getOutputMode().name()); - if (RecordingUtils.IS_COMPOSED(this.recordingProperties.outputMode()) && this.recordingProperties.hasVideo()) { + if (RecordingProperties.IS_COMPOSED(this.recordingProperties.outputMode()) + && this.recordingProperties.hasVideo()) { json.addProperty("resolution", this.recordingProperties.resolution()); json.addProperty("frameRate", this.recordingProperties.frameRate()); json.addProperty("recordingLayout", this.recordingProperties.recordingLayout().name()); diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java index 8e248936..6568f617 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java @@ -80,7 +80,6 @@ import io.openvidu.server.utils.DockerManager; import io.openvidu.server.utils.JsonUtils; import io.openvidu.server.utils.LocalCustomFileManager; import io.openvidu.server.utils.LocalDockerManager; -import io.openvidu.server.utils.RecordingUtils; import io.openvidu.server.utils.RemoteOperationUtils; public class RecordingManager { @@ -443,7 +442,7 @@ public class RecordingManager { this.singleStreamRecordingService.startRecorderEndpointForPublisherEndpoint(recording.getId(), profile, participant, new CountDownLatch(1)); - } else if (RecordingUtils.IS_COMPOSED(recording.getOutputMode()) && !recording.hasVideo()) { + } else if (RecordingProperties.IS_COMPOSED(recording.getOutputMode()) && !recording.hasVideo()) { // Connect this stream to existing Composite recorder log.info("Joining PublisherEndpoint to existing Composite in session {} for new stream of participant {}", session.getSessionId(), participant.getParticipantPublicId()); @@ -479,7 +478,7 @@ public class RecordingManager { } catch (InterruptedException e) { log.error("Exception while waiting for state change", e); } - } else if (RecordingUtils.IS_COMPOSED(recording.getOutputMode()) && !recording.hasVideo()) { + } else if (RecordingProperties.IS_COMPOSED(recording.getOutputMode()) && !recording.hasVideo()) { // Disconnect this stream from existing Composite recorder log.info("Removing PublisherEndpoint from Composite in session {} for stream of participant {}", session.getSessionId(), streamId); diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java index ffc1f054..12d6627f 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingService.java @@ -38,7 +38,6 @@ import io.openvidu.server.recording.RecordingDownloader; import io.openvidu.server.recording.RecordingUploader; import io.openvidu.server.utils.CommandExecutor; import io.openvidu.server.utils.CustomFileManager; -import io.openvidu.server.utils.RecordingUtils; public abstract class RecordingService { @@ -160,7 +159,7 @@ public abstract class RecordingService { RecordingProperties.Builder builder = new RecordingProperties.Builder().name(recordingId) .outputMode(properties.outputMode()).hasAudio(properties.hasAudio()).hasVideo(properties.hasVideo()) .mediaNode(properties.mediaNode()); - if (RecordingUtils.IS_COMPOSED(properties.outputMode()) && properties.hasVideo()) { + if (RecordingProperties.IS_COMPOSED(properties.outputMode()) && properties.hasVideo()) { builder.resolution(properties.resolution()); builder.frameRate(properties.frameRate()); builder.recordingLayout(properties.recordingLayout()); 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 ce0ad625..37ab747b 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 @@ -56,7 +56,6 @@ import io.openvidu.java.client.ConnectionType; import io.openvidu.java.client.IceServerProperties; import io.openvidu.java.client.MediaMode; import io.openvidu.java.client.Recording.OutputMode; -import io.openvidu.java.client.RecordingLayout; import io.openvidu.java.client.RecordingProperties; import io.openvidu.java.client.SessionProperties; import io.openvidu.java.client.VideoCodec; @@ -70,7 +69,6 @@ import io.openvidu.server.core.Token; import io.openvidu.server.kurento.core.KurentoMediaOptions; import io.openvidu.server.recording.Recording; import io.openvidu.server.recording.service.RecordingManager; -import io.openvidu.server.utils.RecordingUtils; import io.openvidu.server.utils.RestUtils; /** @@ -427,9 +425,9 @@ public class SessionRestController { RecordingProperties recordingProperties; try { recordingProperties = getRecordingPropertiesFromParams(params, session).build(); - } catch (RuntimeException e) { + } catch (IllegalStateException e) { return this.generateErrorResponse(e.getMessage(), "/sessions", HttpStatus.UNPROCESSABLE_ENTITY); - } catch (Exception e) { + } catch (RuntimeException e) { return this.generateErrorResponse(e.getMessage(), "/sessions", HttpStatus.BAD_REQUEST); } @@ -853,208 +851,10 @@ public class SessionRestController { return builder; } - protected RecordingProperties.Builder getRecordingPropertiesFromParams(Map params, Session session) - throws Exception { - - // Final properties being used - String nameFinal = null; - Boolean hasAudioFinal = null; - Boolean hasVideoFinal = null; - OutputMode outputModeFinal = null; - RecordingLayout recordingLayoutFinal = null; - String resolutionFinal = null; - Integer frameRateFinal = null; - Long shmSizeFinal = null; - String customLayoutFinal = null; - Boolean ignoreFailedStreamsFinal = null; - - RecordingProperties defaultProps = session.getSessionProperties().defaultRecordingProperties(); - - // Default properties configured in Session - String nameDefault = defaultProps.name(); - Boolean hasAudioDefault = defaultProps.hasAudio(); - Boolean hasVideoDefault = defaultProps.hasVideo(); - OutputMode outputModeDefault = defaultProps.outputMode(); - RecordingLayout recordingLayoutDefault = defaultProps.recordingLayout(); - String resolutionDefault = defaultProps.resolution(); - Integer frameRateDefault = defaultProps.frameRate(); - Long shmSizeDefault = defaultProps.shmSize(); - String customLayoutDefault = defaultProps.customLayout(); - Boolean ignoreFailedStreamsDefault = defaultProps.ignoreFailedStreams(); - - // Provided properties through params - String sessionIdParam; - String nameParam; - Boolean hasAudioParam; - Boolean hasVideoParam; - String outputModeStringParam; - String recordingLayoutStringParam; - String resolutionParam; - Integer frameRateParam; - Long shmSizeParam = null; - String customLayoutParam; - Boolean ignoreFailedStreamsParam; - - try { - sessionIdParam = (String) params.get("session"); - nameParam = (String) params.get("name"); - hasAudioParam = (Boolean) params.get("hasAudio"); - hasVideoParam = (Boolean) params.get("hasVideo"); - outputModeStringParam = (String) params.get("outputMode"); - recordingLayoutStringParam = (String) params.get("recordingLayout"); - resolutionParam = (String) params.get("resolution"); - frameRateParam = (Integer) params.get("frameRate"); - if (params.get("shmSize") != null) { - shmSizeParam = Long.parseLong(params.get("shmSize").toString()); - } - customLayoutParam = (String) params.get("customLayout"); - ignoreFailedStreamsParam = (Boolean) params.get("ignoreFailedStreams"); - } catch (ClassCastException | NumberFormatException e) { - throw new Exception("Type error in some parameter: " + e.getMessage()); - } - - if (sessionIdParam == null) { - // "session" parameter not found - throw new Exception("\"session\" parameter is mandatory"); - } - - if (nameParam != null && !nameParam.isEmpty()) { - if (!sessionManager.formatChecker.isValidRecordingName(nameParam)) { - throw new Exception("Parameter 'name' is wrong. Must be an alphanumeric string [a-zA-Z0-9_-]+"); - } - nameFinal = nameParam; - } else if (nameDefault != null) { - nameFinal = nameDefault; - } else { - nameFinal = ""; - } - - if (hasAudioParam != null) { - hasAudioFinal = hasAudioParam; - } else if (hasAudioDefault != null) { - hasAudioFinal = hasAudioDefault; - } else { - hasAudioFinal = RecordingProperties.DefaultValues.hasAudio; - } - - if (hasVideoParam != null) { - hasVideoFinal = hasVideoParam; - } else if (hasAudioDefault != null) { - hasVideoFinal = hasVideoDefault; - } else { - hasVideoFinal = RecordingProperties.DefaultValues.hasVideo; - } - - if (!hasAudioFinal && !hasVideoFinal) { - // Cannot start a recording with both "hasAudio" and "hasVideo" to false - throw new RuntimeException("Cannot start a recording with both \"hasAudio\" and \"hasVideo\" set to false"); - } - - if (outputModeStringParam != null) { - try { - outputModeFinal = OutputMode.valueOf(outputModeStringParam); - - // If param outputMode is COMPOSED when default is COMPOSED_QUICK_START, - // change outputMode to COMPOSED_QUICK_START (and vice versa) - if (OutputMode.COMPOSED_QUICK_START.equals(outputModeDefault) - && OutputMode.COMPOSED.equals(outputModeFinal)) { - outputModeFinal = OutputMode.COMPOSED_QUICK_START; - } else if (OutputMode.COMPOSED.equals(outputModeDefault) - && OutputMode.COMPOSED_QUICK_START.equals(outputModeFinal)) { - outputModeFinal = OutputMode.COMPOSED; - } - - } catch (Exception e) { - throw new Exception("Type error in parameter 'outputMode'"); - } - } else if (outputModeDefault != null) { - outputModeFinal = outputModeDefault; - } else { - outputModeFinal = RecordingProperties.DefaultValues.outputMode; - } - - if (RecordingUtils.IS_COMPOSED(outputModeFinal)) { - - if (recordingLayoutStringParam != null) { - try { - recordingLayoutFinal = RecordingLayout.valueOf(recordingLayoutStringParam); - } catch (Exception e) { - throw new Exception("Type error in parameter 'recordingLayout'"); - } - } else if (recordingLayoutDefault != null) { - recordingLayoutFinal = recordingLayoutDefault; - } else { - recordingLayoutFinal = RecordingProperties.DefaultValues.recordingLayout; - } - - if (resolutionParam != null) { - if (!sessionManager.formatChecker.isAcceptableRecordingResolution(resolutionParam)) { - throw new RuntimeException( - "Wrong 'resolution' parameter. Acceptable values from 100 to 1999 for both width and height"); - } - resolutionFinal = resolutionParam; - } else if (resolutionDefault != null) { - resolutionFinal = resolutionDefault; - } else { - resolutionFinal = RecordingProperties.DefaultValues.resolution; - } - - if (frameRateParam != null) { - if (!sessionManager.formatChecker.isAcceptableRecordingFrameRate(frameRateParam)) { - throw new RuntimeException( - "Wrong 'resolution' parameter. Acceptable values from 100 to 1999 for both width and height"); - } - frameRateFinal = frameRateParam; - } else if (frameRateDefault != null) { - frameRateFinal = frameRateDefault; - } else { - frameRateFinal = RecordingProperties.DefaultValues.frameRate; - } - - if (shmSizeParam != null) { - if (!sessionManager.formatChecker.isAcceptableRecordingShmSize(shmSizeParam)) { - throw new RuntimeException("Wrong \"shmSize\" parameter. Must be 134217728 (128 MB) minimum"); - } - shmSizeFinal = shmSizeParam; - } else if (shmSizeDefault != null) { - shmSizeFinal = shmSizeDefault; - } else { - shmSizeFinal = RecordingProperties.DefaultValues.shmSize; - } - - if (RecordingLayout.CUSTOM.equals(recordingLayoutFinal)) { - if (customLayoutParam != null) { - customLayoutFinal = customLayoutParam; - } else if (customLayoutDefault != null) { - customLayoutFinal = customLayoutDefault; - } else { - customLayoutFinal = ""; - } - } - } else if (OutputMode.INDIVIDUAL.equals(outputModeFinal)) { - if (ignoreFailedStreamsParam != null) { - ignoreFailedStreamsFinal = ignoreFailedStreamsParam; - } else if (ignoreFailedStreamsDefault != null) { - ignoreFailedStreamsFinal = ignoreFailedStreamsDefault; - } else { - ignoreFailedStreamsFinal = RecordingProperties.DefaultValues.ignoreFailedStreams; - } - } - - RecordingProperties.Builder builder = new RecordingProperties.Builder(); - builder.name(nameFinal).hasAudio(hasAudioFinal).hasVideo(hasVideoFinal).outputMode(outputModeFinal); - if (RecordingUtils.IS_COMPOSED(outputModeFinal) && hasVideoFinal) { - builder.recordingLayout(recordingLayoutFinal); - builder.resolution(resolutionFinal); - builder.frameRate(frameRateFinal); - builder.shmSize(shmSizeFinal); - if (RecordingLayout.CUSTOM.equals(recordingLayoutFinal)) { - builder.customLayout(customLayoutFinal); - } - } - if (OutputMode.INDIVIDUAL.equals(outputModeFinal)) { - builder.ignoreFailedStreams(ignoreFailedStreamsFinal); - } + protected RecordingProperties.Builder getRecordingPropertiesFromParams(Map params, Session session) + throws RuntimeException { + RecordingProperties.Builder builder = RecordingProperties.fromJson(params, + session.getSessionProperties().defaultRecordingProperties()); return builder; } 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 1f7384ec..cd9dc8f3 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 @@ -47,6 +47,7 @@ import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; import io.openvidu.client.internal.ProtocolElements; import io.openvidu.java.client.ConnectionProperties; +import io.openvidu.java.client.utils.FormatChecker; import io.openvidu.server.config.OpenviduBuildInfo; import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.core.EndReason; @@ -277,7 +278,7 @@ public class RpcHandler extends DefaultJsonRpcHandler { if (tokenObj != null) { String clientMetadata = getStringParam(request, ProtocolElements.JOINROOM_METADATA_PARAM); - if (sessionManager.formatChecker.isServerMetadataFormatCorrect(clientMetadata)) { + if (FormatChecker.isServerMetadataFormatCorrect(clientMetadata)) { // While closing a session users can't join if (session.closingLock.readLock().tryLock()) { diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/FormatChecker.java b/openvidu-server/src/main/java/io/openvidu/server/utils/FormatChecker.java deleted file mode 100644 index 3dfce9c3..00000000 --- a/openvidu-server/src/main/java/io/openvidu/server/utils/FormatChecker.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * (C) Copyright 2017-2022 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.utils; - -public class FormatChecker { - - public boolean isAcceptableRecordingResolution(String stringResolution) { - // Matches every string with format "AxB", being A and B any number not starting - // with 0 and 3 digits long or 4 digits long if they start with 1 - return stringResolution.matches("^(?!(0))(([0-9]{3})|1([0-9]{3}))x(?!0)(([0-9]{3})|1([0-9]{3}))$"); - } - - public boolean isAcceptableRecordingFrameRate(Integer frameRate) { - // Integer greater than 0 and below 120 - return (frameRate > 0 && frameRate <= 120); - } - - public boolean isAcceptableRecordingShmSize(Long shmSize) { - // Long grater than 134217728 (128 MB) - return (shmSize >= 134217728L); - } - - public boolean isServerMetadataFormatCorrect(String metadata) { - return true; - } - - public boolean isValidCustomSessionId(String customSessionId) { - return isValidAlphanumeric(customSessionId); - } - - public boolean isValidRecordingName(String recodingName) { - return isValidAlphanumeric(recodingName); - } - - private boolean isValidAlphanumeric(String str) { - return str.matches("[a-zA-Z0-9_-]+"); - } - -} diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/RecordingUtils.java b/openvidu-server/src/main/java/io/openvidu/server/utils/RecordingUtils.java index 1e078a94..cc8ad787 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/utils/RecordingUtils.java +++ b/openvidu-server/src/main/java/io/openvidu/server/utils/RecordingUtils.java @@ -1,15 +1,10 @@ package io.openvidu.server.utils; -import io.openvidu.java.client.Recording.OutputMode; import io.openvidu.java.client.RecordingProperties; import io.openvidu.server.core.Session; public final class RecordingUtils { - public final static boolean IS_COMPOSED(OutputMode outputMode) { - return (OutputMode.COMPOSED.equals(outputMode) || OutputMode.COMPOSED_QUICK_START.equals(outputMode)); - } - public final static RecordingProperties RECORDING_PROPERTIES_WITH_MEDIA_NODE(Session session) { RecordingProperties recordingProperties = session.getSessionProperties().defaultRecordingProperties(); if (recordingProperties.mediaNode() == null) { 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 536d37e2..7bf192e7 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 @@ -3226,6 +3226,8 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST); body = "{'session':'CUSTOM_SESSION_ID','name':999}"; restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST); + body = "{'session':'CUSTOM_SESSION_ID','name':'notalphanumeric@'}"; + restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST); body = "{'session':'CUSTOM_SESSION_ID','name':'NAME','outputMode':'NOT_EXISTS'}"; restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST); body = "{'session':'CUSTOM_SESSION_ID','name':'NAME','outputMode':'COMPOSED','recordingLayout':'NOT_EXISTS'}"; @@ -3236,7 +3238,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_BAD_REQUEST); // 422 - body = "{'session':'CUSTOM_SESSION_ID','name':'NAME','outputMode':'COMPOSED','recordingLayout':'BEST_FIT','customLayout':'CUSTOM_LAYOUT','hasAudio':false,'hasVideo':false}"; + body = "{'session':'CUSTOM_SESSION_ID','name':'NAME~','outputMode':'COMPOSED','recordingLayout':'BEST_FIT','customLayout':'CUSTOM_LAYOUT','hasAudio':false,'hasVideo':false}"; restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_UNPROCESSABLE_ENTITY); body = "{'session':'CUSTOM_SESSION_ID','name':'NAME','outputMode':'COMPOSED','recordingLayout':'BEST_FIT','customLayout':'CUSTOM_LAYOUT','hasAudio':true,'hasVideo':true,'resolution':'1920x2000'}"; restClient.rest(HttpMethod.POST, "/openvidu/api/recordings/start", body, HttpStatus.SC_UNPROCESSABLE_ENTITY); @@ -3848,8 +3850,8 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { user.getDriver().findElement(By.id("session-settings-btn-0")).click(); Thread.sleep(1000); - String rareCharsName = "öæééEstoSi`+´çḈ€$"; - user.getDriver().findElement(By.id("recording-name-field")).sendKeys(rareCharsName); + String recordingName = "1234abcABC_-~"; + user.getDriver().findElement(By.id("recording-name-field")).sendKeys(recordingName); user.getDriver().findElement(By.id("recording-mode-select")).click(); Thread.sleep(500); user.getDriver().findElement(By.id("option-ALWAYS")).click(); @@ -3888,7 +3890,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { Assert.assertEquals("Wrong recording startTime/timestamp in webhook event", event.get("startTime").getAsLong(), event.get("timestamp").getAsLong()); Assert.assertNull("Wrong recording reason in webhook event (should be null)", event.get("reason")); - Assert.assertEquals("Wrong recording name in webhook event", rareCharsName, + Assert.assertEquals("Wrong recording name in webhook event", recordingName, event.get("name").getAsString()); user.getDriver().findElement(By.id("add-user-btn")).click();