diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java index 876b72f6..e12d868a 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenVidu.java @@ -195,8 +195,11 @@ public class OpenVidu { json.put("session", sessionId); json.put("name", properties.name()); json.put("outputMode", properties.outputMode()); + json.put("hasAudio", properties.hasAudio()); + json.put("hasVideo", properties.hasVideo()); if (Recording.OutputMode.COMPOSED.equals(properties.outputMode())) { + json.put("resolution", properties.resolution()); json.put("recordingLayout", (properties.recordingLayout() != null) ? properties.recordingLayout().name() : ""); if (RecordingLayout.CUSTOM.equals(properties.recordingLayout())) { 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 ed803b6d..6f7973b5 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 @@ -99,6 +99,7 @@ public class Recording { .outputMode(outputMode).hasAudio((boolean) json.get("hasAudio")) .hasVideo((boolean) json.get("hasVideo")); if (OutputMode.COMPOSED.equals(outputMode)) { + builder.resolution((String) json.get("resolution")); builder.recordingLayout(RecordingLayout.valueOf((String) json.get("recordingLayout"))); String customLayout = (String) json.get("customLayout"); if (customLayout != null) { @@ -152,8 +153,8 @@ public class Recording { * {@link io.openvidu.java.client.RecordingProperties.Builder#customLayout(String)} * has been called */ - public RecordingLayout getCustomLayout() { - return this.recordingProperties.recordingLayout(); + public String getCustomLayout() { + return this.recordingProperties.customLayout(); } /** @@ -195,6 +196,14 @@ public class Recording { return url; } + /** + * Resolution of the video file. Only defined if OutputMode of the Recording is + * set to {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED} + */ + public String getResolution() { + return this.recordingProperties.resolution(); + } + /** * true if the recording has an audio track, false * otherwise (currently fixed to true) 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 1224775d..d9ea42a9 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 @@ -27,6 +27,7 @@ public class RecordingProperties { private Recording.OutputMode outputMode; private RecordingLayout recordingLayout; private String customLayout; + private String resolution; private boolean hasAudio; private boolean hasVideo; @@ -37,8 +38,9 @@ public class RecordingProperties { private String name = ""; private Recording.OutputMode outputMode = Recording.OutputMode.COMPOSED; - private RecordingLayout recordingLayout; + private RecordingLayout recordingLayout = RecordingLayout.BEST_FIT; private String customLayout = ""; + private String resolution = "1920x1080"; private boolean hasAudio = true; private boolean hasVideo = true; @@ -47,7 +49,7 @@ public class RecordingProperties { */ public RecordingProperties build() { return new RecordingProperties(this.name, this.outputMode, this.recordingLayout, this.customLayout, - this.hasAudio, this.hasVideo); + this.resolution, this.hasAudio, this.hasVideo); } /** @@ -100,6 +102,21 @@ public class RecordingProperties { return this; } + /** + * Call this method to specify the recording resolution. Will only have effect + * if + * {@link io.openvidu.java.client.RecordingProperties.Builder#outputMode(Recording.OutputMode)} + * has been called with value + * {@link io.openvidu.java.client.Recording.OutputMode#COMPOSED}. For + * {@link io.openvidu.java.client.Recording.OutputMode#INDIVIDUAL} all + * individual video files will have the native resolution of the published + * stream + */ + public RecordingProperties.Builder resolution(String resolution) { + this.resolution = resolution; + return this; + } + /** * Call this method to specify whether or not to record the audio track */ @@ -119,11 +136,12 @@ public class RecordingProperties { } protected RecordingProperties(String name, Recording.OutputMode outputMode, RecordingLayout layout, - String customLayout, boolean hasAudio, boolean hasVideo) { + String customLayout, String resolution, boolean hasAudio, boolean hasVideo) { this.name = name; this.outputMode = outputMode; this.recordingLayout = layout; this.customLayout = customLayout; + this.resolution = resolution; this.hasAudio = hasAudio; this.hasVideo = hasVideo; } @@ -164,6 +182,13 @@ public class RecordingProperties { return this.customLayout; } + /** + * Defines the resolution of the recorded video + */ + public String resolution() { + return this.resolution; + } + /** * Defines if the recording has an audio track or not */ diff --git a/openvidu-node-client/src/OpenVidu.ts b/openvidu-node-client/src/OpenVidu.ts index 24bc56ac..1d002023 100644 --- a/openvidu-node-client/src/OpenVidu.ts +++ b/openvidu-node-client/src/OpenVidu.ts @@ -22,6 +22,7 @@ import { Recording } from './Recording'; import { RecordingProperties } from './RecordingProperties'; import { Session } from './Session'; import { SessionProperties } from './SessionProperties'; +import { RecordingLayout } from './RecordingLayout'; /** * @hidden @@ -142,26 +143,29 @@ export class OpenVidu { data = JSON.stringify({ session: sessionId, name: !!properties.name ? properties.name : '', - outputMode: !!properties.outputMode ? properties.outputMode : '', - recordingLayout: !!properties.recordingLayout ? properties.recordingLayout : '', - customLayout: !!properties.customLayout ? properties.customLayout : '' + outputMode: !!properties.outputMode ? properties.outputMode : Recording.OutputMode.COMPOSED, + hasAudio: !!(properties.hasAudio), + hasVideo: !!(properties.hasVideo) }); + if (data.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED]) { + data.resolution = !!properties.resolution ? properties.resolution : '1920x1080'; + data.recordingLayout = !!properties.recordingLayout ? properties.recordingLayout : RecordingLayout.BEST_FIT; + if (data.recordingLayout.toString() === RecordingLayout[RecordingLayout.CUSTOM]) { + data.customLayout = !!properties.customLayout ? properties.customLayout : ''; + } + } } else { data = JSON.stringify({ session: sessionId, name: param2, - outputMode: '', - recordingLayout: '', - customLayout: '' + outputMode: Recording.OutputMode.COMPOSED }); } } else { data = JSON.stringify({ session: sessionId, name: '', - outputMode: '', - recordingLayout: '', - customLayout: '' + outputMode: Recording.OutputMode.COMPOSED }); } diff --git a/openvidu-node-client/src/Recording.ts b/openvidu-node-client/src/Recording.ts index 3c2d0659..2db328b2 100644 --- a/openvidu-node-client/src/Recording.ts +++ b/openvidu-node-client/src/Recording.ts @@ -83,6 +83,7 @@ export class Recording { hasVideo: !!json['hasVideo'] }; if (this.properties.outputMode.toString() === Recording.OutputMode[Recording.OutputMode.COMPOSED]) { + this.properties.resolution = !!(json['resolution']) ? json['resolution'] : '1920x1080'; this.properties.recordingLayout = !!(json['recordingLayout']) ? json['recordingLayout'] : RecordingLayout.BEST_FIT; if (this.properties.recordingLayout.toString() === RecordingLayout[RecordingLayout.CUSTOM]) { this.properties.customLayout = json['customLayout']; diff --git a/openvidu-node-client/src/RecordingProperties.ts b/openvidu-node-client/src/RecordingProperties.ts index 8379a2a3..ee1b1181 100644 --- a/openvidu-node-client/src/RecordingProperties.ts +++ b/openvidu-node-client/src/RecordingProperties.ts @@ -48,6 +48,14 @@ export interface RecordingProperties { */ customLayout?: string; + /** + * Recording video file resolution.
+ * Will only have effect if [[RecordingProperties.outputMode]] + * is set to [[Recording.OutputMode.COMPOSED]]. For [[Recording.OutputMode.INDIVIDUAL]] all + * individual video files will have the native resolution of the published stream + */ + resolution?: string; + /** * Whether or not to record the audio track (currently fixed to true) */ diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventRecording.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventRecording.java index 72b0800f..d1a4fdb7 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventRecording.java +++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREventRecording.java @@ -2,6 +2,7 @@ package io.openvidu.server.cdr; import com.google.gson.JsonObject; +import io.openvidu.java.client.RecordingLayout; import io.openvidu.server.recording.Recording; public class CDREventRecording extends CDREventEnd { @@ -25,7 +26,15 @@ public class CDREventRecording extends CDREventEnd { JsonObject json = super.toJson(); json.addProperty("id", this.recording.getId()); json.addProperty("name", this.recording.getName()); - json.addProperty("recordingLayout", this.recording.getRecordingLayout().name()); + json.addProperty("outputMode", this.recording.getOutputMode().name()); + if (io.openvidu.java.client.Recording.OutputMode.COMPOSED.equals(this.recording.getOutputMode())) { + json.addProperty("resolution", this.recording.getResolution()); + json.addProperty("recordingLayout", this.recording.getRecordingLayout().name()); + if (RecordingLayout.CUSTOM.equals(this.recording.getRecordingLayout()) + && this.recording.getCustomLayout() != null && !this.recording.getCustomLayout().isEmpty()) { + json.addProperty("customLayout", this.recording.getCustomLayout()); + } + } json.addProperty("hasAudio", this.recording.hasAudio()); json.addProperty("hasVideo", this.recording.hasVideo()); json.addProperty("size", this.recording.getSize()); diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java index a94e06a5..1a84b39d 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java +++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CallDetailRecord.java @@ -42,8 +42,8 @@ import io.openvidu.server.recording.service.RecordingManager; * - 'participantLeft': {sessionId, timestamp, participantId, startTime, duration, reason} * - 'webrtcConnectionCreated' {sessionId, timestamp, participantId, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate]} * - 'webrtcConnectionDestroyed' {sessionId, timestamp, participantId, startTime, duration, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate], reason} - * - 'recordingStarted' {sessionId, timestamp, id, name, hasAudio, hasVideo, recordingLayout, size} - * - 'recordingStopped' {sessionId, timestamp, id, name, hasAudio, hasVideo, recordingLayout, size} + * - 'recordingStarted' {sessionId, timestamp, id, name, hasAudio, hasVideo, resolution, recordingLayout, size} + * - 'recordingStopped' {sessionId, timestamp, id, name, hasAudio, hasVideo, resolution, recordingLayout, size} * * PROPERTIES VALUES: * @@ -63,6 +63,7 @@ import io.openvidu.server.recording.service.RecordingManager; * - name: string * - hasAudio: boolean * - hasVideo: boolean + * - resolution string * - recordingLayout: string * - size: number * - webrtcConnectionDestroyed.reason: "unsubscribe", "unpublish", "disconnect", "networkDisconnect", "openviduServerStopped" 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 392a693c..39ac8f4b 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 @@ -32,6 +32,7 @@ public class Recording { private long size = 0; // bytes private double duration = 0; // seconds private String url; + private String resolution; private boolean hasAudio = true; private boolean hasVideo = true; private RecordingProperties recordingProperties; @@ -42,6 +43,9 @@ public class Recording { this.id = id; this.status = io.openvidu.java.client.Recording.Status.started; this.recordingProperties = recordingProperties; + this.resolution = this.recordingProperties.resolution(); + this.hasAudio = this.recordingProperties.hasAudio(); + this.hasVideo = this.recordingProperties.hasVideo(); } public Recording(JsonObject json) { @@ -66,8 +70,11 @@ public class Recording { io.openvidu.java.client.Recording.OutputMode outputMode = io.openvidu.java.client.Recording.OutputMode .valueOf(json.get("outputMode").getAsString()); RecordingProperties.Builder builder = new RecordingProperties.Builder().name(json.get("name").getAsString()) - .outputMode(outputMode); + .outputMode(outputMode).hasAudio(json.get("hasAudio").getAsBoolean()) + .hasVideo(json.get("hasVideo").getAsBoolean()); if (io.openvidu.java.client.Recording.OutputMode.COMPOSED.equals(outputMode)) { + this.resolution = json.get("resolution").getAsString(); + builder.resolution(this.resolution); RecordingLayout recordingLayout = RecordingLayout.valueOf(json.get("recordingLayout").getAsString()); builder.recordingLayout(recordingLayout); if (RecordingLayout.CUSTOM.equals(recordingLayout)) { @@ -153,6 +160,14 @@ public class Recording { this.url = url; } + public String getResolution() { + return resolution; + } + + public void setResolution(String resolution) { + this.resolution = resolution; + } + public boolean hasAudio() { return hasAudio; } @@ -175,6 +190,7 @@ public class Recording { json.addProperty("name", this.recordingProperties.name()); json.addProperty("outputMode", this.getOutputMode().name()); if (io.openvidu.java.client.Recording.OutputMode.COMPOSED.equals(this.recordingProperties.outputMode())) { + json.addProperty("resolution", this.resolution); json.addProperty("recordingLayout", this.recordingProperties.recordingLayout().name()); if (RecordingLayout.CUSTOM.equals(this.recordingProperties.recordingLayout())) { json.addProperty("customLayout", this.recordingProperties.customLayout()); diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java index d673b02f..c3ba4c54 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java @@ -94,7 +94,7 @@ public class ComposedRecordingService extends RecordingService { String layoutUrl = this.getLayoutUrl(recording, this.getShortSessionId(session)); envs.add("URL=" + layoutUrl); - envs.add("RESOLUTION=1920x1080"); + envs.add("RESOLUTION=" + properties.resolution()); envs.add("FRAMERATE=30"); envs.add("VIDEO_ID=" + recordingId); envs.add("VIDEO_NAME=" + properties.name()); @@ -216,6 +216,7 @@ public class ComposedRecordingService extends RecordingService { recording.setStatus(io.openvidu.java.client.Recording.Status.stopped); recording.setDuration(infoUtils.getDurationInSeconds()); recording.setSize(infoUtils.getSizeInBytes()); + recording.setResolution(infoUtils.videoWidth() + "x" + infoUtils.videoHeight()); recording.setHasAudio(infoUtils.hasAudio()); recording.setHasVideo(infoUtils.hasVideo()); 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 6c07d119..9ffc0230 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 @@ -50,8 +50,10 @@ public abstract class RecordingService { if (properties.name() == null || properties.name().isEmpty()) { // No name provided for the recording file. Use recordingId RecordingProperties.Builder builder = new RecordingProperties.Builder().name(recordingId) - .outputMode(properties.outputMode()); + .outputMode(properties.outputMode()).hasAudio(properties.hasAudio()) + .hasVideo(properties.hasVideo()); if (io.openvidu.java.client.Recording.OutputMode.COMPOSED.equals(properties.outputMode())) { + builder.resolution(properties.resolution()); builder.recordingLayout(properties.recordingLayout()); if (RecordingLayout.CUSTOM.equals(properties.recordingLayout())) { builder.customLayout(properties.customLayout()); 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 b5646831..3ec30b0a 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 @@ -53,6 +53,7 @@ import io.openvidu.server.core.SessionManager; import io.openvidu.server.kurento.core.KurentoTokenOptions; import io.openvidu.server.recording.Recording; import io.openvidu.server.recording.service.RecordingManager; +import io.openvidu.server.utils.FormatChecker; /** * @@ -298,6 +299,7 @@ public class SessionRestController { String sessionId = (String) params.get("session"); String name = (String) params.get("name"); String outputModeString = (String) params.get("outputMode"); + String resolution = (String) params.get("resolution"); String recordingLayoutString = (String) params.get("recordingLayout"); String customLayout = (String) params.get("customLayout"); @@ -336,6 +338,15 @@ public class SessionRestController { RecordingProperties.Builder builder = new RecordingProperties.Builder().name(name).outputMode(outputMode); if (outputMode.equals(io.openvidu.java.client.Recording.OutputMode.COMPOSED)) { + + if (resolution != null) { + if (new FormatChecker().isAcceptableResolution(resolution)) { + builder.resolution(resolution); + } else { + return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY); + } + } + RecordingLayout recordingLayout; if (recordingLayoutString == null || recordingLayoutString.isEmpty()) { // "recordingLayout" parameter not defined. Use global layout from @@ -354,8 +365,6 @@ public class SessionRestController { : customLayout; builder.customLayout(customLayout); } - - builder.build(); } try { 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 new file mode 100644 index 00000000..d3849603 --- /dev/null +++ b/openvidu-server/src/main/java/io/openvidu/server/utils/FormatChecker.java @@ -0,0 +1,28 @@ +/* + * (C) Copyright 2017-2019 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 isAcceptableResolution(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}))$"); + } + +}