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_PEERSTREAMVIDEOACTIVE_PARAM = "videoActive";
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_SDPOFFER_PARAM = "sdpOffer";
@ -52,6 +53,7 @@ public class ProtocolElements {
public static final String PUBLISHVIDEO_AUDIOACTIVE_PARAM = "audioActive";
public static final String PUBLISHVIDEO_VIDEOACTIVE_PARAM = "videoActive";
public static final String PUBLISHVIDEO_TYPEOFVIDEO_PARAM = "typeOfVideo";
public static final String PUBLISHVIDEO_FRAMERATE_PARAM = "frameRate";
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_NAME_PARAM = "name";
public static final String PARTICIPANTLEFT_REASON_PARAM = "reason";
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_VIDEOACTIVE_PARAM = "videoActive";
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_NAME_PARAM = "name";
public static final String PARTICIPANTUNPUBLISHED_REASON_PARAM = "reason";
public static final String PARTICIPANTSENDMESSAGE_METHOD = "sendMessage";
public static final String PARTICIPANTSENDMESSAGE_DATA_PARAM = "data";
@ -109,8 +114,14 @@ public class ProtocolElements {
public static final String ICECANDIDATE_CANDIDATE_PARAM = "candidate";
public static final String ICECANDIDATE_SDPMID_PARAM = "sdpMid";
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 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 CONNECTION_CREATED = "webrtcConnectionCreated";
static final String CONNECTION_DESTROYED = "webrtcConnectionDestroyed";
static final String RECORDING_STARTED = "recordingStarted";
static final String RECORDING_STOPPED = "recordingStopped";
protected String eventName;
protected String sessionId;
protected Long timeStamp;
private Long startTime;
private Integer duration;
private Participant participant;
private MediaOptions mediaOptions;
private String receivingFrom;
private Long startTime;
private Integer duration;
protected Long timeStamp;
private String reason;
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);
}
@ -46,12 +54,13 @@ public class CDREvent implements Comparable<CDREvent> {
}
public CDREvent(String eventName, Participant participant, String sessionId, MediaOptions mediaOptions,
String receivingFrom, Long startTime) {
String receivingFrom, Long startTime, String reason) {
this(eventName, sessionId);
this.participant = participant;
this.mediaOptions = mediaOptions;
this.receivingFrom = receivingFrom;
this.startTime = startTime;
this.reason = reason;
}
public MediaOptions getMediaOptions() {
@ -82,6 +91,7 @@ public class CDREvent implements Comparable<CDREvent> {
json.put("videoEnabled", this.mediaOptions.videoActive);
if (this.mediaOptions.videoActive) {
json.put("videoSource", this.mediaOptions.typeOfVideo);
json.put("videoFramerate", this.mediaOptions.frameRate);
}
if (this.receivingFrom != null) {
json.put("receivingFrom", this.receivingFrom);
@ -92,11 +102,15 @@ public class CDREvent implements Comparable<CDREvent> {
json.put("endTime", this.timeStamp);
json.put("duration", (this.timeStamp - this.startTime) / 1000);
}
if (this.reason != null) {
json.put("reason", this.reason);
}
JSONObject root = new JSONObject();
root.put(this.eventName, json);
return root.toString();
return root.toJSONString();
}
@Override

View File

@ -13,78 +13,101 @@ import io.openvidu.server.core.MediaOptions;
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'
*
* - Participant unique identifier
* - Session unique identifier
* - Inbound or Outbound WebRTC connection
* - <if inbound connection> Sender unique identifier
* - Audio media stream enabled
* - Video media stream enabled
* - <if Video media stream enabled> Video source [CAMERA, SCREEN]
* - Time of start of the call
* - Time of end of the call
* - Total time duration
* - 'sessionCreated': {sessionId, timestamp}
* - 'sessionDestroyed': {sessionId, timestamp, startTime, endTime, duration }
* - 'participantJoined': {sessionId, timestamp, participantId}
* - 'participantLeft': {sessionId, timestamp, participantId, startTime, endTime, duration, reason}
* - 'webrtcConnectionCreated' {sessionId, timestamp, participantId, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate] }
* - 'webrtcConnectionDestroyed' {sessionId, timestamp, participantId, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate], reason }
* - 'recordingStarted' {sessionId, timestamp}
* - 'recordingStopped' {sessionId, timestamp}
*
* 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)
*/
public class CallDetailRecord {
private Logger log = LoggerFactory.getLogger(CallDetailRecord.class);
private Map<String, CDREvent> sessions = new ConcurrentHashMap<>();
private Map<String, CDREvent> participants = new ConcurrentHashMap<>();
private Map<String, CDREvent> publications = new ConcurrentHashMap<>();
private Map<String, Set<CDREvent>> subscriptions = new ConcurrentHashMap<>();
public void recordSessionCreated(String sessionId) {
CDREvent e = new CDREvent(CDREvent.SESSION_CREATED, sessionId);
this.sessions.put(sessionId, e);
log.info("{}", e);
}
public void recordSessionDestroyed(String sessionId) {
public void recordSessionDestroyed(String sessionId, String reason) {
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) {
CDREvent e = new CDREvent(CDREvent.PARTICIPANT_JOINED, participant, sessionId);
this.participants.put(participant.getParticipantPublicId(), 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());
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) {
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);
log.info("{}", publisher);
}
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());
this.subscriptions.putIfAbsent(participant.getParticipantPublicId(), new ConcurrentSkipListSet<>());
this.subscriptions.get(participant.getParticipantPublicId()).add(subscriber);
log.info("{}", subscriber);
}
public boolean stopPublisher(String participantPublicId) {
public boolean stopPublisher(String participantPublicId, String reason) {
CDREvent publisher = this.publications.remove(participantPublicId);
if (publisher != null) {
publisher = new CDREvent(CDREvent.CONNECTION_DESTROYED, publisher);
publisher = new CDREvent(CDREvent.CONNECTION_DESTROYED, publisher, reason);
log.info("{}", publisher);
return true;
}
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);
if (participantSubscriptions != null) {
CDREvent subscription;
@ -92,7 +115,7 @@ public class CallDetailRecord {
subscription = it.next();
if (subscription.getReceivingFrom().equals(senderPublicId)) {
it.remove();
subscription = new CDREvent(CDREvent.CONNECTION_DESTROYED, subscription);
subscription = new CDREvent(CDREvent.CONNECTION_DESTROYED, subscription, reason);
log.info("{}", subscription);
return true;
}
@ -101,17 +124,14 @@ public class CallDetailRecord {
return false;
}
public void stopAllSubscriptions(String participantPublicId) {
Set<CDREvent> participantSubscriptions = this.subscriptions.get(participantPublicId);
if (participantSubscriptions != null) {
CDREvent subscription;
for (Iterator<CDREvent> it = participantSubscriptions.iterator(); it.hasNext();) {
subscription = it.next();
subscription = new CDREvent(CDREvent.CONNECTION_DESTROYED, subscription);
log.info("{}", subscription);
}
this.subscriptions.remove(participantPublicId).clear();
}
public void recordRecordingStarted(String sessionId) {
CDREvent recording = new CDREvent(CDREvent.RECORDING_STARTED, sessionId);
log.info("{}", recording);
}
public void recordRecordingStopped(String sessionId) {
CDREvent recording = new CDREvent(CDREvent.RECORDING_STOPPED, sessionId);
log.info("{}", recording);
}
}

View File

@ -3,6 +3,8 @@ package io.openvidu.server.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import io.openvidu.server.core.ParticipantRole;
@Component
public class OpenviduConfig {
@ -24,12 +26,15 @@ public class OpenviduConfig {
@Value("${openvidu.recording.path}")
private String openviduRecordingPath;
@Value("${openvidu.recording.free-access}")
boolean openviduRecordingFreeAccess;
@Value("${openvidu.recording.public-access}")
boolean openviduRecordingPublicAccess;
@Value("${openvidu.recording.notification}")
String openviduRecordingNotification;
@Value("${openvidu.recording.version}")
String openviduRecordingVersion;
@Value("#{'${spring.profiles.active:}'.length() > 0 ? '${spring.profiles.active:}'.split(',') : \"default\"}")
private String springProfile;
@ -63,8 +68,8 @@ public class OpenviduConfig {
return this.openviduRecordingPath;
}
public boolean getOpenViduRecordingFreeAccess() {
return this.openviduRecordingFreeAccess;
public boolean getOpenViduRecordingPublicAccess() {
return this.openviduRecordingPublicAccess;
}
public void setOpenViduRecordingPath(String recordingPath) {
@ -87,4 +92,23 @@ public class OpenviduConfig {
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("/").authenticated();
if (openviduConf.getOpenViduRecordingFreeAccess()) {
if (openviduConf.getOpenViduRecordingPublicAccess()) {
conf = conf.antMatchers("/recordings/*").permitAll();
} else {
conf = conf.antMatchers("/recordings/*").authenticated();

View File

@ -5,11 +5,13 @@ public class MediaOptions {
public boolean audioActive;
public boolean videoActive;
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.videoActive = videoActive;
this.typeOfVideo = typeOfVideo;
this.frameRate = frameRate;
}
}

View File

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

View File

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

View File

@ -1,10 +1,15 @@
package io.openvidu.server.core;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.config.InfoHandler;
import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.recording.Recording;
import io.openvidu.server.rpc.RpcNotificationService;
public class SessionEventsHandler {
private static final Logger log = LoggerFactory.getLogger(SessionEventsHandler.class);
@Autowired
protected RpcNotificationService rpcNotificationService;
@ -29,24 +37,28 @@ public class SessionEventsHandler {
@Autowired
protected CallDetailRecord CDR;
@Autowired
protected OpenviduConfig openviduConfig;
Map<String, Recording> recordingsStarted = new ConcurrentHashMap<>();
ReentrantLock lock = new ReentrantLock();
public void onSessionCreated(String sessionId) {
if (openviduConfig.isCdrEnabled()) {
CDR.recordSessionCreated(sessionId);
}
}
public void onSessionClosed(String sessionId) {
public void onSessionClosed(String sessionId, String reason) {
if (openviduConfig.isCdrEnabled()) {
CDR.recordSessionDestroyed(sessionId);
CDR.recordSessionDestroyed(sessionId, reason);
}
}
public void onParticipantJoined(Participant participant, String sessionId,
Set<Participant> existingParticipants, Integer transactionId, OpenViduException error) {
public void onParticipantJoined(Participant participant, String sessionId, Set<Participant> existingParticipants,
Integer transactionId, OpenViduException error) {
if (error != null) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return;
@ -54,80 +66,92 @@ public class SessionEventsHandler {
JsonObject result = new JsonObject();
JsonArray resultArray = new JsonArray();
for (Participant p : existingParticipants) {
for (Participant existingParticipant : existingParticipants) {
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
participantJson.addProperty(ProtocolElements.JOINROOM_METADATA_PARAM, p.getFullMetadata());
participantJson.addProperty(ProtocolElements.JOINROOM_METADATA_PARAM,
existingParticipant.getFullMetadata());
if (p.isStreaming()) {
if (existingParticipant.isStreaming()) {
String streamId = "";
if ("SCREEN".equals(p.getTypeOfVideo())) {
if ("SCREEN".equals(existingParticipant.getTypeOfVideo())) {
streamId = "SCREEN";
} else if (p.isVideoActive()) {
} else if (existingParticipant.isVideoActive()) {
streamId = "CAMERA";
} else if (p.isAudioActive()) {
} else if (existingParticipant.isAudioActive()) {
streamId = "MICRO";
}
JsonObject stream = new JsonObject();
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMID_PARAM,
p.getParticipantPublicId() + "_" + streamId);
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMAUDIOACTIVE_PARAM, p.isAudioActive());
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMVIDEOACTIVE_PARAM, p.isVideoActive());
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMTYPEOFVIDEO_PARAM, p.getTypeOfVideo());
existingParticipant.getParticipantPublicId() + "_" + streamId);
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMAUDIOACTIVE_PARAM,
existingParticipant.isAudioActive());
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();
streamsArray.add(stream);
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
notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_USER_PARAM,
participant.getParticipantPublicId());
notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_METADATA_PARAM, participant.getFullMetadata());
// If RECORDER participant has joined do NOT send 'participantJoined'
// notification to existing participants. 'recordingStarted' will be sent to all
// existing participants when recorder first subscribe to a stream
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
JsonObject notifParams = new JsonObject();
rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
ProtocolElements.PARTICIPANTJOINED_METHOD, notifParams);
// Metadata associated to new participant
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_METADATA_PARAM, participant.getFullMetadata());
result.add("value", resultArray);
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, result);
if (openviduConfig.isCdrEnabled()) {
CDR.recordParticipantJoined(participant, sessionId);
}
}
public void onParticipantLeft(Participant participant, String sessionId,
Set<Participant> remainingParticipants, Integer transactionId, OpenViduException error) {
public void onParticipantLeft(Participant participant, String sessionId, Set<Participant> remainingParticipants,
Integer transactionId, OpenViduException error, String reason) {
if (error != null) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return;
}
boolean isPublishing = false;
if (openviduConfig.isCdrEnabled()) {
isPublishing = CDR.stopPublisher(participant.getParticipantPublicId());
CDR.stopAllSubscriptions(participant.getParticipantPublicId());
if (ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
// RECORDER participant
return;
}
JsonObject params = new JsonObject();
params.addProperty(ProtocolElements.PARTICIPANTLEFT_NAME_PARAM, participant.getParticipantPublicId());
params.addProperty(ProtocolElements.PARTICIPANTLEFT_REASON_PARAM, reason);
for (Participant p : remainingParticipants) {
rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
ProtocolElements.PARTICIPANTLEFT_METHOD, params);
if (isPublishing && openviduConfig.isCdrEnabled()) {
CDR.stopSubscriber(p.getParticipantPublicId(), participant.getParticipantPublicId());
}
}
if (transactionId != null) {
@ -135,16 +159,10 @@ public class SessionEventsHandler {
// leaving the session
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,
String sdpAnswer, Set<Participant> participants, Integer transactionId, OpenViduException error) {
public void onPublishMedia(Participant participant, String sessionId, MediaOptions mediaOptions, String sdpAnswer,
Set<Participant> participants, Integer transactionId, OpenViduException error) {
if (error != null) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return;
@ -171,6 +189,7 @@ public class SessionEventsHandler {
stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_AUDIOACTIVE_PARAM, mediaOptions.audioActive);
stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_VIDEOACTIVE_PARAM, mediaOptions.videoActive);
stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_TYPEOFVIDEO_PARAM, mediaOptions.typeOfVideo);
stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_FRAMERATE_PARAM, mediaOptions.frameRate);
JsonArray streamsArray = new JsonArray();
streamsArray.add(stream);
@ -184,26 +203,19 @@ public class SessionEventsHandler {
ProtocolElements.PARTICIPANTPUBLISHED_METHOD, params);
}
}
if (openviduConfig.isCdrEnabled()) {
CDR.recordNewPublisher(participant, sessionId, mediaOptions);
}
}
public void onUnpublishMedia(Participant participant, Set<Participant> participants, Integer transactionId,
OpenViduException error) {
OpenViduException error, String reason) {
if (error != null) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return;
}
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject());
if (openviduConfig.isCdrEnabled()) {
CDR.stopPublisher(participant.getParticipantPublicId());
}
JsonObject params = new JsonObject();
params.addProperty(ProtocolElements.PARTICIPANTUNPUBLISHED_NAME_PARAM, participant.getParticipantPublicId());
params.addProperty(ProtocolElements.PARTICIPANTUNPUBLISHED_REASON_PARAM, reason);
for (Participant p : participants) {
if (p.getParticipantPrivateId().equals(participant.getParticipantPrivateId())) {
@ -211,15 +223,12 @@ public class SessionEventsHandler {
} else {
rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
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,
OpenViduException error) {
public void onSubscribe(Participant participant, Session session, String senderName, String sdpAnswer,
Integer transactionId, OpenViduException error) {
if (error != null) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return;
@ -228,21 +237,27 @@ public class SessionEventsHandler {
result.addProperty(ProtocolElements.RECEIVEVIDEO_SDPANSWER_PARAM, sdpAnswer);
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, result);
if (openviduConfig.isCdrEnabled()) {
CDR.recordNewSubscriber(participant, sessionId, senderName);
if (ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
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) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return;
}
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,
@ -308,7 +323,78 @@ public class SessionEventsHandler {
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() {
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.google.gson.JsonObject;
@ -20,10 +21,25 @@ import io.openvidu.client.OpenViduException.Code;
import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.java.client.SessionProperties;
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 {
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, SessionProperties> sessionProperties = new ConcurrentHashMap<>();
@ -35,11 +51,11 @@ public abstract class SessionManager {
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 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);
@ -57,7 +73,7 @@ public abstract class SessionManager {
* 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 {
this.sessionidParticipantpublicidParticipant.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;
}
}
@ -252,7 +268,7 @@ public abstract class SessionManager {
public Participant newRecorderParticipant(String sessionId, String participantPrivatetId, Token token,
String clientMetadata) {
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);
this.sessionidParticipantpublicidParticipant.get(sessionId).put(participantPublicId, p);
return p;
@ -305,7 +321,7 @@ public abstract class SessionManager {
log.info("Closing all sessions");
for (String sessionId : sessions.keySet()) {
try {
closeSession(sessionId);
closeSession(sessionId, "openviduServerDestroyed");
} catch (Exception e) {
log.warn("Error closing session '{}'", sessionId, e);
}
@ -328,7 +344,7 @@ public abstract class SessionManager {
* @throws OpenViduException
* 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);
if (session == null) {
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());
for (String pid : pids) {
try {
session.leave(pid);
session.leave(pid, reason);
} catch (OpenViduException 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);
sessionProperties.remove(sessionId);
@ -354,6 +372,11 @@ public abstract class SessionManager {
sessionidTokenTokenobj.remove(sessionId);
log.warn("Session '{}' removed and closed", sessionId);
if (recordingService.sessionIsBeingRecorded(session.getSessionId())) {
recordingService.stopRecording(session);
}
return participants;
}

View File

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

View File

@ -21,7 +21,10 @@ import org.slf4j.LoggerFactory;
import io.openvidu.client.OpenViduException;
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.core.MediaOptions;
import io.openvidu.server.core.Participant;
import io.openvidu.server.kurento.MutedMediaType;
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 InfoHandler infoHandler;
private CallDetailRecord CDR;
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, 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(),
participant.getClientMetadata());
this.session = kurentoSession;
@ -60,9 +64,10 @@ public class KurentoParticipant extends Participant {
}
}
this.infoHandler = infoHandler;
this.CDR = CDR;
}
public void createPublishingEndpoint() {
public void createPublishingEndpoint(MediaOptions mediaOptions) {
publisher.createEndpoint(endPointLatch);
if (getPublisher().getEndpoint() == null) {
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());
addEndpointListeners(this.publisher);
CDR.recordNewPublisher(this, this.session.getSessionId(), mediaOptions);
}
public void shapePublisherMedia(MediaElement element, MediaType type) {
@ -194,10 +203,10 @@ public class KurentoParticipant extends Participant {
return sdpResponse;
}
public void unpublishMedia() {
public void unpublishMedia(String reason) {
log.info("PARTICIPANT {}: unpublishing media stream from room {}", this.getParticipantPublicId(),
this.session.getSessionId());
releasePublisherEndpoint();
releasePublisherEndpoint(reason);
this.publisher = new PublisherEndpoint(webParticipant, this, this.getParticipantPublicId(),
pipeline);
log.info(
@ -269,6 +278,11 @@ public class KurentoParticipant extends Participant {
log.trace("PARTICIPANT {}: Subscribing SdpAnswer is {}", this.getParticipantPublicId(), sdpAnswer);
log.info("PARTICIPANT {}: Is now receiving video from {} in room {}", this.getParticipantPublicId(), senderName,
this.session.getSessionId());
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(this.getParticipantPublicId())) {
CDR.recordNewSubscriber(this, this.session.getSessionId(), sender.getParticipantPublicId());
}
return sdpAnswer;
} catch (KurentoServerException e) {
// 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);
}
this.subscribers.remove(senderName);
releaseSubscriberEndpoint(senderName, subscriber);
releaseSubscriberEndpoint(senderName, subscriber, "");
}
return null;
}
public void cancelReceivingMedia(String senderName) {
public void cancelReceivingMedia(String senderName, String reason) {
log.info("PARTICIPANT {}: cancel receiving media from {}", this.getParticipantPublicId(), senderName);
SubscriberEndpoint subscriberEndpoint = subscribers.remove(senderName);
if (subscriberEndpoint == null || subscriberEndpoint.getEndpoint() == null) {
log.warn("PARTICIPANT {}: Trying to cancel receiving video from user {}. "
+ "But there is no such subscriber endpoint.", this.getParticipantPublicId(), senderName);
} else {
releaseSubscriberEndpoint(senderName, subscriberEndpoint);
releaseSubscriberEndpoint(senderName, subscriberEndpoint, reason);
log.info("PARTICIPANT {}: stopped receiving media from {} in room {}", this.getParticipantPublicId(), senderName,
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());
if (isClosed()) {
log.warn("PARTICIPANT {}: Already closed", this.getParticipantPublicId());
@ -357,7 +371,7 @@ public class KurentoParticipant extends Participant {
for (String remoteParticipantName : subscribers.keySet()) {
SubscriberEndpoint subscriber = this.subscribers.get(remoteParticipantName);
if (subscriber != null && subscriber.getEndpoint() != null) {
releaseSubscriberEndpoint(remoteParticipantName, subscriber);
releaseSubscriberEndpoint(remoteParticipantName, subscriber, reason);
log.debug("PARTICIPANT {}: Released subscriber endpoint to {}", this.getParticipantPublicId(),
remoteParticipantName);
} else {
@ -367,7 +381,7 @@ public class KurentoParticipant extends Participant {
this.getParticipantPublicId(), remoteParticipantName);
}
}
releasePublisherEndpoint();
releasePublisherEndpoint(reason);
}
/**
@ -410,7 +424,7 @@ public class KurentoParticipant extends Participant {
session.sendMediaError(this.getParticipantPrivateId(), desc);
}
private void releasePublisherEndpoint() {
private void releasePublisherEndpoint(String reason) {
if (publisher != null && publisher.getEndpoint() != null) {
publisher.unregisterErrorListeners();
for (MediaElement el : publisher.getMediaElements()) {
@ -419,15 +433,23 @@ public class KurentoParticipant extends Participant {
releaseElement(getParticipantPublicId(), publisher.getEndpoint());
this.streaming = false;
publisher = null;
CDR.stopPublisher(this.getParticipantPublicId(), reason);
} else {
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) {
subscriber.unregisterErrorListeners();
releaseElement(senderName, subscriber.getEndpoint());
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(this.getParticipantPublicId())) {
CDR.stopSubscriber(this.getParticipantPublicId(), senderName, reason);
}
} else {
log.warn("PARTICIPANT {}: Trying to release subscriber endpoint for '{}' but is null",
this.getParticipantPublicId(), senderName);

View File

@ -19,7 +19,9 @@ import org.slf4j.LoggerFactory;
import io.openvidu.client.OpenViduException;
import io.openvidu.client.OpenViduException.Code;
import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.java.client.SessionProperties;
import io.openvidu.server.cdr.CallDetailRecord;
import io.openvidu.server.core.Participant;
import io.openvidu.server.core.Session;
@ -50,14 +52,17 @@ public class KurentoSession implements Session {
private Object pipelineReleaseLock = new Object();
private volatile boolean pipelineReleased = false;
private boolean destroyKurentoClient;
private CallDetailRecord CDR;
public KurentoSession(String sessionId, SessionProperties sessionProperties, KurentoClient kurentoClient, KurentoSessionEventsHandler kurentoSessionHandler,
boolean destroyKurentoClient) {
boolean destroyKurentoClient, CallDetailRecord CDR) {
this.sessionId = sessionId;
this.sessionProperties = sessionProperties;
this.kurentoClient = kurentoClient;
this.destroyKurentoClient = destroyKurentoClient;
this.kurentoSessionHandler = kurentoSessionHandler;
this.CDR = CDR;
log.debug("New SESSION instance with id '{}'", sessionId);
}
@ -76,7 +81,7 @@ public class KurentoSession implements Session {
checkClosed();
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);
filterStates.forEach((filterId, state) -> {
@ -85,6 +90,10 @@ public class KurentoSession implements Session {
});
log.info("SESSION {}: Added participant {}", sessionId, participant);
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
CDR.recordParticipantJoined(participant, sessionId);
}
}
public void newPublisher(Participant participant) {
@ -102,7 +111,7 @@ public class KurentoSession implements Session {
participants.values(), participant.getParticipantPublicId());
}
public void cancelPublisher(Participant participant) {
public void cancelPublisher(Participant participant, String reason) {
deregisterPublisher();
// cancel recv video from this publisher
@ -110,7 +119,7 @@ public class KurentoSession implements Session {
if (participant.equals(subscriber)) {
continue;
}
subscriber.cancelReceivingMedia(participant.getParticipantPublicId());
subscriber.cancelReceivingMedia(participant.getParticipantPublicId(), reason);
}
@ -120,7 +129,7 @@ public class KurentoSession implements Session {
}
@Override
public void leave(String participantPrivateId) throws OpenViduException {
public void leave(String participantPrivateId, String reason) throws OpenViduException {
checkClosed();
@ -135,8 +144,12 @@ public class KurentoSession implements Session {
if (participant.isStreaming()) {
this.deregisterPublisher();
}
this.removeParticipant(participant);
participant.close();
this.removeParticipant(participant, reason);
participant.close(reason);
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
CDR.recordParticipantLeft(participant, participant.getSession().getSessionId(), reason);
}
}
@Override
@ -163,12 +176,12 @@ public class KurentoSession implements Session {
}
@Override
public boolean close() {
public boolean close(String reason) {
if (!closed) {
for (KurentoParticipant participant : participants.values()) {
participant.releaseAllFilters();
participant.close();
participant.close(reason);
}
participants.clear();
@ -208,7 +221,7 @@ public class KurentoSession implements Session {
}
}
private void removeParticipant(Participant participant) {
private void removeParticipant(Participant participant, String reason) {
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());
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.OpenViduKurentoClientSessionInfo;
import io.openvidu.server.kurento.endpoint.SdpType;
import io.openvidu.server.recording.ComposedRecordingService;
import io.openvidu.server.rpc.RpcHandler;
import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.core.MediaOptions;
import io.openvidu.server.core.Participant;
import io.openvidu.server.core.Session;
@ -42,13 +40,7 @@ public class KurentoSessionManager extends SessionManager {
private KurentoClientProvider kcProvider;
@Autowired
private KurentoSessionEventsHandler sessionHandler;
@Autowired
private ComposedRecordingService recordingService;
@Autowired
OpenviduConfig openviduConfig;
private KurentoSessionEventsHandler kurentoSessionEventsHandler;
@Override
public synchronized void joinRoom(Participant participant, String sessionId, Integer transactionId) {
@ -57,8 +49,8 @@ public class KurentoSessionManager extends SessionManager {
KurentoClientSessionInfo kcSessionInfo = new OpenViduKurentoClientSessionInfo(
participant.getParticipantPrivateId(), sessionId);
KurentoSession session = (KurentoSession) sessions.get(sessionId);
if (session == null && kcSessionInfo != null) {
SessionProperties properties = sessionProperties.get(sessionId);
if (properties == null && this.isInsecureParticipant(participant.getParticipantPrivateId())) {
@ -85,27 +77,30 @@ public class KurentoSessionManager extends SessionManager {
} catch (OpenViduException e) {
log.warn("PARTICIPANT {}: Error joining/creating session {}", participant.getParticipantPublicId(),
sessionId, e);
sessionHandler.onParticipantJoined(participant, sessionId, null, transactionId, e);
sessionEventsHandler.onParticipantJoined(participant, sessionId, null, transactionId, e);
}
if (existingParticipants != null) {
sessionHandler.onParticipantJoined(participant, sessionId, existingParticipants, transactionId, null);
sessionEventsHandler.onParticipantJoined(participant, sessionId, existingParticipants, transactionId, null);
}
}
@Override
public void leaveRoom(Participant participant, Integer transactionId) {
public void leaveRoom(Participant participant, Integer transactionId, String reason) {
log.debug("Request [LEAVE_ROOM] ({})", participant.getParticipantPublicId());
KurentoParticipant kParticipant = (KurentoParticipant) participant;
KurentoSession session = kParticipant.getSession();
String sessionId = session.getSessionId();
if (session.isClosed()) {
log.warn("'{}' is trying to leave from session '{}' but it is closing",
participant.getParticipantPublicId(), sessionId);
throw new OpenViduException(Code.ROOM_CLOSED_ERROR_CODE, "'" + participant.getParticipantPublicId()
+ "' 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) {
Participant p = sessionidParticipantpublicidParticipant.get(sessionId)
@ -127,6 +122,8 @@ public class KurentoSessionManager extends SessionManager {
showTokens();
// Close Session if no more participants
Set<Participant> remainingParticipants = null;
try {
remainingParticipants = getParticipants(sessionId);
@ -134,10 +131,15 @@ public class KurentoSessionManager extends SessionManager {
log.debug("Possible collision when closing the session '{}' (not found)");
remainingParticipants = Collections.emptySet();
}
sessionEventsHandler.onParticipantLeft(participant, sessionId, remainingParticipants, transactionId, null,
reason);
if (remainingParticipants.isEmpty()) {
log.info("No more participants in session '{}', removing it and closing it", sessionId);
if (session.close()) {
sessionHandler.onSessionClosed(sessionId);
if (session.close(reason)) {
sessionEventsHandler.onSessionClosed(sessionId, "lastParticipantLeft");
}
sessions.remove(sessionId);
@ -148,17 +150,21 @@ public class KurentoSessionManager extends SessionManager {
showTokens();
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())
&& ArchiveMode.ALWAYS.equals(session.getSessionProperties().archiveMode())
&& ProtocolElements.RECORDER_PARTICIPANT_ID_PUBLICID
&& ProtocolElements.RECORDER_PARTICIPANT_PUBLICID
.equals(remainingParticipants.iterator().next().getParticipantPublicId())) {
log.info("Last participant left. Stopping recording for session {}", sessionId);
evictParticipant(session.getParticipantByPublicId("RECORDER").getParticipantPrivateId());
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;
KurentoSession session = kurentoParticipant.getSession();
kurentoParticipant.createPublishingEndpoint();
kurentoParticipant.createPublishingEndpoint(mediaOptions);
for (MediaElement elem : kurentoOptions.mediaElements) {
kurentoParticipant.getPublisher().apply(elem);
@ -218,13 +224,14 @@ public class KurentoSessionManager extends SessionManager {
OpenViduException e = new OpenViduException(Code.MEDIA_SDP_ERROR_CODE,
"Error generating SDP response for publishing user " + participant.getParticipantPublicId());
log.error("PARTICIPANT {}: Error publishing media", participant.getParticipantPublicId(), e);
sessionHandler.onPublishMedia(participant, session.getSessionId(), mediaOptions, sdpAnswer, participants,
transactionId, e);
sessionEventsHandler.onPublishMedia(participant, session.getSessionId(), mediaOptions, sdpAnswer,
participants, transactionId, e);
}
if (this.openviduConfig.isRecordingModuleEnabled()
&& MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode())
&& ArchiveMode.ALWAYS.equals(session.getSessionProperties().archiveMode())
&& !recordingService.sessionIsBeingRecorded(session.getSessionId())
&& session.getActivePublishers() == 0) {
recordingService.startRecording(session);
}
@ -234,17 +241,18 @@ public class KurentoSessionManager extends SessionManager {
kurentoParticipant.setAudioActive(kurentoOptions.audioActive);
kurentoParticipant.setVideoActive(kurentoOptions.videoActive);
kurentoParticipant.setTypeOfVideo(kurentoOptions.typeOfVideo);
kurentoParticipant.setFrameRate(kurentoOptions.frameRate);
participants = kurentoParticipant.getSession().getParticipants();
if (sdpAnswer != null) {
sessionHandler.onPublishMedia(participant, session.getSessionId(), mediaOptions, sdpAnswer, participants,
transactionId, null);
sessionEventsHandler.onPublishMedia(participant, session.getSessionId(), mediaOptions, sdpAnswer,
participants, transactionId, null);
}
}
@Override
public void unpublishVideo(Participant participant, Integer transactionId) {
public void unpublishVideo(Participant participant, Integer transactionId, String reason) {
try {
KurentoParticipant kParticipant = (KurentoParticipant) participant;
KurentoSession session = kParticipant.getSession();
@ -254,16 +262,16 @@ public class KurentoSessionManager extends SessionManager {
throw new OpenViduException(Code.USER_NOT_STREAMING_ERROR_CODE,
"Participant '" + participant.getParticipantPublicId() + "' is not streaming media");
}
kParticipant.unpublishMedia();
session.cancelPublisher(participant);
kParticipant.unpublishMedia(reason);
session.cancelPublisher(participant, reason);
Set<Participant> participants = session.getParticipants();
sessionHandler.onUnpublishMedia(participant, participants, transactionId, null);
sessionEventsHandler.onUnpublishMedia(participant, participants, transactionId, null, reason);
} catch (OpenViduException 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) {
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) {
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());
}
kParticipant.cancelReceivingMedia(senderName);
kParticipant.cancelReceivingMedia(senderName, "unsubscribe");
sessionHandler.onUnsubscribe(participant, senderName, transactionId, null);
sessionEventsHandler.onUnsubscribe(participant, senderName, transactionId, null);
}
@Override
@ -338,7 +346,7 @@ public class KurentoSessionManager extends SessionManager {
try {
JsonObject messageJSON = new JsonParser().parse(message).getAsJsonObject();
KurentoParticipant kParticipant = (KurentoParticipant) participant;
sessionHandler.onSendMessage(participant, messageJSON,
sessionEventsHandler.onSendMessage(participant, messageJSON,
getParticipants(kParticipant.getSession().getSessionId()), transactionId, null);
} catch (JsonSyntaxException | IllegalStateException e) {
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={} ({})",
endpointName, candidate, sdpMLineIndex, sdpMid, participant.getParticipantPublicId());
kParticipant.addIceCandidate(endpointName, new IceCandidate(candidate, sdpMid, sdpMLineIndex));
sessionHandler.onRecvIceCandidate(participant, transactionId, null);
sessionEventsHandler.onRecvIceCandidate(participant, transactionId, null);
} catch (OpenViduException e) {
log.error("PARTICIPANT {}: Error receiving ICE " + "candidate (epName={}, candidate={})",
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");
}
KurentoClient kurentoClient = kcProvider.getKurentoClient(kcSessionInfo);
session = new KurentoSession(sessionId, sessionProperties, kurentoClient, sessionHandler,
kcProvider.destroyWhenUnused());
session = new KurentoSession(sessionId, sessionProperties, kurentoClient, kurentoSessionEventsHandler,
kcProvider.destroyWhenUnused(), this.CDR);
KurentoSession oldSession = (KurentoSession) sessions.putIfAbsent(sessionId, session);
if (oldSession != null) {
@ -396,7 +404,7 @@ public class KurentoSessionManager extends SessionManager {
}
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
public void evictParticipant(String participantPrivateId) throws OpenViduException {
public void evictParticipant(String participantPrivateId, String reason) throws OpenViduException {
Participant participant = this.getParticipant(participantPrivateId);
this.leaveRoom(participant, null);
sessionHandler.onParticipantEvicted(participant);
this.leaveRoom(participant, null, reason);
sessionEventsHandler.onParticipantEvicted(participant);
}
@Override
@ -420,9 +428,11 @@ public class KurentoSessionManager extends SessionManager {
boolean audioActive = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_AUDIOACTIVE_PARAM);
boolean videoActive = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_VIDEOACTIVE_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);
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.config.OpenviduConfig;
import io.openvidu.server.core.Session;
import io.openvidu.server.core.SessionEventsHandler;
@Service
public class ComposedRecordingService {
@ -59,6 +60,9 @@ public class ComposedRecordingService {
@Autowired
OpenviduConfig openviduConfig;
@Autowired
private SessionEventsHandler sessionHandler;
private Map<String, String> containers = new ConcurrentHashMap<>();
private Map<String, String> sessionsContainers = new ConcurrentHashMap<>();
@ -81,12 +85,13 @@ public class ComposedRecordingService {
List<String> envs = new ArrayList<>();
String shortSessionId = session.getSessionId().substring(session.getSessionId().lastIndexOf('/') + 1,
session.getSessionId().length());
String videoId = this.getFreeRecordingId(session.getSessionId(), shortSessionId);
String recordingId = this.getFreeRecordingId(session.getSessionId(), shortSessionId);
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.sessionHandler.setRecordingStarted(session.getSessionId(), recording);
this.startingRecordings.put(recording.getId(), recording);
String uid = null;
@ -106,7 +111,7 @@ public class ComposedRecordingService {
+ "/" + secret);
envs.add("RESOLUTION=1920x1080");
envs.add("FRAMERATE=30");
envs.add("VIDEO_NAME=" + videoId);
envs.add("VIDEO_NAME=" + recordingId);
envs.add("VIDEO_FORMAT=mp4");
envs.add("USER_ID=" + uid);
envs.add("RECORDING_JSON=" + recording.toJson().toJSONString());
@ -115,9 +120,9 @@ public class ComposedRecordingService {
log.debug("Recorder connecting to url {}",
"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);
@ -171,7 +176,7 @@ public class ComposedRecordingService {
RecordingInfoUtils infoUtils = new RecordingInfoUtils(
this.openviduConfig.getOpenViduRecordingPath() + recording.getName() + ".info");
if (openviduConfig.getOpenViduRecordingFreeAccess()) {
if (openviduConfig.getOpenViduRecordingPublicAccess()) {
recording.setStatus(Recording.Status.available);
} else {
recording.setStatus(Recording.Status.stopped);
@ -181,7 +186,7 @@ public class ComposedRecordingService {
recording.setHasAudio(infoUtils.hasAudio());
recording.setHasVideo(infoUtils.hasVideo());
if (openviduConfig.getOpenViduRecordingFreeAccess()) {
if (openviduConfig.getOpenViduRecordingPublicAccess()) {
recording.setUrl(this.openviduConfig.getFinalUrl() + "recordings/" + recording.getName() + ".mp4");
}
@ -189,7 +194,9 @@ public class ComposedRecordingService {
throw new OpenViduException(Code.RECORDING_REPORT_ERROR_CODE,
"There was an error generating the metadata report file for the recording");
}
this.sessionHandler.sendRecordingStoppedNotification(session, recording);
return recording;
}
@ -341,7 +348,7 @@ public class ComposedRecordingService {
for (int i = 0; i < files.length; i++) {
Recording recording = this.getRecordingFromFile(files[i]);
if (recording != null) {
if (openviduConfig.getOpenViduRecordingFreeAccess()) {
if (openviduConfig.getOpenViduRecordingPublicAccess()) {
if (Recording.Status.stopped.equals(recording.getStatus())) {
recording.setStatus(Recording.Status.available);
recording.setUrl(

View File

@ -16,8 +16,6 @@
*/
package io.openvidu.server.rest;
import static org.kurento.commons.PropertiesManager.getProperty;
import java.util.Collection;
import java.util.Map;
import java.util.NoSuchElementException;
@ -36,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import io.openvidu.client.OpenViduException;
import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.java.client.ArchiveLayout;
import io.openvidu.java.client.ArchiveMode;
import io.openvidu.java.client.MediaMode;
@ -55,9 +54,6 @@ import io.openvidu.server.recording.ComposedRecordingService;
@RequestMapping("/api")
public class SessionRestController {
private static final int UPDATE_SPEAKER_INTERVAL_DEFAULT = 1800;
private static final int THRESHOLD_SPEAKER_DEFAULT = -50;
@Autowired
private SessionManager sessionManager;
@ -69,16 +65,6 @@ public class SessionRestController {
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")
@RequestMapping(value = "/sessions", method = RequestMethod.POST)
public ResponseEntity<JSONObject> getSessionId(@RequestBody(required = false) Map<?, ?> params) {
@ -197,9 +183,14 @@ public class SessionRestController {
// Session is not being recorded
return new ResponseEntity<JSONObject>(HttpStatus.CONFLICT);
}
Session session = sessionManager.getSession(recording.getSessionId());
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);
}

View File

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

View File

@ -30,11 +30,16 @@
"description": "Where to store the recorded video files"
},
{
"name": "openvidu.recording.free-access",
"name": "openvidu.recording.public-access",
"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",
"type": "java.lang.String",
"description": "Tag for openvidu/openvidu-recording Docker image"

View File

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