openvidu-server: added RecordingProperties feature

pull/73/head
pabloFuente 2018-04-20 12:10:06 +02:00
parent b8d6ac5f3e
commit a228b1242e
5 changed files with 87 additions and 39 deletions

View File

@ -2,6 +2,7 @@ package io.openvidu.server.cdr;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import io.openvidu.java.client.RecordingLayout;
import io.openvidu.server.core.MediaOptions; import io.openvidu.server.core.MediaOptions;
import io.openvidu.server.core.Participant; import io.openvidu.server.core.Participant;
import io.openvidu.server.recording.Recording; import io.openvidu.server.recording.Recording;
@ -33,6 +34,7 @@ public class CDREvent implements Comparable<CDREvent> {
private String name; private String name;
private Boolean hasAudio; private Boolean hasAudio;
private Boolean hasVideo; private Boolean hasVideo;
private RecordingLayout layout;
public CDREvent(String eventName, CDREvent event) { public CDREvent(String eventName, CDREvent event) {
this(eventName, event.participant, event.sessionId, event.mediaOptions, event.receivingFrom, event.startTime, event.reason); this(eventName, event.participant, event.sessionId, event.mediaOptions, event.receivingFrom, event.startTime, event.reason);
@ -69,6 +71,7 @@ public class CDREvent implements Comparable<CDREvent> {
this.size = recording.getSize(); this.size = recording.getSize();
this.hasAudio = recording.hasAudio(); this.hasAudio = recording.hasAudio();
this.hasVideo = recording.hasVideo(); this.hasVideo = recording.hasVideo();
this.layout = recording.getLayout();
} }
public CDREvent(String eventName, Participant participant, String sessionId) { public CDREvent(String eventName, Participant participant, String sessionId) {
@ -146,6 +149,9 @@ public class CDREvent implements Comparable<CDREvent> {
if (this.hasVideo != null) { if (this.hasVideo != null) {
json.put("hasVideo", this.hasVideo); json.put("hasVideo", this.hasVideo);
} }
if (this.layout != null) {
json.put("layout", this.layout.name());
}
JSONObject root = new JSONObject(); JSONObject root = new JSONObject();
root.put(this.eventName, json); root.put(this.eventName, json);

View File

@ -20,6 +20,7 @@ import io.openvidu.client.OpenViduException.Code;
import io.openvidu.client.internal.ProtocolElements; import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.java.client.RecordingLayout; import io.openvidu.java.client.RecordingLayout;
import io.openvidu.java.client.RecordingMode; import io.openvidu.java.client.RecordingMode;
import io.openvidu.java.client.RecordingProperties;
import io.openvidu.java.client.MediaMode; import io.openvidu.java.client.MediaMode;
import io.openvidu.java.client.SessionProperties; import io.openvidu.java.client.SessionProperties;
import io.openvidu.server.core.SessionManager; import io.openvidu.server.core.SessionManager;
@ -55,7 +56,8 @@ public class KurentoSessionManager extends SessionManager {
SessionProperties properties = sessionProperties.get(sessionId); SessionProperties properties = sessionProperties.get(sessionId);
if (properties == null && this.isInsecureParticipant(participant.getParticipantPrivateId())) { if (properties == null && this.isInsecureParticipant(participant.getParticipantPrivateId())) {
properties = new SessionProperties.Builder().mediaMode(MediaMode.ROUTED) properties = new SessionProperties.Builder().mediaMode(MediaMode.ROUTED)
.recordingMode(RecordingMode.ALWAYS).recordingLayout(RecordingLayout.BEST_FIT).build(); .recordingMode(RecordingMode.ALWAYS).defaultRecordingLayout(RecordingLayout.BEST_FIT)
.build();
} }
createSession(kcSessionInfo, properties); createSession(kcSessionInfo, properties);
} }
@ -235,7 +237,8 @@ public class KurentoSessionManager extends SessionManager {
&& session.getActivePublishers() == 0) { && session.getActivePublishers() == 0) {
// Insecure session recording // Insecure session recording
new Thread(() -> { new Thread(() -> {
recordingService.startRecording(session, null); recordingService.startRecording(session, new RecordingProperties.Builder().name("")
.recordingLayout(session.getSessionProperties().defaultRecordingLayout()).build());
}).start(); }).start();
} }

View File

@ -47,6 +47,7 @@ import com.github.dockerjava.core.command.PullImageResultCallback;
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.RecordingProperties;
import io.openvidu.server.CommandExecutor; import io.openvidu.server.CommandExecutor;
import io.openvidu.server.OpenViduServer; import io.openvidu.server.OpenViduServer;
import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.config.OpenviduConfig;
@ -81,19 +82,20 @@ public class ComposedRecordingService {
this.dockerClient = DockerClientBuilder.getInstance(config).build(); this.dockerClient = DockerClientBuilder.getInstance(config).build();
} }
public Recording startRecording(Session session, String name) { public Recording startRecording(Session session, RecordingProperties properties) {
List<String> envs = new ArrayList<>(); List<String> envs = new ArrayList<>();
String shortSessionId = session.getSessionId().substring(session.getSessionId().lastIndexOf('/') + 1, String shortSessionId = session.getSessionId().substring(session.getSessionId().lastIndexOf('/') + 1,
session.getSessionId().length()); session.getSessionId().length());
String recordingId = this.getFreeRecordingId(session.getSessionId(), shortSessionId); String recordingId = this.getFreeRecordingId(session.getSessionId(), shortSessionId);
String secret = openviduConfig.getOpenViduSecret(); String secret = openviduConfig.getOpenViduSecret();
if (name == null || name.isEmpty()) { if (properties.name() == null || properties.name().isEmpty()) {
// No name provided for the recording file // No name provided for the recording file
name = recordingId; properties = new RecordingProperties.Builder().name(recordingId)
.recordingLayout(properties.recordingLayout()).build();
} }
Recording recording = new Recording(session.getSessionId(), recordingId, name); Recording recording = new Recording(session.getSessionId(), recordingId, properties);
this.sessionsRecordings.put(session.getSessionId(), recording); this.sessionsRecordings.put(session.getSessionId(), recording);
this.sessionHandler.setRecordingStarted(session.getSessionId(), recording); this.sessionHandler.setRecordingStarted(session.getSessionId(), recording);
@ -110,14 +112,14 @@ public class ComposedRecordingService {
} }
String location = OpenViduServer.publicUrl.replaceFirst("wss://", ""); String location = OpenViduServer.publicUrl.replaceFirst("wss://", "");
String layoutUrl = session.getSessionProperties().recordingLayout().name().toLowerCase().replaceAll("_", "-"); String layoutUrl = properties.recordingLayout().name().toLowerCase().replaceAll("_", "-");
envs.add("URL=https://OPENVIDUAPP:" + secret + "@" + location + "/#/layout-" + layoutUrl + "/" + shortSessionId envs.add("URL=https://OPENVIDUAPP:" + secret + "@" + location + "/#/layout-" + layoutUrl + "/" + shortSessionId
+ "/" + secret); + "/" + secret);
envs.add("RESOLUTION=1920x1080"); envs.add("RESOLUTION=1920x1080");
envs.add("FRAMERATE=30"); envs.add("FRAMERATE=30");
envs.add("VIDEO_ID=" + recordingId); envs.add("VIDEO_ID=" + recordingId);
envs.add("VIDEO_NAME=" + name); envs.add("VIDEO_NAME=" + properties.name());
envs.add("VIDEO_FORMAT=mp4"); envs.add("VIDEO_FORMAT=mp4");
envs.add("USER_ID=" + uid); envs.add("USER_ID=" + uid);
envs.add("RECORDING_JSON=" + recording.toJson().toJSONString()); envs.add("RECORDING_JSON=" + recording.toJson().toJSONString());
@ -128,7 +130,7 @@ public class ComposedRecordingService {
String containerId = this.runRecordingContainer(envs, "recording_" + recordingId); String containerId = this.runRecordingContainer(envs, "recording_" + recordingId);
this.waitForVideoFileNotEmpty(name); this.waitForVideoFileNotEmpty(properties.name());
this.sessionsContainers.put(session.getSessionId(), containerId); this.sessionsContainers.put(session.getSessionId(), containerId);
@ -378,7 +380,7 @@ public class ComposedRecordingService {
// Cannot delete an active recording // Cannot delete an active recording
return HttpStatus.CONFLICT; return HttpStatus.CONFLICT;
} }
String name = getRecordingFromHost(recordingId).getName(); String name = getRecordingFromHost(recordingId).getName();
File folder = new File(this.openviduConfig.getOpenViduRecordingPath()); File folder = new File(this.openviduConfig.getOpenViduRecordingPath());

View File

@ -2,6 +2,9 @@ package io.openvidu.server.recording;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import io.openvidu.java.client.RecordingLayout;
import io.openvidu.java.client.RecordingProperties;
public class Recording { public class Recording {
public enum Status { public enum Status {
@ -9,14 +12,13 @@ public class Recording {
started, // The recording has started and is going on started, // The recording has started and is going on
stopped, // The recording has finished OK stopped, // The recording has finished OK
available, // The recording is available for downloading. This status is reached for all available, // The recording is available for downloading. This status is reached for all
// stopped recordings if property 'openvidu.recording.free-access' is true // stopped recordings if property 'openvidu.recording.free-access' is true
failed; // The recording has failed failed; // The recording has failed
} }
private Status status; private Recording.Status status;
private String id; private String id;
private String name;
private String sessionId; private String sessionId;
private long createdAt; // milliseconds (UNIX Epoch time) private long createdAt; // milliseconds (UNIX Epoch time)
private long size = 0; // bytes private long size = 0; // bytes
@ -24,18 +26,18 @@ public class Recording {
private String url; private String url;
private boolean hasAudio = true; private boolean hasAudio = true;
private boolean hasVideo = true; private boolean hasVideo = true;
private RecordingProperties recordingProperties;
public Recording(String sessionId, String id, String name) { public Recording(String sessionId, String id, RecordingProperties recordingProperties) {
this.sessionId = sessionId; this.sessionId = sessionId;
this.createdAt = System.currentTimeMillis(); this.createdAt = System.currentTimeMillis();
this.id = id; this.id = id;
this.name = name;
this.status = Status.started; this.status = Status.started;
this.recordingProperties = recordingProperties;
} }
public Recording(JSONObject json) { public Recording(JSONObject json) {
this.id = (String) json.get("id"); this.id = (String) json.get("id");
this.name = (String) json.get("name");
this.sessionId = (String) json.get("sessionId"); this.sessionId = (String) json.get("sessionId");
this.createdAt = (long) json.get("createdAt"); this.createdAt = (long) json.get("createdAt");
this.size = (long) json.get("size"); this.size = (long) json.get("size");
@ -44,6 +46,8 @@ public class Recording {
this.hasAudio = (boolean) json.get("hasAudio"); this.hasAudio = (boolean) json.get("hasAudio");
this.hasVideo = (boolean) json.get("hasVideo"); this.hasVideo = (boolean) json.get("hasVideo");
this.status = Status.valueOf((String) json.get("status")); this.status = Status.valueOf((String) json.get("status"));
this.recordingProperties = new RecordingProperties.Builder().name((String) json.get("name"))
.recordingLayout(RecordingLayout.valueOf((String) json.get("layout"))).build();
} }
public Status getStatus() { public Status getStatus() {
@ -62,13 +66,21 @@ public class Recording {
this.id = id; this.id = id;
} }
public String getName() { public String getName() {
return name; return this.recordingProperties.name();
} }
public String setName() {
return this.recordingProperties.name();
}
public void setName(String name) { public RecordingLayout getLayout() {
this.name = name; return this.recordingProperties.recordingLayout();
} }
public RecordingLayout setLayout() {
return this.recordingProperties.recordingLayout();
}
public String getSessionId() { public String getSessionId() {
return sessionId; return sessionId;
@ -130,7 +142,8 @@ public class Recording {
public JSONObject toJson() { public JSONObject toJson() {
JSONObject json = new JSONObject(); JSONObject json = new JSONObject();
json.put("id", this.id); json.put("id", this.id);
json.put("name", this.name); json.put("name", this.recordingProperties.name());
json.put("layout", this.recordingProperties.recordingLayout().name());
json.put("sessionId", this.sessionId); json.put("sessionId", this.sessionId);
json.put("createdAt", this.createdAt); json.put("createdAt", this.createdAt);
json.put("size", this.size); json.put("size", this.size);

View File

@ -37,6 +37,7 @@ import io.openvidu.client.OpenViduException;
import io.openvidu.client.internal.ProtocolElements; import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.java.client.RecordingLayout; import io.openvidu.java.client.RecordingLayout;
import io.openvidu.java.client.RecordingMode; import io.openvidu.java.client.RecordingMode;
import io.openvidu.java.client.RecordingProperties;
import io.openvidu.java.client.MediaMode; import io.openvidu.java.client.MediaMode;
import io.openvidu.java.client.SessionProperties; import io.openvidu.java.client.SessionProperties;
import io.openvidu.server.core.ParticipantRole; import io.openvidu.server.core.ParticipantRole;
@ -72,7 +73,7 @@ public class SessionRestController {
SessionProperties.Builder builder = new SessionProperties.Builder(); SessionProperties.Builder builder = new SessionProperties.Builder();
if (params != null) { if (params != null) {
String recordingModeString = (String) params.get("recordingMode"); String recordingModeString = (String) params.get("recordingMode");
String recordingLayoutString = (String) params.get("recordingLayout"); String defaultRecordingLayoutString = (String) params.get("defaultRecordingLayout");
String mediaModeString = (String) params.get("mediaMode"); String mediaModeString = (String) params.get("mediaMode");
try { try {
@ -80,18 +81,18 @@ public class SessionRestController {
RecordingMode recordingMode = RecordingMode.valueOf(recordingModeString); RecordingMode recordingMode = RecordingMode.valueOf(recordingModeString);
builder = builder.recordingMode(recordingMode); builder = builder.recordingMode(recordingMode);
} }
if (recordingLayoutString != null) { if (defaultRecordingLayoutString != null) {
RecordingLayout recordingLayout = RecordingLayout.valueOf(recordingLayoutString); RecordingLayout defaultRecordingLayout = RecordingLayout.valueOf(defaultRecordingLayoutString);
builder = builder.recordingLayout(recordingLayout); builder = builder.defaultRecordingLayout(defaultRecordingLayout);
} }
if (mediaModeString != null) { if (mediaModeString != null) {
MediaMode mediaMode = MediaMode.valueOf(mediaModeString); MediaMode mediaMode = MediaMode.valueOf(mediaModeString);
builder = builder.mediaMode(mediaMode); builder = builder.mediaMode(mediaMode);
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
return this.generateErrorResponse("RecordingMode " + params.get("recordingMode") + " | " + "RecordingLayout " return this.generateErrorResponse("RecordingMode " + params.get("recordingMode") + " | "
+ params.get("recordingLayout") + " | " + "MediaMode " + params.get("mediaMode") + "Default RecordingLayout " + params.get("defaultRecordingLayout") + " | " + "MediaMode "
+ " are not defined", "/api/tokens", HttpStatus.BAD_REQUEST); + params.get("mediaMode") + " are not defined", "/api/tokens", HttpStatus.BAD_REQUEST);
} }
} }
@ -109,9 +110,20 @@ public class SessionRestController {
try { try {
String sessionId = (String) params.get("session"); String sessionId = (String) params.get("session");
ParticipantRole role = ParticipantRole.valueOf((String) params.get("role")); String roleString = (String) params.get("role");
String metadata = (String) params.get("data"); String metadata = (String) params.get("data");
ParticipantRole role;
if (roleString != null) {
role = ParticipantRole.valueOf(roleString);
} else {
role = ParticipantRole.PUBLISHER;
}
if (metadata == null) {
metadata = "";
}
String token = sessionManager.newToken(sessionId, role, metadata); String token = sessionManager.newToken(sessionId, role, metadata);
JSONObject responseJson = new JSONObject(); JSONObject responseJson = new JSONObject();
responseJson.put("id", token); responseJson.put("id", token);
@ -136,6 +148,7 @@ public class SessionRestController {
String sessionId = (String) params.get("session"); String sessionId = (String) params.get("session");
String name = (String) params.get("name"); String name = (String) params.get("name");
String recordingLayoutString = (String) params.get("recordingLayout");
if (sessionId == null) { if (sessionId == null) {
// "session" parameter not found // "session" parameter not found
@ -158,7 +171,18 @@ public class SessionRestController {
return new ResponseEntity<JSONObject>(HttpStatus.CONFLICT); return new ResponseEntity<JSONObject>(HttpStatus.CONFLICT);
} }
Recording startedRecording = this.recordingService.startRecording(session, name); RecordingLayout recordingLayout;
if (recordingLayoutString == null || recordingLayoutString.isEmpty()) {
// "recordingLayout" parameter not defined. Use global layout from
// SessionProperties
// (it is always configured as it has RecordingLayout.BEST_FIT as default value)
recordingLayout = session.getSessionProperties().defaultRecordingLayout();
} else {
recordingLayout = RecordingLayout.valueOf(recordingLayoutString);
}
Recording startedRecording = this.recordingService.startRecording(session,
new RecordingProperties.Builder().name(name).recordingLayout(recordingLayout).build());
return new ResponseEntity<>(startedRecording.toJson(), HttpStatus.OK); return new ResponseEntity<>(startedRecording.toJson(), HttpStatus.OK);
} }
@ -184,14 +208,14 @@ public class SessionRestController {
// Session is not being recorded // Session is not being recorded
return new ResponseEntity<JSONObject>(HttpStatus.CONFLICT); return new ResponseEntity<JSONObject>(HttpStatus.CONFLICT);
} }
Session session = sessionManager.getSession(recording.getSessionId()); Session session = sessionManager.getSession(recording.getSessionId());
Recording stoppedRecording = this.recordingService Recording stoppedRecording = this.recordingService.stopRecording(session);
.stopRecording(session);
sessionManager.evictParticipant(session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID)
sessionManager.evictParticipant(session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID).getParticipantPrivateId(), "EVICT_RECORDER"); .getParticipantPrivateId(), "EVICT_RECORDER");
return new ResponseEntity<>(stoppedRecording.toJson(), HttpStatus.OK); return new ResponseEntity<>(stoppedRecording.toJson(), HttpStatus.OK);
} }