mirror of https://github.com/OpenVidu/openvidu.git
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.
parent
12758e528a
commit
390ce2224e
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
|
||||||
subscription = new CDREvent(CDREvent.CONNECTION_DESTROYED, subscription);
|
|
||||||
log.info("{}", subscription);
|
|
||||||
}
|
|
||||||
this.subscriptions.remove(participantPublicId).clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void recordRecordingStopped(String sessionId) {
|
||||||
|
CDREvent recording = new CDREvent(CDREvent.RECORDING_STOPPED, sessionId);
|
||||||
|
log.info("{}", recording);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
JsonObject notifParams = new JsonObject();
|
||||||
|
|
||||||
// Metadata associated to new participant
|
// Metadata associated to new participant
|
||||||
notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_USER_PARAM,
|
notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_USER_PARAM,
|
||||||
participant.getParticipantPublicId());
|
participant.getParticipantPublicId());
|
||||||
notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_METADATA_PARAM, participant.getFullMetadata());
|
notifParams.addProperty(ProtocolElements.PARTICIPANTJOINED_METADATA_PARAM,
|
||||||
|
participant.getFullMetadata());
|
||||||
|
|
||||||
rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
|
rpcNotificationService.sendNotification(existingParticipant.getParticipantPrivateId(),
|
||||||
ProtocolElements.PARTICIPANTJOINED_METHOD, notifParams);
|
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, 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) {
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,9 +30,14 @@
|
||||||
"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",
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue