openvidu-server: bug fixes for automatic stop of recordings

pull/203/head
pabloFuente 2019-01-29 16:41:36 +01:00
parent 9168de7b33
commit 2b68b3d6de
5 changed files with 66 additions and 68 deletions

View File

@ -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,27 +190,15 @@ 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 {}",
// Start countdown
log.info("Last participant left. Starting {} seconds countdown for stopping recording of session {}",
this.openviduConfig.getOpenviduRecordingAutostopTimeout(), sessionId);
recordingManager.initAutomaticRecordingStopThread(session);
}
}
}
// Finally close websocket session if required
if (closeWebSocket) {
@ -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 {

View File

@ -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 (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;
}

View File

@ -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();

View File

@ -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);

View File

@ -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 {