mirror of https://github.com/OpenVidu/openvidu.git
openvidu-server: recording status refactoring
parent
a528e1da8e
commit
690900a45b
|
@ -450,8 +450,9 @@ public class OpenVidu {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a recording. The recording must have status
|
* Deletes a recording. The recording must have status
|
||||||
* {@link io.openvidu.java.client.Recording.Status#stopped} or
|
* {@link io.openvidu.java.client.Recording.Status#stopped},
|
||||||
* {@link io.openvidu.java.client.Recording.Status#available}
|
* {@link io.openvidu.java.client.Recording.Status#ready} or
|
||||||
|
* {@link io.openvidu.java.client.Recording.Status#failed}
|
||||||
*
|
*
|
||||||
* @param recordingId The id property of the recording you want to delete
|
* @param recordingId The id property of the recording you want to delete
|
||||||
*
|
*
|
||||||
|
|
|
@ -30,7 +30,8 @@ public class Recording {
|
||||||
public enum Status {
|
public enum Status {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recording is starting (cannot be stopped)
|
* The recording is starting (cannot be stopped). Some recording may not go
|
||||||
|
* through this status and directly reach "started" status
|
||||||
*/
|
*/
|
||||||
starting,
|
starting,
|
||||||
|
|
||||||
|
@ -40,27 +41,21 @@ public class Recording {
|
||||||
started,
|
started,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recording has finished OK
|
* The recording has stopped and is being processed. At some point it will reach
|
||||||
|
* "ready" status
|
||||||
*/
|
*/
|
||||||
stopped,
|
stopped,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recording has stopped but is being processed. This status will change to
|
* The recording has finished OK and is available for download through OpenVidu
|
||||||
* stopped/available or failed
|
* Server recordings endpoint:
|
||||||
|
* https://YOUR_OPENVIDUSERVER_IP/recordings/{RECORDING_ID}/{RECORDING_NAME}.{EXTENSION}
|
||||||
*/
|
*/
|
||||||
processing,
|
ready,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recording is available for downloading. This status is reached for all
|
* The recording has failed. This status may be reached from "starting",
|
||||||
* stopped recordings if
|
* "started" and "stopped" status
|
||||||
* <a href="https://openvidu.io/docs/reference-docs/openvidu-server-params/"
|
|
||||||
* target="_blank">OpenVidu Server configuration</a> property
|
|
||||||
* <code>openvidu.recording.public-access</code> is true
|
|
||||||
*/
|
|
||||||
available,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The recording has failed
|
|
||||||
*/
|
*/
|
||||||
failed;
|
failed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -359,7 +359,7 @@ export class OpenVidu {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a [[Recording]]. The recording must have status `stopped` or `available`
|
* Deletes a [[Recording]]. The recording must have status `stopped`, `ready` or `failed`
|
||||||
*
|
*
|
||||||
* @param recordingId
|
* @param recordingId
|
||||||
*
|
*
|
||||||
|
|
|
@ -101,7 +101,8 @@ export namespace Recording {
|
||||||
export enum Status {
|
export enum Status {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recording is starting (cannot be stopped)
|
* The recording is starting (cannot be stopped). Some recording may not go
|
||||||
|
* through this status and directly reach "started" status
|
||||||
*/
|
*/
|
||||||
starting = 'starting',
|
starting = 'starting',
|
||||||
|
|
||||||
|
@ -111,25 +112,21 @@ export namespace Recording {
|
||||||
started = 'started',
|
started = 'started',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recording has finished OK
|
* The recording has stopped and is being processed. At some point it will reach
|
||||||
*/
|
* "ready" status
|
||||||
|
*/
|
||||||
stopped = 'stopped',
|
stopped = 'stopped',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recording has stopped but is being processed. This status will change to
|
* The recording has finished OK and is available for download through OpenVidu
|
||||||
* stopped/available or failed when processing ends
|
* Server recordings endpoint:
|
||||||
*/
|
* https://YOUR_OPENVIDUSERVER_IP/recordings/{RECORDING_ID}/{RECORDING_NAME}.{EXTENSION}
|
||||||
processing = 'processing',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The recording is available for downloading. This status is reached for all
|
|
||||||
* stopped recordings if [OpenVidu Server configuration](https://openvidu.io/docs/reference-docs/openvidu-server-params/)
|
|
||||||
* property `openvidu.recording.public-access` is true
|
|
||||||
*/
|
*/
|
||||||
available = 'available',
|
ready = 'ready',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recording has failed
|
* The recording has failed. This status may be reached from "starting",
|
||||||
|
* "started" and "stopped" status
|
||||||
*/
|
*/
|
||||||
failed = 'failed'
|
failed = 'failed'
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,8 @@ public class CDREventRecording extends CDREventEnd {
|
||||||
protected Recording recording;
|
protected Recording recording;
|
||||||
|
|
||||||
// recordingStarted
|
// recordingStarted
|
||||||
public CDREventRecording(String sessionId, Recording recording) {
|
public CDREventRecording(Recording recording) {
|
||||||
super(CDREventName.recordingStarted, sessionId, recording.getCreatedAt());
|
super(CDREventName.recordingStarted, recording.getSessionId(), recording.getCreatedAt());
|
||||||
this.recording = recording;
|
this.recording = recording;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,33 +20,41 @@ package io.openvidu.server.cdr;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
import io.openvidu.java.client.Recording.Status;
|
import io.openvidu.java.client.Recording.Status;
|
||||||
|
import io.openvidu.java.client.RecordingLayout;
|
||||||
import io.openvidu.server.core.EndReason;
|
import io.openvidu.server.core.EndReason;
|
||||||
import io.openvidu.server.recording.Recording;
|
import io.openvidu.server.recording.Recording;
|
||||||
|
|
||||||
public class CDREventRecordingStatus extends CDREventRecording {
|
public class CDREventRecordingStatus extends CDREventEnd {
|
||||||
|
|
||||||
|
private Recording recording;
|
||||||
private Status status;
|
private Status status;
|
||||||
|
|
||||||
public CDREventRecordingStatus(String sessionId, Recording recording, Status status) {
|
public CDREventRecordingStatus(Recording recording, Long startTime, EndReason reason, Long timestamp,
|
||||||
super(sessionId, recording);
|
Status status) {
|
||||||
this.eventName = CDREventName.recordingStatusChanged;
|
super(CDREventName.recordingStatusChanged, recording.getSessionId(), startTime, reason, timestamp);
|
||||||
|
this.recording = recording;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CDREventRecordingStatus(CDREventRecording recordingStartedEvent, Recording recording, EndReason finalReason,
|
|
||||||
long timestamp, Status status) {
|
|
||||||
super(recordingStartedEvent, recording, finalReason, timestamp);
|
|
||||||
this.eventName = CDREventName.recordingStatusChanged;
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Status getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonObject toJson() {
|
public JsonObject toJson() {
|
||||||
JsonObject json = super.toJson();
|
JsonObject json = super.toJson();
|
||||||
|
json.addProperty("id", this.recording.getId());
|
||||||
|
json.addProperty("name", this.recording.getName());
|
||||||
|
json.addProperty("outputMode", this.recording.getOutputMode().name());
|
||||||
|
if (io.openvidu.java.client.Recording.OutputMode.COMPOSED.equals(this.recording.getOutputMode())
|
||||||
|
&& this.recording.hasVideo()) {
|
||||||
|
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());
|
||||||
|
json.addProperty("duration", this.recording.getDuration());
|
||||||
json.addProperty("status", this.status.name());
|
json.addProperty("status", this.status.name());
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,34 +198,25 @@ public class CallDetailRecord {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recordRecordingStarted(String sessionId, Recording recording) {
|
public void recordRecordingStarted(Recording recording) {
|
||||||
CDREventRecording recordingStartedEvent = new CDREventRecording(sessionId, recording);
|
CDREventRecording recordingStartedEvent = new CDREventRecording(recording);
|
||||||
this.recordings.putIfAbsent(recording.getId(), recordingStartedEvent);
|
this.recordings.putIfAbsent(recording.getId(), recordingStartedEvent);
|
||||||
this.log(new CDREventRecording(sessionId, recording));
|
this.log(recordingStartedEvent);
|
||||||
this.recordRecordingStatusChanged(sessionId, recording, Status.started);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recordRecordingStopped(String sessionId, Recording recording, EndReason reason) {
|
public void recordRecordingStopped(Recording recording, EndReason reason, long timestamp) {
|
||||||
CDREventRecording recordingStartedEvent = this.recordings.remove(recording.getId());
|
CDREventRecording recordingStartedEvent = this.recordings.remove(recording.getId());
|
||||||
final long timestamp = System.currentTimeMillis();
|
|
||||||
CDREventRecording recordingStoppedEvent = new CDREventRecording(recordingStartedEvent, recording,
|
CDREventRecording recordingStoppedEvent = new CDREventRecording(recordingStartedEvent, recording,
|
||||||
RecordingManager.finalReason(reason), timestamp);
|
RecordingManager.finalReason(reason), timestamp);
|
||||||
this.log(recordingStoppedEvent);
|
this.log(recordingStoppedEvent);
|
||||||
|
|
||||||
this.recordRecordingStatusChanged(recordingStartedEvent, recording, RecordingManager.finalReason(reason),
|
|
||||||
timestamp, Status.stopped);
|
|
||||||
|
|
||||||
// Summary: update ended recording
|
// Summary: update ended recording
|
||||||
sessionManager.getAccumulatedRecordings(sessionId).add(recordingStoppedEvent);
|
sessionManager.getAccumulatedRecordings(recording.getSessionId()).add(recordingStoppedEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recordRecordingStatusChanged(String sessionId, Recording recording, Status status) {
|
public void recordRecordingStatusChanged(Recording recording, EndReason finalReason, long timestamp,
|
||||||
this.log(new CDREventRecordingStatus(sessionId, recording, status));
|
Status status) {
|
||||||
}
|
this.log(new CDREventRecordingStatus(recording, recording.getCreatedAt(), finalReason, timestamp, status));
|
||||||
|
|
||||||
public void recordRecordingStatusChanged(CDREventRecording recordingStartedEvent, Recording recording,
|
|
||||||
EndReason finalReason, long timestamp, Status status) {
|
|
||||||
this.log(new CDREventRecordingStatus(recordingStartedEvent, recording, finalReason, timestamp, status));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void log(CDREvent event) {
|
private void log(CDREvent event) {
|
||||||
|
|
|
@ -13,4 +13,8 @@ public class DummyRecordingDownloader implements RecordingDownloader {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelDownload(String recordingId) {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,4 +25,6 @@ public interface RecordingDownloader {
|
||||||
public void downloadRecording(Recording recording, Collection<String> streamIds, Runnable callback)
|
public void downloadRecording(Recording recording, Collection<String> streamIds, Runnable callback)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
|
public void cancelDownload(String recordingId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,10 +94,10 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Recording stopRecording(Session session, Recording recording, EndReason reason) {
|
public Recording stopRecording(Session session, Recording recording, EndReason reason) {
|
||||||
|
recording = this.sealRecordingMetadataFileAsStopped(recording);
|
||||||
if (recording.hasVideo()) {
|
if (recording.hasVideo()) {
|
||||||
return this.stopRecordingWithVideo(session, recording, reason);
|
return this.stopRecordingWithVideo(session, recording, reason);
|
||||||
} else {
|
} else {
|
||||||
recording = this.sealRecordingMetadataFileAsProcessing(recording);
|
|
||||||
return this.stopRecordingAudioOnly(session, recording, reason, 0);
|
return this.stopRecordingAudioOnly(session, recording, reason, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,7 +240,7 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
if (containerId == null) {
|
if (containerId == null) {
|
||||||
|
|
||||||
// Session was closed while recording container was initializing
|
// Session was closed while recording container was initializing
|
||||||
// Wait until containerId is available and force its stop and removal
|
// Wait until containerId is available and force its stop and deletion
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
log.warn("Session closed while starting recording container");
|
log.warn("Session closed while starting recording container");
|
||||||
boolean containerClosed = false;
|
boolean containerClosed = false;
|
||||||
|
@ -311,25 +311,23 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
log.error("COMPOSED recording {} with hasVideo=true has not video track", recordingId);
|
log.error("COMPOSED recording {} with hasVideo=true has not video track", recordingId);
|
||||||
recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
|
recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
|
||||||
} else {
|
} else {
|
||||||
recording.setStatus(io.openvidu.java.client.Recording.Status.stopped);
|
recording.setStatus(io.openvidu.java.client.Recording.Status.ready);
|
||||||
recording.setDuration(infoUtils.getDurationInSeconds());
|
recording.setDuration(infoUtils.getDurationInSeconds());
|
||||||
recording.setSize(infoUtils.getSizeInBytes());
|
recording.setSize(infoUtils.getSizeInBytes());
|
||||||
recording.setResolution(infoUtils.videoWidth() + "x" + infoUtils.videoHeight());
|
recording.setResolution(infoUtils.videoWidth() + "x" + infoUtils.videoHeight());
|
||||||
recording.setHasAudio(infoUtils.hasAudio());
|
recording.setHasAudio(infoUtils.hasAudio());
|
||||||
recording.setHasVideo(infoUtils.hasVideo());
|
recording.setHasVideo(infoUtils.hasVideo());
|
||||||
}
|
}
|
||||||
|
|
||||||
infoUtils.deleteFilePath();
|
infoUtils.deleteFilePath();
|
||||||
|
|
||||||
recording = this.recordingManager.updateRecordingUrl(recording);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
|
recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
|
||||||
throw new OpenViduException(Code.RECORDING_REPORT_ERROR_CODE,
|
throw new OpenViduException(Code.RECORDING_REPORT_ERROR_CODE,
|
||||||
"There was an error generating the metadata report file for the recording");
|
"There was an error generating the metadata report file for the recording");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cdr.recordRecordingStopped(recording.getSessionId(), recording, reason);
|
final long timestamp = System.currentTimeMillis();
|
||||||
|
this.cdr.recordRecordingStopped(recording, reason, timestamp);
|
||||||
|
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);
|
||||||
|
@ -383,9 +381,13 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
long finalSize = videoFile.length();
|
long finalSize = videoFile.length();
|
||||||
double finalDuration = (double) compositeWrapper.getDuration() / 1000;
|
double finalDuration = (double) compositeWrapper.getDuration() / 1000;
|
||||||
this.updateFilePermissions(filesPath);
|
this.updateFilePermissions(filesPath);
|
||||||
finalRecordingArray[0] = this.sealRecordingMetadataFileAsStopped(recording, finalSize, finalDuration,
|
finalRecordingArray[0] = this.sealRecordingMetadataFileAsReady(recording, finalSize, finalDuration,
|
||||||
filesPath + RecordingManager.RECORDING_ENTITY_FILE + recording.getId());
|
filesPath + RecordingManager.RECORDING_ENTITY_FILE + recording.getId());
|
||||||
cdr.recordRecordingStopped(finalRecordingArray[0].getSessionId(), finalRecordingArray[0], reason);
|
|
||||||
|
final long timestamp = System.currentTimeMillis();
|
||||||
|
cdr.recordRecordingStopped(finalRecordingArray[0], reason, timestamp);
|
||||||
|
cdr.recordRecordingStatusChanged(finalRecordingArray[0], reason, timestamp,
|
||||||
|
finalRecordingArray[0].getStatus());
|
||||||
});
|
});
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error while downloading recording {}: {}", recording.getName(), e.getMessage());
|
log.error("Error while downloading recording {}: {}", recording.getName(), e.getMessage());
|
||||||
|
|
|
@ -55,6 +55,7 @@ import io.openvidu.client.OpenViduException;
|
||||||
import io.openvidu.client.OpenViduException.Code;
|
import io.openvidu.client.OpenViduException.Code;
|
||||||
import io.openvidu.client.internal.ProtocolElements;
|
import io.openvidu.client.internal.ProtocolElements;
|
||||||
import io.openvidu.java.client.Recording.OutputMode;
|
import io.openvidu.java.client.Recording.OutputMode;
|
||||||
|
import io.openvidu.java.client.Recording.Status;
|
||||||
import io.openvidu.java.client.RecordingProperties;
|
import io.openvidu.java.client.RecordingProperties;
|
||||||
import io.openvidu.server.cdr.CallDetailRecord;
|
import io.openvidu.server.cdr.CallDetailRecord;
|
||||||
import io.openvidu.server.config.OpenviduConfig;
|
import io.openvidu.server.config.OpenviduConfig;
|
||||||
|
@ -187,7 +188,9 @@ public class RecordingManager {
|
||||||
}
|
}
|
||||||
this.updateRecordingManagerCollections(session, recording);
|
this.updateRecordingManagerCollections(session, recording);
|
||||||
|
|
||||||
this.cdr.recordRecordingStarted(session.getSessionId(), recording);
|
this.cdr.recordRecordingStarted(recording);
|
||||||
|
this.cdr.recordRecordingStatusChanged(recording, null, recording.getCreatedAt(),
|
||||||
|
io.openvidu.java.client.Recording.Status.started);
|
||||||
|
|
||||||
if (!(OutputMode.COMPOSED.equals(properties.outputMode()) && properties.hasVideo())) {
|
if (!(OutputMode.COMPOSED.equals(properties.outputMode()) && properties.hasVideo())) {
|
||||||
// Directly send recording started notification for all cases except for
|
// Directly send recording started notification for all cases except for
|
||||||
|
@ -211,6 +214,10 @@ public class RecordingManager {
|
||||||
} else {
|
} else {
|
||||||
recording = this.sessionsRecordings.get(session.getSessionId());
|
recording = this.sessionsRecordings.get(session.getSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final long timestamp = System.currentTimeMillis();
|
||||||
|
this.cdr.recordRecordingStatusChanged(recording, reason, timestamp, Status.stopped);
|
||||||
|
|
||||||
switch (recording.getOutputMode()) {
|
switch (recording.getOutputMode()) {
|
||||||
case COMPOSED:
|
case COMPOSED:
|
||||||
recording = this.composedRecordingService.stopRecording(session, recording, reason);
|
recording = this.composedRecordingService.stopRecording(session, recording, reason);
|
||||||
|
@ -312,8 +319,7 @@ public class RecordingManager {
|
||||||
|
|
||||||
public Collection<Recording> getFinishedRecordings() {
|
public Collection<Recording> getFinishedRecordings() {
|
||||||
return this.getAllRecordingsFromHost().stream()
|
return this.getAllRecordingsFromHost().stream()
|
||||||
.filter(recording -> (recording.getStatus().equals(io.openvidu.java.client.Recording.Status.stopped)
|
.filter(recording -> recording.getStatus().equals(io.openvidu.java.client.Recording.Status.ready))
|
||||||
|| recording.getStatus().equals(io.openvidu.java.client.Recording.Status.available)))
|
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,6 +358,11 @@ public class RecordingManager {
|
||||||
if (recording == null) {
|
if (recording == null) {
|
||||||
return HttpStatus.NOT_FOUND;
|
return HttpStatus.NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
if (io.openvidu.java.client.Recording.Status.stopped.equals(recording.getStatus())) {
|
||||||
|
// Recording is being downloaded from remote host
|
||||||
|
log.warn("Cancelling ongoing download process of recording {}", recording.getId());
|
||||||
|
this.recordingDownloader.cancelDownload(recording.getId());
|
||||||
|
}
|
||||||
|
|
||||||
File folder = new File(this.openviduConfig.getOpenViduRecordingPath());
|
File folder = new File(this.openviduConfig.getOpenViduRecordingPath());
|
||||||
File[] files = folder.listFiles();
|
File[] files = folder.listFiles();
|
||||||
|
@ -443,30 +454,6 @@ public class RecordingManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recording updateRecordingUrl(Recording recording) {
|
|
||||||
if (openviduConfig.getOpenViduRecordingPublicAccess()) {
|
|
||||||
if (io.openvidu.java.client.Recording.Status.stopped.equals(recording.getStatus())) {
|
|
||||||
|
|
||||||
String extension;
|
|
||||||
switch (recording.getOutputMode()) {
|
|
||||||
case COMPOSED:
|
|
||||||
extension = recording.hasVideo() ? "mp4" : "webm";
|
|
||||||
break;
|
|
||||||
case INDIVIDUAL:
|
|
||||||
extension = "zip";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
extension = "mp4";
|
|
||||||
}
|
|
||||||
|
|
||||||
recording.setUrl(this.openviduConfig.getFinalUrl() + "recordings/" + recording.getId() + "/"
|
|
||||||
+ recording.getName() + "." + extension);
|
|
||||||
recording.setStatus(io.openvidu.java.client.Recording.Status.available);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return recording;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Recording getRecordingFromHost(String recordingId) {
|
private Recording getRecordingFromHost(String recordingId) {
|
||||||
log.info(this.openviduConfig.getOpenViduRecordingPath() + recordingId + "/"
|
log.info(this.openviduConfig.getOpenViduRecordingPath() + recordingId + "/"
|
||||||
+ RecordingManager.RECORDING_ENTITY_FILE + recordingId);
|
+ RecordingManager.RECORDING_ENTITY_FILE + recordingId);
|
||||||
|
@ -474,9 +461,6 @@ public class RecordingManager {
|
||||||
+ RecordingManager.RECORDING_ENTITY_FILE + recordingId);
|
+ RecordingManager.RECORDING_ENTITY_FILE + recordingId);
|
||||||
log.info("File exists: " + file.exists());
|
log.info("File exists: " + file.exists());
|
||||||
Recording recording = this.getRecordingFromEntityFile(file);
|
Recording recording = this.getRecordingFromEntityFile(file);
|
||||||
if (recording != null) {
|
|
||||||
this.updateRecordingUrl(recording);
|
|
||||||
}
|
|
||||||
return recording;
|
return recording;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,7 +475,6 @@ public class RecordingManager {
|
||||||
for (int j = 0; j < innerFiles.length; j++) {
|
for (int j = 0; j < innerFiles.length; j++) {
|
||||||
Recording recording = this.getRecordingFromEntityFile(innerFiles[j]);
|
Recording recording = this.getRecordingFromEntityFile(innerFiles[j]);
|
||||||
if (recording != null) {
|
if (recording != null) {
|
||||||
this.updateRecordingUrl(recording);
|
|
||||||
recordingEntities.add(recording);
|
recordingEntities.add(recording);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ 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;
|
||||||
|
@ -82,31 +81,29 @@ public abstract class RecordingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update and overwrites metadata recording file to set it in "processing"
|
* Update and overwrites metadata recording file to set it in "stopped" status.
|
||||||
* status. Recording size and duration will remain as 0
|
* Recording size and duration will remain as 0
|
||||||
*
|
*
|
||||||
* @return updated Recording object
|
* @return updated Recording object
|
||||||
*/
|
*/
|
||||||
protected Recording sealRecordingMetadataFileAsProcessing(Recording recording) {
|
protected Recording sealRecordingMetadataFileAsStopped(Recording recording) {
|
||||||
final String entityFile = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/"
|
final String entityFile = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/"
|
||||||
+ RecordingManager.RECORDING_ENTITY_FILE + recording.getId();
|
+ RecordingManager.RECORDING_ENTITY_FILE + recording.getId();
|
||||||
Recording rec = this.sealRecordingMetadataFile(recording, 0, 0,
|
return this.sealRecordingMetadataFile(recording, 0, 0, io.openvidu.java.client.Recording.Status.stopped,
|
||||||
io.openvidu.java.client.Recording.Status.processing, entityFile);
|
entityFile);
|
||||||
this.cdr.recordRecordingStatusChanged(recording.getSessionId(), recording, Status.processing);
|
|
||||||
return rec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update and overwrites metadata recording file to set it in "stopped" (or
|
* Update and overwrites metadata recording file to set it in "ready" (or
|
||||||
* "failed") status
|
* "failed") status
|
||||||
*
|
*
|
||||||
* @return updated Recording object
|
* @return updated Recording object
|
||||||
*/
|
*/
|
||||||
protected Recording sealRecordingMetadataFileAsStopped(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
|
io.openvidu.java.client.Recording.Status status = io.openvidu.java.client.Recording.Status.failed
|
||||||
.equals(recording.getStatus()) ? io.openvidu.java.client.Recording.Status.failed
|
.equals(recording.getStatus()) ? io.openvidu.java.client.Recording.Status.failed
|
||||||
: io.openvidu.java.client.Recording.Status.stopped;
|
: io.openvidu.java.client.Recording.Status.ready;
|
||||||
|
|
||||||
final String entityFile = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/"
|
final String entityFile = this.openviduConfig.getOpenViduRecordingPath() + recording.getId() + "/"
|
||||||
+ RecordingManager.RECORDING_ENTITY_FILE + recording.getId();
|
+ RecordingManager.RECORDING_ENTITY_FILE + recording.getId();
|
||||||
|
@ -119,7 +116,6 @@ public abstract class RecordingService {
|
||||||
recording.setSize(size); // Size in bytes
|
recording.setSize(size); // Size in bytes
|
||||||
recording.setDuration(duration > 0 ? duration : 0); // Duration in seconds
|
recording.setDuration(duration > 0 ? duration : 0); // Duration in seconds
|
||||||
this.fileWriter.overwriteFile(metadataFilePath, recording.toJson().toString());
|
this.fileWriter.overwriteFile(metadataFilePath, recording.toJson().toString());
|
||||||
recording = this.recordingManager.updateRecordingUrl(recording);
|
|
||||||
|
|
||||||
log.info("Sealed recording metadata file at {} with status [{}]", metadataFilePath, status.name());
|
log.info("Sealed recording metadata file at {} with status [{}]", metadataFilePath, status.name());
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ public class SingleStreamRecordingService extends RecordingService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Recording stopRecording(Session session, Recording recording, EndReason reason) {
|
public Recording stopRecording(Session session, Recording recording, EndReason reason) {
|
||||||
recording = this.sealRecordingMetadataFileAsProcessing(recording);
|
recording = this.sealRecordingMetadataFileAsStopped(recording);
|
||||||
return this.stopRecording(session, recording, reason, 0);
|
return this.stopRecording(session, recording, reason, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +171,11 @@ public class SingleStreamRecordingService extends RecordingService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finalRecordingArray[0] = this.sealMetadataFiles(recording);
|
finalRecordingArray[0] = this.sealMetadataFiles(recording);
|
||||||
cdr.recordRecordingStopped(finalRecordingArray[0].getSessionId(), finalRecordingArray[0], reason);
|
|
||||||
|
final long timestamp = System.currentTimeMillis();
|
||||||
|
cdr.recordRecordingStopped(finalRecordingArray[0], reason, timestamp);
|
||||||
|
cdr.recordRecordingStatusChanged(finalRecordingArray[0], reason, timestamp,
|
||||||
|
finalRecordingArray[0].getStatus());
|
||||||
});
|
});
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error while downloading recording {}", recording.getName());
|
log.error("Error while downloading recording {}", recording.getName());
|
||||||
|
@ -427,7 +431,7 @@ public class SingleStreamRecordingService extends RecordingService {
|
||||||
double duration = (double) (maxEndTime - minStartTime) / 1000;
|
double duration = (double) (maxEndTime - minStartTime) / 1000;
|
||||||
duration = duration > 0 ? duration : 0;
|
duration = duration > 0 ? duration : 0;
|
||||||
|
|
||||||
recording = this.sealRecordingMetadataFileAsStopped(recording, accumulatedSize, duration, metadataFilePath);
|
recording = this.sealRecordingMetadataFileAsReady(recording, accumulatedSize, duration, metadataFilePath);
|
||||||
|
|
||||||
return recording;
|
return recording;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2209,8 +2209,8 @@ public class OpenViduTestAppE2eTest {
|
||||||
Assert.assertTrue("Wrong recording size. Excepected > 0 and was " + recording.getSize(),
|
Assert.assertTrue("Wrong recording size. Excepected > 0 and was " + recording.getSize(),
|
||||||
recording.getSize() > 0);
|
recording.getSize() > 0);
|
||||||
Assert.assertNull("Wrong recording url. Expected not null and was null", recording.getUrl());
|
Assert.assertNull("Wrong recording url. Expected not null and was null", recording.getUrl());
|
||||||
Assert.assertEquals("Wrong recording status. Expected stopped and was " + recording.getStatus().name(),
|
Assert.assertEquals("Wrong recording status. Expected ready and was " + recording.getStatus().name(),
|
||||||
Recording.Status.stopped, recording.getStatus());
|
Recording.Status.ready, recording.getStatus());
|
||||||
Assert.assertFalse("Session shouldn't be being recorded", session.isBeingRecorded());
|
Assert.assertFalse("Session shouldn't be being recorded", session.isBeingRecorded());
|
||||||
Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch());
|
Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch());
|
||||||
|
|
||||||
|
@ -2261,7 +2261,7 @@ public class OpenViduTestAppE2eTest {
|
||||||
Assert.assertTrue("Wrong recording duration", recording2.getDuration() > 0);
|
Assert.assertTrue("Wrong recording duration", recording2.getDuration() > 0);
|
||||||
Assert.assertTrue("Wrong recording size", recording2.getSize() > 0);
|
Assert.assertTrue("Wrong recording size", recording2.getSize() > 0);
|
||||||
Assert.assertNull("Wrong recording url", recording2.getUrl());
|
Assert.assertNull("Wrong recording url", recording2.getUrl());
|
||||||
Assert.assertEquals("Wrong recording status", Recording.Status.stopped, recording2.getStatus());
|
Assert.assertEquals("Wrong recording status", Recording.Status.ready, recording2.getStatus());
|
||||||
Assert.assertFalse("Session shouldn't be being recorded", session.isBeingRecorded());
|
Assert.assertFalse("Session shouldn't be being recorded", session.isBeingRecorded());
|
||||||
Assert.assertFalse("Session.fetch() should return false", session.fetch());
|
Assert.assertFalse("Session.fetch() should return false", session.fetch());
|
||||||
|
|
||||||
|
@ -2773,6 +2773,9 @@ public class OpenViduTestAppE2eTest {
|
||||||
event.get("outputMode").getAsString());
|
event.get("outputMode").getAsString());
|
||||||
Assert.assertEquals("Wrong recording outputMode in webhook event", 0, event.get("size").getAsLong());
|
Assert.assertEquals("Wrong recording outputMode in webhook event", 0, event.get("size").getAsLong());
|
||||||
Assert.assertEquals("Wrong recording outputMode in webhook event", 0, event.get("duration").getAsLong());
|
Assert.assertEquals("Wrong recording outputMode in webhook event", 0, event.get("duration").getAsLong());
|
||||||
|
Assert.assertEquals("Wrong recording startTime/timestamp in webhook event",
|
||||||
|
event.get("startTime").getAsLong(), event.get("timestamp").getAsLong());
|
||||||
|
Assert.assertNull("Wrong recording reason in webhook event (should be null)", event.get("reason"));
|
||||||
|
|
||||||
user.getDriver().findElement(By.id("add-user-btn")).click();
|
user.getDriver().findElement(By.id("add-user-btn")).click();
|
||||||
user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .join-btn")).click();
|
user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .join-btn")).click();
|
||||||
|
@ -2796,16 +2799,34 @@ public class OpenViduTestAppE2eTest {
|
||||||
CustomWebhook.waitForEvent("participantLeft", 2);
|
CustomWebhook.waitForEvent("participantLeft", 2);
|
||||||
event = CustomWebhook.waitForEvent("recordingStatusChanged", 2);
|
event = CustomWebhook.waitForEvent("recordingStatusChanged", 2);
|
||||||
|
|
||||||
Assert.assertEquals("Wrong recording status in webhook event", "processing",
|
OV.fetch();
|
||||||
|
List<Recording> recs = OV.listRecordings();
|
||||||
|
Assert.assertEquals("Wrong number of recording entities", 1, recs.size());
|
||||||
|
Recording rec = recs.get(0);
|
||||||
|
|
||||||
|
Assert.assertEquals("Wrong recording status in webhook event", "stopped",
|
||||||
event.get("status").getAsString());
|
event.get("status").getAsString());
|
||||||
Assert.assertEquals("Wrong recording outputMode in webhook event", 0, event.get("size").getAsLong());
|
Assert.assertEquals("Wrong recording outputMode in webhook event", 0, event.get("size").getAsLong());
|
||||||
Assert.assertEquals("Wrong recording outputMode in webhook event", 0, event.get("duration").getAsLong());
|
Assert.assertEquals("Wrong recording outputMode in webhook event", 0, event.get("duration").getAsLong());
|
||||||
|
Assert.assertEquals("Wrong recording reason in webhook event", "sessionClosedByServer",
|
||||||
|
event.get("reason").getAsString());
|
||||||
|
Assert.assertEquals("Wrong recording reason in webhook event", rec.getCreatedAt(),
|
||||||
|
event.get("startTime").getAsLong());
|
||||||
|
|
||||||
event = CustomWebhook.waitForEvent("recordingStatusChanged", 2);
|
event = CustomWebhook.waitForEvent("recordingStatusChanged", 2);
|
||||||
Assert.assertEquals("Wrong recording status in webhook event", "stopped",
|
|
||||||
event.get("status").getAsString());
|
OV.fetch();
|
||||||
|
recs = OV.listRecordings();
|
||||||
|
Assert.assertEquals("Wrong number of recording entities", 1, recs.size());
|
||||||
|
rec = recs.get(0);
|
||||||
|
|
||||||
|
Assert.assertEquals("Wrong recording status in webhook event", "ready", event.get("status").getAsString());
|
||||||
Assert.assertTrue("Wrong recording outputMode in webhook event", event.get("size").getAsLong() > 0);
|
Assert.assertTrue("Wrong recording outputMode in webhook event", event.get("size").getAsLong() > 0);
|
||||||
Assert.assertTrue("Wrong recording outputMode in webhook event", event.get("duration").getAsLong() > 0);
|
Assert.assertTrue("Wrong recording outputMode in webhook event", event.get("duration").getAsLong() > 0);
|
||||||
|
Assert.assertEquals("Wrong recording reason in webhook event", "sessionClosedByServer",
|
||||||
|
event.get("reason").getAsString());
|
||||||
|
Assert.assertEquals("Wrong recording reason in webhook event", rec.getCreatedAt(),
|
||||||
|
event.get("startTime").getAsLong());
|
||||||
|
|
||||||
CustomWebhook.waitForEvent("sessionDestroyed", 2);
|
CustomWebhook.waitForEvent("sessionDestroyed", 2);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue