mirror of https://github.com/OpenVidu/openvidu.git
openvidu-server: recording upload refactoring
parent
693e2e6583
commit
01894918b7
|
@ -61,8 +61,12 @@ import io.openvidu.server.kurento.kms.FixedOneKmsManager;
|
||||||
import io.openvidu.server.kurento.kms.KmsManager;
|
import io.openvidu.server.kurento.kms.KmsManager;
|
||||||
import io.openvidu.server.kurento.kms.LoadManager;
|
import io.openvidu.server.kurento.kms.LoadManager;
|
||||||
import io.openvidu.server.recording.DummyRecordingDownloader;
|
import io.openvidu.server.recording.DummyRecordingDownloader;
|
||||||
|
import io.openvidu.server.recording.DummyRecordingUploader;
|
||||||
import io.openvidu.server.recording.RecordingDownloader;
|
import io.openvidu.server.recording.RecordingDownloader;
|
||||||
|
import io.openvidu.server.recording.RecordingUploader;
|
||||||
import io.openvidu.server.recording.service.RecordingManager;
|
import io.openvidu.server.recording.service.RecordingManager;
|
||||||
|
import io.openvidu.server.recording.service.RecordingManagerUtils;
|
||||||
|
import io.openvidu.server.recording.service.RecordingManagerUtilsLocalStorage;
|
||||||
import io.openvidu.server.rpc.RpcHandler;
|
import io.openvidu.server.rpc.RpcHandler;
|
||||||
import io.openvidu.server.rpc.RpcNotificationService;
|
import io.openvidu.server.rpc.RpcNotificationService;
|
||||||
import io.openvidu.server.utils.CommandExecutor;
|
import io.openvidu.server.utils.CommandExecutor;
|
||||||
|
@ -184,6 +188,20 @@ public class OpenViduServer implements JsonRpcConfigurer {
|
||||||
return new KurentoParticipantEndpointConfig();
|
return new KurentoParticipantEndpointConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
@DependsOn({ "openviduConfig", "recordingManager" })
|
||||||
|
public RecordingManagerUtils recordingManagerUtils(OpenviduConfig openviduConfig,
|
||||||
|
RecordingManager recordingManager) {
|
||||||
|
return new RecordingManagerUtilsLocalStorage(openviduConfig, recordingManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public RecordingUploader recordingUpload() {
|
||||||
|
return new DummyRecordingUploader();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public RecordingDownloader recordingDownload() {
|
public RecordingDownloader recordingDownload() {
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package io.openvidu.server.recording;
|
||||||
|
|
||||||
|
public class DummyRecordingUploader implements RecordingUploader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uploadRecording(Recording recording, Runnable successCallback, Runnable errorCallback) {
|
||||||
|
// Just immediately run success callback function
|
||||||
|
successCallback.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storeAsUploadingRecording(String recording) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBeingUploaded(String recordingId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package io.openvidu.server.recording;
|
||||||
|
|
||||||
|
public interface RecordingUploader {
|
||||||
|
|
||||||
|
void uploadRecording(Recording recording, Runnable successCallback, Runnable errorCallback);
|
||||||
|
|
||||||
|
void storeAsUploadingRecording(String recording);
|
||||||
|
|
||||||
|
boolean isBeingUploaded(String recordingId);
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,14 @@
|
||||||
package io.openvidu.server.recording.service;
|
package io.openvidu.server.recording.service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.github.dockerjava.api.model.Bind;
|
import com.github.dockerjava.api.model.Bind;
|
||||||
import com.github.dockerjava.api.model.Volume;
|
import com.github.dockerjava.api.model.Volume;
|
||||||
|
|
||||||
import io.openvidu.client.OpenViduException;
|
import io.openvidu.client.OpenViduException;
|
||||||
import io.openvidu.java.client.RecordingProperties;
|
import io.openvidu.java.client.RecordingProperties;
|
||||||
import io.openvidu.server.cdr.CallDetailRecord;
|
import io.openvidu.server.cdr.CallDetailRecord;
|
||||||
|
@ -10,24 +17,22 @@ import io.openvidu.server.core.EndReason;
|
||||||
import io.openvidu.server.core.Session;
|
import io.openvidu.server.core.Session;
|
||||||
import io.openvidu.server.recording.Recording;
|
import io.openvidu.server.recording.Recording;
|
||||||
import io.openvidu.server.recording.RecordingDownloader;
|
import io.openvidu.server.recording.RecordingDownloader;
|
||||||
|
import io.openvidu.server.recording.RecordingUploader;
|
||||||
import io.openvidu.server.utils.QuarantineKiller;
|
import io.openvidu.server.utils.QuarantineKiller;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ComposedQuickStartRecordingService extends ComposedRecordingService {
|
public class ComposedQuickStartRecordingService extends ComposedRecordingService {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ComposedRecordingService.class);
|
private static final Logger log = LoggerFactory.getLogger(ComposedRecordingService.class);
|
||||||
|
|
||||||
public ComposedQuickStartRecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader, OpenviduConfig openviduConfig, CallDetailRecord cdr, QuarantineKiller quarantineKiller) {
|
public ComposedQuickStartRecordingService(RecordingManager recordingManager,
|
||||||
super(recordingManager, recordingDownloader, openviduConfig, cdr, quarantineKiller);
|
RecordingDownloader recordingDownloader, RecordingUploader recordingUploader, OpenviduConfig openviduConfig,
|
||||||
|
CallDetailRecord cdr, QuarantineKiller quarantineKiller) {
|
||||||
|
super(recordingManager, recordingDownloader, recordingUploader, openviduConfig, cdr, quarantineKiller);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopRecordingContainer(Session session, EndReason reason) {
|
public void stopRecordingContainer(Session session, EndReason reason) {
|
||||||
log.info("Stopping COMPOSED_QUICK_START of session {}. Reason: {}",
|
log.info("Stopping COMPOSED_QUICK_START of session {}. Reason: {}", session.getSessionId(),
|
||||||
session.getSessionId(), RecordingManager.finalReason(reason));
|
RecordingManager.finalReason(reason));
|
||||||
|
|
||||||
String containerId = this.sessionsContainers.get(session.getSessionId());
|
String containerId = this.sessionsContainers.get(session.getSessionId());
|
||||||
|
|
||||||
|
@ -35,7 +40,8 @@ public class ComposedQuickStartRecordingService extends ComposedRecordingService
|
||||||
try {
|
try {
|
||||||
dockerManager.removeDockerContainer(containerId, true);
|
dockerManager.removeDockerContainer(containerId, true);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Can't remove COMPOSED_QUICK_START recording container from session {}", session.getSessionId());
|
log.error("Can't remove COMPOSED_QUICK_START recording container from session {}",
|
||||||
|
session.getSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
containers.remove(containerId);
|
containers.remove(containerId);
|
||||||
|
@ -119,16 +125,18 @@ public class ComposedQuickStartRecordingService extends ComposedRecordingService
|
||||||
|
|
||||||
recording = updateRecordingAttributes(recording);
|
recording = updateRecordingAttributes(recording);
|
||||||
|
|
||||||
this.sealRecordingMetadataFileAsReady(recording, recording.getSize(), recording.getDuration(), getMetadataFilePath(recording));
|
this.sealRecordingMetadataFileAsReady(recording, recording.getSize(), recording.getDuration(),
|
||||||
|
getMetadataFilePath(recording));
|
||||||
cleanRecordingMaps(recording);
|
cleanRecordingMaps(recording);
|
||||||
|
|
||||||
final long timestamp = System.currentTimeMillis();
|
|
||||||
this.cdr.recordRecordingStatusChanged(recording, reason, timestamp, recording.getStatus());
|
|
||||||
|
|
||||||
if (session != null && reason != null) {
|
if (session != null && reason != null) {
|
||||||
this.recordingManager.sessionHandler.sendRecordingStoppedNotification(session, recording, reason);
|
this.recordingManager.sessionHandler.sendRecordingStoppedNotification(session, recording, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Recording[] finalRecordingArray = new Recording[1];
|
||||||
|
finalRecordingArray[0] = recording;
|
||||||
|
this.uploadRecording(finalRecordingArray[0], reason);
|
||||||
|
|
||||||
// Decrement active recordings
|
// Decrement active recordings
|
||||||
// ((KurentoSession) session).getKms().getActiveRecordings().decrementAndGet();
|
// ((KurentoSession) session).getKms().getActiveRecordings().decrementAndGet();
|
||||||
|
|
||||||
|
@ -138,32 +146,40 @@ public class ComposedQuickStartRecordingService extends ComposedRecordingService
|
||||||
public void runComposedQuickStartContainer(Session session) {
|
public void runComposedQuickStartContainer(Session session) {
|
||||||
// Start recording container if output mode=COMPOSED_QUICK_START
|
// Start recording container if output mode=COMPOSED_QUICK_START
|
||||||
Session recorderSession = session;
|
Session recorderSession = session;
|
||||||
io.openvidu.java.client.Recording.OutputMode defaultOutputMode = recorderSession.getSessionProperties().defaultOutputMode();
|
io.openvidu.java.client.Recording.OutputMode defaultOutputMode = recorderSession.getSessionProperties()
|
||||||
|
.defaultOutputMode();
|
||||||
if (io.openvidu.java.client.Recording.OutputMode.COMPOSED_QUICK_START.equals(defaultOutputMode)
|
if (io.openvidu.java.client.Recording.OutputMode.COMPOSED_QUICK_START.equals(defaultOutputMode)
|
||||||
&& sessionsContainers.get(recorderSession.getSessionId()) == null) {
|
&& sessionsContainers.get(recorderSession.getSessionId()) == null) {
|
||||||
// Retry to run if container is launched for the same session quickly after close it
|
// Retry to run if container is launched for the same session quickly after
|
||||||
|
// close it
|
||||||
int secondsToRetry = 10;
|
int secondsToRetry = 10;
|
||||||
int secondsBetweenRetries = 1;
|
int secondsBetweenRetries = 1;
|
||||||
int seconds = 0;
|
int seconds = 0;
|
||||||
boolean launched = false;
|
boolean launched = false;
|
||||||
while (!launched && seconds < secondsToRetry) {
|
while (!launched && seconds < secondsToRetry) {
|
||||||
try {
|
try {
|
||||||
log.info("Launching COMPOSED_QUICK_START recording container for session: {}", recorderSession.getSessionId());
|
log.info("Launching COMPOSED_QUICK_START recording container for session: {}",
|
||||||
|
recorderSession.getSessionId());
|
||||||
runContainer(recorderSession, new RecordingProperties.Builder().name("")
|
runContainer(recorderSession, new RecordingProperties.Builder().name("")
|
||||||
.outputMode(recorderSession.getSessionProperties().defaultOutputMode())
|
.outputMode(recorderSession.getSessionProperties().defaultOutputMode())
|
||||||
.recordingLayout(recorderSession.getSessionProperties().defaultRecordingLayout())
|
.recordingLayout(recorderSession.getSessionProperties().defaultRecordingLayout())
|
||||||
.customLayout(recorderSession.getSessionProperties().defaultCustomLayout()).build());
|
.customLayout(recorderSession.getSessionProperties().defaultCustomLayout()).build());
|
||||||
log.info("COMPOSED_QUICK_START recording container launched for session: {}", recorderSession.getSessionId());
|
log.info("COMPOSED_QUICK_START recording container launched for session: {}",
|
||||||
|
recorderSession.getSessionId());
|
||||||
launched = true;
|
launched = true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Failed to launch COMPOSED_QUICK_START recording container for session {}. Trying again in {} seconds", recorderSession.getSessionId(), secondsBetweenRetries);
|
log.warn(
|
||||||
|
"Failed to launch COMPOSED_QUICK_START recording container for session {}. Trying again in {} seconds",
|
||||||
|
recorderSession.getSessionId(), secondsBetweenRetries);
|
||||||
try {
|
try {
|
||||||
Thread.sleep(secondsBetweenRetries * 1000);
|
Thread.sleep(secondsBetweenRetries * 1000);
|
||||||
} catch (InterruptedException e2) {}
|
} catch (InterruptedException e2) {
|
||||||
|
}
|
||||||
seconds++;
|
seconds++;
|
||||||
} finally {
|
} finally {
|
||||||
if (seconds == secondsToRetry && !launched) {
|
if (seconds == secondsToRetry && !launched) {
|
||||||
log.error("Error launchaing COMPOSED_QUICK_ªSTART recording container for session {}", recorderSession.getSessionId());
|
log.error("Error launchaing COMPOSED_QUICK_ªSTART recording container for session {}",
|
||||||
|
recorderSession.getSessionId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ import io.openvidu.server.recording.CompositeWrapper;
|
||||||
import io.openvidu.server.recording.Recording;
|
import io.openvidu.server.recording.Recording;
|
||||||
import io.openvidu.server.recording.RecordingDownloader;
|
import io.openvidu.server.recording.RecordingDownloader;
|
||||||
import io.openvidu.server.recording.RecordingInfoUtils;
|
import io.openvidu.server.recording.RecordingInfoUtils;
|
||||||
|
import io.openvidu.server.recording.RecordingUploader;
|
||||||
import io.openvidu.server.utils.DockerManager;
|
import io.openvidu.server.utils.DockerManager;
|
||||||
import io.openvidu.server.utils.QuarantineKiller;
|
import io.openvidu.server.utils.QuarantineKiller;
|
||||||
|
|
||||||
|
@ -70,8 +71,9 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
protected DockerManager dockerManager;
|
protected DockerManager dockerManager;
|
||||||
|
|
||||||
public ComposedRecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader,
|
public ComposedRecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader,
|
||||||
OpenviduConfig openviduConfig, CallDetailRecord cdr, QuarantineKiller quarantineKiller) {
|
RecordingUploader recordingUploader, OpenviduConfig openviduConfig, CallDetailRecord cdr,
|
||||||
super(recordingManager, recordingDownloader, openviduConfig, cdr, quarantineKiller);
|
QuarantineKiller quarantineKiller) {
|
||||||
|
super(recordingManager, recordingDownloader, recordingUploader, openviduConfig, cdr, quarantineKiller);
|
||||||
this.dockerManager = new DockerManager();
|
this.dockerManager = new DockerManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,7 +280,9 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
log.warn("Deleting unusable files for recording {}", recordingId);
|
log.warn("Deleting unusable files for recording {}", recordingId);
|
||||||
if (HttpStatus.NO_CONTENT
|
if (HttpStatus.NO_CONTENT
|
||||||
.equals(this.recordingManager.deleteRecordingFromHost(recordingId, true))) {
|
.equals(this.recordingManager.deleteRecordingFromHost(recordingId, true))) {
|
||||||
log.warn("Files properly deleted");
|
log.warn("Files properly deleted for recording {}", recordingId);
|
||||||
|
} else {
|
||||||
|
log.warn("No files found for recording {}", recordingId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,13 +304,15 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
getMetadataFilePath(recording));
|
getMetadataFilePath(recording));
|
||||||
cleanRecordingMaps(recording);
|
cleanRecordingMaps(recording);
|
||||||
|
|
||||||
final long timestamp = System.currentTimeMillis();
|
|
||||||
this.cdr.recordRecordingStatusChanged(recording, reason, timestamp, recording.getStatus());
|
|
||||||
|
|
||||||
if (session != null && reason != null) {
|
if (session != null && reason != null) {
|
||||||
this.recordingManager.sessionHandler.sendRecordingStoppedNotification(session, recording, reason);
|
this.recordingManager.sessionHandler.sendRecordingStoppedNotification(session, recording, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Upload if necessary
|
||||||
|
final Recording[] finalRecordingArray = new Recording[1];
|
||||||
|
finalRecordingArray[0] = recording;
|
||||||
|
this.uploadRecording(finalRecordingArray[0], reason);
|
||||||
|
|
||||||
// Decrement active recordings
|
// Decrement active recordings
|
||||||
// ((KurentoSession) session).getKms().getActiveRecordings().decrementAndGet();
|
// ((KurentoSession) session).getKms().getActiveRecordings().decrementAndGet();
|
||||||
}
|
}
|
||||||
|
@ -354,6 +360,7 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
finalRecordingArray[0] = recording;
|
finalRecordingArray[0] = recording;
|
||||||
try {
|
try {
|
||||||
this.recordingDownloader.downloadRecording(finalRecordingArray[0], null, () -> {
|
this.recordingDownloader.downloadRecording(finalRecordingArray[0], null, () -> {
|
||||||
|
|
||||||
String filesPath = this.openviduConfig.getOpenViduRecordingPath() + finalRecordingArray[0].getId()
|
String filesPath = this.openviduConfig.getOpenViduRecordingPath() + finalRecordingArray[0].getId()
|
||||||
+ "/";
|
+ "/";
|
||||||
File videoFile = new File(filesPath + finalRecordingArray[0].getName() + ".webm");
|
File videoFile = new File(filesPath + finalRecordingArray[0].getName() + ".webm");
|
||||||
|
@ -364,16 +371,15 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
finalDuration,
|
finalDuration,
|
||||||
filesPath + RecordingManager.RECORDING_ENTITY_FILE + finalRecordingArray[0].getId());
|
filesPath + RecordingManager.RECORDING_ENTITY_FILE + finalRecordingArray[0].getId());
|
||||||
|
|
||||||
final long timestamp = System.currentTimeMillis();
|
|
||||||
cdr.recordRecordingStatusChanged(finalRecordingArray[0], reason, timestamp,
|
|
||||||
finalRecordingArray[0].getStatus());
|
|
||||||
|
|
||||||
// Decrement active recordings once it is downloaded
|
// Decrement active recordings once it is downloaded
|
||||||
((KurentoSession) session).getKms().getActiveRecordings().decrementAndGet();
|
((KurentoSession) session).getKms().getActiveRecordings().decrementAndGet();
|
||||||
|
|
||||||
// Now we can drop Media Node if waiting-idle-to-terminate
|
// Now we can drop Media Node if waiting-idle-to-terminate
|
||||||
this.quarantineKiller.dropMediaNode(session.getMediaNodeId());
|
this.quarantineKiller.dropMediaNode(session.getMediaNodeId());
|
||||||
|
|
||||||
|
// Upload if necessary
|
||||||
|
this.uploadRecording(finalRecordingArray[0], reason);
|
||||||
|
|
||||||
});
|
});
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error while downloading recording {}: {}", finalRecordingArray[0].getName(), e.getMessage());
|
log.error("Error while downloading recording {}: {}", finalRecordingArray[0].getName(), e.getMessage());
|
||||||
|
|
|
@ -72,6 +72,7 @@ import io.openvidu.server.kurento.kms.Kms;
|
||||||
import io.openvidu.server.kurento.kms.KmsManager;
|
import io.openvidu.server.kurento.kms.KmsManager;
|
||||||
import io.openvidu.server.recording.Recording;
|
import io.openvidu.server.recording.Recording;
|
||||||
import io.openvidu.server.recording.RecordingDownloader;
|
import io.openvidu.server.recording.RecordingDownloader;
|
||||||
|
import io.openvidu.server.recording.RecordingUploader;
|
||||||
import io.openvidu.server.utils.CustomFileManager;
|
import io.openvidu.server.utils.CustomFileManager;
|
||||||
import io.openvidu.server.utils.DockerManager;
|
import io.openvidu.server.utils.DockerManager;
|
||||||
import io.openvidu.server.utils.JsonUtils;
|
import io.openvidu.server.utils.JsonUtils;
|
||||||
|
@ -93,9 +94,15 @@ public class RecordingManager {
|
||||||
@Autowired
|
@Autowired
|
||||||
private SessionManager sessionManager;
|
private SessionManager sessionManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected RecordingManagerUtils recordingManagerUtils;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RecordingDownloader recordingDownloader;
|
private RecordingDownloader recordingDownloader;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RecordingUploader recordingUploader;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected OpenviduConfig openviduConfig;
|
protected OpenviduConfig openviduConfig;
|
||||||
|
|
||||||
|
@ -119,7 +126,7 @@ public class RecordingManager {
|
||||||
private ScheduledThreadPoolExecutor automaticRecordingStopExecutor = new ScheduledThreadPoolExecutor(
|
private ScheduledThreadPoolExecutor automaticRecordingStopExecutor = new ScheduledThreadPoolExecutor(
|
||||||
Runtime.getRuntime().availableProcessors());
|
Runtime.getRuntime().availableProcessors());
|
||||||
|
|
||||||
static final String RECORDING_ENTITY_FILE = ".recording.";
|
public static final String RECORDING_ENTITY_FILE = ".recording.";
|
||||||
public static final String IMAGE_NAME = "openvidu/openvidu-recording";
|
public static final String IMAGE_NAME = "openvidu/openvidu-recording";
|
||||||
static String IMAGE_TAG;
|
static String IMAGE_TAG;
|
||||||
|
|
||||||
|
@ -159,12 +166,12 @@ public class RecordingManager {
|
||||||
RecordingManager.IMAGE_TAG = openviduConfig.getOpenViduRecordingVersion();
|
RecordingManager.IMAGE_TAG = openviduConfig.getOpenViduRecordingVersion();
|
||||||
|
|
||||||
this.dockerManager = new DockerManager();
|
this.dockerManager = new DockerManager();
|
||||||
this.composedRecordingService = new ComposedRecordingService(this, recordingDownloader, openviduConfig, cdr,
|
this.composedRecordingService = new ComposedRecordingService(this, recordingDownloader, recordingUploader,
|
||||||
quarantineKiller);
|
|
||||||
this.composedQuickStartRecordingService = new ComposedQuickStartRecordingService(this, recordingDownloader,
|
|
||||||
openviduConfig, cdr, quarantineKiller);
|
openviduConfig, cdr, quarantineKiller);
|
||||||
this.singleStreamRecordingService = new SingleStreamRecordingService(this, recordingDownloader, openviduConfig,
|
this.composedQuickStartRecordingService = new ComposedQuickStartRecordingService(this, recordingDownloader,
|
||||||
cdr, quarantineKiller);
|
recordingUploader, openviduConfig, cdr, quarantineKiller);
|
||||||
|
this.singleStreamRecordingService = new SingleStreamRecordingService(this, recordingDownloader,
|
||||||
|
recordingUploader, openviduConfig, cdr, quarantineKiller);
|
||||||
|
|
||||||
log.info("Recording module required: Downloading openvidu/openvidu-recording:"
|
log.info("Recording module required: Downloading openvidu/openvidu-recording:"
|
||||||
+ openviduConfig.getOpenViduRecordingVersion() + " Docker image (350MB aprox)");
|
+ openviduConfig.getOpenViduRecordingVersion() + " Docker image (350MB aprox)");
|
||||||
|
@ -432,64 +439,105 @@ public class RecordingManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Recording> getFinishedRecordings() {
|
public Collection<Recording> getFinishedRecordings() {
|
||||||
return this.getAllRecordingsFromHost().stream().filter(recording -> recording.getStatus().equals(Status.ready))
|
return recordingManagerUtils.getAllRecordingsFromStorage().stream()
|
||||||
.collect(Collectors.toSet());
|
.filter(recording -> recording.getStatus().equals(Status.ready)).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recording getRecording(String recordingId) {
|
public Recording getRecording(String recordingId) {
|
||||||
return this.getRecordingFromHost(recordingId);
|
return recordingManagerUtils.getRecordingFromStorage(recordingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Recording> getAllRecordings() {
|
public Collection<Recording> getAllRecordings() {
|
||||||
return this.getAllRecordingsFromHost();
|
return recordingManagerUtils.getAllRecordingsFromStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFreeRecordingId(String sessionId) {
|
public String getFreeRecordingId(String sessionId) {
|
||||||
Set<String> recordingIds = this.getRecordingIdsFromHost();
|
return recordingManagerUtils.getFreeRecordingId(sessionId);
|
||||||
String recordingId = sessionId;
|
|
||||||
boolean isPresent = recordingIds.contains(recordingId);
|
|
||||||
int i = 1;
|
|
||||||
while (isPresent) {
|
|
||||||
recordingId = sessionId + "-" + i;
|
|
||||||
i++;
|
|
||||||
isPresent = recordingIds.contains(recordingId);
|
|
||||||
}
|
|
||||||
return recordingId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpStatus deleteRecordingFromHost(String recordingId, boolean force) {
|
public HttpStatus deleteRecordingFromHost(String recordingId, boolean force) {
|
||||||
|
|
||||||
if (!force && (this.startedRecordings.containsKey(recordingId)
|
if (this.startedRecordings.containsKey(recordingId) || this.startingRecordings.containsKey(recordingId)) {
|
||||||
|| this.startingRecordings.containsKey(recordingId))) {
|
if (!force) {
|
||||||
// Cannot delete an active recording
|
// Cannot delete an active recording
|
||||||
return HttpStatus.CONFLICT;
|
return HttpStatus.CONFLICT;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Recording recording = getRecordingFromHost(recordingId);
|
Recording recording = recordingManagerUtils.getRecordingFromStorage(recordingId);
|
||||||
if (recording == null) {
|
if (recording == null) {
|
||||||
return HttpStatus.NOT_FOUND;
|
return HttpStatus.NOT_FOUND;
|
||||||
}
|
}
|
||||||
if (Status.stopped.equals(recording.getStatus())) {
|
if (Status.stopped.equals(recording.getStatus())) {
|
||||||
// Recording is being downloaded from remote host
|
// Recording is being downloaded from remote host or being uploaded
|
||||||
log.warn("Cancelling ongoing download process of recording {}", recording.getId());
|
log.warn("Recording {} status is \"stopped\". Cancelling possible ongoing download process", recording.getId());
|
||||||
this.recordingDownloader.cancelDownload(recording.getId());
|
this.recordingDownloader.cancelDownload(recording.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
File folder = new File(this.openviduConfig.getOpenViduRecordingPath());
|
return recordingManagerUtils.deleteRecordingFromStorage(recordingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getAllRecordingIdsFromLocalStorage() {
|
||||||
|
File folder = new File(openviduConfig.getOpenViduRecordingPath());
|
||||||
|
File[] files = folder.listFiles();
|
||||||
|
|
||||||
|
Set<String> fileNamesNoExtension = new HashSet<>();
|
||||||
|
for (int i = 0; i < files.length; i++) {
|
||||||
|
if (files[i].isDirectory()) {
|
||||||
|
File[] innerFiles = files[i].listFiles();
|
||||||
|
for (int j = 0; j < innerFiles.length; j++) {
|
||||||
|
if (innerFiles[j].isFile()
|
||||||
|
&& innerFiles[j].getName().startsWith(RecordingManager.RECORDING_ENTITY_FILE)) {
|
||||||
|
fileNamesNoExtension
|
||||||
|
.add(innerFiles[j].getName().replaceFirst(RecordingManager.RECORDING_ENTITY_FILE, ""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileNamesNoExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpStatus deleteRecordingFromLocalStorage(String recordingId) {
|
||||||
|
File folder = new File(openviduConfig.getOpenViduRecordingPath());
|
||||||
File[] files = folder.listFiles();
|
File[] files = folder.listFiles();
|
||||||
for (int i = 0; i < files.length; i++) {
|
for (int i = 0; i < files.length; i++) {
|
||||||
if (files[i].isDirectory() && files[i].getName().equals(recordingId)) {
|
if (files[i].isDirectory() && files[i].getName().equals(recordingId)) {
|
||||||
// Correct folder. Delete it
|
// Correct folder. Delete it
|
||||||
try {
|
try {
|
||||||
FileUtils.deleteDirectory(files[i]);
|
FileUtils.deleteDirectory(files[i]);
|
||||||
|
return HttpStatus.NO_CONTENT;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Couldn't delete folder {}", files[i].getAbsolutePath());
|
log.error("Couldn't delete folder {}", files[i].getAbsolutePath());
|
||||||
|
return HttpStatus.INTERNAL_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return HttpStatus.NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
return HttpStatus.NO_CONTENT;
|
public File getRecordingEntityFileFromLocalStorage(String recordingId) {
|
||||||
|
String metadataFilePath = openviduConfig.getOpenViduRecordingPath() + recordingId + "/"
|
||||||
|
+ RecordingManager.RECORDING_ENTITY_FILE + recordingId;
|
||||||
|
return new File(metadataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Recording> getAllRecordingsFromLocalStorage() {
|
||||||
|
File folder = new File(openviduConfig.getOpenViduRecordingPath());
|
||||||
|
File[] files = folder.listFiles();
|
||||||
|
Set<Recording> recordingEntities = new HashSet<>();
|
||||||
|
for (int i = 0; i < files.length; i++) {
|
||||||
|
if (files[i].isDirectory()) {
|
||||||
|
File[] innerFiles = files[i].listFiles();
|
||||||
|
for (int j = 0; j < innerFiles.length; j++) {
|
||||||
|
Recording recording = getRecordingFromEntityFile(innerFiles[j]);
|
||||||
|
if (recording != null) {
|
||||||
|
recordingEntities.add(recording);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return recordingEntities;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recording getRecordingFromEntityFile(File file) {
|
public Recording getRecordingFromEntityFile(File file) {
|
||||||
|
@ -501,28 +549,27 @@ public class RecordingManager {
|
||||||
log.error("Error reading recording entity file {}: {}", file.getAbsolutePath(), (e.getMessage()));
|
log.error("Error reading recording entity file {}: {}", file.getAbsolutePath(), (e.getMessage()));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Recording recording = new Recording(json);
|
return getRecordingFromJson(json);
|
||||||
if (Status.ready.equals(recording.getStatus()) || Status.failed.equals(recording.getStatus())) {
|
|
||||||
recording.setUrl(getRecordingUrl(recording));
|
|
||||||
}
|
|
||||||
return recording;
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRecordingUrl(Recording recording) {
|
public Recording getRecordingFromJson(JsonObject json) {
|
||||||
return openviduConfig.getFinalUrl() + "recordings/" + recording.getId() + "/" + recording.getName() + "."
|
Recording recording = new Recording(json);
|
||||||
+ this.getExtensionFromRecording(recording);
|
if (Status.ready.equals(recording.getStatus())
|
||||||
|
&& composedQuickStartRecordingService.isBeingUploaded(recording)) {
|
||||||
|
// Recording has finished but is being uploaded
|
||||||
|
recording.setStatus(Status.stopped);
|
||||||
|
recording.setUrl(null);
|
||||||
|
} else if (Status.ready.equals(recording.getStatus()) || Status.failed.equals(recording.getStatus())) {
|
||||||
|
// Recording has been completely processed and must include URL
|
||||||
|
recording.setUrl(recordingManagerUtils.getRecordingUrl(recording));
|
||||||
|
}
|
||||||
|
return recording;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getExtensionFromRecording(Recording recording) {
|
public String getRecordingUrl(Recording recording) {
|
||||||
if (OutputMode.INDIVIDUAL.equals(recording.getOutputMode())) {
|
return recordingManagerUtils.getRecordingUrl(recording);
|
||||||
return "zip";
|
|
||||||
} else if (recording.hasVideo()) {
|
|
||||||
return "mp4";
|
|
||||||
} else {
|
|
||||||
return "webm";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initAutomaticRecordingStopThread(final Session session) {
|
public void initAutomaticRecordingStopThread(final Session session) {
|
||||||
|
@ -623,56 +670,6 @@ public class RecordingManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Recording getRecordingFromHost(String recordingId) {
|
|
||||||
log.info(this.openviduConfig.getOpenViduRecordingPath() + recordingId + "/"
|
|
||||||
+ RecordingManager.RECORDING_ENTITY_FILE + recordingId);
|
|
||||||
File file = new File(this.openviduConfig.getOpenViduRecordingPath() + recordingId + "/"
|
|
||||||
+ RecordingManager.RECORDING_ENTITY_FILE + recordingId);
|
|
||||||
log.info("File exists: " + file.exists());
|
|
||||||
Recording recording = this.getRecordingFromEntityFile(file);
|
|
||||||
return recording;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<Recording> getAllRecordingsFromHost() {
|
|
||||||
File folder = new File(this.openviduConfig.getOpenViduRecordingPath());
|
|
||||||
File[] files = folder.listFiles();
|
|
||||||
|
|
||||||
Set<Recording> recordingEntities = new HashSet<>();
|
|
||||||
for (int i = 0; i < files.length; i++) {
|
|
||||||
if (files[i].isDirectory()) {
|
|
||||||
File[] innerFiles = files[i].listFiles();
|
|
||||||
for (int j = 0; j < innerFiles.length; j++) {
|
|
||||||
Recording recording = this.getRecordingFromEntityFile(innerFiles[j]);
|
|
||||||
if (recording != null) {
|
|
||||||
recordingEntities.add(recording);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return recordingEntities;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> getRecordingIdsFromHost() {
|
|
||||||
File folder = new File(this.openviduConfig.getOpenViduRecordingPath());
|
|
||||||
File[] files = folder.listFiles();
|
|
||||||
|
|
||||||
Set<String> fileNamesNoExtension = new HashSet<>();
|
|
||||||
for (int i = 0; i < files.length; i++) {
|
|
||||||
if (files[i].isDirectory()) {
|
|
||||||
File[] innerFiles = files[i].listFiles();
|
|
||||||
for (int j = 0; j < innerFiles.length; j++) {
|
|
||||||
if (innerFiles[j].isFile()
|
|
||||||
&& innerFiles[j].getName().startsWith(RecordingManager.RECORDING_ENTITY_FILE)) {
|
|
||||||
fileNamesNoExtension
|
|
||||||
.add(innerFiles[j].getName().replaceFirst(RecordingManager.RECORDING_ENTITY_FILE, ""));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fileNamesNoExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkRecordingPaths(String openviduRecordingPath, String openviduRecordingCustomLayout)
|
private void checkRecordingPaths(String openviduRecordingPath, String openviduRecordingCustomLayout)
|
||||||
throws OpenViduException {
|
throws OpenViduException {
|
||||||
log.info("Initializing recording paths");
|
log.info("Initializing recording paths");
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
package io.openvidu.server.recording.service;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
import io.openvidu.java.client.Recording.OutputMode;
|
||||||
|
import io.openvidu.server.config.OpenviduConfig;
|
||||||
|
import io.openvidu.server.recording.Recording;
|
||||||
|
import io.openvidu.server.utils.JsonUtils;
|
||||||
|
|
||||||
|
public abstract class RecordingManagerUtils {
|
||||||
|
|
||||||
|
protected OpenviduConfig openviduConfig;
|
||||||
|
protected RecordingManager recordingManager;
|
||||||
|
|
||||||
|
protected JsonUtils jsonUtils = new JsonUtils();
|
||||||
|
|
||||||
|
public RecordingManagerUtils(OpenviduConfig openviduConfig, RecordingManager recordingManager) {
|
||||||
|
this.openviduConfig = openviduConfig;
|
||||||
|
this.recordingManager = recordingManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Recording getRecordingFromStorage(String recordingId);
|
||||||
|
|
||||||
|
public abstract Set<Recording> getAllRecordingsFromStorage();
|
||||||
|
|
||||||
|
public abstract HttpStatus deleteRecordingFromStorage(String recordingId);
|
||||||
|
|
||||||
|
protected abstract String getRecordingUrl(Recording recording);
|
||||||
|
|
||||||
|
protected abstract Set<String> getAllRecordingIdsFromStorage();
|
||||||
|
|
||||||
|
protected String getExtensionFromRecording(Recording recording) {
|
||||||
|
if (OutputMode.INDIVIDUAL.equals(recording.getOutputMode())) {
|
||||||
|
return "zip";
|
||||||
|
} else if (recording.hasVideo()) {
|
||||||
|
return "mp4";
|
||||||
|
} else {
|
||||||
|
return "webm";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFreeRecordingId(String sessionId) {
|
||||||
|
Set<String> recordingIds = getAllRecordingIdsFromStorage();
|
||||||
|
return getNextAvailableRecordingId(sessionId, recordingIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getNextAvailableRecordingId(String baseRecordingId, Set<String> existingRecordingIds) {
|
||||||
|
String recordingId = baseRecordingId;
|
||||||
|
boolean isPresent = existingRecordingIds.contains(recordingId);
|
||||||
|
int i = 1;
|
||||||
|
while (isPresent) {
|
||||||
|
recordingId = baseRecordingId + "-" + i;
|
||||||
|
i++;
|
||||||
|
isPresent = existingRecordingIds.contains(recordingId);
|
||||||
|
}
|
||||||
|
return recordingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package io.openvidu.server.recording.service;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
import io.openvidu.server.config.OpenviduConfig;
|
||||||
|
import io.openvidu.server.recording.Recording;
|
||||||
|
|
||||||
|
public class RecordingManagerUtilsLocalStorage extends RecordingManagerUtils {
|
||||||
|
|
||||||
|
public RecordingManagerUtilsLocalStorage(OpenviduConfig openviduConfig, RecordingManager recordingManager) {
|
||||||
|
super(openviduConfig, recordingManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Recording getRecordingFromStorage(String recordingId) {
|
||||||
|
File file = recordingManager.getRecordingEntityFileFromLocalStorage(recordingId);
|
||||||
|
return recordingManager.getRecordingFromEntityFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Recording> getAllRecordingsFromStorage() {
|
||||||
|
return recordingManager.getAllRecordingsFromLocalStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpStatus deleteRecordingFromStorage(String recordingId) {
|
||||||
|
return recordingManager.deleteRecordingFromLocalStorage(recordingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRecordingUrl(Recording recording) {
|
||||||
|
return openviduConfig.getFinalUrl() + "recordings/" + recording.getId() + "/" + recording.getName() + "."
|
||||||
|
+ this.getExtensionFromRecording(recording);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Set<String> getAllRecordingIdsFromStorage() {
|
||||||
|
File folder = new File(openviduConfig.getOpenViduRecordingPath());
|
||||||
|
File[] files = folder.listFiles();
|
||||||
|
|
||||||
|
Set<String> fileNamesNoExtension = new HashSet<>();
|
||||||
|
for (int i = 0; i < files.length; i++) {
|
||||||
|
if (files[i].isDirectory()) {
|
||||||
|
File[] innerFiles = files[i].listFiles();
|
||||||
|
for (int j = 0; j < innerFiles.length; j++) {
|
||||||
|
if (innerFiles[j].isFile()
|
||||||
|
&& innerFiles[j].getName().startsWith(RecordingManager.RECORDING_ENTITY_FILE)) {
|
||||||
|
fileNamesNoExtension
|
||||||
|
.add(innerFiles[j].getName().replaceFirst(RecordingManager.RECORDING_ENTITY_FILE, ""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileNamesNoExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import io.openvidu.client.OpenViduException;
|
import io.openvidu.client.OpenViduException;
|
||||||
import io.openvidu.client.OpenViduException.Code;
|
import io.openvidu.client.OpenViduException.Code;
|
||||||
|
import io.openvidu.java.client.Recording.Status;
|
||||||
import io.openvidu.java.client.RecordingLayout;
|
import io.openvidu.java.client.RecordingLayout;
|
||||||
import io.openvidu.java.client.RecordingProperties;
|
import io.openvidu.java.client.RecordingProperties;
|
||||||
import io.openvidu.server.cdr.CallDetailRecord;
|
import io.openvidu.server.cdr.CallDetailRecord;
|
||||||
|
@ -32,6 +33,7 @@ import io.openvidu.server.core.EndReason;
|
||||||
import io.openvidu.server.core.Session;
|
import io.openvidu.server.core.Session;
|
||||||
import io.openvidu.server.recording.Recording;
|
import io.openvidu.server.recording.Recording;
|
||||||
import io.openvidu.server.recording.RecordingDownloader;
|
import io.openvidu.server.recording.RecordingDownloader;
|
||||||
|
import io.openvidu.server.recording.RecordingUploader;
|
||||||
import io.openvidu.server.utils.CommandExecutor;
|
import io.openvidu.server.utils.CommandExecutor;
|
||||||
import io.openvidu.server.utils.CustomFileManager;
|
import io.openvidu.server.utils.CustomFileManager;
|
||||||
import io.openvidu.server.utils.QuarantineKiller;
|
import io.openvidu.server.utils.QuarantineKiller;
|
||||||
|
@ -44,14 +46,17 @@ public abstract class RecordingService {
|
||||||
protected OpenviduConfig openviduConfig;
|
protected OpenviduConfig openviduConfig;
|
||||||
protected RecordingManager recordingManager;
|
protected RecordingManager recordingManager;
|
||||||
protected RecordingDownloader recordingDownloader;
|
protected RecordingDownloader recordingDownloader;
|
||||||
|
protected RecordingUploader recordingUploader;
|
||||||
protected CallDetailRecord cdr;
|
protected CallDetailRecord cdr;
|
||||||
protected QuarantineKiller quarantineKiller;
|
protected QuarantineKiller quarantineKiller;
|
||||||
protected CustomFileManager fileWriter = new CustomFileManager();
|
protected CustomFileManager fileWriter = new CustomFileManager();
|
||||||
|
|
||||||
RecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader,
|
RecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader,
|
||||||
OpenviduConfig openviduConfig, CallDetailRecord cdr, QuarantineKiller quarantineKiller) {
|
RecordingUploader recordingUploader, OpenviduConfig openviduConfig, CallDetailRecord cdr,
|
||||||
|
QuarantineKiller quarantineKiller) {
|
||||||
this.recordingManager = recordingManager;
|
this.recordingManager = recordingManager;
|
||||||
this.recordingDownloader = recordingDownloader;
|
this.recordingDownloader = recordingDownloader;
|
||||||
|
this.recordingUploader = recordingUploader;
|
||||||
this.openviduConfig = openviduConfig;
|
this.openviduConfig = openviduConfig;
|
||||||
this.cdr = cdr;
|
this.cdr = cdr;
|
||||||
this.quarantineKiller = quarantineKiller;
|
this.quarantineKiller = quarantineKiller;
|
||||||
|
@ -105,9 +110,14 @@ public abstract class RecordingService {
|
||||||
*/
|
*/
|
||||||
protected Recording sealRecordingMetadataFileAsReady(Recording recording, long size, double duration,
|
protected Recording sealRecordingMetadataFileAsReady(Recording recording, long size, double duration,
|
||||||
String metadataFilePath) {
|
String metadataFilePath) {
|
||||||
io.openvidu.java.client.Recording.Status status = io.openvidu.java.client.Recording.Status.failed
|
Status status = Status.failed.equals(recording.getStatus()) ? Status.failed : Status.ready;
|
||||||
.equals(recording.getStatus()) ? io.openvidu.java.client.Recording.Status.failed
|
|
||||||
: io.openvidu.java.client.Recording.Status.ready;
|
if (Status.ready.equals(status)) {
|
||||||
|
// Prevent uploading recordings from being retrieved from REST API with "ready"
|
||||||
|
// status. This will force their status back to "stopped" on GET until upload
|
||||||
|
// process has finished
|
||||||
|
storeAsUploadingRecording(recording);
|
||||||
|
}
|
||||||
|
|
||||||
// Status is now failed or ready. Url property must be defined
|
// Status is now failed or ready. Url property must be defined
|
||||||
recording.setUrl(recordingManager.getRecordingUrl(recording));
|
recording.setUrl(recordingManager.getRecordingUrl(recording));
|
||||||
|
@ -195,6 +205,25 @@ public abstract class RecordingService {
|
||||||
return folderPath + RecordingManager.RECORDING_ENTITY_FILE + recording.getId();
|
return folderPath + RecordingManager.RECORDING_ENTITY_FILE + recording.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void uploadRecording(final Recording recording, EndReason reason) {
|
||||||
|
recordingUploader.uploadRecording(recording, () -> {
|
||||||
|
final long timestamp = System.currentTimeMillis();
|
||||||
|
cdr.recordRecordingStatusChanged(recording, reason, timestamp, recording.getStatus());
|
||||||
|
}, () -> {
|
||||||
|
final long timestamp = System.currentTimeMillis();
|
||||||
|
cdr.recordRecordingStatusChanged(recording, reason, timestamp,
|
||||||
|
io.openvidu.java.client.Recording.Status.failed);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void storeAsUploadingRecording(Recording recording) {
|
||||||
|
recordingUploader.storeAsUploadingRecording(recording.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isBeingUploaded(Recording recording) {
|
||||||
|
return recordingUploader.isBeingUploaded(recording.getId());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple wrapper for returning update RecordingProperties and a free
|
* Simple wrapper for returning update RecordingProperties and a free
|
||||||
* recordingId when starting a new recording
|
* recordingId when starting a new recording
|
||||||
|
|
|
@ -64,6 +64,7 @@ import io.openvidu.server.kurento.endpoint.PublisherEndpoint;
|
||||||
import io.openvidu.server.recording.RecorderEndpointWrapper;
|
import io.openvidu.server.recording.RecorderEndpointWrapper;
|
||||||
import io.openvidu.server.recording.Recording;
|
import io.openvidu.server.recording.Recording;
|
||||||
import io.openvidu.server.recording.RecordingDownloader;
|
import io.openvidu.server.recording.RecordingDownloader;
|
||||||
|
import io.openvidu.server.recording.RecordingUploader;
|
||||||
import io.openvidu.server.utils.QuarantineKiller;
|
import io.openvidu.server.utils.QuarantineKiller;
|
||||||
|
|
||||||
public class SingleStreamRecordingService extends RecordingService {
|
public class SingleStreamRecordingService extends RecordingService {
|
||||||
|
@ -76,8 +77,9 @@ public class SingleStreamRecordingService extends RecordingService {
|
||||||
private final String INDIVIDUAL_STREAM_METADATA_FILE = ".stream.";
|
private final String INDIVIDUAL_STREAM_METADATA_FILE = ".stream.";
|
||||||
|
|
||||||
public SingleStreamRecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader,
|
public SingleStreamRecordingService(RecordingManager recordingManager, RecordingDownloader recordingDownloader,
|
||||||
OpenviduConfig openviduConfig, CallDetailRecord cdr, QuarantineKiller quarantineKiller) {
|
RecordingUploader recordingUploader, OpenviduConfig openviduConfig, CallDetailRecord cdr,
|
||||||
super(recordingManager, recordingDownloader, openviduConfig, cdr, quarantineKiller);
|
QuarantineKiller quarantineKiller) {
|
||||||
|
super(recordingManager, recordingDownloader, recordingUploader, openviduConfig, cdr, quarantineKiller);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -179,10 +181,6 @@ public class SingleStreamRecordingService extends RecordingService {
|
||||||
}
|
}
|
||||||
finalRecordingArray[0] = this.sealMetadataFiles(finalRecordingArray[0]);
|
finalRecordingArray[0] = this.sealMetadataFiles(finalRecordingArray[0]);
|
||||||
|
|
||||||
final long timestamp = System.currentTimeMillis();
|
|
||||||
cdr.recordRecordingStatusChanged(finalRecordingArray[0], reason, timestamp,
|
|
||||||
finalRecordingArray[0].getStatus());
|
|
||||||
|
|
||||||
cleanRecordingWrappers(finalRecordingArray[0].getSessionId());
|
cleanRecordingWrappers(finalRecordingArray[0].getSessionId());
|
||||||
|
|
||||||
// Decrement active recordings once it is downloaded
|
// Decrement active recordings once it is downloaded
|
||||||
|
@ -191,6 +189,9 @@ public class SingleStreamRecordingService extends RecordingService {
|
||||||
// Now we can drop Media Node if waiting-idle-to-terminate
|
// Now we can drop Media Node if waiting-idle-to-terminate
|
||||||
this.quarantineKiller.dropMediaNode(session.getMediaNodeId());
|
this.quarantineKiller.dropMediaNode(session.getMediaNodeId());
|
||||||
|
|
||||||
|
// Upload if necessary
|
||||||
|
this.uploadRecording(finalRecordingArray[0], reason);
|
||||||
|
|
||||||
});
|
});
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error while downloading recording {}", finalRecordingArray[0].getName());
|
log.error("Error while downloading recording {}", finalRecordingArray[0].getName());
|
||||||
|
|
|
@ -20,6 +20,7 @@ package io.openvidu.server.utils;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.kurento.jsonrpc.Props;
|
import org.kurento.jsonrpc.Props;
|
||||||
|
@ -56,13 +57,15 @@ public class JsonUtils {
|
||||||
|
|
||||||
public JsonElement fromFileToJsonElement(String filePath)
|
public JsonElement fromFileToJsonElement(String filePath)
|
||||||
throws IOException, FileNotFoundException, JsonParseException, IllegalStateException {
|
throws IOException, FileNotFoundException, JsonParseException, IllegalStateException {
|
||||||
JsonElement json = null;
|
return fromReaderToJsonElement(new FileReader(filePath));
|
||||||
FileReader reader = null;
|
|
||||||
try {
|
|
||||||
reader = new FileReader(filePath);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JsonObject fromReaderToJsonObject(Reader reader) throws IOException {
|
||||||
|
return this.fromReaderToJsonElement(reader).getAsJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonElement fromReaderToJsonElement(Reader reader) throws IOException {
|
||||||
|
JsonElement json = null;
|
||||||
try {
|
try {
|
||||||
json = JsonParser.parseReader(reader);
|
json = JsonParser.parseReader(reader);
|
||||||
} catch (JsonParseException | IllegalStateException exception) {
|
} catch (JsonParseException | IllegalStateException exception) {
|
||||||
|
|
Loading…
Reference in New Issue