CDR refactoring: now all operations are recorded in a low-level context (not in SessionEventsHandler). Added 'videoFramerate', 'reason' and recording events. New property openvidu.recording.notification. Recordings stopped upon openvidu-server stopped.

pull/73/head
pabloFuente 2018-04-17 15:05:44 +02:00
parent 12758e528a
commit 390ce2224e
19 changed files with 487 additions and 250 deletions

View File

@ -44,6 +44,7 @@ public class ProtocolElements {
public static final String JOINROOM_PEERSTREAMAUDIOACTIVE_PARAM = "audioActive"; public static final String JOINROOM_PEERSTREAMAUDIOACTIVE_PARAM = "audioActive";
public static final String JOINROOM_PEERSTREAMVIDEOACTIVE_PARAM = "videoActive"; public static final String JOINROOM_PEERSTREAMVIDEOACTIVE_PARAM = "videoActive";
public static final String JOINROOM_PEERSTREAMTYPEOFVIDEO_PARAM = "typeOfVideo"; public static final String JOINROOM_PEERSTREAMTYPEOFVIDEO_PARAM = "typeOfVideo";
public static final String JOINROOM_PEERSTREAMFRAMERATE_PARAM = "frameRate";
public static final String PUBLISHVIDEO_METHOD = "publishVideo"; public static final String PUBLISHVIDEO_METHOD = "publishVideo";
public static final String PUBLISHVIDEO_SDPOFFER_PARAM = "sdpOffer"; public static final String PUBLISHVIDEO_SDPOFFER_PARAM = "sdpOffer";
@ -52,6 +53,7 @@ public class ProtocolElements {
public static final String PUBLISHVIDEO_AUDIOACTIVE_PARAM = "audioActive"; public static final String PUBLISHVIDEO_AUDIOACTIVE_PARAM = "audioActive";
public static final String PUBLISHVIDEO_VIDEOACTIVE_PARAM = "videoActive"; public static final String PUBLISHVIDEO_VIDEOACTIVE_PARAM = "videoActive";
public static final String PUBLISHVIDEO_TYPEOFVIDEO_PARAM = "typeOfVideo"; public static final String PUBLISHVIDEO_TYPEOFVIDEO_PARAM = "typeOfVideo";
public static final String PUBLISHVIDEO_FRAMERATE_PARAM = "frameRate";
public static final String UNPUBLISHVIDEO_METHOD = "unpublishVideo"; public static final String UNPUBLISHVIDEO_METHOD = "unpublishVideo";
@ -79,6 +81,7 @@ public class ProtocolElements {
public static final String PARTICIPANTLEFT_METHOD = "participantLeft"; public static final String PARTICIPANTLEFT_METHOD = "participantLeft";
public static final String PARTICIPANTLEFT_NAME_PARAM = "name"; public static final String PARTICIPANTLEFT_NAME_PARAM = "name";
public static final String PARTICIPANTLEFT_REASON_PARAM = "reason";
public static final String PARTICIPANTEVICTED_METHOD = "participantEvicted"; public static final String PARTICIPANTEVICTED_METHOD = "participantEvicted";
@ -89,9 +92,11 @@ public class ProtocolElements {
public static final String PARTICIPANTPUBLISHED_AUDIOACTIVE_PARAM = "audioActive"; public static final String PARTICIPANTPUBLISHED_AUDIOACTIVE_PARAM = "audioActive";
public static final String PARTICIPANTPUBLISHED_VIDEOACTIVE_PARAM = "videoActive"; public static final String PARTICIPANTPUBLISHED_VIDEOACTIVE_PARAM = "videoActive";
public static final String PARTICIPANTPUBLISHED_TYPEOFVIDEO_PARAM = "typeOfVideo"; public static final String PARTICIPANTPUBLISHED_TYPEOFVIDEO_PARAM = "typeOfVideo";
public static final String PARTICIPANTPUBLISHED_FRAMERATE_PARAM = "frameRate";
public static final String PARTICIPANTUNPUBLISHED_METHOD = "participantUnpublished"; public static final String PARTICIPANTUNPUBLISHED_METHOD = "participantUnpublished";
public static final String PARTICIPANTUNPUBLISHED_NAME_PARAM = "name"; public static final String PARTICIPANTUNPUBLISHED_NAME_PARAM = "name";
public static final String PARTICIPANTUNPUBLISHED_REASON_PARAM = "reason";
public static final String PARTICIPANTSENDMESSAGE_METHOD = "sendMessage"; public static final String PARTICIPANTSENDMESSAGE_METHOD = "sendMessage";
public static final String PARTICIPANTSENDMESSAGE_DATA_PARAM = "data"; public static final String PARTICIPANTSENDMESSAGE_DATA_PARAM = "data";
@ -110,7 +115,13 @@ public class ProtocolElements {
public static final String ICECANDIDATE_SDPMID_PARAM = "sdpMid"; public static final String ICECANDIDATE_SDPMID_PARAM = "sdpMid";
public static final String ICECANDIDATE_SDPMLINEINDEX_PARAM = "sdpMLineIndex"; public static final String ICECANDIDATE_SDPMLINEINDEX_PARAM = "sdpMLineIndex";
public static final String RECORDINGSTARTED_METHOD = "recordingStarted";
public static final String RECORDINGSTARTED_ID_PARAM = "id";
public static final String RECORDINGSTOPPED_METHOD = "recordingStopped";
public static final String RECORDINGSTOPPED_ID_PARAM = "id";
public static final String CUSTOM_NOTIFICATION = "custonNotification"; public static final String CUSTOM_NOTIFICATION = "custonNotification";
public static final String RECORDER_PARTICIPANT_ID_PUBLICID = "RECORDER"; public static final String RECORDER_PARTICIPANT_PUBLICID = "RECORDER";
} }

View File

@ -13,18 +13,26 @@ public class CDREvent implements Comparable<CDREvent> {
static final String PARTICIPANT_LEFT = "participantLeft"; static final String PARTICIPANT_LEFT = "participantLeft";
static final String CONNECTION_CREATED = "webrtcConnectionCreated"; static final String CONNECTION_CREATED = "webrtcConnectionCreated";
static final String CONNECTION_DESTROYED = "webrtcConnectionDestroyed"; static final String CONNECTION_DESTROYED = "webrtcConnectionDestroyed";
static final String RECORDING_STARTED = "recordingStarted";
static final String RECORDING_STOPPED = "recordingStopped";
protected String eventName; protected String eventName;
protected String sessionId; protected String sessionId;
protected Long timeStamp;
private Long startTime;
private Integer duration;
private Participant participant; private Participant participant;
private MediaOptions mediaOptions; private MediaOptions mediaOptions;
private String receivingFrom; private String receivingFrom;
private Long startTime; private String reason;
private Integer duration;
protected Long timeStamp;
public CDREvent(String eventName, CDREvent event) { public CDREvent(String eventName, CDREvent event) {
this(eventName, event.participant, event.sessionId, event.mediaOptions, event.receivingFrom, event.startTime); this(eventName, event.participant, event.sessionId, event.mediaOptions, event.receivingFrom, event.startTime, event.reason);
this.duration = (int) (this.timeStamp - this.startTime / 1000);
}
public CDREvent(String eventName, CDREvent event, String reason) {
this(eventName, event.participant, event.sessionId, event.mediaOptions, event.receivingFrom, event.startTime, reason);
this.duration = (int) (this.timeStamp - this.startTime / 1000); this.duration = (int) (this.timeStamp - this.startTime / 1000);
} }
@ -46,12 +54,13 @@ public class CDREvent implements Comparable<CDREvent> {
} }
public CDREvent(String eventName, Participant participant, String sessionId, MediaOptions mediaOptions, public CDREvent(String eventName, Participant participant, String sessionId, MediaOptions mediaOptions,
String receivingFrom, Long startTime) { String receivingFrom, Long startTime, String reason) {
this(eventName, sessionId); this(eventName, sessionId);
this.participant = participant; this.participant = participant;
this.mediaOptions = mediaOptions; this.mediaOptions = mediaOptions;
this.receivingFrom = receivingFrom; this.receivingFrom = receivingFrom;
this.startTime = startTime; this.startTime = startTime;
this.reason = reason;
} }
public MediaOptions getMediaOptions() { public MediaOptions getMediaOptions() {
@ -82,6 +91,7 @@ public class CDREvent implements Comparable<CDREvent> {
json.put("videoEnabled", this.mediaOptions.videoActive); json.put("videoEnabled", this.mediaOptions.videoActive);
if (this.mediaOptions.videoActive) { if (this.mediaOptions.videoActive) {
json.put("videoSource", this.mediaOptions.typeOfVideo); json.put("videoSource", this.mediaOptions.typeOfVideo);
json.put("videoFramerate", this.mediaOptions.frameRate);
} }
if (this.receivingFrom != null) { if (this.receivingFrom != null) {
json.put("receivingFrom", this.receivingFrom); json.put("receivingFrom", this.receivingFrom);
@ -93,10 +103,14 @@ public class CDREvent implements Comparable<CDREvent> {
json.put("duration", (this.timeStamp - this.startTime) / 1000); json.put("duration", (this.timeStamp - this.startTime) / 1000);
} }
if (this.reason != null) {
json.put("reason", this.reason);
}
JSONObject root = new JSONObject(); JSONObject root = new JSONObject();
root.put(this.eventName, json); root.put(this.eventName, json);
return root.toString(); return root.toJSONString();
} }
@Override @Override

View File

@ -13,19 +13,40 @@ import io.openvidu.server.core.MediaOptions;
import io.openvidu.server.core.Participant; import io.openvidu.server.core.Participant;
/** /**
* CDR logger to register all information of each WebRTC connection: * CDR logger to register all information of a Session.
* Enabled by property 'openvidu.cdr=true' * Enabled by property 'openvidu.cdr=true'
* *
* - Participant unique identifier * - 'sessionCreated': {sessionId, timestamp}
* - Session unique identifier * - 'sessionDestroyed': {sessionId, timestamp, startTime, endTime, duration }
* - Inbound or Outbound WebRTC connection * - 'participantJoined': {sessionId, timestamp, participantId}
* - <if inbound connection> Sender unique identifier * - 'participantLeft': {sessionId, timestamp, participantId, startTime, endTime, duration, reason}
* - Audio media stream enabled * - 'webrtcConnectionCreated' {sessionId, timestamp, participantId, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate] }
* - Video media stream enabled * - 'webrtcConnectionDestroyed' {sessionId, timestamp, participantId, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate], reason }
* - <if Video media stream enabled> Video source [CAMERA, SCREEN] * - 'recordingStarted' {sessionId, timestamp}
* - Time of start of the call * - 'recordingStopped' {sessionId, timestamp}
* - Time of end of the call *
* - Total time duration * PROPERTIES VALUES:
*
* - sessionId: string
* - timestamp: number
* - startTime: number
* - endTime: number
* - duration: number
* - participantId: string
* - connection: "INBOUND", "OUTBOUND"
* - receivingFrom: string
* - audioEnabled: boolean
* - videoEnabled: boolean
* - videoSource: "CAMERA", "SCREEN"
* - videoFramerate: number
* - webrtcConnectionDestroyed.reason: "unsubscribe", "unpublish", "disconnect", "networkDisconnect", "openviduServerDestroyed"
* - participantLeft.reason: "unsubscribe", "unpublish", "disconnect", "networkDisconnect", "openviduServerDestroyed"
* - sessionDestroyed.reason: "lastParticipantLeft", "openviduServerDestroyed"
*
* [OPTIONAL_PROPERTIES]:
* - receivingFrom: only if connection = "INBOUND"
* - videoSource: only if videoEnabled = true
* - videoFramerate: only if videoEnabled = true
* *
* @author Pablo Fuente (pablofuenteperez@gmail.com) * @author Pablo Fuente (pablofuenteperez@gmail.com)
*/ */
@ -44,9 +65,9 @@ public class CallDetailRecord {
log.info("{}", e); log.info("{}", e);
} }
public void recordSessionDestroyed(String sessionId) { public void recordSessionDestroyed(String sessionId, String reason) {
CDREvent e = this.sessions.remove(sessionId); CDREvent e = this.sessions.remove(sessionId);
log.info("{}", new CDREvent(CDREvent.SESSION_DESTROYED, e)); log.info("{}", new CDREvent(CDREvent.SESSION_DESTROYED, e, reason));
} }
public void recordParticipantJoined(Participant participant, String sessionId) { public void recordParticipantJoined(Participant participant, String sessionId) {
@ -55,36 +76,38 @@ public class CallDetailRecord {
log.info("{}", e); log.info("{}", e);
} }
public void recordParticipantLeft(Participant participant, String sessionId) { public void recordParticipantLeft(Participant participant, String sessionId, String reason) {
CDREvent e = this.participants.remove(participant.getParticipantPublicId()); CDREvent e = this.participants.remove(participant.getParticipantPublicId());
log.info("{}", new CDREvent(CDREvent.PARTICIPANT_LEFT, e)); log.info("{}", new CDREvent(CDREvent.PARTICIPANT_LEFT, e, reason));
} }
public void recordNewPublisher(Participant participant, String sessionId, MediaOptions mediaOptions) { public void recordNewPublisher(Participant participant, String sessionId, MediaOptions mediaOptions) {
CDREvent publisher = new CDREvent(CDREvent.CONNECTION_CREATED, participant, sessionId, mediaOptions, null, System.currentTimeMillis()); CDREvent publisher = new CDREvent(CDREvent.CONNECTION_CREATED, participant, sessionId, mediaOptions, null,
System.currentTimeMillis(), null);
this.publications.put(participant.getParticipantPublicId(), publisher); this.publications.put(participant.getParticipantPublicId(), publisher);
log.info("{}", publisher); log.info("{}", publisher);
} }
public void recordNewSubscriber(Participant participant, String sessionId, String senderPublicId) { public boolean stopPublisher(String participantPublicId, String reason) {
CDREvent publisher = this.publications.get(senderPublicId);
CDREvent subscriber = new CDREvent(CDREvent.CONNECTION_CREATED, participant, sessionId, publisher.getMediaOptions(), publisher.getParticipantPublicId(), System.currentTimeMillis());
this.subscriptions.putIfAbsent(participant.getParticipantPublicId(), new ConcurrentSkipListSet<>());
this.subscriptions.get(participant.getParticipantPublicId()).add(subscriber);
log.info("{}", subscriber);
}
public boolean stopPublisher(String participantPublicId) {
CDREvent publisher = this.publications.remove(participantPublicId); CDREvent publisher = this.publications.remove(participantPublicId);
if (publisher != null) { if (publisher != null) {
publisher = new CDREvent(CDREvent.CONNECTION_DESTROYED, publisher); publisher = new CDREvent(CDREvent.CONNECTION_DESTROYED, publisher, reason);
log.info("{}", publisher); log.info("{}", publisher);
return true; return true;
} }
return false; return false;
} }
public boolean stopSubscriber(String participantPublicId, String senderPublicId) { public void recordNewSubscriber(Participant participant, String sessionId, String senderPublicId) {
CDREvent publisher = this.publications.get(senderPublicId);
CDREvent subscriber = new CDREvent(CDREvent.CONNECTION_CREATED, participant, sessionId,
publisher.getMediaOptions(), publisher.getParticipantPublicId(), System.currentTimeMillis(), null);
this.subscriptions.putIfAbsent(participant.getParticipantPublicId(), new ConcurrentSkipListSet<>());
this.subscriptions.get(participant.getParticipantPublicId()).add(subscriber);
log.info("{}", subscriber);
}
public boolean stopSubscriber(String participantPublicId, String senderPublicId, String reason) {
Set<CDREvent> participantSubscriptions = this.subscriptions.get(participantPublicId); Set<CDREvent> participantSubscriptions = this.subscriptions.get(participantPublicId);
if (participantSubscriptions != null) { if (participantSubscriptions != null) {
CDREvent subscription; CDREvent subscription;
@ -92,7 +115,7 @@ public class CallDetailRecord {
subscription = it.next(); subscription = it.next();
if (subscription.getReceivingFrom().equals(senderPublicId)) { if (subscription.getReceivingFrom().equals(senderPublicId)) {
it.remove(); it.remove();
subscription = new CDREvent(CDREvent.CONNECTION_DESTROYED, subscription); subscription = new CDREvent(CDREvent.CONNECTION_DESTROYED, subscription, reason);
log.info("{}", subscription); log.info("{}", subscription);
return true; return true;
} }
@ -101,17 +124,14 @@ public class CallDetailRecord {
return false; return false;
} }
public void stopAllSubscriptions(String participantPublicId) { public void recordRecordingStarted(String sessionId) {
Set<CDREvent> participantSubscriptions = this.subscriptions.get(participantPublicId); CDREvent recording = new CDREvent(CDREvent.RECORDING_STARTED, sessionId);
if (participantSubscriptions != null) { log.info("{}", recording);
CDREvent subscription; }
for (Iterator<CDREvent> it = participantSubscriptions.iterator(); it.hasNext();) {
subscription = it.next(); public void recordRecordingStopped(String sessionId) {
subscription = new CDREvent(CDREvent.CONNECTION_DESTROYED, subscription); CDREvent recording = new CDREvent(CDREvent.RECORDING_STOPPED, sessionId);
log.info("{}", subscription); log.info("{}", recording);
}
this.subscriptions.remove(participantPublicId).clear();
}
} }
} }

View File

@ -3,6 +3,8 @@ package io.openvidu.server.config;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import io.openvidu.server.core.ParticipantRole;
@Component @Component
public class OpenviduConfig { public class OpenviduConfig {
@ -24,8 +26,11 @@ public class OpenviduConfig {
@Value("${openvidu.recording.path}") @Value("${openvidu.recording.path}")
private String openviduRecordingPath; private String openviduRecordingPath;
@Value("${openvidu.recording.free-access}") @Value("${openvidu.recording.public-access}")
boolean openviduRecordingFreeAccess; boolean openviduRecordingPublicAccess;
@Value("${openvidu.recording.notification}")
String openviduRecordingNotification;
@Value("${openvidu.recording.version}") @Value("${openvidu.recording.version}")
String openviduRecordingVersion; String openviduRecordingVersion;
@ -63,8 +68,8 @@ public class OpenviduConfig {
return this.openviduRecordingPath; return this.openviduRecordingPath;
} }
public boolean getOpenViduRecordingFreeAccess() { public boolean getOpenViduRecordingPublicAccess() {
return this.openviduRecordingFreeAccess; return this.openviduRecordingPublicAccess;
} }
public void setOpenViduRecordingPath(String recordingPath) { public void setOpenViduRecordingPath(String recordingPath) {
@ -87,4 +92,23 @@ public class OpenviduConfig {
return springProfile; return springProfile;
} }
public ParticipantRole[] getRolesFromRecordingNotification() {
ParticipantRole[] roles;
switch (this.openviduRecordingNotification) {
case "none":
roles = new ParticipantRole[0];
break;
case "publisher_moderator":
roles = new ParticipantRole[] { ParticipantRole.PUBLISHER, ParticipantRole.MODERATOR };
break;
case "all":
roles = new ParticipantRole[] { ParticipantRole.SUBSCRIBER, ParticipantRole.PUBLISHER,
ParticipantRole.MODERATOR };
break;
default:
roles = new ParticipantRole[] { ParticipantRole.PUBLISHER, ParticipantRole.MODERATOR };
}
return roles;
}
} }

View File

@ -29,7 +29,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
.antMatchers(HttpMethod.GET, "/config/**").authenticated() .antMatchers(HttpMethod.GET, "/config/**").authenticated()
.antMatchers("/").authenticated(); .antMatchers("/").authenticated();
if (openviduConf.getOpenViduRecordingFreeAccess()) { if (openviduConf.getOpenViduRecordingPublicAccess()) {
conf = conf.antMatchers("/recordings/*").permitAll(); conf = conf.antMatchers("/recordings/*").permitAll();
} else { } else {
conf = conf.antMatchers("/recordings/*").authenticated(); conf = conf.antMatchers("/recordings/*").authenticated();

View File

@ -5,11 +5,13 @@ public class MediaOptions {
public boolean audioActive; public boolean audioActive;
public boolean videoActive; public boolean videoActive;
public String typeOfVideo; public String typeOfVideo;
public int frameRate;
public MediaOptions(boolean audioActive, boolean videoActive, String typeOfVideo) { public MediaOptions(boolean audioActive, boolean videoActive, String typeOfVideo, int frameRate) {
this.audioActive = audioActive; this.audioActive = audioActive;
this.videoActive = videoActive; this.videoActive = videoActive;
this.typeOfVideo = typeOfVideo; this.typeOfVideo = typeOfVideo;
this.frameRate = frameRate;
} }
} }

View File

@ -11,6 +11,7 @@ public class Participant {
protected boolean audioActive = true; protected boolean audioActive = true;
protected boolean videoActive = true; protected boolean videoActive = true;
protected String typeOfVideo; // CAMERA, SCREEN protected String typeOfVideo; // CAMERA, SCREEN
protected int frameRate;
protected boolean streaming = false; protected boolean streaming = false;
protected volatile boolean closed; protected volatile boolean closed;
@ -102,6 +103,14 @@ public class Participant {
this.typeOfVideo = typeOfVideo; this.typeOfVideo = typeOfVideo;
} }
public int getFrameRate() {
return this.frameRate;
}
public void setFrameRate(int frameRate) {
this.frameRate = frameRate;
}
public String getFullMetadata() { public String getFullMetadata() {
String fullMetadata; String fullMetadata;
if ((!this.clientMetadata.isEmpty()) && (!this.serverMetadata.isEmpty())) { if ((!this.clientMetadata.isEmpty()) && (!this.serverMetadata.isEmpty())) {

View File

@ -12,9 +12,9 @@ public interface Session {
void join(Participant participant); void join(Participant participant);
void leave(String participantPrivateId); void leave(String participantPrivateId, String reason);
boolean close(); boolean close(String reason);
boolean isClosed(); boolean isClosed();

View File

@ -1,10 +1,15 @@
package io.openvidu.server.core; package io.openvidu.server.core;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
@ -17,10 +22,13 @@ import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.server.cdr.CallDetailRecord; import io.openvidu.server.cdr.CallDetailRecord;
import io.openvidu.server.config.InfoHandler; import io.openvidu.server.config.InfoHandler;
import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.recording.Recording;
import io.openvidu.server.rpc.RpcNotificationService; import io.openvidu.server.rpc.RpcNotificationService;
public class SessionEventsHandler { public class SessionEventsHandler {
private static final Logger log = LoggerFactory.getLogger(SessionEventsHandler.class);
@Autowired @Autowired
protected RpcNotificationService rpcNotificationService; protected RpcNotificationService rpcNotificationService;
@ -33,20 +41,24 @@ public class SessionEventsHandler {
@Autowired @Autowired
protected OpenviduConfig openviduConfig; protected OpenviduConfig openviduConfig;
Map<String, Recording> recordingsStarted = new ConcurrentHashMap<>();
ReentrantLock lock = new ReentrantLock();
public void onSessionCreated(String sessionId) { public void onSessionCreated(String sessionId) {
if (openviduConfig.isCdrEnabled()) { if (openviduConfig.isCdrEnabled()) {
CDR.recordSessionCreated(sessionId); CDR.recordSessionCreated(sessionId);
} }
} }
public void onSessionClosed(String sessionId) { public void onSessionClosed(String sessionId, String reason) {
if (openviduConfig.isCdrEnabled()) { if (openviduConfig.isCdrEnabled()) {
CDR.recordSessionDestroyed(sessionId); CDR.recordSessionDestroyed(sessionId, reason);
} }
} }
public void onParticipantJoined(Participant participant, String sessionId, public void onParticipantJoined(Participant participant, String sessionId, Set<Participant> existingParticipants,
Set<Participant> existingParticipants, Integer transactionId, OpenViduException error) { Integer transactionId, OpenViduException error) {
if (error != null) { if (error != null) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error); rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return; return;
@ -54,80 +66,92 @@ public class SessionEventsHandler {
JsonObject result = new JsonObject(); JsonObject result = new JsonObject();
JsonArray resultArray = new JsonArray(); JsonArray resultArray = new JsonArray();
for (Participant p : existingParticipants) {
for (Participant existingParticipant : existingParticipants) {
JsonObject participantJson = new JsonObject(); JsonObject participantJson = new JsonObject();
participantJson.addProperty(ProtocolElements.JOINROOM_PEERID_PARAM, p.getParticipantPublicId()); participantJson.addProperty(ProtocolElements.JOINROOM_PEERID_PARAM,
existingParticipant.getParticipantPublicId());
// Metadata associated to each existing participant // Metadata associated to each existing participant
participantJson.addProperty(ProtocolElements.JOINROOM_METADATA_PARAM, p.getFullMetadata()); participantJson.addProperty(ProtocolElements.JOINROOM_METADATA_PARAM,
existingParticipant.getFullMetadata());
if (p.isStreaming()) { if (existingParticipant.isStreaming()) {
String streamId = ""; String streamId = "";
if ("SCREEN".equals(p.getTypeOfVideo())) { if ("SCREEN".equals(existingParticipant.getTypeOfVideo())) {
streamId = "SCREEN"; streamId = "SCREEN";
} else if (p.isVideoActive()) { } else if (existingParticipant.isVideoActive()) {
streamId = "CAMERA"; streamId = "CAMERA";
} else if (p.isAudioActive()) { } else if (existingParticipant.isAudioActive()) {
streamId = "MICRO"; streamId = "MICRO";
} }
JsonObject stream = new JsonObject(); JsonObject stream = new JsonObject();
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMID_PARAM, stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMID_PARAM,
p.getParticipantPublicId() + "_" + streamId); existingParticipant.getParticipantPublicId() + "_" + streamId);
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMAUDIOACTIVE_PARAM, p.isAudioActive()); stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMAUDIOACTIVE_PARAM,
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMVIDEOACTIVE_PARAM, p.isVideoActive()); existingParticipant.isAudioActive());
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMTYPEOFVIDEO_PARAM, p.getTypeOfVideo()); stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMVIDEOACTIVE_PARAM,
existingParticipant.isVideoActive());
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMTYPEOFVIDEO_PARAM,
existingParticipant.getTypeOfVideo());
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMFRAMERATE_PARAM,
existingParticipant.getFrameRate());
JsonArray streamsArray = new JsonArray(); JsonArray streamsArray = new JsonArray();
streamsArray.add(stream); streamsArray.add(stream);
participantJson.add(ProtocolElements.JOINROOM_PEERSTREAMS_PARAM, streamsArray); participantJson.add(ProtocolElements.JOINROOM_PEERSTREAMS_PARAM, streamsArray);
} }
resultArray.add(participantJson);
JsonObject notifParams = new JsonObject(); // Avoid emitting 'connectionCreated' event of existing RECORDER participant in
// openvidu-browser in newly joined participants
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(existingParticipant.getParticipantPublicId())) {
resultArray.add(participantJson);
}
// Metadata associated to new participant // If RECORDER participant has joined do NOT send 'participantJoined'
notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_USER_PARAM, // notification to existing participants. 'recordingStarted' will be sent to all
participant.getParticipantPublicId()); // existing participants when recorder first subscribe to a stream
notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_METADATA_PARAM, participant.getFullMetadata()); if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
JsonObject notifParams = new JsonObject();
rpcNotificationService.sendNotification(p.getParticipantPrivateId(), // Metadata associated to new participant
ProtocolElements.PARTICIPANTJOINED_METHOD, notifParams); notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_USER_PARAM,
participant.getParticipantPublicId());
notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_METADATA_PARAM,
participant.getFullMetadata());
rpcNotificationService.sendNotification(existingParticipant.getParticipantPrivateId(),
ProtocolElements.PARTICIPANTJOINED_METHOD, notifParams);
}
} }
result.addProperty(ProtocolElements.PARTICIPANTJOINED_USER_PARAM, participant.getParticipantPublicId()); result.addProperty(ProtocolElements.PARTICIPANTJOINED_USER_PARAM, participant.getParticipantPublicId());
result.addProperty(ProtocolElements.PARTICIPANTJOINED_METADATA_PARAM, participant.getFullMetadata()); result.addProperty(ProtocolElements.PARTICIPANTJOINED_METADATA_PARAM, participant.getFullMetadata());
result.add("value", resultArray); result.add("value", resultArray);
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, result); rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, result);
if (openviduConfig.isCdrEnabled()) {
CDR.recordParticipantJoined(participant, sessionId);
}
} }
public void onParticipantLeft(Participant participant, String sessionId, public void onParticipantLeft(Participant participant, String sessionId, Set<Participant> remainingParticipants,
Set<Participant> remainingParticipants, Integer transactionId, OpenViduException error) { Integer transactionId, OpenViduException error, String reason) {
if (error != null) { if (error != null) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error); rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return; return;
} }
boolean isPublishing = false; if (ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
if (openviduConfig.isCdrEnabled()) { // RECORDER participant
isPublishing = CDR.stopPublisher(participant.getParticipantPublicId()); return;
CDR.stopAllSubscriptions(participant.getParticipantPublicId());
} }
JsonObject params = new JsonObject(); JsonObject params = new JsonObject();
params.addProperty(ProtocolElements.PARTICIPANTLEFT_NAME_PARAM, participant.getParticipantPublicId()); params.addProperty(ProtocolElements.PARTICIPANTLEFT_NAME_PARAM, participant.getParticipantPublicId());
params.addProperty(ProtocolElements.PARTICIPANTLEFT_REASON_PARAM, reason);
for (Participant p : remainingParticipants) { for (Participant p : remainingParticipants) {
rpcNotificationService.sendNotification(p.getParticipantPrivateId(), rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
ProtocolElements.PARTICIPANTLEFT_METHOD, params); ProtocolElements.PARTICIPANTLEFT_METHOD, params);
if (isPublishing && openviduConfig.isCdrEnabled()) {
CDR.stopSubscriber(p.getParticipantPublicId(), participant.getParticipantPublicId());
}
} }
if (transactionId != null) { if (transactionId != null) {
@ -135,16 +159,10 @@ public class SessionEventsHandler {
// leaving the session // leaving the session
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject()); rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject());
} }
rpcNotificationService.closeRpcSession(participant.getParticipantPrivateId());
if (openviduConfig.isCdrEnabled()) {
CDR.recordParticipantLeft(participant, sessionId);
}
} }
public void onPublishMedia(Participant participant, String sessionId, MediaOptions mediaOptions, public void onPublishMedia(Participant participant, String sessionId, MediaOptions mediaOptions, String sdpAnswer,
String sdpAnswer, Set<Participant> participants, Integer transactionId, OpenViduException error) { Set<Participant> participants, Integer transactionId, OpenViduException error) {
if (error != null) { if (error != null) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error); rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return; return;
@ -171,6 +189,7 @@ public class SessionEventsHandler {
stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_AUDIOACTIVE_PARAM, mediaOptions.audioActive); stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_AUDIOACTIVE_PARAM, mediaOptions.audioActive);
stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_VIDEOACTIVE_PARAM, mediaOptions.videoActive); stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_VIDEOACTIVE_PARAM, mediaOptions.videoActive);
stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_TYPEOFVIDEO_PARAM, mediaOptions.typeOfVideo); stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_TYPEOFVIDEO_PARAM, mediaOptions.typeOfVideo);
stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_FRAMERATE_PARAM, mediaOptions.frameRate);
JsonArray streamsArray = new JsonArray(); JsonArray streamsArray = new JsonArray();
streamsArray.add(stream); streamsArray.add(stream);
@ -184,26 +203,19 @@ public class SessionEventsHandler {
ProtocolElements.PARTICIPANTPUBLISHED_METHOD, params); ProtocolElements.PARTICIPANTPUBLISHED_METHOD, params);
} }
} }
if (openviduConfig.isCdrEnabled()) {
CDR.recordNewPublisher(participant, sessionId, mediaOptions);
}
} }
public void onUnpublishMedia(Participant participant, Set<Participant> participants, Integer transactionId, public void onUnpublishMedia(Participant participant, Set<Participant> participants, Integer transactionId,
OpenViduException error) { OpenViduException error, String reason) {
if (error != null) { if (error != null) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error); rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return; return;
} }
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject()); rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject());
if (openviduConfig.isCdrEnabled()) {
CDR.stopPublisher(participant.getParticipantPublicId());
}
JsonObject params = new JsonObject(); JsonObject params = new JsonObject();
params.addProperty(ProtocolElements.PARTICIPANTUNPUBLISHED_NAME_PARAM, participant.getParticipantPublicId()); params.addProperty(ProtocolElements.PARTICIPANTUNPUBLISHED_NAME_PARAM, participant.getParticipantPublicId());
params.addProperty(ProtocolElements.PARTICIPANTUNPUBLISHED_REASON_PARAM, reason);
for (Participant p : participants) { for (Participant p : participants) {
if (p.getParticipantPrivateId().equals(participant.getParticipantPrivateId())) { if (p.getParticipantPrivateId().equals(participant.getParticipantPrivateId())) {
@ -211,15 +223,12 @@ public class SessionEventsHandler {
} else { } else {
rpcNotificationService.sendNotification(p.getParticipantPrivateId(), rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params); ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params);
if (openviduConfig.isCdrEnabled()) {
CDR.stopSubscriber(p.getParticipantPublicId(), participant.getParticipantPublicId());
}
} }
} }
} }
public void onSubscribe(Participant participant, String sessionId, String senderName, String sdpAnswer, Integer transactionId, public void onSubscribe(Participant participant, Session session, String senderName, String sdpAnswer,
OpenViduException error) { Integer transactionId, OpenViduException error) {
if (error != null) { if (error != null) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error); rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return; return;
@ -228,21 +237,27 @@ public class SessionEventsHandler {
result.addProperty(ProtocolElements.RECEIVEVIDEO_SDPANSWER_PARAM, sdpAnswer); result.addProperty(ProtocolElements.RECEIVEVIDEO_SDPANSWER_PARAM, sdpAnswer);
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, result); rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, result);
if (openviduConfig.isCdrEnabled()) { if (ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
CDR.recordNewSubscriber(participant, sessionId, senderName); lock.lock();
try {
Recording recording = this.recordingsStarted.remove(session.getSessionId());
if (recording != null) {
// RECORDER participant is now receiving video from the first publisher
this.sendRecordingStartedNotification(session, recording);
}
} finally {
lock.unlock();
}
} }
} }
public void onUnsubscribe(Participant participant, String senderName, Integer transactionId, OpenViduException error) { public void onUnsubscribe(Participant participant, String senderName, Integer transactionId,
OpenViduException error) {
if (error != null) { if (error != null) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error); rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return; return;
} }
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject()); rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject());
if (openviduConfig.isCdrEnabled()) {
CDR.stopSubscriber(participant.getParticipantPublicId(), senderName);
}
} }
public void onSendMessage(Participant participant, JsonObject message, Set<Participant> participants, public void onSendMessage(Participant participant, JsonObject message, Set<Participant> participants,
@ -308,7 +323,78 @@ public class SessionEventsHandler {
ProtocolElements.PARTICIPANTEVICTED_METHOD, new JsonObject()); ProtocolElements.PARTICIPANTEVICTED_METHOD, new JsonObject());
} }
public void sendRecordingStartedNotification(Session session, Recording recording) {
if (openviduConfig.isCdrEnabled()) {
CDR.recordRecordingStarted(session.getSessionId());
}
// Filter participants by roles according to "openvidu.recording.notification"
Set<Participant> filteredParticipants = this.filterParticipantsByRole(
this.openviduConfig.getRolesFromRecordingNotification(), session.getParticipants());
JsonObject params = new JsonObject();
params.addProperty(ProtocolElements.RECORDINGSTARTED_ID_PARAM, recording.getId());
for (Participant p : filteredParticipants) {
rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
ProtocolElements.RECORDINGSTARTED_METHOD, params);
}
}
public void sendRecordingStoppedNotification(Session session, Recording recording) {
if (openviduConfig.isCdrEnabled()) {
CDR.recordRecordingStopped(session.getSessionId());
}
// Be sure to clean this map (this should return null)
this.recordingsStarted.remove(session.getSessionId());
// Filter participants by roles according to "openvidu.recording.notification"
Set<Participant> existingParticipants;
try {
existingParticipants = session.getParticipants();
} catch (OpenViduException exception) {
// Session is already closed. This happens when ArchiveMode.ALWAYS and last
// participant has left the session. No notification needs to be sent
log.warn("Session already closed when trying to send 'recordingStopped' notification");
return;
}
Set<Participant> filteredParticipants = this.filterParticipantsByRole(
this.openviduConfig.getRolesFromRecordingNotification(), existingParticipants);
JsonObject params = new JsonObject();
params.addProperty(ProtocolElements.RECORDINGSTOPPED_ID_PARAM, recording.getId());
for (Participant p : filteredParticipants) {
rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
ProtocolElements.RECORDINGSTOPPED_METHOD, params);
}
}
public void closeRpcSession(String participantPrivateId) {
this.rpcNotificationService.closeRpcSession(participantPrivateId);
}
public void setRecordingStarted(String sessionId, Recording recording) {
this.recordingsStarted.put(sessionId, recording);
}
public InfoHandler getInfoHandler() { public InfoHandler getInfoHandler() {
return this.infoHandler; return this.infoHandler;
} }
private Set<Participant> filterParticipantsByRole(ParticipantRole[] roles, Set<Participant> participants) {
return participants.stream().filter(part -> {
boolean isRole = false;
for (ParticipantRole role : roles) {
isRole = role.equals(part.getToken().getRole());
if (isRole)
break;
}
return isRole;
}).collect(Collectors.toSet());
}
} }

View File

@ -12,6 +12,7 @@ import org.apache.commons.lang3.RandomStringUtils;
import org.kurento.jsonrpc.message.Request; import org.kurento.jsonrpc.message.Request;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@ -20,11 +21,26 @@ import io.openvidu.client.OpenViduException.Code;
import io.openvidu.client.internal.ProtocolElements; import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.java.client.SessionProperties; import io.openvidu.java.client.SessionProperties;
import io.openvidu.server.OpenViduServer; import io.openvidu.server.OpenViduServer;
import io.openvidu.server.cdr.CallDetailRecord;
import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.recording.ComposedRecordingService;
public abstract class SessionManager { public abstract class SessionManager {
private static final Logger log = LoggerFactory.getLogger(SessionManager.class); private static final Logger log = LoggerFactory.getLogger(SessionManager.class);
@Autowired
protected SessionEventsHandler sessionEventsHandler;
@Autowired
protected ComposedRecordingService recordingService;
@Autowired
protected CallDetailRecord CDR;
@Autowired
protected OpenviduConfig openviduConfig;
protected ConcurrentMap<String, Session> sessions = new ConcurrentHashMap<>(); protected ConcurrentMap<String, Session> sessions = new ConcurrentHashMap<>();
protected ConcurrentMap<String, SessionProperties> sessionProperties = new ConcurrentHashMap<>(); protected ConcurrentMap<String, SessionProperties> sessionProperties = new ConcurrentHashMap<>();
protected ConcurrentMap<String, ConcurrentHashMap<String, Token>> sessionidTokenTokenobj = new ConcurrentHashMap<>(); protected ConcurrentMap<String, ConcurrentHashMap<String, Token>> sessionidTokenTokenobj = new ConcurrentHashMap<>();
@ -35,11 +51,11 @@ public abstract class SessionManager {
public abstract void joinRoom(Participant participant, String sessionId, Integer transactionId); public abstract void joinRoom(Participant participant, String sessionId, Integer transactionId);
public abstract void leaveRoom(Participant participant, Integer transactionId); public abstract void leaveRoom(Participant participant, Integer transactionId, String reason);
public abstract void publishVideo(Participant participant, MediaOptions mediaOptions, Integer transactionId); public abstract void publishVideo(Participant participant, MediaOptions mediaOptions, Integer transactionId);
public abstract void unpublishVideo(Participant participant, Integer transactionId); public abstract void unpublishVideo(Participant participant, Integer transactionId, String reason);
public abstract void subscribe(Participant participant, String senderName, String sdpOffer, Integer transactionId); public abstract void subscribe(Participant participant, String senderName, String sdpOffer, Integer transactionId);
@ -57,7 +73,7 @@ public abstract class SessionManager {
* other participants about the one that's just been evicted. * other participants about the one that's just been evicted.
* *
*/ */
public void evictParticipant(String participantPrivateId) throws OpenViduException { public void evictParticipant(String participantPrivateId, String reason) throws OpenViduException {
} }
/** /**
@ -187,7 +203,7 @@ public abstract class SessionManager {
} else { } else {
this.sessionidParticipantpublicidParticipant.putIfAbsent(sessionId, new ConcurrentHashMap<>()); this.sessionidParticipantpublicidParticipant.putIfAbsent(sessionId, new ConcurrentHashMap<>());
this.sessionidTokenTokenobj.putIfAbsent(sessionId, new ConcurrentHashMap<>()); this.sessionidTokenTokenobj.putIfAbsent(sessionId, new ConcurrentHashMap<>());
this.sessionidTokenTokenobj.get(sessionId).putIfAbsent(token, new Token(token)); this.sessionidTokenTokenobj.get(sessionId).putIfAbsent(token, new Token(token, ParticipantRole.PUBLISHER, ""));
return true; return true;
} }
} }
@ -252,7 +268,7 @@ public abstract class SessionManager {
public Participant newRecorderParticipant(String sessionId, String participantPrivatetId, Token token, public Participant newRecorderParticipant(String sessionId, String participantPrivatetId, Token token,
String clientMetadata) { String clientMetadata) {
if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null) { if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null) {
String participantPublicId = ProtocolElements.RECORDER_PARTICIPANT_ID_PUBLICID; String participantPublicId = ProtocolElements.RECORDER_PARTICIPANT_PUBLICID;
Participant p = new Participant(participantPrivatetId, participantPublicId, token, clientMetadata); Participant p = new Participant(participantPrivatetId, participantPublicId, token, clientMetadata);
this.sessionidParticipantpublicidParticipant.get(sessionId).put(participantPublicId, p); this.sessionidParticipantpublicidParticipant.get(sessionId).put(participantPublicId, p);
return p; return p;
@ -305,7 +321,7 @@ public abstract class SessionManager {
log.info("Closing all sessions"); log.info("Closing all sessions");
for (String sessionId : sessions.keySet()) { for (String sessionId : sessions.keySet()) {
try { try {
closeSession(sessionId); closeSession(sessionId, "openviduServerDestroyed");
} catch (Exception e) { } catch (Exception e) {
log.warn("Error closing session '{}'", sessionId, e); log.warn("Error closing session '{}'", sessionId, e);
} }
@ -328,7 +344,7 @@ public abstract class SessionManager {
* @throws OpenViduException * @throws OpenViduException
* in case the session doesn't exist or has been already closed * in case the session doesn't exist or has been already closed
*/ */
private Set<Participant> closeSession(String sessionId) { private Set<Participant> closeSession(String sessionId, String reason) {
Session session = sessions.get(sessionId); Session session = sessions.get(sessionId);
if (session == null) { if (session == null) {
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, "Session '" + sessionId + "' not found"); throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, "Session '" + sessionId + "' not found");
@ -341,12 +357,14 @@ public abstract class SessionManager {
Set<String> pids = participants.stream().map(Participant::getParticipantPrivateId).collect(Collectors.toSet()); Set<String> pids = participants.stream().map(Participant::getParticipantPrivateId).collect(Collectors.toSet());
for (String pid : pids) { for (String pid : pids) {
try { try {
session.leave(pid); session.leave(pid, reason);
} catch (OpenViduException e) { } catch (OpenViduException e) {
log.warn("Error evicting participant with id '{}' from session '{}'", pid, sessionId, e); log.warn("Error evicting participant with id '{}' from session '{}'", pid, sessionId, e);
} }
} }
session.close(); if (session.close(reason)) {
sessionEventsHandler.onSessionClosed(sessionId, reason);
}
sessions.remove(sessionId); sessions.remove(sessionId);
sessionProperties.remove(sessionId); sessionProperties.remove(sessionId);
@ -354,6 +372,11 @@ public abstract class SessionManager {
sessionidTokenTokenobj.remove(sessionId); sessionidTokenTokenobj.remove(sessionId);
log.warn("Session '{}' removed and closed", sessionId); log.warn("Session '{}' removed and closed", sessionId);
if (recordingService.sessionIsBeingRecorded(session.getSessionId())) {
recordingService.stopRecording(session);
}
return participants; return participants;
} }

View File

@ -16,8 +16,8 @@ public class KurentoMediaOptions extends MediaOptions {
public KurentoMediaOptions(boolean isOffer, String sdpOffer, MediaElement loopbackAlternativeSrc, public KurentoMediaOptions(boolean isOffer, String sdpOffer, MediaElement loopbackAlternativeSrc,
MediaType loopbackConnectionType, boolean audioActive, boolean videoActive, String typeOfVideo, MediaType loopbackConnectionType, boolean audioActive, boolean videoActive, String typeOfVideo,
boolean doLoopback, MediaElement... mediaElements) { int frameRate, boolean doLoopback, MediaElement... mediaElements) {
super(audioActive, videoActive, typeOfVideo); super(audioActive, videoActive, typeOfVideo, frameRate);
this.isOffer = isOffer; this.isOffer = isOffer;
this.sdpOffer = sdpOffer; this.sdpOffer = sdpOffer;
this.loopbackAlternativeSrc = loopbackAlternativeSrc; this.loopbackAlternativeSrc = loopbackAlternativeSrc;

View File

@ -21,7 +21,10 @@ 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.client.internal.ProtocolElements;
import io.openvidu.server.cdr.CallDetailRecord;
import io.openvidu.server.config.InfoHandler; import io.openvidu.server.config.InfoHandler;
import io.openvidu.server.core.MediaOptions;
import io.openvidu.server.core.Participant; import io.openvidu.server.core.Participant;
import io.openvidu.server.kurento.MutedMediaType; import io.openvidu.server.kurento.MutedMediaType;
import io.openvidu.server.kurento.endpoint.MediaEndpoint; import io.openvidu.server.kurento.endpoint.MediaEndpoint;
@ -34,6 +37,7 @@ public class KurentoParticipant extends Participant {
private static final Logger log = LoggerFactory.getLogger(KurentoParticipant.class); private static final Logger log = LoggerFactory.getLogger(KurentoParticipant.class);
private InfoHandler infoHandler; private InfoHandler infoHandler;
private CallDetailRecord CDR;
private boolean webParticipant = true; private boolean webParticipant = true;
@ -46,7 +50,7 @@ public class KurentoParticipant extends Participant {
private final ConcurrentMap<String, Filter> filters = new ConcurrentHashMap<>(); private final ConcurrentMap<String, Filter> filters = new ConcurrentHashMap<>();
private final ConcurrentMap<String, SubscriberEndpoint> subscribers = new ConcurrentHashMap<String, SubscriberEndpoint>(); private final ConcurrentMap<String, SubscriberEndpoint> subscribers = new ConcurrentHashMap<String, SubscriberEndpoint>();
public KurentoParticipant(Participant participant, KurentoSession kurentoSession, MediaPipeline pipeline, InfoHandler infoHandler) { public KurentoParticipant(Participant participant, KurentoSession kurentoSession, MediaPipeline pipeline, InfoHandler infoHandler, CallDetailRecord CDR) {
super(participant.getParticipantPrivateId(), participant.getParticipantPublicId(), participant.getToken(), super(participant.getParticipantPrivateId(), participant.getParticipantPublicId(), participant.getToken(),
participant.getClientMetadata()); participant.getClientMetadata());
this.session = kurentoSession; this.session = kurentoSession;
@ -60,9 +64,10 @@ public class KurentoParticipant extends Participant {
} }
} }
this.infoHandler = infoHandler; this.infoHandler = infoHandler;
this.CDR = CDR;
} }
public void createPublishingEndpoint() { public void createPublishingEndpoint(MediaOptions mediaOptions) {
publisher.createEndpoint(endPointLatch); publisher.createEndpoint(endPointLatch);
if (getPublisher().getEndpoint() == null) { if (getPublisher().getEndpoint() == null) {
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE, "Unable to create publisher endpoint"); throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE, "Unable to create publisher endpoint");
@ -70,6 +75,10 @@ public class KurentoParticipant extends Participant {
this.publisher.getEndpoint().addTag("name", "PUBLISHER " + this.getParticipantPublicId()); this.publisher.getEndpoint().addTag("name", "PUBLISHER " + this.getParticipantPublicId());
addEndpointListeners(this.publisher); addEndpointListeners(this.publisher);
CDR.recordNewPublisher(this, this.session.getSessionId(), mediaOptions);
} }
public void shapePublisherMedia(MediaElement element, MediaType type) { public void shapePublisherMedia(MediaElement element, MediaType type) {
@ -194,10 +203,10 @@ public class KurentoParticipant extends Participant {
return sdpResponse; return sdpResponse;
} }
public void unpublishMedia() { public void unpublishMedia(String reason) {
log.info("PARTICIPANT {}: unpublishing media stream from room {}", this.getParticipantPublicId(), log.info("PARTICIPANT {}: unpublishing media stream from room {}", this.getParticipantPublicId(),
this.session.getSessionId()); this.session.getSessionId());
releasePublisherEndpoint(); releasePublisherEndpoint(reason);
this.publisher = new PublisherEndpoint(webParticipant, this, this.getParticipantPublicId(), this.publisher = new PublisherEndpoint(webParticipant, this, this.getParticipantPublicId(),
pipeline); pipeline);
log.info( log.info(
@ -269,6 +278,11 @@ public class KurentoParticipant extends Participant {
log.trace("PARTICIPANT {}: Subscribing SdpAnswer is {}", this.getParticipantPublicId(), sdpAnswer); log.trace("PARTICIPANT {}: Subscribing SdpAnswer is {}", this.getParticipantPublicId(), sdpAnswer);
log.info("PARTICIPANT {}: Is now receiving video from {} in room {}", this.getParticipantPublicId(), senderName, log.info("PARTICIPANT {}: Is now receiving video from {} in room {}", this.getParticipantPublicId(), senderName,
this.session.getSessionId()); this.session.getSessionId());
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(this.getParticipantPublicId())) {
CDR.recordNewSubscriber(this, this.session.getSessionId(), sender.getParticipantPublicId());
}
return sdpAnswer; return sdpAnswer;
} catch (KurentoServerException e) { } catch (KurentoServerException e) {
// TODO Check object status when KurentoClient sets this info in the object // TODO Check object status when KurentoClient sets this info in the object
@ -279,19 +293,19 @@ public class KurentoParticipant extends Participant {
log.error("Exception connecting subscriber endpoint " + "to publisher endpoint", e); log.error("Exception connecting subscriber endpoint " + "to publisher endpoint", e);
} }
this.subscribers.remove(senderName); this.subscribers.remove(senderName);
releaseSubscriberEndpoint(senderName, subscriber); releaseSubscriberEndpoint(senderName, subscriber, "");
} }
return null; return null;
} }
public void cancelReceivingMedia(String senderName) { public void cancelReceivingMedia(String senderName, String reason) {
log.info("PARTICIPANT {}: cancel receiving media from {}", this.getParticipantPublicId(), senderName); log.info("PARTICIPANT {}: cancel receiving media from {}", this.getParticipantPublicId(), senderName);
SubscriberEndpoint subscriberEndpoint = subscribers.remove(senderName); SubscriberEndpoint subscriberEndpoint = subscribers.remove(senderName);
if (subscriberEndpoint == null || subscriberEndpoint.getEndpoint() == null) { if (subscriberEndpoint == null || subscriberEndpoint.getEndpoint() == null) {
log.warn("PARTICIPANT {}: Trying to cancel receiving video from user {}. " log.warn("PARTICIPANT {}: Trying to cancel receiving video from user {}. "
+ "But there is no such subscriber endpoint.", this.getParticipantPublicId(), senderName); + "But there is no such subscriber endpoint.", this.getParticipantPublicId(), senderName);
} else { } else {
releaseSubscriberEndpoint(senderName, subscriberEndpoint); releaseSubscriberEndpoint(senderName, subscriberEndpoint, reason);
log.info("PARTICIPANT {}: stopped receiving media from {} in room {}", this.getParticipantPublicId(), senderName, log.info("PARTICIPANT {}: stopped receiving media from {} in room {}", this.getParticipantPublicId(), senderName,
this.session.getSessionId()); this.session.getSessionId());
} }
@ -347,7 +361,7 @@ public class KurentoParticipant extends Participant {
} }
} }
public void close() { public void close(String reason) {
log.debug("PARTICIPANT {}: Closing user", this.getParticipantPublicId()); log.debug("PARTICIPANT {}: Closing user", this.getParticipantPublicId());
if (isClosed()) { if (isClosed()) {
log.warn("PARTICIPANT {}: Already closed", this.getParticipantPublicId()); log.warn("PARTICIPANT {}: Already closed", this.getParticipantPublicId());
@ -357,7 +371,7 @@ public class KurentoParticipant extends Participant {
for (String remoteParticipantName : subscribers.keySet()) { for (String remoteParticipantName : subscribers.keySet()) {
SubscriberEndpoint subscriber = this.subscribers.get(remoteParticipantName); SubscriberEndpoint subscriber = this.subscribers.get(remoteParticipantName);
if (subscriber != null && subscriber.getEndpoint() != null) { if (subscriber != null && subscriber.getEndpoint() != null) {
releaseSubscriberEndpoint(remoteParticipantName, subscriber); releaseSubscriberEndpoint(remoteParticipantName, subscriber, reason);
log.debug("PARTICIPANT {}: Released subscriber endpoint to {}", this.getParticipantPublicId(), log.debug("PARTICIPANT {}: Released subscriber endpoint to {}", this.getParticipantPublicId(),
remoteParticipantName); remoteParticipantName);
} else { } else {
@ -367,7 +381,7 @@ public class KurentoParticipant extends Participant {
this.getParticipantPublicId(), remoteParticipantName); this.getParticipantPublicId(), remoteParticipantName);
} }
} }
releasePublisherEndpoint(); releasePublisherEndpoint(reason);
} }
/** /**
@ -410,7 +424,7 @@ public class KurentoParticipant extends Participant {
session.sendMediaError(this.getParticipantPrivateId(), desc); session.sendMediaError(this.getParticipantPrivateId(), desc);
} }
private void releasePublisherEndpoint() { private void releasePublisherEndpoint(String reason) {
if (publisher != null && publisher.getEndpoint() != null) { if (publisher != null && publisher.getEndpoint() != null) {
publisher.unregisterErrorListeners(); publisher.unregisterErrorListeners();
for (MediaElement el : publisher.getMediaElements()) { for (MediaElement el : publisher.getMediaElements()) {
@ -419,15 +433,23 @@ public class KurentoParticipant extends Participant {
releaseElement(getParticipantPublicId(), publisher.getEndpoint()); releaseElement(getParticipantPublicId(), publisher.getEndpoint());
this.streaming = false; this.streaming = false;
publisher = null; publisher = null;
CDR.stopPublisher(this.getParticipantPublicId(), reason);
} else { } else {
log.warn("PARTICIPANT {}: Trying to release publisher endpoint but is null", getParticipantPublicId()); log.warn("PARTICIPANT {}: Trying to release publisher endpoint but is null", getParticipantPublicId());
} }
} }
private void releaseSubscriberEndpoint(String senderName, SubscriberEndpoint subscriber) { private void releaseSubscriberEndpoint(String senderName, SubscriberEndpoint subscriber, String reason) {
if (subscriber != null) { if (subscriber != null) {
subscriber.unregisterErrorListeners(); subscriber.unregisterErrorListeners();
releaseElement(senderName, subscriber.getEndpoint()); releaseElement(senderName, subscriber.getEndpoint());
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(this.getParticipantPublicId())) {
CDR.stopSubscriber(this.getParticipantPublicId(), senderName, reason);
}
} else { } else {
log.warn("PARTICIPANT {}: Trying to release subscriber endpoint for '{}' but is null", log.warn("PARTICIPANT {}: Trying to release subscriber endpoint for '{}' but is null",
this.getParticipantPublicId(), senderName); this.getParticipantPublicId(), senderName);

View File

@ -19,7 +19,9 @@ 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.client.internal.ProtocolElements;
import io.openvidu.java.client.SessionProperties; import io.openvidu.java.client.SessionProperties;
import io.openvidu.server.cdr.CallDetailRecord;
import io.openvidu.server.core.Participant; import io.openvidu.server.core.Participant;
import io.openvidu.server.core.Session; import io.openvidu.server.core.Session;
@ -51,13 +53,16 @@ public class KurentoSession implements Session {
private volatile boolean pipelineReleased = false; private volatile boolean pipelineReleased = false;
private boolean destroyKurentoClient; private boolean destroyKurentoClient;
private CallDetailRecord CDR;
public KurentoSession(String sessionId, SessionProperties sessionProperties, KurentoClient kurentoClient, KurentoSessionEventsHandler kurentoSessionHandler, public KurentoSession(String sessionId, SessionProperties sessionProperties, KurentoClient kurentoClient, KurentoSessionEventsHandler kurentoSessionHandler,
boolean destroyKurentoClient) { boolean destroyKurentoClient, CallDetailRecord CDR) {
this.sessionId = sessionId; this.sessionId = sessionId;
this.sessionProperties = sessionProperties; this.sessionProperties = sessionProperties;
this.kurentoClient = kurentoClient; this.kurentoClient = kurentoClient;
this.destroyKurentoClient = destroyKurentoClient; this.destroyKurentoClient = destroyKurentoClient;
this.kurentoSessionHandler = kurentoSessionHandler; this.kurentoSessionHandler = kurentoSessionHandler;
this.CDR = CDR;
log.debug("New SESSION instance with id '{}'", sessionId); log.debug("New SESSION instance with id '{}'", sessionId);
} }
@ -76,7 +81,7 @@ public class KurentoSession implements Session {
checkClosed(); checkClosed();
createPipeline(); createPipeline();
KurentoParticipant kurentoParticipant = new KurentoParticipant(participant, this, getPipeline(), kurentoSessionHandler.getInfoHandler()); KurentoParticipant kurentoParticipant = new KurentoParticipant(participant, this, getPipeline(), kurentoSessionHandler.getInfoHandler(), this.CDR);
participants.put(participant.getParticipantPrivateId(), kurentoParticipant); participants.put(participant.getParticipantPrivateId(), kurentoParticipant);
filterStates.forEach((filterId, state) -> { filterStates.forEach((filterId, state) -> {
@ -85,6 +90,10 @@ public class KurentoSession implements Session {
}); });
log.info("SESSION {}: Added participant {}", sessionId, participant); log.info("SESSION {}: Added participant {}", sessionId, participant);
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
CDR.recordParticipantJoined(participant, sessionId);
}
} }
public void newPublisher(Participant participant) { public void newPublisher(Participant participant) {
@ -102,7 +111,7 @@ public class KurentoSession implements Session {
participants.values(), participant.getParticipantPublicId()); participants.values(), participant.getParticipantPublicId());
} }
public void cancelPublisher(Participant participant) { public void cancelPublisher(Participant participant, String reason) {
deregisterPublisher(); deregisterPublisher();
// cancel recv video from this publisher // cancel recv video from this publisher
@ -110,7 +119,7 @@ public class KurentoSession implements Session {
if (participant.equals(subscriber)) { if (participant.equals(subscriber)) {
continue; continue;
} }
subscriber.cancelReceivingMedia(participant.getParticipantPublicId()); subscriber.cancelReceivingMedia(participant.getParticipantPublicId(), reason);
} }
@ -120,7 +129,7 @@ public class KurentoSession implements Session {
} }
@Override @Override
public void leave(String participantPrivateId) throws OpenViduException { public void leave(String participantPrivateId, String reason) throws OpenViduException {
checkClosed(); checkClosed();
@ -135,8 +144,12 @@ public class KurentoSession implements Session {
if (participant.isStreaming()) { if (participant.isStreaming()) {
this.deregisterPublisher(); this.deregisterPublisher();
} }
this.removeParticipant(participant); this.removeParticipant(participant, reason);
participant.close(); participant.close(reason);
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
CDR.recordParticipantLeft(participant, participant.getSession().getSessionId(), reason);
}
} }
@Override @Override
@ -163,12 +176,12 @@ public class KurentoSession implements Session {
} }
@Override @Override
public boolean close() { public boolean close(String reason) {
if (!closed) { if (!closed) {
for (KurentoParticipant participant : participants.values()) { for (KurentoParticipant participant : participants.values()) {
participant.releaseAllFilters(); participant.releaseAllFilters();
participant.close(); participant.close(reason);
} }
participants.clear(); participants.clear();
@ -208,7 +221,7 @@ public class KurentoSession implements Session {
} }
} }
private void removeParticipant(Participant participant) { private void removeParticipant(Participant participant, String reason) {
checkClosed(); checkClosed();
@ -216,7 +229,7 @@ public class KurentoSession implements Session {
log.debug("SESSION {}: Cancel receiving media from participant '{}' for other participant", this.sessionId, participant.getParticipantPublicId()); log.debug("SESSION {}: Cancel receiving media from participant '{}' for other participant", this.sessionId, participant.getParticipantPublicId());
for (KurentoParticipant other : participants.values()) { for (KurentoParticipant other : participants.values()) {
other.cancelReceivingMedia(participant.getParticipantPublicId()); other.cancelReceivingMedia(participant.getParticipantPublicId(), reason);
} }
} }

View File

@ -27,9 +27,7 @@ import io.openvidu.server.kurento.KurentoClientProvider;
import io.openvidu.server.kurento.KurentoClientSessionInfo; import io.openvidu.server.kurento.KurentoClientSessionInfo;
import io.openvidu.server.kurento.OpenViduKurentoClientSessionInfo; import io.openvidu.server.kurento.OpenViduKurentoClientSessionInfo;
import io.openvidu.server.kurento.endpoint.SdpType; import io.openvidu.server.kurento.endpoint.SdpType;
import io.openvidu.server.recording.ComposedRecordingService;
import io.openvidu.server.rpc.RpcHandler; import io.openvidu.server.rpc.RpcHandler;
import io.openvidu.server.config.OpenviduConfig;
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.core.Session; import io.openvidu.server.core.Session;
@ -42,13 +40,7 @@ public class KurentoSessionManager extends SessionManager {
private KurentoClientProvider kcProvider; private KurentoClientProvider kcProvider;
@Autowired @Autowired
private KurentoSessionEventsHandler sessionHandler; private KurentoSessionEventsHandler kurentoSessionEventsHandler;
@Autowired
private ComposedRecordingService recordingService;
@Autowired
OpenviduConfig openviduConfig;
@Override @Override
public synchronized void joinRoom(Participant participant, String sessionId, Integer transactionId) { public synchronized void joinRoom(Participant participant, String sessionId, Integer transactionId) {
@ -57,8 +49,8 @@ public class KurentoSessionManager extends SessionManager {
KurentoClientSessionInfo kcSessionInfo = new OpenViduKurentoClientSessionInfo( KurentoClientSessionInfo kcSessionInfo = new OpenViduKurentoClientSessionInfo(
participant.getParticipantPrivateId(), sessionId); participant.getParticipantPrivateId(), sessionId);
KurentoSession session = (KurentoSession) sessions.get(sessionId); KurentoSession session = (KurentoSession) sessions.get(sessionId);
if (session == null && kcSessionInfo != null) { if (session == null && kcSessionInfo != null) {
SessionProperties properties = sessionProperties.get(sessionId); SessionProperties properties = sessionProperties.get(sessionId);
if (properties == null && this.isInsecureParticipant(participant.getParticipantPrivateId())) { if (properties == null && this.isInsecureParticipant(participant.getParticipantPrivateId())) {
@ -85,27 +77,30 @@ public class KurentoSessionManager extends SessionManager {
} catch (OpenViduException e) { } catch (OpenViduException e) {
log.warn("PARTICIPANT {}: Error joining/creating session {}", participant.getParticipantPublicId(), log.warn("PARTICIPANT {}: Error joining/creating session {}", participant.getParticipantPublicId(),
sessionId, e); sessionId, e);
sessionHandler.onParticipantJoined(participant, sessionId, null, transactionId, e); sessionEventsHandler.onParticipantJoined(participant, sessionId, null, transactionId, e);
} }
if (existingParticipants != null) { if (existingParticipants != null) {
sessionHandler.onParticipantJoined(participant, sessionId, existingParticipants, transactionId, null); sessionEventsHandler.onParticipantJoined(participant, sessionId, existingParticipants, transactionId, null);
} }
} }
@Override @Override
public void leaveRoom(Participant participant, Integer transactionId) { public void leaveRoom(Participant participant, Integer transactionId, String reason) {
log.debug("Request [LEAVE_ROOM] ({})", participant.getParticipantPublicId()); log.debug("Request [LEAVE_ROOM] ({})", participant.getParticipantPublicId());
KurentoParticipant kParticipant = (KurentoParticipant) participant; KurentoParticipant kParticipant = (KurentoParticipant) participant;
KurentoSession session = kParticipant.getSession(); KurentoSession session = kParticipant.getSession();
String sessionId = session.getSessionId(); String sessionId = session.getSessionId();
if (session.isClosed()) { if (session.isClosed()) {
log.warn("'{}' is trying to leave from session '{}' but it is closing", log.warn("'{}' is trying to leave from session '{}' but it is closing",
participant.getParticipantPublicId(), sessionId); participant.getParticipantPublicId(), sessionId);
throw new OpenViduException(Code.ROOM_CLOSED_ERROR_CODE, "'" + participant.getParticipantPublicId() throw new OpenViduException(Code.ROOM_CLOSED_ERROR_CODE, "'" + participant.getParticipantPublicId()
+ "' is trying to leave from session '" + sessionId + "' but it is closing"); + "' is trying to leave from session '" + sessionId + "' but it is closing");
} }
session.leave(participant.getParticipantPrivateId()); session.leave(participant.getParticipantPrivateId(), reason);
// Update control data structures
if (sessionidParticipantpublicidParticipant.get(sessionId) != null) { if (sessionidParticipantpublicidParticipant.get(sessionId) != null) {
Participant p = sessionidParticipantpublicidParticipant.get(sessionId) Participant p = sessionidParticipantpublicidParticipant.get(sessionId)
@ -127,6 +122,8 @@ public class KurentoSessionManager extends SessionManager {
showTokens(); showTokens();
// Close Session if no more participants
Set<Participant> remainingParticipants = null; Set<Participant> remainingParticipants = null;
try { try {
remainingParticipants = getParticipants(sessionId); remainingParticipants = getParticipants(sessionId);
@ -134,10 +131,15 @@ public class KurentoSessionManager extends SessionManager {
log.debug("Possible collision when closing the session '{}' (not found)"); log.debug("Possible collision when closing the session '{}' (not found)");
remainingParticipants = Collections.emptySet(); remainingParticipants = Collections.emptySet();
} }
sessionEventsHandler.onParticipantLeft(participant, sessionId, remainingParticipants, transactionId, null,
reason);
if (remainingParticipants.isEmpty()) { if (remainingParticipants.isEmpty()) {
log.info("No more participants in session '{}', removing it and closing it", sessionId); log.info("No more participants in session '{}', removing it and closing it", sessionId);
if (session.close()) { if (session.close(reason)) {
sessionHandler.onSessionClosed(sessionId); sessionEventsHandler.onSessionClosed(sessionId, "lastParticipantLeft");
} }
sessions.remove(sessionId); sessions.remove(sessionId);
@ -148,17 +150,21 @@ public class KurentoSessionManager extends SessionManager {
showTokens(); showTokens();
log.warn("Session '{}' removed and closed", sessionId); log.warn("Session '{}' removed and closed", sessionId);
}
if (remainingParticipants.size() == 1 && openviduConfig.isRecordingModuleEnabled() } else if (remainingParticipants.size() == 1 && openviduConfig.isRecordingModuleEnabled()
&& MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode()) && MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode())
&& ArchiveMode.ALWAYS.equals(session.getSessionProperties().archiveMode()) && ArchiveMode.ALWAYS.equals(session.getSessionProperties().archiveMode())
&& ProtocolElements.RECORDER_PARTICIPANT_ID_PUBLICID && ProtocolElements.RECORDER_PARTICIPANT_PUBLICID
.equals(remainingParticipants.iterator().next().getParticipantPublicId())) { .equals(remainingParticipants.iterator().next().getParticipantPublicId())) {
log.info("Last participant left. Stopping recording for session {}", sessionId); log.info("Last participant left. Stopping recording for session {}", sessionId);
evictParticipant(session.getParticipantByPublicId("RECORDER").getParticipantPrivateId());
recordingService.stopRecording(session); recordingService.stopRecording(session);
evictParticipant(session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID)
.getParticipantPrivateId(), "EVICT_RECORDER");
} }
sessionHandler.onParticipantLeft(participant, sessionId, remainingParticipants, transactionId, null);
// Finally close websocket session
sessionEventsHandler.closeRpcSession(participant.getParticipantPrivateId());
} }
/** /**
@ -205,7 +211,7 @@ public class KurentoSessionManager extends SessionManager {
SdpType sdpType = kurentoOptions.isOffer ? SdpType.OFFER : SdpType.ANSWER; SdpType sdpType = kurentoOptions.isOffer ? SdpType.OFFER : SdpType.ANSWER;
KurentoSession session = kurentoParticipant.getSession(); KurentoSession session = kurentoParticipant.getSession();
kurentoParticipant.createPublishingEndpoint(); kurentoParticipant.createPublishingEndpoint(mediaOptions);
for (MediaElement elem : kurentoOptions.mediaElements) { for (MediaElement elem : kurentoOptions.mediaElements) {
kurentoParticipant.getPublisher().apply(elem); kurentoParticipant.getPublisher().apply(elem);
@ -218,13 +224,14 @@ public class KurentoSessionManager extends SessionManager {
OpenViduException e = new OpenViduException(Code.MEDIA_SDP_ERROR_CODE, OpenViduException e = new OpenViduException(Code.MEDIA_SDP_ERROR_CODE,
"Error generating SDP response for publishing user " + participant.getParticipantPublicId()); "Error generating SDP response for publishing user " + participant.getParticipantPublicId());
log.error("PARTICIPANT {}: Error publishing media", participant.getParticipantPublicId(), e); log.error("PARTICIPANT {}: Error publishing media", participant.getParticipantPublicId(), e);
sessionHandler.onPublishMedia(participant, session.getSessionId(), mediaOptions, sdpAnswer, participants, sessionEventsHandler.onPublishMedia(participant, session.getSessionId(), mediaOptions, sdpAnswer,
transactionId, e); participants, transactionId, e);
} }
if (this.openviduConfig.isRecordingModuleEnabled() if (this.openviduConfig.isRecordingModuleEnabled()
&& MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode()) && MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode())
&& ArchiveMode.ALWAYS.equals(session.getSessionProperties().archiveMode()) && ArchiveMode.ALWAYS.equals(session.getSessionProperties().archiveMode())
&& !recordingService.sessionIsBeingRecorded(session.getSessionId())
&& session.getActivePublishers() == 0) { && session.getActivePublishers() == 0) {
recordingService.startRecording(session); recordingService.startRecording(session);
} }
@ -234,17 +241,18 @@ public class KurentoSessionManager extends SessionManager {
kurentoParticipant.setAudioActive(kurentoOptions.audioActive); kurentoParticipant.setAudioActive(kurentoOptions.audioActive);
kurentoParticipant.setVideoActive(kurentoOptions.videoActive); kurentoParticipant.setVideoActive(kurentoOptions.videoActive);
kurentoParticipant.setTypeOfVideo(kurentoOptions.typeOfVideo); kurentoParticipant.setTypeOfVideo(kurentoOptions.typeOfVideo);
kurentoParticipant.setFrameRate(kurentoOptions.frameRate);
participants = kurentoParticipant.getSession().getParticipants(); participants = kurentoParticipant.getSession().getParticipants();
if (sdpAnswer != null) { if (sdpAnswer != null) {
sessionHandler.onPublishMedia(participant, session.getSessionId(), mediaOptions, sdpAnswer, participants, sessionEventsHandler.onPublishMedia(participant, session.getSessionId(), mediaOptions, sdpAnswer,
transactionId, null); participants, transactionId, null);
} }
} }
@Override @Override
public void unpublishVideo(Participant participant, Integer transactionId) { public void unpublishVideo(Participant participant, Integer transactionId, String reason) {
try { try {
KurentoParticipant kParticipant = (KurentoParticipant) participant; KurentoParticipant kParticipant = (KurentoParticipant) participant;
KurentoSession session = kParticipant.getSession(); KurentoSession session = kParticipant.getSession();
@ -254,16 +262,16 @@ public class KurentoSessionManager extends SessionManager {
throw new OpenViduException(Code.USER_NOT_STREAMING_ERROR_CODE, throw new OpenViduException(Code.USER_NOT_STREAMING_ERROR_CODE,
"Participant '" + participant.getParticipantPublicId() + "' is not streaming media"); "Participant '" + participant.getParticipantPublicId() + "' is not streaming media");
} }
kParticipant.unpublishMedia(); kParticipant.unpublishMedia(reason);
session.cancelPublisher(participant); session.cancelPublisher(participant, reason);
Set<Participant> participants = session.getParticipants(); Set<Participant> participants = session.getParticipants();
sessionHandler.onUnpublishMedia(participant, participants, transactionId, null); sessionEventsHandler.onUnpublishMedia(participant, participants, transactionId, null, reason);
} catch (OpenViduException e) { } catch (OpenViduException e) {
log.warn("PARTICIPANT {}: Error unpublishing media", participant.getParticipantPublicId(), e); log.warn("PARTICIPANT {}: Error unpublishing media", participant.getParticipantPublicId(), e);
sessionHandler.onUnpublishMedia(participant, null, transactionId, e); sessionEventsHandler.onUnpublishMedia(participant, null, transactionId, e, "");
} }
} }
@ -304,10 +312,10 @@ public class KurentoSessionManager extends SessionManager {
} }
} catch (OpenViduException e) { } catch (OpenViduException e) {
log.error("PARTICIPANT {}: Error subscribing to {}", participant.getParticipantPublicId(), senderName, e); log.error("PARTICIPANT {}: Error subscribing to {}", participant.getParticipantPublicId(), senderName, e);
sessionHandler.onSubscribe(participant, session.getSessionId(), senderName, null, transactionId, e); sessionEventsHandler.onSubscribe(participant, session, senderName, null, transactionId, e);
} }
if (sdpAnswer != null) { if (sdpAnswer != null) {
sessionHandler.onSubscribe(participant, session.getSessionId(), senderName, sdpAnswer, transactionId, null); sessionEventsHandler.onSubscribe(participant, session, senderName, sdpAnswer, transactionId, null);
} }
} }
@ -328,9 +336,9 @@ public class KurentoSessionManager extends SessionManager {
"User " + senderName + " not found in room " + session.getSessionId()); "User " + senderName + " not found in room " + session.getSessionId());
} }
kParticipant.cancelReceivingMedia(senderName); kParticipant.cancelReceivingMedia(senderName, "unsubscribe");
sessionHandler.onUnsubscribe(participant, senderName, transactionId, null); sessionEventsHandler.onUnsubscribe(participant, senderName, transactionId, null);
} }
@Override @Override
@ -338,7 +346,7 @@ public class KurentoSessionManager extends SessionManager {
try { try {
JsonObject messageJSON = new JsonParser().parse(message).getAsJsonObject(); JsonObject messageJSON = new JsonParser().parse(message).getAsJsonObject();
KurentoParticipant kParticipant = (KurentoParticipant) participant; KurentoParticipant kParticipant = (KurentoParticipant) participant;
sessionHandler.onSendMessage(participant, messageJSON, sessionEventsHandler.onSendMessage(participant, messageJSON,
getParticipants(kParticipant.getSession().getSessionId()), transactionId, null); getParticipants(kParticipant.getSession().getSessionId()), transactionId, null);
} catch (JsonSyntaxException | IllegalStateException e) { } catch (JsonSyntaxException | IllegalStateException e) {
throw new OpenViduException(Code.SIGNAL_FORMAT_INVALID_ERROR_CODE, throw new OpenViduException(Code.SIGNAL_FORMAT_INVALID_ERROR_CODE,
@ -354,11 +362,11 @@ public class KurentoSessionManager extends SessionManager {
log.debug("Request [ICE_CANDIDATE] endpoint={} candidate={} " + "sdpMLineIdx={} sdpMid={} ({})", log.debug("Request [ICE_CANDIDATE] endpoint={} candidate={} " + "sdpMLineIdx={} sdpMid={} ({})",
endpointName, candidate, sdpMLineIndex, sdpMid, participant.getParticipantPublicId()); endpointName, candidate, sdpMLineIndex, sdpMid, participant.getParticipantPublicId());
kParticipant.addIceCandidate(endpointName, new IceCandidate(candidate, sdpMid, sdpMLineIndex)); kParticipant.addIceCandidate(endpointName, new IceCandidate(candidate, sdpMid, sdpMLineIndex));
sessionHandler.onRecvIceCandidate(participant, transactionId, null); sessionEventsHandler.onRecvIceCandidate(participant, transactionId, null);
} catch (OpenViduException e) { } catch (OpenViduException e) {
log.error("PARTICIPANT {}: Error receiving ICE " + "candidate (epName={}, candidate={})", log.error("PARTICIPANT {}: Error receiving ICE " + "candidate (epName={}, candidate={})",
participant.getParticipantPublicId(), endpointName, candidate, e); participant.getParticipantPublicId(), endpointName, candidate, e);
sessionHandler.onRecvIceCandidate(participant, transactionId, e); sessionEventsHandler.onRecvIceCandidate(participant, transactionId, e);
} }
} }
@ -382,8 +390,8 @@ public class KurentoSessionManager extends SessionManager {
"Session '" + sessionId + "' already exists"); "Session '" + sessionId + "' already exists");
} }
KurentoClient kurentoClient = kcProvider.getKurentoClient(kcSessionInfo); KurentoClient kurentoClient = kcProvider.getKurentoClient(kcSessionInfo);
session = new KurentoSession(sessionId, sessionProperties, kurentoClient, sessionHandler, session = new KurentoSession(sessionId, sessionProperties, kurentoClient, kurentoSessionEventsHandler,
kcProvider.destroyWhenUnused()); kcProvider.destroyWhenUnused(), this.CDR);
KurentoSession oldSession = (KurentoSession) sessions.putIfAbsent(sessionId, session); KurentoSession oldSession = (KurentoSession) sessions.putIfAbsent(sessionId, session);
if (oldSession != null) { if (oldSession != null) {
@ -396,7 +404,7 @@ public class KurentoSessionManager extends SessionManager {
} }
log.warn("No session '{}' exists yet. Created one using KurentoClient '{}'.", sessionId, kcName); log.warn("No session '{}' exists yet. Created one using KurentoClient '{}'.", sessionId, kcName);
sessionHandler.onSessionCreated(sessionId); sessionEventsHandler.onSessionCreated(sessionId);
} }
/** /**
@ -407,10 +415,10 @@ public class KurentoSessionManager extends SessionManager {
* *
*/ */
@Override @Override
public void evictParticipant(String participantPrivateId) throws OpenViduException { public void evictParticipant(String participantPrivateId, String reason) throws OpenViduException {
Participant participant = this.getParticipant(participantPrivateId); Participant participant = this.getParticipant(participantPrivateId);
this.leaveRoom(participant, null); this.leaveRoom(participant, null, reason);
sessionHandler.onParticipantEvicted(participant); sessionEventsHandler.onParticipantEvicted(participant);
} }
@Override @Override
@ -420,9 +428,11 @@ public class KurentoSessionManager extends SessionManager {
boolean audioActive = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_AUDIOACTIVE_PARAM); boolean audioActive = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_AUDIOACTIVE_PARAM);
boolean videoActive = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_VIDEOACTIVE_PARAM); boolean videoActive = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_VIDEOACTIVE_PARAM);
String typeOfVideo = RpcHandler.getStringParam(request, ProtocolElements.PUBLISHVIDEO_TYPEOFVIDEO_PARAM); String typeOfVideo = RpcHandler.getStringParam(request, ProtocolElements.PUBLISHVIDEO_TYPEOFVIDEO_PARAM);
int frameRate = RpcHandler.getIntParam(request, ProtocolElements.PUBLISHVIDEO_FRAMERATE_PARAM);
boolean doLoopback = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_DOLOOPBACK_PARAM); boolean doLoopback = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_DOLOOPBACK_PARAM);
return new KurentoMediaOptions(true, sdpOffer, null, null, audioActive, videoActive, typeOfVideo, doLoopback); return new KurentoMediaOptions(true, sdpOffer, null, null, audioActive, videoActive, typeOfVideo, frameRate,
doLoopback);
} }
} }

View File

@ -51,6 +51,7 @@ 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;
import io.openvidu.server.core.Session; import io.openvidu.server.core.Session;
import io.openvidu.server.core.SessionEventsHandler;
@Service @Service
public class ComposedRecordingService { public class ComposedRecordingService {
@ -60,6 +61,9 @@ public class ComposedRecordingService {
@Autowired @Autowired
OpenviduConfig openviduConfig; OpenviduConfig openviduConfig;
@Autowired
private SessionEventsHandler sessionHandler;
private Map<String, String> containers = new ConcurrentHashMap<>(); private Map<String, String> containers = new ConcurrentHashMap<>();
private Map<String, String> sessionsContainers = new ConcurrentHashMap<>(); private Map<String, String> sessionsContainers = new ConcurrentHashMap<>();
private Map<String, Recording> startingRecordings = new ConcurrentHashMap<>(); private Map<String, Recording> startingRecordings = new ConcurrentHashMap<>();
@ -81,12 +85,13 @@ public class ComposedRecordingService {
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 videoId = this.getFreeRecordingId(session.getSessionId(), shortSessionId); String recordingId = this.getFreeRecordingId(session.getSessionId(), shortSessionId);
String secret = openviduConfig.getOpenViduSecret(); String secret = openviduConfig.getOpenViduSecret();
Recording recording = new Recording(session.getSessionId(), videoId, videoId); Recording recording = new Recording(session.getSessionId(), recordingId, recordingId);
this.sessionsRecordings.put(session.getSessionId(), recording); this.sessionsRecordings.put(session.getSessionId(), recording);
this.sessionHandler.setRecordingStarted(session.getSessionId(), recording);
this.startingRecordings.put(recording.getId(), recording); this.startingRecordings.put(recording.getId(), recording);
String uid = null; String uid = null;
@ -106,7 +111,7 @@ public class ComposedRecordingService {
+ "/" + secret); + "/" + secret);
envs.add("RESOLUTION=1920x1080"); envs.add("RESOLUTION=1920x1080");
envs.add("FRAMERATE=30"); envs.add("FRAMERATE=30");
envs.add("VIDEO_NAME=" + videoId); envs.add("VIDEO_NAME=" + recordingId);
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());
@ -115,9 +120,9 @@ public class ComposedRecordingService {
log.debug("Recorder connecting to url {}", log.debug("Recorder connecting to url {}",
"https://OPENVIDUAPP:" + secret + "@localhost:8443/#/layout-best-fit/" + shortSessionId + "/" + secret); "https://OPENVIDUAPP:" + secret + "@localhost:8443/#/layout-best-fit/" + shortSessionId + "/" + secret);
String containerId = this.runRecordingContainer(envs, "recording_" + videoId); String containerId = this.runRecordingContainer(envs, "recording_" + recordingId);
this.waitForVideoFileNotEmpty(videoId); this.waitForVideoFileNotEmpty(recordingId);
this.sessionsContainers.put(session.getSessionId(), containerId); this.sessionsContainers.put(session.getSessionId(), containerId);
@ -171,7 +176,7 @@ public class ComposedRecordingService {
RecordingInfoUtils infoUtils = new RecordingInfoUtils( RecordingInfoUtils infoUtils = new RecordingInfoUtils(
this.openviduConfig.getOpenViduRecordingPath() + recording.getName() + ".info"); this.openviduConfig.getOpenViduRecordingPath() + recording.getName() + ".info");
if (openviduConfig.getOpenViduRecordingFreeAccess()) { if (openviduConfig.getOpenViduRecordingPublicAccess()) {
recording.setStatus(Recording.Status.available); recording.setStatus(Recording.Status.available);
} else { } else {
recording.setStatus(Recording.Status.stopped); recording.setStatus(Recording.Status.stopped);
@ -181,7 +186,7 @@ public class ComposedRecordingService {
recording.setHasAudio(infoUtils.hasAudio()); recording.setHasAudio(infoUtils.hasAudio());
recording.setHasVideo(infoUtils.hasVideo()); recording.setHasVideo(infoUtils.hasVideo());
if (openviduConfig.getOpenViduRecordingFreeAccess()) { if (openviduConfig.getOpenViduRecordingPublicAccess()) {
recording.setUrl(this.openviduConfig.getFinalUrl() + "recordings/" + recording.getName() + ".mp4"); recording.setUrl(this.openviduConfig.getFinalUrl() + "recordings/" + recording.getName() + ".mp4");
} }
@ -190,6 +195,8 @@ public class ComposedRecordingService {
"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.sessionHandler.sendRecordingStoppedNotification(session, recording);
return recording; return recording;
} }
@ -341,7 +348,7 @@ public class ComposedRecordingService {
for (int i = 0; i < files.length; i++) { for (int i = 0; i < files.length; i++) {
Recording recording = this.getRecordingFromFile(files[i]); Recording recording = this.getRecordingFromFile(files[i]);
if (recording != null) { if (recording != null) {
if (openviduConfig.getOpenViduRecordingFreeAccess()) { if (openviduConfig.getOpenViduRecordingPublicAccess()) {
if (Recording.Status.stopped.equals(recording.getStatus())) { if (Recording.Status.stopped.equals(recording.getStatus())) {
recording.setStatus(Recording.Status.available); recording.setStatus(Recording.Status.available);
recording.setUrl( recording.setUrl(

View File

@ -16,8 +16,6 @@
*/ */
package io.openvidu.server.rest; package io.openvidu.server.rest;
import static org.kurento.commons.PropertiesManager.getProperty;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
@ -36,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException;
import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.java.client.ArchiveLayout; import io.openvidu.java.client.ArchiveLayout;
import io.openvidu.java.client.ArchiveMode; import io.openvidu.java.client.ArchiveMode;
import io.openvidu.java.client.MediaMode; import io.openvidu.java.client.MediaMode;
@ -55,9 +54,6 @@ import io.openvidu.server.recording.ComposedRecordingService;
@RequestMapping("/api") @RequestMapping("/api")
public class SessionRestController { public class SessionRestController {
private static final int UPDATE_SPEAKER_INTERVAL_DEFAULT = 1800;
private static final int THRESHOLD_SPEAKER_DEFAULT = -50;
@Autowired @Autowired
private SessionManager sessionManager; private SessionManager sessionManager;
@ -69,16 +65,6 @@ public class SessionRestController {
return sessionManager.getSessions(); return sessionManager.getSessions();
} }
@RequestMapping("/getUpdateSpeakerInterval")
public Integer getUpdateSpeakerInterval() {
return Integer.valueOf(getProperty("updateSpeakerInterval", UPDATE_SPEAKER_INTERVAL_DEFAULT));
}
@RequestMapping("/getThresholdSpeaker")
public Integer getThresholdSpeaker() {
return Integer.valueOf(getProperty("thresholdSpeaker", THRESHOLD_SPEAKER_DEFAULT));
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@RequestMapping(value = "/sessions", method = RequestMethod.POST) @RequestMapping(value = "/sessions", method = RequestMethod.POST)
public ResponseEntity<JSONObject> getSessionId(@RequestBody(required = false) Map<?, ?> params) { public ResponseEntity<JSONObject> getSessionId(@RequestBody(required = false) Map<?, ?> params) {
@ -198,8 +184,13 @@ public class SessionRestController {
return new ResponseEntity<JSONObject>(HttpStatus.CONFLICT); return new ResponseEntity<JSONObject>(HttpStatus.CONFLICT);
} }
Session session = sessionManager.getSession(recording.getSessionId());
Recording stoppedRecording = this.recordingService Recording stoppedRecording = this.recordingService
.stopRecording(sessionManager.getSession(recording.getSessionId())); .stopRecording(session);
sessionManager.evictParticipant(session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID).getParticipantPrivateId(), "EVICT_RECORDER");
return new ResponseEntity<>(stoppedRecording.toJson(), HttpStatus.OK); return new ResponseEntity<>(stoppedRecording.toJson(), HttpStatus.OK);
} }

View File

@ -21,7 +21,6 @@ import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.config.OpenviduConfig;
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.core.ParticipantRole;
import io.openvidu.server.core.SessionManager; import io.openvidu.server.core.SessionManager;
import io.openvidu.server.core.Token; import io.openvidu.server.core.Token;
@ -176,18 +175,18 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
if (sessionId == null) { // null when afterConnectionClosed if (sessionId == null) { // null when afterConnectionClosed
log.warn("No session information found for participant with privateId {}. " log.warn("No session information found for participant with privateId {}. "
+ "Using the admin method to evict the user.", participantPrivateId); + "Using the admin method to evict the user.", participantPrivateId);
leaveRoomAfterConnClosed(participantPrivateId); leaveRoomAfterConnClosed(participantPrivateId, "");
} else { } else {
// Sanity check: don't call leaveRoom unless the id checks out // Sanity check: don't call leaveRoom unless the id checks out
Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId); Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId);
if (participant != null) { if (participant != null) {
log.info("Participant {} is leaving session {}", participant.getParticipantPublicId(), sessionId); log.info("Participant {} is leaving session {}", participant.getParticipantPublicId(), sessionId);
sessionManager.leaveRoom(participant, request.getId()); sessionManager.leaveRoom(participant, request.getId(), "disconnect");
log.info("Participant {} has left session {}", participant.getParticipantPublicId(), sessionId); log.info("Participant {} has left session {}", participant.getParticipantPublicId(), sessionId);
} else { } else {
log.warn("Participant with private id {} not found in session {}. " log.warn("Participant with private id {} not found in session {}. "
+ "Using the admin method to evict the user.", participantPrivateId, sessionId); + "Using the admin method to evict the user.", participantPrivateId, sessionId);
leaveRoomAfterConnClosed(participantPrivateId); leaveRoomAfterConnClosed(participantPrivateId, "");
} }
} }
} }
@ -263,12 +262,12 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
String sessionId = rpcConnection.getSessionId(); String sessionId = rpcConnection.getSessionId();
Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId); Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId);
sessionManager.unpublishVideo(participant, request.getId()); sessionManager.unpublishVideo(participant, request.getId(), "unpublish");
} }
public void leaveRoomAfterConnClosed(String participantPrivateId) { public void leaveRoomAfterConnClosed(String participantPrivateId, String reason) {
try { try {
sessionManager.evictParticipant(participantPrivateId); sessionManager.evictParticipant(participantPrivateId, reason);
log.info("Evicted participant with privateId {}", participantPrivateId); log.info("Evicted participant with privateId {}", participantPrivateId);
} catch (OpenViduException e) { } catch (OpenViduException e) {
log.warn("Unable to evict: {}", e.getMessage()); log.warn("Unable to evict: {}", e.getMessage());
@ -290,7 +289,7 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
if (rpc != null && rpc.getSessionId() != null) { if (rpc != null && rpc.getSessionId() != null) {
io.openvidu.server.core.Session session = this.sessionManager.getSession(rpc.getSessionId()); io.openvidu.server.core.Session session = this.sessionManager.getSession(rpc.getSessionId());
if (session != null && session.getParticipantByPrivateId(rpc.getParticipantPrivateId()) != null) { if (session != null && session.getParticipantByPrivateId(rpc.getParticipantPrivateId()) != null) {
leaveRoomAfterConnClosed(rpc.getParticipantPrivateId()); leaveRoomAfterConnClosed(rpc.getParticipantPrivateId(), "networkDisconnect");
} }
} }
@ -300,7 +299,7 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
log.warn( log.warn(
"Evicting participant with private id {} because a transport error took place and its web socket connection is now closed", "Evicting participant with private id {} because a transport error took place and its web socket connection is now closed",
rpcSession.getSessionId()); rpcSession.getSessionId());
this.leaveRoomAfterConnClosed(rpcSessionId); this.leaveRoomAfterConnClosed(rpcSessionId, "networkDisconnect");
this.webSocketTransportError.remove(rpcSessionId); this.webSocketTransportError.remove(rpcSessionId);
} }
} }

View File

@ -30,11 +30,16 @@
"description": "Where to store the recorded video files" "description": "Where to store the recorded video files"
}, },
{ {
"name": "openvidu.recording.free-access", "name": "openvidu.recording.public-access",
"type": "java.lang.Boolean", "type": "java.lang.Boolean",
"description": "'true' to allow free access to the video files specified in 'openviu.recording.path'. 'false' to only allow access to authenticated users" "description": "'true' to allow public access to the video files specified in 'openviu.recording.path'. 'false' to only allow access to authenticated users"
}, },
{ {
"name": "openvidu.recording.notification",
"type": "java.lang.String",
"description": "Which users will receive a notfication (client events 'recordingStarted' and 'recordingStopped') when recording starts and stops: 'none', 'publisher_moderator', 'all'"
},
{
"name": "openvidu.recording.version", "name": "openvidu.recording.version",
"type": "java.lang.String", "type": "java.lang.String",
"description": "Tag for openvidu/openvidu-recording Docker image" "description": "Tag for openvidu/openvidu-recording Docker image"

View File

@ -16,4 +16,5 @@ openvidu.publicurl: local
openvidu.cdr: false openvidu.cdr: false
openvidu.recording: false openvidu.recording: false
openvidu.recording.path: /opt/openvidu/recordings openvidu.recording.path: /opt/openvidu/recordings
openvidu.recording.free-access: false openvidu.recording.public-access: false
openvidu.recording.notification: publisher_moderator