openvidu-server: CDR refactoring. Session and Participant createdAt property

pull/121/head
pabloFuente 2018-09-07 09:54:38 +02:00
parent 41a53c79f8
commit c75252508c
18 changed files with 326 additions and 213 deletions

View File

@ -19,184 +19,38 @@ package io.openvidu.server.cdr;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import io.openvidu.java.client.RecordingLayout; public class CDREvent {
import io.openvidu.server.core.MediaOptions;
import io.openvidu.server.core.Participant;
import io.openvidu.server.recording.Recording;
public class CDREvent implements Comparable<CDREvent> {
protected CDREventName eventName;
protected String sessionId; protected String sessionId;
protected Long timeStamp; protected Long timeStamp;
private Long startTime; private CDREventName eventName;
private Integer duration;
private Participant participant;
private String location;
private String platform;
private MediaOptions mediaOptions;
private String receivingFrom;
private String reason;
// Recording events public CDREvent(CDREventName eventName, String sessionId, Long timeStamp) {
private Long size;
private String id;
private String name;
private Boolean hasAudio;
private Boolean hasVideo;
private RecordingLayout recordingLayout;
public CDREvent(CDREventName eventName, CDREvent event) {
this(eventName, event.participant, event.sessionId, event.mediaOptions, event.receivingFrom, event.startTime,
event.reason);
this.duration = (int) (this.timeStamp - this.startTime / 1000);
}
public CDREvent(CDREventName 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);
}
public CDREvent(CDREventName eventName, String sessionId) {
this.eventName = eventName; this.eventName = eventName;
if ((sessionId.indexOf('/')) != -1) {
this.sessionId = sessionId.substring(sessionId.lastIndexOf('/') + 1, sessionId.length());
} else {
this.sessionId = sessionId; this.sessionId = sessionId;
} this.timeStamp = timeStamp;
this.timeStamp = System.currentTimeMillis();
this.startTime = this.timeStamp;
} }
public CDREvent(CDREventName eventName, String sessionId, Recording recording, String reason) { public String getSessionId() {
this.eventName = eventName; return this.sessionId;
if ((sessionId.indexOf('/')) != -1) {
this.sessionId = sessionId.substring(sessionId.lastIndexOf('/') + 1, sessionId.length());
} else {
this.sessionId = sessionId;
}
this.timeStamp = System.currentTimeMillis();
this.id = recording.getId();
this.name = recording.getName();
this.duration = (int) recording.getDuration();
this.size = recording.getSize();
this.hasAudio = recording.hasAudio();
this.hasVideo = recording.hasVideo();
this.recordingLayout = recording.getRecordingLayout();
this.reason = reason;
} }
public CDREvent(CDREventName eventName, Participant participant, String sessionId) { public Long getTimestamp() {
this(eventName, sessionId); return this.timeStamp;
this.participant = participant;
this.location = participant.getLocation();
this.platform = participant.getPlatform();
this.startTime = this.timeStamp;
} }
public CDREvent(CDREventName eventName, Participant participant, String sessionId, MediaOptions mediaOptions, public JsonObject toJson() {
String receivingFrom, Long startTime, String reason) { JsonObject json = new JsonObject();
this(eventName, sessionId); json.addProperty("sessionId", this.sessionId);
this.participant = participant; json.addProperty("timestamp", this.timeStamp);
this.mediaOptions = mediaOptions; return json;
this.receivingFrom = receivingFrom;
this.startTime = startTime;
this.reason = reason;
}
public MediaOptions getMediaOptions() {
return mediaOptions;
}
public String getParticipantPublicId() {
return this.participant.getParticipantPublicId();
}
public String getReceivingFrom() {
return this.receivingFrom;
} }
@Override @Override
public String toString() { public String toString() {
JsonObject json = new JsonObject();
json.addProperty("sessionId", this.sessionId);
json.addProperty("timestamp", this.timeStamp);
if (this.participant != null) {
json.addProperty("participantId", this.participant.getParticipantPublicId());
}
if (this.location != null) {
json.addProperty("location", this.location);
}
if (this.platform != null) {
json.addProperty("platform", this.platform);
}
if (this.mediaOptions != null) {
json.addProperty("connection", this.receivingFrom != null ? "INBOUND" : "OUTBOUND");
json.addProperty("audioEnabled", this.mediaOptions.hasAudio());
json.addProperty("videoEnabled", this.mediaOptions.hasVideo());
if (this.mediaOptions.hasVideo()) {
json.addProperty("videoSource", this.mediaOptions.getTypeOfVideo());
json.addProperty("videoFramerate", this.mediaOptions.getFrameRate());
}
if (this.receivingFrom != null) {
json.addProperty("receivingFrom", this.receivingFrom);
}
}
if (this.startTime != null && this.duration != null) {
json.addProperty("startTime", this.startTime);
json.addProperty("endTime", this.timeStamp);
json.addProperty("duration", (this.timeStamp - this.startTime) / 1000);
} else if (this.duration != null) {
json.addProperty("duration", duration);
}
if (this.reason != null) {
json.addProperty("reason", this.reason);
}
if (this.id != null) {
json.addProperty("id", this.id);
}
if (this.name != null) {
json.addProperty("name", this.name);
}
if (this.size != null) {
json.addProperty("size", this.size);
}
if (this.hasAudio != null) {
json.addProperty("hasAudio", this.hasAudio);
}
if (this.hasVideo != null) {
json.addProperty("hasVideo", this.hasVideo);
}
if (this.recordingLayout != null) {
json.addProperty("recordingLayout", this.recordingLayout.name());
}
JsonObject root = new JsonObject(); JsonObject root = new JsonObject();
root.add(this.eventName.name(), json); root.add(this.eventName.name(), this.toJson());
return root.toString(); return root.toString();
} }
@Override
public int compareTo(CDREvent other) {
if (this.participant.equals(other.participant)) {
if (this.receivingFrom != null && other.receivingFrom != null) {
if (this.receivingFrom.equals(other.receivingFrom)) {
return 0;
} else {
return 1;
}
} else {
if (this.receivingFrom == null && other.receivingFrom == null) {
return 0;
} else {
return 1;
}
}
}
return 1;
}
} }

View File

@ -0,0 +1,37 @@
package io.openvidu.server.cdr;
import com.google.gson.JsonObject;
public class CDREventEnd extends CDREvent {
protected Long startTime;
protected Integer duration;
protected String reason;
public CDREventEnd(CDREventName eventName, String sessionId, Long timestamp) {
super(eventName, sessionId, timestamp);
}
public CDREventEnd(CDREventName eventName, String sessionId, Long startTime, String reason) {
super(eventName, sessionId, System.currentTimeMillis());
this.startTime = startTime;
this.duration = (int) ((this.timeStamp - this.startTime) / 1000);
this.reason = reason;
}
@Override
public JsonObject toJson() {
JsonObject json = super.toJson();
if (this.startTime != null) {
json.addProperty("startTime", this.startTime);
}
if (this.duration != null) {
json.addProperty("duration", this.duration);
}
if (this.reason != null) {
json.addProperty("reason", this.reason);
}
return json;
}
}

View File

@ -0,0 +1,32 @@
package io.openvidu.server.cdr;
import com.google.gson.JsonObject;
import io.openvidu.server.core.Participant;
public class CDREventParticipant extends CDREventEnd {
private Participant participant;
// participantJoined
public CDREventParticipant(String sessionId, Participant participant) {
super(CDREventName.participantJoined, sessionId, participant.getCreatedAt());
this.participant = participant;
}
// participantLeft
public CDREventParticipant(CDREvent event, String reason) {
super(CDREventName.participantLeft, event.getSessionId(), event.getTimestamp(), reason);
this.participant = ((CDREventParticipant) event).participant;
}
@Override
public JsonObject toJson() {
JsonObject json = super.toJson();
json.addProperty("participantId", this.participant.getParticipantPublicId());
json.addProperty("location", this.participant.getLocation());
json.addProperty("platform", this.participant.getPlatform());
return json;
}
}

View File

@ -0,0 +1,36 @@
package io.openvidu.server.cdr;
import com.google.gson.JsonObject;
import io.openvidu.server.recording.Recording;
public class CDREventRecording extends CDREventEnd {
private Recording recording;
// recordingStarted
public CDREventRecording(String sessionId, Recording recording) {
super(CDREventName.recordingStarted, sessionId, recording.getCreatedAt());
this.recording = recording;
}
// recordingStopped
public CDREventRecording(CDREvent event, String reason) {
super(CDREventName.recordingStopped, event.getSessionId(), event.getTimestamp(), reason);
this.recording = ((CDREventRecording) event).recording;
}
@Override
public JsonObject toJson() {
JsonObject json = super.toJson();
json.addProperty("id", this.recording.getId());
json.addProperty("name", this.recording.getName());
json.addProperty("recordingLayout", this.recording.getRecordingLayout().name());
json.addProperty("hasAudio", this.recording.hasAudio());
json.addProperty("hasVideo", this.recording.hasVideo());
json.addProperty("size", this.recording.getSize());
json.addProperty("duration", this.recording.getDuration());
return json;
}
}

View File

@ -0,0 +1,20 @@
package io.openvidu.server.cdr;
import io.openvidu.server.core.Session;
public class CDREventSession extends CDREventEnd {
Session session;
// sessionCreated
public CDREventSession(Session session) {
super(CDREventName.sessionCreated, session.getSessionId(), session.getStartTime());
this.session = session;
}
// sessionDestroyed
public CDREventSession(CDREvent event, String reason) {
super(CDREventName.sessionDestroyed, event.getSessionId(), event.getTimestamp(), reason);
}
}

View File

@ -0,0 +1,70 @@
package io.openvidu.server.cdr;
import com.google.gson.JsonObject;
import io.openvidu.server.core.MediaOptions;
public class CDREventWebrtcConnection extends CDREventEnd implements Comparable<CDREventWebrtcConnection> {
String participantId;
MediaOptions mediaOptions;
String receivingFrom;
// webrtcConnectionCreated
public CDREventWebrtcConnection(String sessionId, String participantId, MediaOptions mediaOptions,
String receivingFrom) {
super(CDREventName.webrtcConnectionCreated, sessionId, System.currentTimeMillis());
this.participantId = participantId;
this.mediaOptions = mediaOptions;
this.receivingFrom = receivingFrom;
}
// webrtcConnectionDestroyed
public CDREventWebrtcConnection(CDREvent event, String reason) {
super(CDREventName.webrtcConnectionDestroyed, event.getSessionId(), event.getTimestamp(), reason);
CDREventWebrtcConnection e = (CDREventWebrtcConnection) event;
this.participantId = e.participantId;
this.mediaOptions = e.mediaOptions;
this.receivingFrom = e.receivingFrom;
}
@Override
public JsonObject toJson() {
JsonObject json = super.toJson();
json.addProperty("participantId", this.participantId);
if (this.receivingFrom != null) {
json.addProperty("connection", "INBOUND");
json.addProperty("receivingFrom", this.receivingFrom);
} else {
json.addProperty("connection", "OUTBOUND");
}
if (this.mediaOptions.hasVideo()) {
json.addProperty("videoSource", this.mediaOptions.getTypeOfVideo());
json.addProperty("videoFramerate", this.mediaOptions.getFrameRate());
json.addProperty("videoDimensions", this.mediaOptions.getVideoDimensions());
}
json.addProperty("audioEnabled", this.mediaOptions.hasAudio());
json.addProperty("videoEnabled", this.mediaOptions.hasVideo());
return json;
}
public int compareTo(CDREventWebrtcConnection other) {
if (this.participantId.equals(other.participantId)) {
if (this.receivingFrom != null && other.receivingFrom != null) {
if (this.receivingFrom.equals(other.receivingFrom)) {
return 0;
} else {
return 1;
}
} else {
if (this.receivingFrom == null && other.receivingFrom == null) {
return 0;
} else {
return 1;
}
}
}
return 1;
}
}

View File

@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired;
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.Session;
import io.openvidu.server.recording.Recording; import io.openvidu.server.recording.Recording;
/** /**
@ -37,11 +38,11 @@ import io.openvidu.server.recording.Recording;
* Enabled by property 'openvidu.cdr=true' * Enabled by property 'openvidu.cdr=true'
* *
* - 'sessionCreated': {sessionId, timestamp} * - 'sessionCreated': {sessionId, timestamp}
* - 'sessionDestroyed': {sessionId, timestamp, startTime, endTime, duration, reason} * - 'sessionDestroyed': {sessionId, timestamp, startTime, duration, reason}
* - 'participantJoined': {sessionId, timestamp, participantId, location, platform} * - 'participantJoined': {sessionId, timestamp, participantId, location, platform}
* - 'participantLeft': {sessionId, timestamp, participantId, startTime, endTime, duration, reason} * - 'participantLeft': {sessionId, timestamp, participantId, startTime, duration, reason}
* - 'webrtcConnectionCreated' {sessionId, timestamp, participantId, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate]} * - 'webrtcConnectionCreated' {sessionId, timestamp, participantId, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate]}
* - 'webrtcConnectionDestroyed' {sessionId, timestamp, participantId, startTime, endTime, duration, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate], reason} * - 'webrtcConnectionDestroyed' {sessionId, timestamp, participantId, startTime, duration, connection, [receivingFrom], audioEnabled, videoEnabled, [videoSource], [videoFramerate], reason}
* - 'recordingStarted' {sessionId, timestamp, id, name, hasAudio, hasVideo, recordingLayout, size} * - 'recordingStarted' {sessionId, timestamp, id, name, hasAudio, hasVideo, recordingLayout, size}
* - 'recordingStopped' {sessionId, timestamp, id, name, hasAudio, hasVideo, recordingLayout, size} * - 'recordingStopped' {sessionId, timestamp, id, name, hasAudio, hasVideo, recordingLayout, size}
* *
@ -50,7 +51,6 @@ import io.openvidu.server.recording.Recording;
* - sessionId: string * - sessionId: string
* - timestamp: number * - timestamp: number
* - startTime: number * - startTime: number
* - endTime: number
* - duration: number * - duration: number
* - participantId: string * - participantId: string
* - connection: "INBOUND", "OUTBOUND" * - connection: "INBOUND", "OUTBOUND"
@ -59,6 +59,7 @@ import io.openvidu.server.recording.Recording;
* - videoEnabled: boolean * - videoEnabled: boolean
* - videoSource: "CAMERA", "SCREEN" * - videoSource: "CAMERA", "SCREEN"
* - videoFramerate: number * - videoFramerate: number
* - videoDimensions: string
* - id: string * - id: string
* - name: string * - name: string
* - hasAudio: boolean * - hasAudio: boolean
@ -73,6 +74,7 @@ import io.openvidu.server.recording.Recording;
* - receivingFrom: only if connection = "INBOUND" * - receivingFrom: only if connection = "INBOUND"
* - videoSource: only if videoEnabled = true * - videoSource: only if videoEnabled = true
* - videoFramerate: only if videoEnabled = true * - videoFramerate: only if videoEnabled = true
* - videoDimensions: only if videoEnabled = true
* *
* @author Pablo Fuente (pablofuenteperez@gmail.com) * @author Pablo Fuente (pablofuenteperez@gmail.com)
*/ */
@ -83,75 +85,85 @@ public class CallDetailRecord {
private CDRLogger logger; private CDRLogger logger;
private Map<String, CDREvent> sessions = new ConcurrentHashMap<>(); private Map<String, CDREventSession> sessions = new ConcurrentHashMap<>();
private Map<String, CDREvent> participants = new ConcurrentHashMap<>(); private Map<String, CDREventParticipant> participants = new ConcurrentHashMap<>();
private Map<String, CDREvent> publications = new ConcurrentHashMap<>(); private Map<String, CDREventWebrtcConnection> publications = new ConcurrentHashMap<>();
private Map<String, Set<CDREvent>> subscriptions = new ConcurrentHashMap<>(); private Map<String, Set<CDREventWebrtcConnection>> subscriptions = new ConcurrentHashMap<>();
private Map<String, CDREventRecording> recordings = new ConcurrentHashMap<>();
private final List<String> lastParticipantLeftReasons = Arrays.asList(new String[] {"disconnect", "forceDisconnectByUser", "forceDisconnectByServer", "networkDisconnect"}); private final List<String> lastParticipantLeftReasons = Arrays.asList(
new String[] { "disconnect", "forceDisconnectByUser", "forceDisconnectByServer", "networkDisconnect" });
public CallDetailRecord(CDRLogger logger) { public CallDetailRecord(CDRLogger logger) {
this.logger = logger; this.logger = logger;
} }
public void recordSessionCreated(String sessionId) { public void recordSessionCreated(Session session) {
CDREvent e = new CDREvent(CDREventName.sessionCreated, sessionId); CDREventSession e = new CDREventSession(session);
this.sessions.put(sessionId, e); this.sessions.put(session.getSessionId(), e);
if (openviduConfig.isCdrEnabled()) this.logger.log(e); if (openviduConfig.isCdrEnabled())
this.logger.log(e);
} }
public void recordSessionDestroyed(String sessionId, String reason) { public void recordSessionDestroyed(String sessionId, String reason) {
CDREvent e = this.sessions.remove(sessionId); CDREvent e = this.sessions.remove(sessionId);
if (openviduConfig.isCdrEnabled()) this.logger.log(new CDREvent(CDREventName.sessionDestroyed, e, this.finalReason(reason))); if (openviduConfig.isCdrEnabled())
this.logger.log(new CDREventSession(e, this.finalReason(reason)));
} }
public void recordParticipantJoined(Participant participant, String sessionId) { public void recordParticipantJoined(Participant participant, String sessionId) {
CDREvent e = new CDREvent(CDREventName.participantJoined, participant, sessionId); CDREventParticipant e = new CDREventParticipant(sessionId, participant);
this.participants.put(participant.getParticipantPublicId(), e); this.participants.put(participant.getParticipantPublicId(), e);
if (openviduConfig.isCdrEnabled()) this.logger.log(e); if (openviduConfig.isCdrEnabled())
this.logger.log(e);
} }
public void recordParticipantLeft(Participant participant, String sessionId, String reason) { public void recordParticipantLeft(Participant participant, String sessionId, String reason) {
CDREvent e = this.participants.remove(participant.getParticipantPublicId()); CDREvent e = this.participants.remove(participant.getParticipantPublicId());
if (openviduConfig.isCdrEnabled()) this.logger.log(new CDREvent(CDREventName.participantLeft, e, reason)); if (openviduConfig.isCdrEnabled())
this.logger.log(new CDREventParticipant(e, reason));
} }
public void recordNewPublisher(Participant participant, String sessionId, MediaOptions mediaOptions) { public void recordNewPublisher(Participant participant, String sessionId, MediaOptions mediaOptions) {
CDREvent publisher = new CDREvent(CDREventName.webrtcConnectionCreated, participant, sessionId, mediaOptions, CDREventWebrtcConnection publisher = new CDREventWebrtcConnection(sessionId,
null, System.currentTimeMillis(), null); participant.getParticipantPublicId(), mediaOptions, null);
this.publications.put(participant.getParticipantPublicId(), publisher); this.publications.put(participant.getParticipantPublicId(), publisher);
if (openviduConfig.isCdrEnabled()) this.logger.log(publisher); if (openviduConfig.isCdrEnabled())
this.logger.log(publisher);
} }
public boolean stopPublisher(String participantPublicId, String reason) { public boolean stopPublisher(String participantPublicId, String reason) {
CDREvent publisher = this.publications.remove(participantPublicId); CDREventWebrtcConnection publisher = this.publications.remove(participantPublicId);
if (publisher != null) { if (publisher != null) {
publisher = new CDREvent(CDREventName.webrtcConnectionDestroyed, publisher, reason); publisher = new CDREventWebrtcConnection(publisher, reason);
if (openviduConfig.isCdrEnabled()) this.logger.log(publisher); if (openviduConfig.isCdrEnabled())
this.logger.log(publisher);
return true; return true;
} }
return false; return false;
} }
public void recordNewSubscriber(Participant participant, String sessionId, String senderPublicId) { public void recordNewSubscriber(Participant participant, String sessionId, String senderPublicId) {
CDREvent publisher = this.publications.get(senderPublicId); CDREventWebrtcConnection publisher = this.publications.get(senderPublicId);
CDREvent subscriber = new CDREvent(CDREventName.webrtcConnectionCreated, participant, sessionId, CDREventWebrtcConnection subscriber = new CDREventWebrtcConnection(sessionId,
publisher.getMediaOptions(), publisher.getParticipantPublicId(), System.currentTimeMillis(), null); participant.getParticipantPublicId(), publisher.mediaOptions, senderPublicId);
this.subscriptions.putIfAbsent(participant.getParticipantPublicId(), new ConcurrentSkipListSet<>()); this.subscriptions.putIfAbsent(participant.getParticipantPublicId(), new ConcurrentSkipListSet<>());
this.subscriptions.get(participant.getParticipantPublicId()).add(subscriber); this.subscriptions.get(participant.getParticipantPublicId()).add(subscriber);
if (openviduConfig.isCdrEnabled()) this.logger.log(subscriber); if (openviduConfig.isCdrEnabled())
this.logger.log(subscriber);
} }
public boolean stopSubscriber(String participantPublicId, String senderPublicId, String reason) { public boolean stopSubscriber(String participantPublicId, String senderPublicId, String reason) {
Set<CDREvent> participantSubscriptions = this.subscriptions.get(participantPublicId); Set<CDREventWebrtcConnection> participantSubscriptions = this.subscriptions.get(participantPublicId);
if (participantSubscriptions != null) { if (participantSubscriptions != null) {
CDREvent subscription; CDREventWebrtcConnection subscription;
for (Iterator<CDREvent> it = participantSubscriptions.iterator(); it.hasNext();) { for (Iterator<CDREventWebrtcConnection> it = participantSubscriptions.iterator(); it.hasNext();) {
subscription = it.next(); subscription = it.next();
if (subscription.getReceivingFrom().equals(senderPublicId)) { if (senderPublicId.equals(subscription.receivingFrom)) {
it.remove(); it.remove();
subscription = new CDREvent(CDREventName.webrtcConnectionDestroyed, subscription, reason); subscription = new CDREventWebrtcConnection(subscription, reason);
if (openviduConfig.isCdrEnabled()) this.logger.log(subscription); if (openviduConfig.isCdrEnabled())
this.logger.log(subscription);
return true; return true;
} }
} }
@ -160,11 +172,16 @@ public class CallDetailRecord {
} }
public void recordRecordingStarted(String sessionId, Recording recording) { public void recordRecordingStarted(String sessionId, Recording recording) {
if (openviduConfig.isCdrEnabled()) this.logger.log(new CDREvent(CDREventName.recordingStarted, sessionId, recording, null)); CDREventRecording recordingStartedEvent = new CDREventRecording(sessionId, recording);
this.recordings.putIfAbsent(recording.getId(), recordingStartedEvent);
if (openviduConfig.isCdrEnabled())
this.logger.log(new CDREventRecording(sessionId, recording));
} }
public void recordRecordingStopped(String sessionId, Recording recording, String reason) { public void recordRecordingStopped(String sessionId, Recording recording, String reason) {
if (openviduConfig.isCdrEnabled()) this.logger.log(new CDREvent(CDREventName.recordingStopped, sessionId, recording, this.finalReason(reason))); CDREventRecording recordingStartedEvent = this.recordings.remove(recording.getId());
if (openviduConfig.isCdrEnabled())
this.logger.log(new CDREventRecording(recordingStartedEvent, this.finalReason(reason)));
} }
private String finalReason(String reason) { private String finalReason(String reason) {

View File

@ -79,6 +79,9 @@ public class OpenviduConfig {
@Value("${coturn.redis.connect-timeout}") @Value("${coturn.redis.connect-timeout}")
private String coturnRedisConnectTimeout; private String coturnRedisConnectTimeout;
@Value("${kms.stats-enabled}")
private boolean kmsStatsEnabled;
@Value("#{'${spring.profiles.active:}'.length() > 0 ? '${spring.profiles.active:}'.split(',') : \"default\"}") @Value("#{'${spring.profiles.active:}'.length() > 0 ? '${spring.profiles.active:}'.split(',') : \"default\"}")
private String springProfile; private String springProfile;
@ -169,6 +172,10 @@ public class OpenviduConfig {
return this.coturnRedisDbname; return this.coturnRedisDbname;
} }
public boolean isKmsStatsEnabled() {
return this.kmsStatsEnabled;
}
public ParticipantRole[] getRolesFromRecordingNotification() { public ParticipantRole[] getRolesFromRecordingNotification() {
ParticipantRole[] roles; ParticipantRole[] roles;
switch (this.openviduRecordingNotification) { switch (this.openviduRecordingNotification) {

View File

@ -23,6 +23,7 @@ public class Participant {
private String participantPrivatetId; // ID to identify the user on server (org.kurento.jsonrpc.Session.id) private String participantPrivatetId; // ID to identify the user on server (org.kurento.jsonrpc.Session.id)
private String participantPublicId; // ID to identify the user on clients private String participantPublicId; // ID to identify the user on clients
private Long createdAt; // Timestamp when this connection was established
private String clientMetadata = ""; // Metadata provided on client side private String clientMetadata = ""; // Metadata provided on client side
private String serverMetadata = ""; // Metadata provided on server side private String serverMetadata = ""; // Metadata provided on server side
private Token token; // Token associated to this participant private Token token; // Token associated to this participant
@ -38,6 +39,7 @@ public class Participant {
String location, String platform) { String location, String platform) {
this.participantPrivatetId = participantPrivatetId; this.participantPrivatetId = participantPrivatetId;
this.participantPublicId = participantPublicId; this.participantPublicId = participantPublicId;
this.createdAt = System.currentTimeMillis();
this.token = token; this.token = token;
this.clientMetadata = clientMetadata; this.clientMetadata = clientMetadata;
if (!token.getServerMetadata().isEmpty()) if (!token.getServerMetadata().isEmpty())
@ -62,6 +64,10 @@ public class Participant {
this.participantPublicId = participantPublicId; this.participantPublicId = participantPublicId;
} }
public Long getCreatedAt() {
return this.createdAt;
}
public String getClientMetadata() { public String getClientMetadata() {
return clientMetadata; return clientMetadata;
} }
@ -187,6 +193,7 @@ public class Participant {
public JsonObject toJson() { public JsonObject toJson() {
JsonObject json = new JsonObject(); JsonObject json = new JsonObject();
json.addProperty("connectionId", this.participantPublicId); json.addProperty("connectionId", this.participantPublicId);
json.addProperty("createdAt", this.createdAt);
json.addProperty("location", this.location); json.addProperty("location", this.location);
json.addProperty("platform", this.platform); json.addProperty("platform", this.platform);
json.addProperty("token", this.token.getToken()); json.addProperty("token", this.token.getToken());

View File

@ -49,4 +49,6 @@ public interface Session {
JsonObject withStatsToJson(); JsonObject withStatsToJson();
Long getStartTime();
} }

View File

@ -64,8 +64,8 @@ public class SessionEventsHandler {
ReentrantLock lock = new ReentrantLock(); ReentrantLock lock = new ReentrantLock();
public void onSessionCreated(String sessionId) { public void onSessionCreated(Session session) {
CDR.recordSessionCreated(sessionId); CDR.recordSessionCreated(session);
} }
public void onSessionClosed(String sessionId, String reason) { public void onSessionClosed(String sessionId, String reason) {
@ -501,7 +501,8 @@ public class SessionEventsHandler {
} else { } else {
// Send response to every other user in the session different than the affected // Send response to every other user in the session different than the affected
// participant or the moderator // participant or the moderator
if (error == null && (moderator == null || !p.getParticipantPrivateId().equals(moderator.getParticipantPrivateId()))) { if (error == null && (moderator == null
|| !p.getParticipantPrivateId().equals(moderator.getParticipantPrivateId()))) {
rpcNotificationService.sendNotification(p.getParticipantPrivateId(), rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
ProtocolElements.STREAMPROPERTYCHANGED_METHOD, params); ProtocolElements.STREAMPROPERTYCHANGED_METHOD, params);
} }
@ -509,8 +510,8 @@ public class SessionEventsHandler {
} }
} }
public void onFilterEventDispatched(String connectionId, String streamId, String filterType, String eventType, Object data, public void onFilterEventDispatched(String connectionId, String streamId, String filterType, String eventType,
Set<Participant> participants, Set<String> subscribedParticipants) { Object data, Set<Participant> participants, Set<String> subscribedParticipants) {
JsonObject params = new JsonObject(); JsonObject params = new JsonObject();
params.addProperty(ProtocolElements.FILTEREVENTLISTENER_CONNECTIONID_PARAM, connectionId); params.addProperty(ProtocolElements.FILTEREVENTLISTENER_CONNECTIONID_PARAM, connectionId);
params.addProperty(ProtocolElements.FILTEREVENTLISTENER_STREAMID_PARAM, streamId); params.addProperty(ProtocolElements.FILTEREVENTLISTENER_STREAMID_PARAM, streamId);

View File

@ -67,6 +67,7 @@ public abstract class SessionManager {
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, Long> sessionCreationTime = new ConcurrentHashMap<>();
protected ConcurrentMap<String, ConcurrentHashMap<String, Participant>> sessionidParticipantpublicidParticipant = new ConcurrentHashMap<>(); protected ConcurrentMap<String, ConcurrentHashMap<String, Participant>> sessionidParticipantpublicidParticipant = new ConcurrentHashMap<>();
protected ConcurrentMap<String, Boolean> insecureUsers = new ConcurrentHashMap<>(); protected ConcurrentMap<String, Boolean> insecureUsers = new ConcurrentHashMap<>();
public ConcurrentMap<String, ConcurrentHashMap<String, Token>> sessionidTokenTokenobj = new ConcurrentHashMap<>(); public ConcurrentMap<String, ConcurrentHashMap<String, Token>> sessionidTokenTokenobj = new ConcurrentHashMap<>();
@ -215,9 +216,10 @@ public abstract class SessionManager {
return null; return null;
} }
public void storeSessionId(String sessionId, SessionProperties sessionProperties) { public void storeSessionId(String sessionId, Long creationTime, SessionProperties sessionProperties) {
this.sessionidParticipantpublicidParticipant.putIfAbsent(sessionId, new ConcurrentHashMap<>()); this.sessionidParticipantpublicidParticipant.putIfAbsent(sessionId, new ConcurrentHashMap<>());
this.sessionProperties.putIfAbsent(sessionId, sessionProperties); this.sessionProperties.putIfAbsent(sessionId, sessionProperties);
this.sessionCreationTime.putIfAbsent(sessionId, creationTime);
showTokens(); showTokens();
} }
@ -269,6 +271,7 @@ public abstract class SessionManager {
} }
} else { } else {
this.sessionidParticipantpublicidParticipant.putIfAbsent(sessionId, new ConcurrentHashMap<>()); this.sessionidParticipantpublicidParticipant.putIfAbsent(sessionId, new ConcurrentHashMap<>());
this.sessionCreationTime.putIfAbsent(sessionId, System.currentTimeMillis());
this.sessionidTokenTokenobj.putIfAbsent(sessionId, new ConcurrentHashMap<>()); this.sessionidTokenTokenobj.putIfAbsent(sessionId, new ConcurrentHashMap<>());
this.sessionidTokenTokenobj.get(sessionId).putIfAbsent(token, this.sessionidTokenTokenobj.get(sessionId).putIfAbsent(token,
new Token(token, ParticipantRole.PUBLISHER, "", new Token(token, ParticipantRole.PUBLISHER, "",
@ -461,6 +464,7 @@ public abstract class SessionManager {
sessions.remove(session.getSessionId()); sessions.remove(session.getSessionId());
sessionProperties.remove(session.getSessionId()); sessionProperties.remove(session.getSessionId());
sessionCreationTime.remove(session.getSessionId());
sessionidParticipantpublicidParticipant.remove(session.getSessionId()); sessionidParticipantpublicidParticipant.remove(session.getSessionId());
sessionidTokenTokenobj.remove(session.getSessionId()); sessionidTokenTokenobj.remove(session.getSessionId());

View File

@ -61,6 +61,7 @@ public class KurentoSession implements Session {
private final ConcurrentMap<String, KurentoParticipant> participants = new ConcurrentHashMap<>(); private final ConcurrentMap<String, KurentoParticipant> participants = new ConcurrentHashMap<>();
private String sessionId; private String sessionId;
private SessionProperties sessionProperties; private SessionProperties sessionProperties;
private Long startTime;
private MediaPipeline pipeline; private MediaPipeline pipeline;
private CountDownLatch pipelineLatch = new CountDownLatch(1); private CountDownLatch pipelineLatch = new CountDownLatch(1);
@ -82,7 +83,7 @@ public class KurentoSession implements Session {
public final ConcurrentHashMap<String, String> publishedStreamIds = new ConcurrentHashMap<>(); public final ConcurrentHashMap<String, String> publishedStreamIds = new ConcurrentHashMap<>();
public KurentoSession(String sessionId, SessionProperties sessionProperties, KurentoClient kurentoClient, public KurentoSession(String sessionId, Long startTime, SessionProperties sessionProperties, KurentoClient kurentoClient,
KurentoSessionEventsHandler kurentoSessionHandler, boolean destroyKurentoClient, CallDetailRecord CDR, KurentoSessionEventsHandler kurentoSessionHandler, boolean destroyKurentoClient, CallDetailRecord CDR,
OpenviduConfig openviduConfig) { OpenviduConfig openviduConfig) {
this.sessionId = sessionId; this.sessionId = sessionId;
@ -92,6 +93,7 @@ public class KurentoSession implements Session {
this.kurentoSessionHandler = kurentoSessionHandler; this.kurentoSessionHandler = kurentoSessionHandler;
this.CDR = CDR; this.CDR = CDR;
this.openviduConfig = openviduConfig; this.openviduConfig = openviduConfig;
this.startTime = startTime;
log.debug("New SESSION instance with id '{}'", sessionId); log.debug("New SESSION instance with id '{}'", sessionId);
} }
@ -297,6 +299,10 @@ public class KurentoSession implements Session {
@Override @Override
public void onSuccess(MediaPipeline result) throws Exception { public void onSuccess(MediaPipeline result) throws Exception {
pipeline = result; pipeline = result;
if (openviduConfig.isKmsStatsEnabled()) {
pipeline.setLatencyStats(true);
log.debug("SESSION {}: WebRTC server stats enabled", sessionId);
}
pipelineLatch.countDown(); pipelineLatch.countDown();
log.debug("SESSION {}: Created MediaPipeline", sessionId); log.debug("SESSION {}: Created MediaPipeline", sessionId);
} }
@ -374,6 +380,7 @@ public class KurentoSession implements Session {
private JsonObject sharedJson(Function<KurentoParticipant, JsonObject> toJsonFunction) { private JsonObject sharedJson(Function<KurentoParticipant, JsonObject> toJsonFunction) {
JsonObject json = new JsonObject(); JsonObject json = new JsonObject();
json.addProperty("sessionId", this.sessionId); json.addProperty("sessionId", this.sessionId);
json.addProperty("createdAt", this.startTime);
json.addProperty("mediaMode", this.sessionProperties.mediaMode().name()); json.addProperty("mediaMode", this.sessionProperties.mediaMode().name());
json.addProperty("recordingMode", this.sessionProperties.recordingMode().name()); json.addProperty("recordingMode", this.sessionProperties.recordingMode().name());
json.addProperty("defaultRecordingLayout", this.sessionProperties.defaultRecordingLayout().name()); json.addProperty("defaultRecordingLayout", this.sessionProperties.defaultRecordingLayout().name());
@ -397,4 +404,9 @@ public class KurentoSession implements Session {
return this.publishedStreamIds.get(streamId); return this.publishedStreamIds.get(streamId);
} }
@Override
public Long getStartTime() {
return this.startTime;
}
} }

View File

@ -474,8 +474,9 @@ public class KurentoSessionManager extends SessionManager {
"Session '" + sessionId + "' already exists"); "Session '" + sessionId + "' already exists");
} }
this.kurentoClient = kcProvider.getKurentoClient(kcSessionInfo); this.kurentoClient = kcProvider.getKurentoClient(kcSessionInfo);
session = new KurentoSession(sessionId, sessionProperties, kurentoClient, kurentoSessionEventsHandler, session = new KurentoSession(sessionId, this.sessionCreationTime.get(sessionId), sessionProperties,
kcProvider.destroyWhenUnused(), this.CDR, this.openviduConfig); kurentoClient, kurentoSessionEventsHandler, kcProvider.destroyWhenUnused(), this.CDR,
this.openviduConfig);
KurentoSession oldSession = (KurentoSession) sessions.putIfAbsent(sessionId, session); KurentoSession oldSession = (KurentoSession) sessions.putIfAbsent(sessionId, session);
if (oldSession != null) { if (oldSession != null) {
@ -488,7 +489,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);
sessionEventsHandler.onSessionCreated(sessionId); sessionEventsHandler.onSessionCreated(session);
} }
@Override @Override

View File

@ -40,6 +40,7 @@ import org.kurento.client.WebRtcEndpoint;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@ -495,6 +496,9 @@ public abstract class MediaEndpoint {
json.add("receivedCandidates", new GsonBuilder().create().toJsonTree(this.receivedCandidateList)); json.add("receivedCandidates", new GsonBuilder().create().toJsonTree(this.receivedCandidateList));
json.addProperty("localCandidate", this.selectedLocalIceCandidate); json.addProperty("localCandidate", this.selectedLocalIceCandidate);
json.addProperty("remoteCandidate", this.selectedRemoteIceCandidate); json.addProperty("remoteCandidate", this.selectedRemoteIceCandidate);
if (openviduConfig.isKmsStatsEnabled()) {
json.addProperty("serverStats", new Gson().toJson(this.webEndpoint.getStats()));
}
JsonArray jsonArray = new JsonArray(); JsonArray jsonArray = new JsonArray();
for (KmsEvent event : this.kmsEvents) { for (KmsEvent event : this.kmsEvents) {

View File

@ -133,9 +133,11 @@ public class SessionRestController {
sessionManager.sessionidTokenTokenobj.putIfAbsent(sessionId, new ConcurrentHashMap<>()); sessionManager.sessionidTokenTokenobj.putIfAbsent(sessionId, new ConcurrentHashMap<>());
} }
sessionManager.storeSessionId(sessionId, sessionProperties); Long creationTime = System.currentTimeMillis();
sessionManager.storeSessionId(sessionId, creationTime, sessionProperties);
JsonObject responseJson = new JsonObject(); JsonObject responseJson = new JsonObject();
responseJson.addProperty("id", sessionId); responseJson.addProperty("id", sessionId);
responseJson.addProperty("createdAt", creationTime);
return new ResponseEntity<>(responseJson.toString(), getResponseHeaders(), HttpStatus.OK); return new ResponseEntity<>(responseJson.toString(), getResponseHeaders(), HttpStatus.OK);
} }

View File

@ -6,6 +6,12 @@
"description": "KMS URL's to which OpenVidu Server will try to connect. They are tested in order until a valid one is found", "description": "KMS URL's to which OpenVidu Server will try to connect. They are tested in order until a valid one is found",
"defaultValue": "[\"ws://localhost:8888/kurento\"]" "defaultValue": "[\"ws://localhost:8888/kurento\"]"
}, },
{
"name": "kms.stats-enabled",
"type": "java.lang.Boolean",
"description": "Whether to activate the KMS WebRTC statistics feature or not. This may increase the processor consumption",
"defaultValue": false
},
{ {
"name": "openvidu.secret", "name": "openvidu.secret",
"type": "java.lang.String", "type": "java.lang.String",
@ -69,7 +75,7 @@
"name": "openvidu.streams.video.max-recv-bandwidth", "name": "openvidu.streams.video.max-recv-bandwidth",
"type": "java.lang.Integer", "type": "java.lang.Integer",
"description": "Maximum video bandwidth sent from clients to OpenVidu Server, in kbps. 0 means unconstrained", "description": "Maximum video bandwidth sent from clients to OpenVidu Server, in kbps. 0 means unconstrained",
"defaultValue": 600 "defaultValue": 1000
}, },
{ {
"name": "openvidu.streams.video.min-recv-bandwidth", "name": "openvidu.streams.video.min-recv-bandwidth",
@ -81,7 +87,7 @@
"name": "openvidu.streams.video.max-send-bandwidth", "name": "openvidu.streams.video.max-send-bandwidth",
"type": "java.lang.Integer", "type": "java.lang.Integer",
"description": "Maximum video bandwidth sent from OpenVidu Server to clients, in kbps. 0 means unconstrained", "description": "Maximum video bandwidth sent from OpenVidu Server to clients, in kbps. 0 means unconstrained",
"defaultValue": 600 "defaultValue": 1000
}, },
{ {
"name": "openvidu.streams.video.min-send-bandwidth", "name": "openvidu.streams.video.min-send-bandwidth",

View File

@ -20,12 +20,13 @@ openvidu.recording.public-access: false
openvidu.recording.notification: publisher_moderator openvidu.recording.notification: publisher_moderator
openvidu.recording.custom-layout: /opt/openvidu/custom-layout openvidu.recording.custom-layout: /opt/openvidu/custom-layout
openvidu.streams.video.max-recv-bandwidth: 600 openvidu.streams.video.max-recv-bandwidth: 1000
openvidu.streams.video.min-recv-bandwidth: 300 openvidu.streams.video.min-recv-bandwidth: 300
openvidu.streams.video.max-send-bandwidth: 600 openvidu.streams.video.max-send-bandwidth: 1000
openvidu.streams.video.min-send-bandwidth: 300 openvidu.streams.video.min-send-bandwidth: 300
kms.uris: [\"ws://localhost:8888/kurento\"] kms.uris: [\"ws://localhost:8888/kurento\"]
kms.stats-enabled: false
coturn.redis.ip: 127.0.0.1 coturn.redis.ip: 127.0.0.1
coturn.redis.dbname: 0 coturn.redis.dbname: 0