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 79e0de49..83663c8c 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 @@ -176,11 +176,9 @@ public class KurentoSessionManager extends SessionManager { if (remainingParticipants.isEmpty()) { if (openviduConfig.isRecordingModuleEnabled() && MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode()) - && (this.recordingManager.sessionIsBeingRecordedIndividual(sessionId) - || (this.recordingManager.sessionIsBeingRecordedComposed(sessionId) - && this.recordingManager.sessionIsBeingRecordedOnlyAudio(sessionId)))) { - // Start countdown to stop recording if INDIVIDUAL mode or COMPOSED audio-only - // (will be aborted if a Publisher starts before timeout) + && (this.recordingManager.sessionIsBeingRecorded(sessionId))) { + // Start countdown to stop recording. Will be aborted if a Publisher starts + // before timeout log.info( "Last participant left. Starting {} seconds countdown for stopping recording of session {}", this.openviduConfig.getOpenviduRecordingAutostopTimeout(), sessionId); @@ -192,25 +190,13 @@ public class KurentoSessionManager extends SessionManager { } } else if (remainingParticipants.size() == 1 && openviduConfig.isRecordingModuleEnabled() && MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode()) - && this.recordingManager.sessionIsBeingRecordedComposed(sessionId) - && !this.recordingManager.sessionIsBeingRecordedOnlyAudio(sessionId) + && this.recordingManager.sessionIsBeingRecorded(sessionId) && ProtocolElements.RECORDER_PARTICIPANT_PUBLICID .equals(remainingParticipants.iterator().next().getParticipantPublicId())) { - if (RecordingMode.ALWAYS.equals(session.getSessionProperties().recordingMode())) { - // Immediately stop recording when last real participant left if - // RecordingMode.ALWAYS - log.info("Last participant left. Stopping recording for session {}", sessionId); - recordingManager.stopRecording(session, null, reason); - evictParticipant(session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), - null, null, "EVICT_RECORDER"); - } else if (RecordingMode.MANUAL.equals(session.getSessionProperties().recordingMode())) { - // Start countdown to stop recording if RecordingMode.MANUAL (will be aborted if - // a Publisher starts before timeout) - log.info( - "Last participant left. Starting {} seconds countdown for stopping recording of session {}", - this.openviduConfig.getOpenviduRecordingAutostopTimeout(), sessionId); - recordingManager.initAutomaticRecordingStopThread(session); - } + // Start countdown + log.info("Last participant left. Starting {} seconds countdown for stopping recording of session {}", + this.openviduConfig.getOpenviduRecordingAutostopTimeout(), sessionId); + recordingManager.initAutomaticRecordingStopThread(session); } } @@ -300,11 +286,11 @@ public class KurentoSessionManager extends SessionManager { && session.getActivePublishers() == 0) { if (RecordingMode.ALWAYS.equals(session.getSessionProperties().recordingMode()) && !recordingManager.sessionIsBeingRecorded(session.getSessionId())) { - // Insecure session recording + // Start automatic recording for sessions configured with RecordingMode.ALWAYS new Thread(() -> { recordingManager.startRecording(session, new RecordingProperties.Builder().name("") - .outputMode(io.openvidu.java.client.Recording.OutputMode.COMPOSED) + .outputMode(session.getSessionProperties().defaultOutputMode()) .recordingLayout(session.getSessionProperties().defaultRecordingLayout()) .customLayout(session.getSessionProperties().defaultCustomLayout()).build()); }).start(); @@ -313,7 +299,7 @@ public class KurentoSessionManager extends SessionManager { // Abort automatic recording stop (user published before timeout) log.info("Participant {} published before timeout finished. Aborting automatic recording stop", participant.getParticipantPublicId()); - boolean stopAborted = recordingManager.abortAutomaticRecordingStopThread(session.getSessionId()); + boolean stopAborted = recordingManager.abortAutomaticRecordingStopThread(session); if (stopAborted) { log.info("Automatic recording stopped succesfully aborted"); } else { 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 bb64e045..0801d153 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 @@ -200,7 +200,7 @@ public class RecordingManager { recording = this.singleStreamRecordingService.stopRecording(session, recording, reason); break; } - this.abortAutomaticRecordingStopThread(session.getSessionId()); + this.abortAutomaticRecordingStopThread(session); return recording; } @@ -260,21 +260,6 @@ public class RecordingManager { return (this.sessionsRecordings.get(sessionId) != null); } - public boolean sessionIsBeingRecordedIndividual(String sessionId) { - Recording rec = this.sessionsRecordings.get(sessionId); - return (rec != null && io.openvidu.java.client.Recording.OutputMode.INDIVIDUAL.equals(rec.getOutputMode())); - } - - public boolean sessionIsBeingRecordedComposed(String sessionId) { - Recording rec = this.sessionsRecordings.get(sessionId); - return (rec != null && io.openvidu.java.client.Recording.OutputMode.COMPOSED.equals(rec.getOutputMode())); - } - - public boolean sessionIsBeingRecordedOnlyAudio(String sessionId) { - Recording rec = this.sessionsRecordings.get(sessionId); - return (rec != null && !rec.hasVideo()); - } - public Recording getStartedRecording(String recordingId) { return this.startedRecordings.get(recordingId); } @@ -363,26 +348,46 @@ public class RecordingManager { log.info("Stopping recording {} after {} seconds wait (no publisher published before timeout)", recordingId, this.openviduConfig.getOpenviduRecordingAutostopTimeout()); - this.stopRecording(null, recordingId, "automaticStop"); - this.automaticRecordingStopThreads.remove(session.getSessionId()); - - if (session.getParticipants().size() == 0 || (session.getParticipants().size() == 1 - && session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID) != null)) { - // Close session if there are no participants connected (except for RECORDER). - // This code won't be executed only when some user reconnects to the session - // but never publishing (publishers automatically abort this thread) - sessionManager.closeSessionAndEmptyCollections(session, "automaticStop"); - sessionManager.showTokens(); + if (this.automaticRecordingStopThreads.remove(session.getSessionId()) != null) { + if (session.getParticipants().size() == 0 || (session.getParticipants().size() == 1 + && session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID) != null)) { + // Close session if there are no participants connected (except for RECORDER). + // This code won't be executed only when some user reconnects to the session + // but never publishing (publishers automatically abort this thread) + log.info("Closing session {} after automatic stop of recording {}", session.getSessionId(), + recordingId); + sessionManager.closeSessionAndEmptyCollections(session, "automaticStop"); + sessionManager.showTokens(); + } else { + this.stopRecording(null, recordingId, "automaticStop"); + } + } else { + // This code is reachable if there already was an automatic stop of a recording + // caused by not user publishing within timeout after recording started, and a + // new automatic stop thread was started by last user leaving the session + log.warn("Recording {} was already automatically stopped by a previous thread", recordingId); } }, this.openviduConfig.getOpenviduRecordingAutostopTimeout(), TimeUnit.SECONDS); this.automaticRecordingStopThreads.putIfAbsent(session.getSessionId(), future); } - public boolean abortAutomaticRecordingStopThread(String sessionId) { - ScheduledFuture future = this.automaticRecordingStopThreads.remove(sessionId); + public boolean abortAutomaticRecordingStopThread(Session session) { + ScheduledFuture future = this.automaticRecordingStopThreads.remove(session.getSessionId()); if (future != null) { - return future.cancel(false); + boolean cancelled = future.cancel(false); + if (session.getParticipants().size() == 0 || (session.getParticipants().size() == 1 + && session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID) != null)) { + // Close session if there are no participants connected (except for RECORDER). + // This code will only be executed if recording is manually stopped during the + // automatic stop timeout, so the session must be also closed + log.info( + "Ongoing recording of session {} was explicetly stopped within timeout for automatic recording stop. Closing session", + session.getSessionId()); + sessionManager.closeSessionAndEmptyCollections(session, "automaticStop"); + sessionManager.showTokens(); + } + return cancelled; } else { return true; } 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 37f3a7be..69455614 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 @@ -54,6 +54,17 @@ public abstract class RecordingService { * store Recording entity) */ protected void generateRecordingMetadataFile(Recording recording) { + String folder = this.openviduConfig.getOpenViduRecordingPath() + recording.getId(); + boolean newFolderCreated = this.fileWriter.createFolderIfNotExists(folder); + + if (newFolderCreated) { + log.info( + "New folder {} created. This means the recording started for a session with no publishers or no media type compatible publishers", + folder); + } else { + log.info("Folder {} already existed. Some publisher is already being recorded", folder); + } + String filePath = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/" + RecordingManager.RECORDING_ENTITY_FILE + recording.getId(); String text = recording.toJson().toString(); diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java index 4794d0f0..ef64c2c9 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java @@ -58,7 +58,6 @@ import io.openvidu.server.kurento.core.KurentoParticipant; import io.openvidu.server.kurento.endpoint.PublisherEndpoint; import io.openvidu.server.recording.RecorderEndpointWrapper; import io.openvidu.server.recording.Recording; -import io.openvidu.server.utils.CommandExecutor; public class SingleStreamRecordingService extends RecordingService { @@ -90,7 +89,6 @@ public class SingleStreamRecordingService extends RecordingService { final int activePublishers = session.getActivePublishers(); final CountDownLatch recordingStartedCountdown = new CountDownLatch(activePublishers); - int incompatibleMediaTypePublishers = 0; for (Participant p : session.getParticipants()) { if (p.isStreaming()) { @@ -102,7 +100,6 @@ public class SingleStreamRecordingService extends RecordingService { log.error( "Cannot start single stream recorder for stream {} in session {}: {}. Skipping to next stream being published", p.getPublisherStreamId(), session.getSessionId(), e.getMessage()); - incompatibleMediaTypePublishers++; recordingStartedCountdown.countDown(); continue; } @@ -121,13 +118,6 @@ public class SingleStreamRecordingService extends RecordingService { log.error("Exception while waiting for state change", e); } - if (activePublishers == 0 || incompatibleMediaTypePublishers == activePublishers) { - // Recording started for a session with some user connected but no publishers - // or with no publisher having a compatible media type stream with the recording - // configuration. Must create recording root folder for storing metadata archive - this.fileWriter.createFolder(this.openviduConfig.getOpenViduRecordingPath() + recording.getId()); - } - this.generateRecordingMetadataFile(recording); this.updateRecordingManagerCollections(session, recording); this.sendRecordingStartedNotification(session, recording); diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/CustomFileWriter.java b/openvidu-server/src/main/java/io/openvidu/server/utils/CustomFileWriter.java index 004692b2..607f4123 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/utils/CustomFileWriter.java +++ b/openvidu-server/src/main/java/io/openvidu/server/utils/CustomFileWriter.java @@ -33,7 +33,7 @@ public class CustomFileWriter { try { this.writeAndCloseOnOutputStreamWriter(new FileOutputStream(filePath), text); } catch (IOException e) { - log.error("Couldn't create file {}. Error: ", filePath, e.getMessage()); + log.error("Couldn't create file {}. Error: {}", filePath, e.getMessage()); } } @@ -41,12 +41,18 @@ public class CustomFileWriter { try { this.writeAndCloseOnOutputStreamWriter(new FileOutputStream(filePath, false), text); } catch (IOException e) { - log.error("Couldn't overwrite file {}. Error: ", filePath, e.getMessage()); + log.error("Couldn't overwrite file {}. Error: {}", filePath, e.getMessage()); } } - public boolean createFolder(String path) { - return new File(path).mkdir(); + public boolean createFolderIfNotExists(String path) { + File folder = new File(path); + if (!folder.exists()) { + return folder.mkdir(); + } else { + return false; + } + } private void writeAndCloseOnOutputStreamWriter(FileOutputStream fos, String text) throws IOException {