openvidu-server: filters support

pull/105/head
pabloFuente 2018-08-01 15:08:25 +02:00
parent ccd8d7a8b9
commit c6f80c153a
22 changed files with 993 additions and 349 deletions

View File

@ -17,7 +17,7 @@
package io.openvidu.server.cdr; package io.openvidu.server.cdr;
import org.json.simple.JSONObject; import com.google.gson.JsonObject;
import io.openvidu.java.client.RecordingLayout; import io.openvidu.java.client.RecordingLayout;
import io.openvidu.server.core.MediaOptions; import io.openvidu.server.core.MediaOptions;
@ -35,7 +35,7 @@ public class CDREvent implements Comparable<CDREvent> {
private MediaOptions mediaOptions; private MediaOptions mediaOptions;
private String receivingFrom; private String receivingFrom;
private String reason; private String reason;
// Recording events // Recording events
private Long size; private Long size;
private String id; private String id;
@ -45,12 +45,14 @@ public class CDREvent implements Comparable<CDREvent> {
private RecordingLayout recordingLayout; private RecordingLayout recordingLayout;
public CDREvent(CDREventName eventName, CDREvent event) { public CDREvent(CDREventName eventName, CDREvent event) {
this(eventName, event.participant, event.sessionId, event.mediaOptions, event.receivingFrom, event.startTime, event.reason); this(eventName, event.participant, event.sessionId, event.mediaOptions, event.receivingFrom, event.startTime,
event.reason);
this.duration = (int) (this.timeStamp - this.startTime / 1000); this.duration = (int) (this.timeStamp - this.startTime / 1000);
} }
public CDREvent(CDREventName eventName, CDREvent event, String reason) { public CDREvent(CDREventName eventName, CDREvent event, String reason) {
this(eventName, event.participant, event.sessionId, event.mediaOptions, event.receivingFrom, event.startTime, 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);
} }
@ -64,7 +66,7 @@ public class CDREvent implements Comparable<CDREvent> {
this.timeStamp = System.currentTimeMillis(); this.timeStamp = System.currentTimeMillis();
this.startTime = this.timeStamp; this.startTime = this.timeStamp;
} }
public CDREvent(CDREventName eventName, String sessionId, Recording recording, String reason) { public CDREvent(CDREventName eventName, String sessionId, Recording recording, String reason) {
this.eventName = eventName; this.eventName = eventName;
if ((sessionId.indexOf('/')) != -1) { if ((sessionId.indexOf('/')) != -1) {
@ -112,60 +114,59 @@ public class CDREvent implements Comparable<CDREvent> {
} }
@Override @Override
@SuppressWarnings("unchecked")
public String toString() { public String toString() {
JSONObject json = new JSONObject(); JsonObject json = new JsonObject();
json.put("sessionId", this.sessionId); json.addProperty("sessionId", this.sessionId);
json.put("timestamp", this.timeStamp); json.addProperty("timestamp", this.timeStamp);
if (this.participant != null) { if (this.participant != null) {
json.put("participantId", this.participant.getParticipantPublicId()); json.addProperty("participantId", this.participant.getParticipantPublicId());
} }
if (this.mediaOptions != null) { if (this.mediaOptions != null) {
json.put("connection", this.receivingFrom != null ? "INBOUND" : "OUTBOUND"); json.addProperty("connection", this.receivingFrom != null ? "INBOUND" : "OUTBOUND");
json.put("audioEnabled", this.mediaOptions.hasAudio()); json.addProperty("audioEnabled", this.mediaOptions.hasAudio());
json.put("videoEnabled", this.mediaOptions.hasVideo()); json.addProperty("videoEnabled", this.mediaOptions.hasVideo());
if (this.mediaOptions.hasVideo()) { if (this.mediaOptions.hasVideo()) {
json.put("videoSource", this.mediaOptions.getTypeOfVideo()); json.addProperty("videoSource", this.mediaOptions.getTypeOfVideo());
json.put("videoFramerate", this.mediaOptions.getFrameRate()); json.addProperty("videoFramerate", this.mediaOptions.getFrameRate());
} }
if (this.receivingFrom != null) { if (this.receivingFrom != null) {
json.put("receivingFrom", this.receivingFrom); json.addProperty("receivingFrom", this.receivingFrom);
} }
} }
if (this.startTime != null && this.duration != null) { if (this.startTime != null && this.duration != null) {
json.put("startTime", this.startTime); json.addProperty("startTime", this.startTime);
json.put("endTime", this.timeStamp); json.addProperty("endTime", this.timeStamp);
json.put("duration", (this.timeStamp - this.startTime) / 1000); json.addProperty("duration", (this.timeStamp - this.startTime) / 1000);
} else if (this.duration != null) { } else if (this.duration != null) {
json.put("duration", duration); json.addProperty("duration", duration);
} }
if (this.reason != null) { if (this.reason != null) {
json.put("reason", this.reason); json.addProperty("reason", this.reason);
} }
if (this.id != null) { if (this.id != null) {
json.put("id", this.id); json.addProperty("id", this.id);
} }
if (this.name != null) { if (this.name != null) {
json.put("name", this.name); json.addProperty("name", this.name);
} }
if (this.size != null) { if (this.size != null) {
json.put("size", this.size); json.addProperty("size", this.size);
} }
if (this.hasAudio != null) { if (this.hasAudio != null) {
json.put("hasAudio", this.hasAudio); json.addProperty("hasAudio", this.hasAudio);
} }
if (this.hasVideo != null) { if (this.hasVideo != null) {
json.put("hasVideo", this.hasVideo); json.addProperty("hasVideo", this.hasVideo);
} }
if (this.recordingLayout != null) { if (this.recordingLayout != null) {
json.put("recordingLayout", this.recordingLayout.name()); json.addProperty("recordingLayout", this.recordingLayout.name());
} }
JSONObject root = new JSONObject(); JsonObject root = new JsonObject();
root.put(this.eventName.name(), json); root.add(this.eventName.name(), json);
return root.toJSONString(); return root.toString();
} }
@Override @Override

View File

@ -17,7 +17,9 @@
package io.openvidu.server.core; package io.openvidu.server.core;
import org.json.simple.JSONObject; import com.google.gson.JsonObject;
import io.openvidu.server.kurento.KurentoFilter;
public class MediaOptions { public class MediaOptions {
@ -28,9 +30,10 @@ public class MediaOptions {
protected String typeOfVideo; protected String typeOfVideo;
protected Integer frameRate; protected Integer frameRate;
protected String videoDimensions; protected String videoDimensions;
protected KurentoFilter filter;
public MediaOptions(Boolean hasAudio, Boolean hasVideo, Boolean audioActive, Boolean videoActive, public MediaOptions(Boolean hasAudio, Boolean hasVideo, Boolean audioActive, Boolean videoActive,
String typeOfVideo, Integer frameRate, String videoDimensions) { String typeOfVideo, Integer frameRate, String videoDimensions, KurentoFilter filter) {
this.hasAudio = hasAudio; this.hasAudio = hasAudio;
this.hasVideo = hasVideo; this.hasVideo = hasVideo;
this.audioActive = audioActive; this.audioActive = audioActive;
@ -38,6 +41,7 @@ public class MediaOptions {
this.typeOfVideo = typeOfVideo; this.typeOfVideo = typeOfVideo;
this.frameRate = frameRate; this.frameRate = frameRate;
this.videoDimensions = videoDimensions; this.videoDimensions = videoDimensions;
this.filter = filter;
} }
public boolean hasAudio() { public boolean hasAudio() {
@ -67,19 +71,32 @@ public class MediaOptions {
public String getVideoDimensions() { public String getVideoDimensions() {
return this.videoDimensions; return this.videoDimensions;
} }
@SuppressWarnings("unchecked") public KurentoFilter getFilter() {
public JSONObject toJSON() { return this.filter;
JSONObject json = new JSONObject(); }
json.put("hasAudio", this.hasAudio);
public void setFilter(KurentoFilter filter) {
this.filter = filter;
}
public JsonObject toJson() {
JsonObject json = new JsonObject();
json.addProperty("hasAudio", this.hasAudio);
if (hasAudio) if (hasAudio)
json.put("audioActive", this.audioActive); json.addProperty("audioActive", this.audioActive);
json.put("hasVideo", this.hasVideo); json.addProperty("hasVideo", this.hasVideo);
if (hasVideo) { if (hasVideo) {
json.put("videoActive", this.videoActive); json.addProperty("videoActive", this.videoActive);
json.put("typeOfVideo", this.typeOfVideo); json.addProperty("typeOfVideo", this.typeOfVideo);
json.put("frameRate", this.frameRate); json.addProperty("frameRate", this.frameRate);
json.put("videoDimensions", this.videoDimensions); json.addProperty("videoDimensions", this.videoDimensions);
}
json.add("filter", this.filter != null ? this.filter.toJson() : new JsonObject());
if (this.filter != null) {
((JsonObject) json.get("filter")).add("lastExecMethod",
this.filter.getLastExecMethod() != null ? this.filter.getLastExecMethod().toJson()
: new JsonObject());
} }
return json; return json;
} }

View File

@ -17,7 +17,7 @@
package io.openvidu.server.core; package io.openvidu.server.core;
import org.json.simple.JSONObject; import com.google.gson.JsonObject;
public class Participant { public class Participant {
@ -163,14 +163,13 @@ public class Participant {
return builder.toString(); return builder.toString();
} }
@SuppressWarnings("unchecked") public JsonObject toJson() {
public JSONObject toJSON() { JsonObject json = new JsonObject();
JSONObject json = new JSONObject(); json.addProperty("connectionId", this.participantPublicId);
json.put("connectionId", this.participantPublicId); json.addProperty("token", this.token.getToken());
json.put("token", this.token.getToken()); json.addProperty("role", this.token.getRole().name());
json.put("role", this.token.getRole().name()); json.addProperty("serverData", this.serverMetadata);
json.put("serverData", this.serverMetadata); json.addProperty("clientData", this.clientMetadata);
json.put("clientData", this.clientMetadata);
return json; return json;
} }

View File

@ -19,7 +19,7 @@ package io.openvidu.server.core;
import java.util.Set; import java.util.Set;
import org.json.simple.JSONObject; import com.google.gson.JsonObject;
import io.openvidu.java.client.SessionProperties; import io.openvidu.java.client.SessionProperties;
@ -45,8 +45,8 @@ public interface Session {
int getActivePublishers(); int getActivePublishers();
JSONObject toJSON(); JsonObject toJson();
JSONObject withStatsToJSON(); JsonObject withStatsToJson();
} }

View File

@ -39,6 +39,7 @@ 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.kurento.KurentoFilter;
import io.openvidu.server.kurento.core.KurentoParticipant; import io.openvidu.server.kurento.core.KurentoParticipant;
import io.openvidu.server.recording.Recording; import io.openvidu.server.recording.Recording;
import io.openvidu.server.rpc.RpcNotificationService; import io.openvidu.server.rpc.RpcNotificationService;
@ -113,6 +114,10 @@ public class SessionEventsHandler {
kParticipant.getPublisherMediaOptions().frameRate); kParticipant.getPublisherMediaOptions().frameRate);
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMVIDEODIMENSIONS_PARAM, stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMVIDEODIMENSIONS_PARAM,
kParticipant.getPublisherMediaOptions().videoDimensions); kParticipant.getPublisherMediaOptions().videoDimensions);
JsonElement filter = kParticipant.getPublisherMediaOptions().getFilter() != null
? kParticipant.getPublisherMediaOptions().getFilter().toJson()
: new JsonObject();
stream.add(ProtocolElements.JOINROOM_PEERSTREAMFILTER_PARAM, filter);
JsonArray streamsArray = new JsonArray(); JsonArray streamsArray = new JsonArray();
streamsArray.add(stream); streamsArray.add(stream);
@ -199,6 +204,8 @@ public class SessionEventsHandler {
stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_TYPEOFVIDEO_PARAM, mediaOptions.typeOfVideo); stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_TYPEOFVIDEO_PARAM, mediaOptions.typeOfVideo);
stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_FRAMERATE_PARAM, mediaOptions.frameRate); stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_FRAMERATE_PARAM, mediaOptions.frameRate);
stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_VIDEODIMENSIONS_PARAM, mediaOptions.videoDimensions); stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_VIDEODIMENSIONS_PARAM, mediaOptions.videoDimensions);
JsonElement filter = mediaOptions.getFilter() != null ? mediaOptions.getFilter().toJson() : new JsonObject();
stream.add(ProtocolElements.JOINROOM_PEERSTREAMFILTER_PARAM, filter);
JsonArray streamsArray = new JsonArray(); JsonArray streamsArray = new JsonArray();
streamsArray.add(stream); streamsArray.add(stream);
@ -234,6 +241,7 @@ public class SessionEventsHandler {
for (Participant p : participants) { for (Participant p : participants) {
if (p.getParticipantPrivateId().equals(participant.getParticipantPrivateId())) { if (p.getParticipantPrivateId().equals(participant.getParticipantPrivateId())) {
// Send response to the affected participant
if (!isRpcFromOwner) { if (!isRpcFromOwner) {
rpcNotificationService.sendNotification(p.getParticipantPrivateId(), rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params); ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params);
@ -247,6 +255,8 @@ public class SessionEventsHandler {
} }
} else { } else {
if (error == null) { if (error == null) {
// Send response to every other user in the session different than the affected
// participant
rpcNotificationService.sendNotification(p.getParticipantPrivateId(), rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params); ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params);
} }
@ -440,6 +450,63 @@ public class SessionEventsHandler {
} }
} }
public void onFilterChanged(Participant participant, Participant moderator, Integer transactionId,
Set<Participant> participants, String streamId, KurentoFilter filter, OpenViduException error,
String reason) {
boolean isRpcFromModerator = transactionId != null && moderator != null;
boolean isRpcFromOwner = transactionId != null && moderator == null;
if (isRpcFromModerator) {
if (error != null) {
rpcNotificationService.sendErrorResponse(moderator.getParticipantPrivateId(), transactionId, null,
error);
return;
}
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject());
}
JsonObject params = new JsonObject();
params.addProperty(ProtocolElements.STREAMPROPERTYCHANGED_CONNECTIONID_PARAM,
participant.getParticipantPublicId());
params.addProperty(ProtocolElements.STREAMPROPERTYCHANGED_STREAMID_PARAM, streamId);
params.addProperty(ProtocolElements.STREAMPROPERTYCHANGED_PROPERTY_PARAM, "filter");
JsonObject filterJson = new JsonObject();
if (filter != null) {
filterJson.addProperty(ProtocolElements.FILTER_TYPE_PARAM, filter.getType());
filterJson.add(ProtocolElements.FILTER_OPTIONS_PARAM, filter.getOptions());
if (filter.getLastExecMethod() != null) {
filterJson.add(ProtocolElements.EXECFILTERMETHOD_LASTEXECMETHOD_PARAM,
filter.getLastExecMethod().toJson());
}
}
params.add(ProtocolElements.STREAMPROPERTYCHANGED_NEWVALUE_PARAM, filterJson);
params.addProperty(ProtocolElements.STREAMPROPERTYCHANGED_REASON_PARAM, reason);
for (Participant p : participants) {
if (p.getParticipantPrivateId().equals(participant.getParticipantPrivateId())) {
// Send response to the affected participant
if (!isRpcFromOwner) {
rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
ProtocolElements.STREAMPROPERTYCHANGED_METHOD, params);
} else {
if (error != null) {
rpcNotificationService.sendErrorResponse(p.getParticipantPrivateId(), transactionId, null,
error);
return;
}
rpcNotificationService.sendResponse(p.getParticipantPrivateId(), transactionId, new JsonObject());
}
} else {
// Send response to every other user in the session different than the affected
// participant
if (error == null) {
rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
ProtocolElements.STREAMPROPERTYCHANGED_METHOD, params);
}
}
}
}
public void closeRpcSession(String participantPrivateId) { public void closeRpcSession(String participantPrivateId) {
this.rpcNotificationService.closeRpcSession(participantPrivateId); this.rpcNotificationService.closeRpcSession(participantPrivateId);
} }

View File

@ -43,6 +43,7 @@ import io.openvidu.server.cdr.CallDetailRecord;
import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.coturn.CoturnCredentialsService; import io.openvidu.server.coturn.CoturnCredentialsService;
import io.openvidu.server.coturn.TurnCredentials; import io.openvidu.server.coturn.TurnCredentials;
import io.openvidu.server.kurento.core.KurentoTokenOptions;
import io.openvidu.server.recording.ComposedRecordingService; import io.openvidu.server.recording.ComposedRecordingService;
public abstract class SessionManager { public abstract class SessionManager {
@ -79,7 +80,8 @@ public abstract class SessionManager {
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, Participant moderator, Integer transactionId, String reason); public abstract void unpublishVideo(Participant participant, Participant moderator, 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);
@ -93,11 +95,21 @@ public abstract class SessionManager {
public abstract void onIceCandidate(Participant participant, String endpointName, String candidate, public abstract void onIceCandidate(Participant participant, String endpointName, String candidate,
int sdpMLineIndex, String sdpMid, Integer transactionId); int sdpMLineIndex, String sdpMid, Integer transactionId);
public abstract boolean unpublishStream(Session session, String streamId, Participant moderator, Integer transactionId, String reason); public abstract boolean unpublishStream(Session session, String streamId, Participant moderator,
Integer transactionId, String reason);
public abstract void evictParticipant(Participant evictedParticipant, Participant moderator, Integer transactionId, public abstract void evictParticipant(Participant evictedParticipant, Participant moderator, Integer transactionId,
String reason); String reason);
public abstract void applyFilter(Session session, String streamId, String filterType, JsonObject filterOptions,
Participant moderator, Integer transactionId, String reason);
public abstract void execFilterMethod(Session session, String streamId, String filterMethod, JsonObject filterParams,
Participant moderator, Integer transactionId, String reason);
public abstract void removeFilter(Session session, String streamId, Participant moderator, Integer transactionId,
String reason);
/** /**
* Returns a Session given its id * Returns a Session given its id
* *
@ -200,7 +212,8 @@ public abstract class SessionManager {
showTokens(); showTokens();
} }
public String newToken(String sessionId, ParticipantRole role, String serverMetadata) throws OpenViduException { public String newToken(String sessionId, ParticipantRole role, String serverMetadata,
KurentoTokenOptions kurentoTokenOptions) throws OpenViduException {
ConcurrentHashMap<String, Token> map = this.sessionidTokenTokenobj.putIfAbsent(sessionId, ConcurrentHashMap<String, Token> map = this.sessionidTokenTokenobj.putIfAbsent(sessionId,
new ConcurrentHashMap<>()); new ConcurrentHashMap<>());
@ -224,7 +237,7 @@ public abstract class SessionManager {
token += "&turnCredential=" + turnCredentials.getCredential(); token += "&turnCredential=" + turnCredentials.getCredential();
} }
} }
Token t = new Token(token, role, serverMetadata, turnCredentials); Token t = new Token(token, role, serverMetadata, turnCredentials, kurentoTokenOptions);
map.putIfAbsent(token, t); map.putIfAbsent(token, t);
showTokens(); showTokens();
@ -252,7 +265,8 @@ public abstract class SessionManager {
new Token(token, ParticipantRole.PUBLISHER, "", new Token(token, ParticipantRole.PUBLISHER, "",
this.coturnCredentialsService.isCoturnAvailable() this.coturnCredentialsService.isCoturnAvailable()
? this.coturnCredentialsService.createUser() ? this.coturnCredentialsService.createUser()
: null)); : null,
null));
return true; return true;
} }
} }

View File

@ -17,9 +17,8 @@
package io.openvidu.server.core; package io.openvidu.server.core;
import org.json.simple.JSONObject;
import io.openvidu.server.coturn.TurnCredentials; import io.openvidu.server.coturn.TurnCredentials;
import io.openvidu.server.kurento.core.KurentoTokenOptions;
public class Token { public class Token {
@ -28,15 +27,19 @@ public class Token {
private String serverMetadata = ""; private String serverMetadata = "";
private TurnCredentials turnCredentials; private TurnCredentials turnCredentials;
private KurentoTokenOptions kurentoTokenOptions;
public Token(String token) { public Token(String token) {
this.token = token; this.token = token;
} }
public Token(String token, ParticipantRole role, String serverMetadata, TurnCredentials turnCredentials) { public Token(String token, ParticipantRole role, String serverMetadata, TurnCredentials turnCredentials,
KurentoTokenOptions kurentoTokenOptions) {
this.token = token; this.token = token;
this.role = role; this.role = role;
this.serverMetadata = serverMetadata; this.serverMetadata = serverMetadata;
this.turnCredentials = turnCredentials; this.turnCredentials = turnCredentials;
this.kurentoTokenOptions = kurentoTokenOptions;
} }
public String getToken() { public String getToken() {
@ -55,6 +58,10 @@ public class Token {
return turnCredentials; return turnCredentials;
} }
public KurentoTokenOptions getKurentoTokenOptions() {
return kurentoTokenOptions;
}
@Override @Override
public String toString() { public String toString() {
if (this.role != null) if (this.role != null)

View File

@ -0,0 +1,84 @@
/*
* (C) Copyright 2017-2018 OpenVidu (https://openvidu.io/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.openvidu.server.kurento;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
public class KurentoFilter {
public class KurentoFilterMethod {
String method;
JsonObject params;
protected KurentoFilterMethod(String method, JsonObject params) {
this.method = method;
this.params = params;
}
public JsonObject toJson() {
JsonObject json = new JsonObject();
json.addProperty("method", lastExecMethod.method);
json.add("params", lastExecMethod.params);
return json;
}
}
String type;
JsonObject options;
KurentoFilterMethod lastExecMethod;
public KurentoFilter(String type, JsonObject options) {
this.type = type;
this.options = options;
}
public KurentoFilter(String type, JsonObject options, String method, JsonObject params) {
this.type = type;
this.options = options;
this.lastExecMethod = new KurentoFilterMethod(method, params);
}
public KurentoFilter(JsonElement json) {
JsonObject jsonObject = json.getAsJsonObject();
this.type = jsonObject.get("type").getAsString();
this.options = jsonObject.get("options").getAsJsonObject();
}
public String getType() {
return type;
}
public JsonObject getOptions() {
return options;
}
public KurentoFilterMethod getLastExecMethod() {
return this.lastExecMethod;
}
public JsonObject toJson() {
JsonObject json = new JsonObject();
json.addProperty("type", type);
json.add("options", options);
json.add("lastExecMethod", this.lastExecMethod != null ? this.lastExecMethod.toJson() : new JsonObject());
return json;
}
}

View File

@ -21,6 +21,7 @@ import org.kurento.client.MediaElement;
import org.kurento.client.MediaType; import org.kurento.client.MediaType;
import io.openvidu.server.core.MediaOptions; import io.openvidu.server.core.MediaOptions;
import io.openvidu.server.kurento.KurentoFilter;
public class KurentoMediaOptions extends MediaOptions { public class KurentoMediaOptions extends MediaOptions {
@ -33,9 +34,9 @@ public class KurentoMediaOptions extends MediaOptions {
public KurentoMediaOptions(boolean isOffer, String sdpOffer, MediaElement loopbackAlternativeSrc, public KurentoMediaOptions(boolean isOffer, String sdpOffer, MediaElement loopbackAlternativeSrc,
MediaType loopbackConnectionType, Boolean hasAudio, Boolean hasVideo, Boolean audioActive, MediaType loopbackConnectionType, Boolean hasAudio, Boolean hasVideo, Boolean audioActive,
Boolean videoActive, String typeOfVideo, Integer frameRate, String videoDimensions, boolean doLoopback, Boolean videoActive, String typeOfVideo, Integer frameRate, String videoDimensions, KurentoFilter filter,
MediaElement... mediaElements) { boolean doLoopback, MediaElement... mediaElements) {
super(hasAudio, hasVideo, audioActive, videoActive, typeOfVideo, frameRate, videoDimensions); super(hasAudio, hasVideo, audioActive, videoActive, typeOfVideo, frameRate, videoDimensions, filter);
this.isOffer = isOffer; this.isOffer = isOffer;
this.sdpOffer = sdpOffer; this.sdpOffer = sdpOffer;
this.loopbackAlternativeSrc = loopbackAlternativeSrc; this.loopbackAlternativeSrc = loopbackAlternativeSrc;

View File

@ -26,11 +26,10 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Function; import java.util.function.Function;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.kurento.client.Continuation; import org.kurento.client.Continuation;
import org.kurento.client.ErrorEvent; import org.kurento.client.ErrorEvent;
import org.kurento.client.Filter; import org.kurento.client.Filter;
import org.kurento.client.GenericMediaElement;
import org.kurento.client.IceCandidate; import org.kurento.client.IceCandidate;
import org.kurento.client.MediaElement; import org.kurento.client.MediaElement;
import org.kurento.client.MediaPipeline; import org.kurento.client.MediaPipeline;
@ -40,6 +39,9 @@ import org.kurento.client.internal.server.KurentoServerException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
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.client.internal.ProtocolElements;
@ -115,7 +117,7 @@ public class KurentoParticipant extends Participant {
} }
public void shapePublisherMedia(MediaElement element, MediaType type) { public void shapePublisherMedia(GenericMediaElement element, MediaType type) {
if (type == null) { if (type == null) {
this.publisher.apply(element); this.publisher.apply(element);
} else { } else {
@ -127,38 +129,25 @@ public class KurentoParticipant extends Participant {
return filters.get(id); return filters.get(id);
} }
public synchronized void addFilterElement(String id, Filter filter) { /*
filters.put(id, filter); * public synchronized void addFilterElement(String id, Filter filter) {
shapePublisherMedia(filter, null); * filters.put(id, filter); shapePublisherMedia(filter, null); }
} *
* public synchronized void disableFilterelement(String filterID, boolean
public synchronized void disableFilterelement(String filterID, boolean releaseElement) { * releaseElement) { Filter filter = getFilterElement(filterID);
Filter filter = getFilterElement(filterID); *
* if (filter != null) { try { publisher.revert(filter, releaseElement); } catch
if (filter != null) { * (OpenViduException e) { // Ignore error } } }
try { *
publisher.revert(filter, releaseElement); * public synchronized void enableFilterelement(String filterID) { Filter filter
} catch (OpenViduException e) { * = getFilterElement(filterID);
// Ignore error *
} * if (filter != null) { try { publisher.apply(filter); } catch
} * (OpenViduException e) { // Ignore exception if element is already used } } }
} */
public synchronized void enableFilterelement(String filterID) {
Filter filter = getFilterElement(filterID);
if (filter != null) {
try {
publisher.apply(filter);
} catch (OpenViduException e) {
// Ignore exception if element is already used
}
}
}
public synchronized void removeFilterElement(String id) { public synchronized void removeFilterElement(String id) {
Filter filter = getFilterElement(id); Filter filter = getFilterElement(id);
filters.remove(id); filters.remove(id);
if (filter != null) { if (filter != null) {
publisher.revert(filter); publisher.revert(filter);
@ -166,10 +155,11 @@ public class KurentoParticipant extends Participant {
} }
public synchronized void releaseAllFilters() { public synchronized void releaseAllFilters() {
// Check this, mutable array? // Check this, mutable array?
filters.forEach((s, filter) -> removeFilterElement(s)); filters.forEach((s, filter) -> removeFilterElement(s));
if(this.publisher.getFilter() != null) {
this.publisher.revert(this.publisher.getFilter());
}
} }
public PublisherEndpoint getPublisher() { public PublisherEndpoint getPublisher() {
@ -544,8 +534,9 @@ public class KurentoParticipant extends Participant {
* event.getPadName() + " | MEDIATYPE: " + event.getMediaType() + * event.getPadName() + " | MEDIATYPE: " + event.getMediaType() +
* " | TIMESTAMP: " + System.currentTimeMillis(); * " | TIMESTAMP: " + System.currentTimeMillis();
* *
* endpoint.flowOutMedia.put(event.getSource().getName() + "/" + * endpoint.flowOutMedia. @SuppressWarnings("unchecked")
* event.getMediaType(), event.getSource()); * put(event.getSource().getName() + "/" + event.getMediaType(),
* event.getSource());
* *
* String msg2; * String msg2;
* *
@ -661,35 +652,38 @@ public class KurentoParticipant extends Participant {
}); });
} }
public MediaPipeline getPipeline() {
return this.pipeline;
}
@Override @Override
public String getPublisherStreamId() { public String getPublisherStreamId() {
return this.publisher.getEndpoint().getTag("name"); return this.publisher.getEndpoint().getTag("name");
} }
@Override @Override
public JSONObject toJSON() { public JsonObject toJson() {
return this.sharedJSON(MediaEndpoint::toJSON); return this.sharedJson(MediaEndpoint::toJson);
} }
public JSONObject withStatsToJSON() { public JsonObject withStatsToJson() {
return this.sharedJSON(MediaEndpoint::withStatsToJSON); return this.sharedJson(MediaEndpoint::withStatsToJson);
} }
@SuppressWarnings("unchecked") private JsonObject sharedJson(Function<MediaEndpoint, JsonObject> toJsonFunction) {
private JSONObject sharedJSON(Function<MediaEndpoint, JSONObject> toJsonFunction) { JsonObject json = super.toJson();
JSONObject json = super.toJSON(); JsonArray publisherEnpoints = new JsonArray();
JSONArray publisherEnpoints = new JSONArray();
if (this.streaming && this.publisher.getEndpoint() != null) { if (this.streaming && this.publisher.getEndpoint() != null) {
publisherEnpoints.add(toJsonFunction.apply(this.publisher)); publisherEnpoints.add(toJsonFunction.apply(this.publisher));
} }
JSONArray subscriberEndpoints = new JSONArray(); JsonArray subscriberEndpoints = new JsonArray();
for (MediaEndpoint sub : this.subscribers.values()) { for (MediaEndpoint sub : this.subscribers.values()) {
if (sub.getEndpoint() != null) { if (sub.getEndpoint() != null) {
subscriberEndpoints.add(toJsonFunction.apply(sub)); subscriberEndpoints.add(toJsonFunction.apply(sub));
} }
} }
json.put("publishers", publisherEnpoints); json.add("publishers", publisherEnpoints);
json.put("subscribers", subscriberEndpoints); json.add("subscribers", subscriberEndpoints);
return json; return json;
} }

View File

@ -26,8 +26,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function; import java.util.function.Function;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.kurento.client.Continuation; import org.kurento.client.Continuation;
import org.kurento.client.ErrorEvent; import org.kurento.client.ErrorEvent;
import org.kurento.client.EventListener; import org.kurento.client.EventListener;
@ -37,6 +35,9 @@ import org.kurento.client.MediaPipeline;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
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.client.internal.ProtocolElements;
@ -361,35 +362,34 @@ public class KurentoSession implements Session {
} }
@Override @Override
public JSONObject toJSON() { public JsonObject toJson() {
return this.sharedJSON(KurentoParticipant::toJSON); return this.sharedJson(KurentoParticipant::toJson);
} }
@Override @Override
public JSONObject withStatsToJSON() { public JsonObject withStatsToJson() {
return this.sharedJSON(KurentoParticipant::withStatsToJSON); return this.sharedJson(KurentoParticipant::withStatsToJson);
} }
@SuppressWarnings("unchecked") 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.put("sessionId", this.sessionId); json.addProperty("mediaMode", this.sessionProperties.mediaMode().name());
json.put("mediaMode", this.sessionProperties.mediaMode().name()); json.addProperty("recordingMode", this.sessionProperties.recordingMode().name());
json.put("recordingMode", this.sessionProperties.recordingMode().name()); json.addProperty("defaultRecordingLayout", this.sessionProperties.defaultRecordingLayout().name());
json.put("defaultRecordingLayout", this.sessionProperties.defaultRecordingLayout().name());
if (RecordingLayout.CUSTOM.equals(this.sessionProperties.defaultRecordingLayout())) { if (RecordingLayout.CUSTOM.equals(this.sessionProperties.defaultRecordingLayout())) {
json.put("defaultCustomLayout", this.sessionProperties.defaultCustomLayout()); json.addProperty("defaultCustomLayout", this.sessionProperties.defaultCustomLayout());
} }
JSONObject connections = new JSONObject(); JsonObject connections = new JsonObject();
JSONArray participants = new JSONArray(); JsonArray participants = new JsonArray();
this.participants.values().forEach(p -> { this.participants.values().forEach(p -> {
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(p.getParticipantPublicId())) { if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(p.getParticipantPublicId())) {
participants.add(toJsonFunction.apply(p)); participants.add(toJsonFunction.apply(p));
} }
}); });
connections.put("numberOfElements", participants.size()); connections.addProperty("numberOfElements", participants.size());
connections.put("content", participants); connections.add("content", participants);
json.put("connections", connections); json.add("connections", connections);
return json; return json;
} }

View File

@ -22,9 +22,10 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.kurento.client.GenericMediaElement;
import org.kurento.client.IceCandidate; import org.kurento.client.IceCandidate;
import org.kurento.client.KurentoClient; import org.kurento.client.KurentoClient;
import org.kurento.client.MediaElement; import org.kurento.jsonrpc.Props;
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;
@ -49,9 +50,11 @@ import io.openvidu.server.core.Session;
import io.openvidu.server.core.SessionManager; import io.openvidu.server.core.SessionManager;
import io.openvidu.server.kurento.KurentoClientProvider; import io.openvidu.server.kurento.KurentoClientProvider;
import io.openvidu.server.kurento.KurentoClientSessionInfo; import io.openvidu.server.kurento.KurentoClientSessionInfo;
import io.openvidu.server.kurento.KurentoFilter;
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.rpc.RpcHandler; import io.openvidu.server.rpc.RpcHandler;
import io.openvidu.server.utils.JsonUtils;
public class KurentoSessionManager extends SessionManager { public class KurentoSessionManager extends SessionManager {
@ -63,6 +66,8 @@ public class KurentoSessionManager extends SessionManager {
@Autowired @Autowired
private KurentoSessionEventsHandler kurentoSessionEventsHandler; private KurentoSessionEventsHandler kurentoSessionEventsHandler;
private KurentoClient kurentoClient;
@Override @Override
public synchronized void joinRoom(Participant participant, String sessionId, Integer transactionId) { public synchronized void joinRoom(Participant participant, String sessionId, Integer transactionId) {
Set<Participant> existingParticipants = null; Set<Participant> existingParticipants = null;
@ -91,7 +96,7 @@ public class KurentoSessionManager extends SessionManager {
log.warn("'{}' is trying to join session '{}' but it is closing", participant.getParticipantPublicId(), log.warn("'{}' is trying to join session '{}' but it is closing", participant.getParticipantPublicId(),
sessionId); sessionId);
throw new OpenViduException(Code.ROOM_CLOSED_ERROR_CODE, "'" + participant.getParticipantPublicId() throw new OpenViduException(Code.ROOM_CLOSED_ERROR_CODE, "'" + participant.getParticipantPublicId()
+ "' is trying to join room '" + sessionId + "' but it is closing"); + "' is trying to join session '" + sessionId + "' but it is closing");
} }
existingParticipants = getParticipants(sessionId); existingParticipants = getParticipants(sessionId);
session.join(participant); session.join(participant);
@ -216,7 +221,7 @@ public class KurentoSessionManager extends SessionManager {
String sdpAnswer = null; String sdpAnswer = null;
KurentoMediaOptions kurentoOptions = (KurentoMediaOptions) mediaOptions; KurentoMediaOptions kurentoOptions = (KurentoMediaOptions) mediaOptions;
KurentoParticipant kurentoParticipant = (KurentoParticipant) participant; KurentoParticipant kParticipant = (KurentoParticipant) participant;
log.debug( log.debug(
"Request [PUBLISH_MEDIA] isOffer={} sdp={} " "Request [PUBLISH_MEDIA] isOffer={} sdp={} "
@ -226,15 +231,22 @@ public class KurentoSessionManager extends SessionManager {
participant.getParticipantPublicId()); participant.getParticipantPublicId());
SdpType sdpType = kurentoOptions.isOffer ? SdpType.OFFER : SdpType.ANSWER; SdpType sdpType = kurentoOptions.isOffer ? SdpType.OFFER : SdpType.ANSWER;
KurentoSession session = kurentoParticipant.getSession(); KurentoSession session = kParticipant.getSession();
kurentoParticipant.createPublishingEndpoint(mediaOptions); kParticipant.createPublishingEndpoint(mediaOptions);
for (MediaElement elem : kurentoOptions.mediaElements) { /*
kurentoParticipant.getPublisher().apply(elem); * for (MediaElement elem : kurentoOptions.mediaElements) {
* kurentoParticipant.getPublisher().apply(elem); }
*/
KurentoTokenOptions kurentoTokenOptions = participant.getToken().getKurentoTokenOptions();
if (kurentoOptions.getFilter() != null && kurentoTokenOptions != null
&& kurentoTokenOptions.isFilterAllowed(kurentoOptions.getFilter().getType())) {
this.applyFilterInPublisher(kParticipant, kurentoOptions.getFilter());
} }
sdpAnswer = kurentoParticipant.publishToRoom(sdpType, kurentoOptions.sdpOffer, kurentoOptions.doLoopback, sdpAnswer = kParticipant.publishToRoom(sdpType, kurentoOptions.sdpOffer, kurentoOptions.doLoopback,
kurentoOptions.loopbackAlternativeSrc, kurentoOptions.loopbackConnectionType); kurentoOptions.loopbackAlternativeSrc, kurentoOptions.loopbackConnectionType);
if (sdpAnswer == null) { if (sdpAnswer == null) {
@ -261,7 +273,7 @@ public class KurentoSessionManager extends SessionManager {
session.newPublisher(participant); session.newPublisher(participant);
participants = kurentoParticipant.getSession().getParticipants(); participants = kParticipant.getSession().getParticipants();
if (sdpAnswer != null) { if (sdpAnswer != null) {
sessionEventsHandler.onPublishMedia(participant, participant.getPublisherStreamId(), session.getSessionId(), sessionEventsHandler.onPublishMedia(participant, participant.getPublisherStreamId(), session.getSessionId(),
@ -277,6 +289,11 @@ public class KurentoSessionManager extends SessionManager {
log.debug("Request [UNPUBLISH_MEDIA] ({})", participant.getParticipantPublicId()); log.debug("Request [UNPUBLISH_MEDIA] ({})", participant.getParticipantPublicId());
if (!participant.isStreaming()) { if (!participant.isStreaming()) {
log.warn(
"PARTICIPANT {}: Requesting to unpublish video of user {} "
+ "in session {} but user is not streaming media",
moderator != null ? moderator.getParticipantPublicId() : participant.getParticipantPublicId(),
participant.getParticipantPublicId(), session.getSessionId());
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");
} }
@ -309,18 +326,18 @@ public class KurentoSessionManager extends SessionManager {
if (senderParticipant == null) { if (senderParticipant == null) {
log.warn( log.warn(
"PARTICIPANT {}: Requesting to recv media from user {} " "PARTICIPANT {}: Requesting to recv media from user {} "
+ "in room {} but user could not be found", + "in session {} but user could not be found",
participant.getParticipantPublicId(), senderName, session.getSessionId()); participant.getParticipantPublicId(), senderName, session.getSessionId());
throw new OpenViduException(Code.USER_NOT_FOUND_ERROR_CODE, throw new OpenViduException(Code.USER_NOT_FOUND_ERROR_CODE,
"User '" + senderName + " not found in room '" + session.getSessionId() + "'"); "User '" + senderName + " not found in session '" + session.getSessionId() + "'");
} }
if (!senderParticipant.isStreaming()) { if (!senderParticipant.isStreaming()) {
log.warn( log.warn(
"PARTICIPANT {}: Requesting to recv media from user {} " "PARTICIPANT {}: Requesting to recv media from user {} "
+ "in room {} but user is not streaming media", + "in session {} but user is not streaming media",
participant.getParticipantPublicId(), senderName, session.getSessionId()); participant.getParticipantPublicId(), senderName, session.getSessionId());
throw new OpenViduException(Code.USER_NOT_STREAMING_ERROR_CODE, throw new OpenViduException(Code.USER_NOT_STREAMING_ERROR_CODE,
"User '" + senderName + " not streaming media in room '" + session.getSessionId() + "'"); "User '" + senderName + " not streaming media in session '" + session.getSessionId() + "'");
} }
sdpAnswer = kParticipant.receiveMediaFrom(senderParticipant, sdpOffer); sdpAnswer = kParticipant.receiveMediaFrom(senderParticipant, sdpOffer);
@ -349,10 +366,10 @@ public class KurentoSessionManager extends SessionManager {
if (sender == null) { if (sender == null) {
log.warn( log.warn(
"PARTICIPANT {}: Requesting to unsubscribe from user {} " "PARTICIPANT {}: Requesting to unsubscribe from user {} "
+ "in room {} but user could not be found", + "in session {} but user could not be found",
participant.getParticipantPublicId(), senderName, session.getSessionId()); participant.getParticipantPublicId(), senderName, session.getSessionId());
throw new OpenViduException(Code.USER_NOT_FOUND_ERROR_CODE, throw new OpenViduException(Code.USER_NOT_FOUND_ERROR_CODE,
"User " + senderName + " not found in room " + session.getSessionId()); "User " + senderName + " not found in session " + session.getSessionId());
} }
kParticipant.cancelReceivingMedia(senderName, "unsubscribe"); kParticipant.cancelReceivingMedia(senderName, "unsubscribe");
@ -363,9 +380,9 @@ public class KurentoSessionManager extends SessionManager {
@Override @Override
public void sendMessage(Participant participant, String message, Integer transactionId) { public void sendMessage(Participant participant, String message, Integer transactionId) {
try { try {
JsonObject messageJSON = new JsonParser().parse(message).getAsJsonObject(); JsonObject messageJson = new JsonParser().parse(message).getAsJsonObject();
KurentoParticipant kParticipant = (KurentoParticipant) participant; KurentoParticipant kParticipant = (KurentoParticipant) participant;
sessionEventsHandler.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,
@ -387,6 +404,7 @@ public class KurentoSessionManager extends SessionManager {
String typeOfVideo = streamProperties.getTypeOfVideo(); String typeOfVideo = streamProperties.getTypeOfVideo();
Integer frameRate = streamProperties.getFrameRate(); Integer frameRate = streamProperties.getFrameRate();
String videoDimensions = streamProperties.getVideoDimensions(); String videoDimensions = streamProperties.getVideoDimensions();
KurentoFilter filter = streamProperties.getFilter();
switch (property) { switch (property) {
case "audioActive": case "audioActive":
@ -401,7 +419,7 @@ public class KurentoSessionManager extends SessionManager {
} }
kParticipant.setPublisherMediaOptions(new MediaOptions(hasAudio, hasVideo, audioActive, videoActive, kParticipant.setPublisherMediaOptions(new MediaOptions(hasAudio, hasVideo, audioActive, videoActive,
typeOfVideo, frameRate, videoDimensions)); typeOfVideo, frameRate, videoDimensions, filter));
sessionEventsHandler.onStreamPropertyChanged(participant, transactionId, sessionEventsHandler.onStreamPropertyChanged(participant, transactionId,
kParticipant.getSession().getParticipants(), streamId, property, newValue, reason); kParticipant.getSession().getParticipants(), streamId, property, newValue, reason);
@ -442,7 +460,7 @@ public class KurentoSessionManager extends SessionManager {
throw new OpenViduException(Code.ROOM_CANNOT_BE_CREATED_ERROR_CODE, throw new OpenViduException(Code.ROOM_CANNOT_BE_CREATED_ERROR_CODE,
"Session '" + sessionId + "' already exists"); "Session '" + sessionId + "' already exists");
} }
KurentoClient kurentoClient = kcProvider.getKurentoClient(kcSessionInfo); this.kurentoClient = kcProvider.getKurentoClient(kcSessionInfo);
session = new KurentoSession(sessionId, sessionProperties, kurentoClient, kurentoSessionEventsHandler, session = new KurentoSession(sessionId, sessionProperties, kurentoClient, kurentoSessionEventsHandler,
kcProvider.destroyWhenUnused(), this.CDR, this.openviduConfig); kcProvider.destroyWhenUnused(), this.CDR, this.openviduConfig);
@ -482,7 +500,7 @@ public class KurentoSessionManager extends SessionManager {
} }
@Override @Override
public MediaOptions generateMediaOptions(Request<JsonObject> request) { public KurentoMediaOptions generateMediaOptions(Request<JsonObject> request) {
String sdpOffer = RpcHandler.getStringParam(request, ProtocolElements.PUBLISHVIDEO_SDPOFFER_PARAM); String sdpOffer = RpcHandler.getStringParam(request, ProtocolElements.PUBLISHVIDEO_SDPOFFER_PARAM);
boolean hasAudio = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_HASAUDIO_PARAM); boolean hasAudio = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_HASAUDIO_PARAM);
@ -491,6 +509,7 @@ public class KurentoSessionManager extends SessionManager {
Boolean audioActive = null, videoActive = null; Boolean audioActive = null, videoActive = null;
String typeOfVideo = null, videoDimensions = null; String typeOfVideo = null, videoDimensions = null;
Integer frameRate = null; Integer frameRate = null;
KurentoFilter kurentoFilter = null;
try { try {
audioActive = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_AUDIOACTIVE_PARAM); audioActive = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_AUDIOACTIVE_PARAM);
@ -512,11 +531,18 @@ public class KurentoSessionManager extends SessionManager {
frameRate = RpcHandler.getIntParam(request, ProtocolElements.PUBLISHVIDEO_FRAMERATE_PARAM); frameRate = RpcHandler.getIntParam(request, ProtocolElements.PUBLISHVIDEO_FRAMERATE_PARAM);
} catch (RuntimeException noParameterFound) { } catch (RuntimeException noParameterFound) {
} }
try {
JsonObject kurentoFilterJson = (JsonObject) RpcHandler.getParam(request,
ProtocolElements.PUBLISHVIDEO_KURENTOFILTER_PARAM);
kurentoFilter = new KurentoFilter(kurentoFilterJson.get("type").getAsString(),
kurentoFilterJson.get("options").getAsJsonObject());
} catch (RuntimeException noParameterFound) {
}
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, hasAudio, hasVideo, audioActive, videoActive, return new KurentoMediaOptions(true, sdpOffer, null, null, hasAudio, hasVideo, audioActive, videoActive,
typeOfVideo, frameRate, videoDimensions, doLoopback); typeOfVideo, frameRate, videoDimensions, kurentoFilter, doLoopback);
} }
@Override @Override
@ -536,4 +562,167 @@ public class KurentoSessionManager extends SessionManager {
} }
} }
@Override
public void applyFilter(Session session, String streamId, String filterType, JsonObject filterOptions,
Participant moderator, Integer transactionId, String reason) {
String participantPrivateId = ((KurentoSession) session).getParticipantPrivateIdFromStreamId(streamId);
if (participantPrivateId != null) {
Participant participant = this.getParticipant(participantPrivateId);
log.debug("Request [APPLY_FILTER] over stream [{}] for reason [{}]", streamId, reason);
KurentoParticipant kParticipant = (KurentoParticipant) participant;
if (!participant.isStreaming()) {
log.warn(
"PARTICIPANT {}: Requesting to applyFilter to user {} "
+ "in session {} but user is not streaming media",
moderator != null ? moderator.getParticipantPublicId() : participant.getParticipantPublicId(),
participant.getParticipantPublicId(), session.getSessionId());
throw new OpenViduException(Code.USER_NOT_STREAMING_ERROR_CODE,
"User '" + participant.getParticipantPublicId() + " not streaming media in session '"
+ session.getSessionId() + "'");
} else if (kParticipant.getPublisher().getFilter() != null) {
log.warn(
"PARTICIPANT {}: Requesting to applyFilter to user {} "
+ "in session {} but user already has a filter",
moderator != null ? moderator.getParticipantPublicId() : participant.getParticipantPublicId(),
participant.getParticipantPublicId(), session.getSessionId());
throw new OpenViduException(Code.EXISTING_FILTER_ALREADY_APPLIED_ERROR_CODE,
"User '" + participant.getParticipantPublicId() + " already has a filter applied in session '"
+ session.getSessionId() + "'");
} else {
try {
KurentoFilter filter = new KurentoFilter(filterType, filterOptions);
this.applyFilterInPublisher(kParticipant, filter);
Set<Participant> participants = kParticipant.getSession().getParticipants();
sessionEventsHandler.onFilterChanged(participant, moderator, transactionId, participants, streamId,
filter, null, reason);
} catch (OpenViduException e) {
log.warn("PARTICIPANT {}: Error applying filter", participant.getParticipantPublicId(), e);
sessionEventsHandler.onFilterChanged(participant, moderator, transactionId, new HashSet<>(),
streamId, null, e, "");
}
}
} else {
log.warn("PARTICIPANT {}: Requesting to applyFilter to stream {} "
+ "in session {} but the owner cannot be found", streamId, session.getSessionId());
throw new OpenViduException(Code.USER_NOT_FOUND_ERROR_CODE,
"Owner of stream '" + streamId + "' not found in session '" + session.getSessionId() + "'");
}
}
@Override
public void execFilterMethod(Session session, String streamId, String filterMethod, JsonObject filterParams,
Participant moderator, Integer transactionId, String reason) {
String participantPrivateId = ((KurentoSession) session).getParticipantPrivateIdFromStreamId(streamId);
if (participantPrivateId != null) {
Participant participant = this.getParticipant(participantPrivateId);
log.debug("Request [EXEC_FILTER_MTEHOD] over stream [{}] for reason [{}]", streamId, reason);
KurentoParticipant kParticipant = (KurentoParticipant) participant;
if (!participant.isStreaming()) {
log.warn(
"PARTICIPANT {}: Requesting to execFilterMethod to user {} "
+ "in session {} but user is not streaming media",
moderator != null ? moderator.getParticipantPublicId() : participant.getParticipantPublicId(),
participant.getParticipantPublicId(), session.getSessionId());
throw new OpenViduException(Code.USER_NOT_STREAMING_ERROR_CODE,
"User '" + participant.getParticipantPublicId() + " not streaming media in session '"
+ session.getSessionId() + "'");
} else if (kParticipant.getPublisher().getFilter() == null) {
log.warn(
"PARTICIPANT {}: Requesting to execFilterMethod to user {} "
+ "in session {} but user does NOT have a filter",
moderator != null ? moderator.getParticipantPublicId() : participant.getParticipantPublicId(),
participant.getParticipantPublicId(), session.getSessionId());
throw new OpenViduException(Code.FILTER_NOT_APPLIED_ERROR_CODE,
"User '" + participant.getParticipantPublicId() + " has no filter applied in session '"
+ session.getSessionId() + "'");
} else {
KurentoFilter updatedFilter = this.execFilterMethodInPublisher(kParticipant, filterMethod,
filterParams);
Set<Participant> participants = kParticipant.getSession().getParticipants();
sessionEventsHandler.onFilterChanged(participant, moderator, transactionId, participants, streamId,
updatedFilter, null, reason);
}
} else {
log.warn("PARTICIPANT {}: Requesting to removeFilter to stream {} "
+ "in session {} but the owner cannot be found", streamId, session.getSessionId());
throw new OpenViduException(Code.USER_NOT_FOUND_ERROR_CODE,
"Owner of stream '" + streamId + "' not found in session '" + session.getSessionId() + "'");
}
}
@Override
public void removeFilter(Session session, String streamId, Participant moderator, Integer transactionId,
String reason) {
String participantPrivateId = ((KurentoSession) session).getParticipantPrivateIdFromStreamId(streamId);
if (participantPrivateId != null) {
Participant participant = this.getParticipant(participantPrivateId);
log.debug("Request [REMOVE_FILTER] over stream [{}] for reason [{}]", streamId, reason);
KurentoParticipant kParticipant = (KurentoParticipant) participant;
if (!participant.isStreaming()) {
log.warn(
"PARTICIPANT {}: Requesting to removeFilter to user {} "
+ "in session {} but user is not streaming media",
moderator != null ? moderator.getParticipantPublicId() : participant.getParticipantPublicId(),
participant.getParticipantPublicId(), session.getSessionId());
throw new OpenViduException(Code.USER_NOT_STREAMING_ERROR_CODE,
"User '" + participant.getParticipantPublicId() + " not streaming media in session '"
+ session.getSessionId() + "'");
} else if (kParticipant.getPublisher().getFilter() == null) {
log.warn(
"PARTICIPANT {}: Requesting to removeFilter to user {} "
+ "in session {} but user does NOT have a filter",
moderator != null ? moderator.getParticipantPublicId() : participant.getParticipantPublicId(),
participant.getParticipantPublicId(), session.getSessionId());
throw new OpenViduException(Code.FILTER_NOT_APPLIED_ERROR_CODE,
"User '" + participant.getParticipantPublicId() + " has no filter applied in session '"
+ session.getSessionId() + "'");
} else {
this.removeFilterInPublisher(kParticipant);
Set<Participant> participants = kParticipant.getSession().getParticipants();
sessionEventsHandler.onFilterChanged(participant, moderator, transactionId, participants, streamId,
null, null, reason);
}
} else {
log.warn("PARTICIPANT {}: Requesting to removeFilter to stream {} "
+ "in session {} but the owner cannot be found", streamId, session.getSessionId());
throw new OpenViduException(Code.USER_NOT_FOUND_ERROR_CODE,
"Owner of stream '" + streamId + "' not found in session '" + session.getSessionId() + "'");
}
}
private void applyFilterInPublisher(KurentoParticipant kParticipant, KurentoFilter filter)
throws OpenViduException {
GenericMediaElement.Builder builder = new GenericMediaElement.Builder(kParticipant.getPipeline(),
filter.getType());
Props props = new JsonUtils().fromJsonObjectToProps(filter.getOptions());
props.forEach(prop -> {
builder.withConstructorParam(prop.getName(), prop.getValue());
});
kParticipant.getPublisher().apply(builder.build());
kParticipant.getPublisher().getMediaOptions().setFilter(filter);
}
private KurentoFilter execFilterMethodInPublisher(KurentoParticipant kParticipant, String method,
JsonObject params) {
kParticipant.getPublisher().execMethod(method, params);
KurentoFilter filter = kParticipant.getPublisher().getMediaOptions().getFilter();
KurentoFilter updatedFilter = new KurentoFilter(filter.getType(), filter.getOptions(), method, params);
kParticipant.getPublisher().getMediaOptions().setFilter(updatedFilter);
return filter;
}
private void removeFilterInPublisher(KurentoParticipant kParticipant) {
kParticipant.getPublisher().revert(kParticipant.getPublisher().getFilter());
kParticipant.getPublisher().getMediaOptions().setFilter(null);
}
/*
* private void addFilterEventListenerInPublisher(KurentoParticipant
* kParticipant) { this.listener =
* kParticipant.getPublisher().getFilter().addEventListener("CodeFound", event
* -> { System.out.println(event.getData()); }); } private void
* removeFilterEventListenerInPublisher(KurentoParticipant kParticipant) {
* kParticipant.getPublisher().getFilter().removeEventListener(this.listener); }
*/
} }

View File

@ -0,0 +1,82 @@
/*
* (C) Copyright 2017-2018 OpenVidu (https://openvidu.io/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.openvidu.server.kurento.core;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
public class KurentoTokenOptions {
private Integer videoMaxRecvBandwidth;
private Integer videoMinRecvBandwidth;
private Integer videoMaxSendBandwidth;
private Integer videoMinSendBandwidth;
private Map<String, Boolean> allowedFilters = new ConcurrentHashMap<>();
public KurentoTokenOptions(JsonObject options) {
if (options.has("videoMaxRecvBandwidth")) {
this.videoMaxRecvBandwidth = options.get("videoMaxRecvBandwidth").getAsInt();
}
if (options.has("videoMinRecvBandwidth")) {
this.videoMinRecvBandwidth = options.get("videoMinRecvBandwidth").getAsInt();
}
if (options.has("videoMaxSendBandwidth")) {
this.videoMaxSendBandwidth = options.get("videoMaxSendBandwidth").getAsInt();
}
if (options.has("videoMinSendBandwidth")) {
this.videoMinSendBandwidth = options.get("videoMinSendBandwidth").getAsInt();
}
if (options.has("allowedFilters")) {
JsonArray filters = options.get("allowedFilters").getAsJsonArray();
Iterator<JsonElement> it = filters.iterator();
while (it.hasNext()) {
this.allowedFilters.put(it.next().getAsString(), true);
}
}
}
public Integer getVideoMaxRecvBandwidth() {
return videoMaxRecvBandwidth;
}
public Integer getVideoMinRecvBandwidth() {
return videoMinRecvBandwidth;
}
public Integer getVideoMaxSendBandwidth() {
return videoMaxSendBandwidth;
}
public Integer getVideoMinSendBandwidth() {
return videoMinSendBandwidth;
}
public String[] getAllowedFilters() {
return allowedFilters.keySet().stream().toArray(String[]::new);
}
public boolean isFilterAllowed(String filterType) {
return this.allowedFilters.containsKey(filterType);
}
}

View File

@ -25,8 +25,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.kurento.client.Continuation; import org.kurento.client.Continuation;
import org.kurento.client.ErrorEvent; import org.kurento.client.ErrorEvent;
import org.kurento.client.EventListener; import org.kurento.client.EventListener;
@ -42,11 +40,16 @@ 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.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
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.server.config.OpenviduConfig; import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.core.Participant; import io.openvidu.server.core.Participant;
import io.openvidu.server.kurento.core.KurentoParticipant; import io.openvidu.server.kurento.core.KurentoParticipant;
import io.openvidu.server.kurento.core.KurentoTokenOptions;
/** /**
* {@link WebRtcEndpoint} wrapper that supports buffering of * {@link WebRtcEndpoint} wrapper that supports buffering of
@ -108,10 +111,27 @@ public abstract class MediaEndpoint {
this.setMediaPipeline(pipeline); this.setMediaPipeline(pipeline);
this.openviduConfig = openviduConfig; this.openviduConfig = openviduConfig;
this.maxRecvKbps = this.openviduConfig.getVideoMaxRecvBandwidth();
this.minRecvKbps = this.openviduConfig.getVideoMinRecvBandwidth(); KurentoTokenOptions kurentoTokenOptions = this.owner.getToken().getKurentoTokenOptions();
this.maxSendKbps = this.openviduConfig.getVideoMaxSendBandwidth(); if (kurentoTokenOptions != null) {
this.minSendKbps = this.openviduConfig.getVideoMinSendBandwidth(); this.maxRecvKbps = kurentoTokenOptions.getVideoMaxRecvBandwidth() != null
? kurentoTokenOptions.getVideoMaxRecvBandwidth()
: this.openviduConfig.getVideoMaxRecvBandwidth();
this.minRecvKbps = kurentoTokenOptions.getVideoMinRecvBandwidth() != null
? kurentoTokenOptions.getVideoMinRecvBandwidth()
: this.openviduConfig.getVideoMinRecvBandwidth();
this.maxSendKbps = kurentoTokenOptions.getVideoMaxSendBandwidth() != null
? kurentoTokenOptions.getVideoMaxSendBandwidth()
: this.openviduConfig.getVideoMaxSendBandwidth();
this.minSendKbps = kurentoTokenOptions.getVideoMinSendBandwidth() != null
? kurentoTokenOptions.getVideoMinSendBandwidth()
: this.openviduConfig.getVideoMinSendBandwidth();
} else {
this.maxRecvKbps = this.openviduConfig.getVideoMaxRecvBandwidth();
this.minRecvKbps = this.openviduConfig.getVideoMinRecvBandwidth();
this.maxSendKbps = this.openviduConfig.getVideoMaxSendBandwidth();
this.minSendKbps = this.openviduConfig.getVideoMinSendBandwidth();
}
} }
public boolean isWeb() { public boolean isWeb() {
@ -464,28 +484,26 @@ public abstract class MediaEndpoint {
public abstract PublisherEndpoint getPublisher(); public abstract PublisherEndpoint getPublisher();
public JSONObject toJSON() { public JsonObject toJson() {
JSONObject json = new JSONObject(); JsonObject json = new JsonObject();
return json; return json;
} }
@SuppressWarnings("unchecked") public JsonObject withStatsToJson() {
public JSONObject withStatsToJSON() { JsonObject json = new JsonObject();
JSONObject json = new JSONObject(); json.addProperty("webrtcTagName", this.getEndpoint().getTag("name"));
json.put("webrtcTagName", this.getEndpoint().getTag("name")); json.add("receivedCandidates", new GsonBuilder().create().toJsonTree(this.receivedCandidateList));
json.put("receivedCandidates", this.receivedCandidateList); json.addProperty("localCandidate", this.selectedLocalIceCandidate);
json.put("localCandidate", this.selectedLocalIceCandidate); json.addProperty("remoteCandidate", this.selectedRemoteIceCandidate);
json.put("remoteCandidate", this.selectedRemoteIceCandidate);
JSONArray jsonArray = new JSONArray();
JsonArray jsonArray = new JsonArray();
for (KmsEvent event : this.kmsEvents) { for (KmsEvent event : this.kmsEvents) {
JSONObject jsonKmsEvent = new JSONObject(); JsonObject jsonKmsEvent = new JsonObject();
jsonKmsEvent.put(event.event.getType(), event.timestamp); jsonKmsEvent.addProperty(event.event.getType(), event.timestamp);
jsonArray.add(jsonKmsEvent); jsonArray.add(jsonKmsEvent);
} }
json.add("events", jsonArray);
json.put("events", jsonArray);
return json; return json;
} }
} }

View File

@ -21,25 +21,31 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import org.json.simple.JSONObject;
import org.kurento.client.Continuation; import org.kurento.client.Continuation;
import org.kurento.client.GenericMediaElement;
import org.kurento.client.ListenerSubscription; import org.kurento.client.ListenerSubscription;
import org.kurento.client.MediaElement; import org.kurento.client.MediaElement;
import org.kurento.client.MediaPipeline; import org.kurento.client.MediaPipeline;
import org.kurento.client.MediaType; import org.kurento.client.MediaType;
import org.kurento.client.PassThrough; import org.kurento.client.PassThrough;
import org.kurento.client.WebRtcEndpoint; import org.kurento.client.WebRtcEndpoint;
import org.kurento.jsonrpc.Props;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
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.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.kurento.TrackType; import io.openvidu.server.kurento.TrackType;
import io.openvidu.server.kurento.core.KurentoParticipant; import io.openvidu.server.kurento.core.KurentoParticipant;
import io.openvidu.server.utils.JsonUtils;
/** /**
* Publisher aspect of the {@link MediaEndpoint}. * Publisher aspect of the {@link MediaEndpoint}.
@ -55,13 +61,15 @@ public class PublisherEndpoint extends MediaEndpoint {
private PassThrough passThru = null; private PassThrough passThru = null;
private ListenerSubscription passThruSubscription = null; private ListenerSubscription passThruSubscription = null;
private GenericMediaElement filter;
private Map<String, MediaElement> elements = new HashMap<String, MediaElement>(); private Map<String, MediaElement> elements = new HashMap<String, MediaElement>();
private LinkedList<String> elementIds = new LinkedList<String>(); private LinkedList<String> elementIds = new LinkedList<String>();
private boolean connected = false; private boolean connected = false;
private Map<String, ListenerSubscription> elementsErrorSubscriptions = new HashMap<String, ListenerSubscription>(); private Map<String, ListenerSubscription> elementsErrorSubscriptions = new HashMap<String, ListenerSubscription>();
public PublisherEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline, OpenviduConfig openviduConfig) { public PublisherEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline,
OpenviduConfig openviduConfig) {
super(web, owner, endpointName, pipeline, openviduConfig, log); super(web, owner, endpointName, pipeline, openviduConfig, log);
} }
@ -92,6 +100,10 @@ public class PublisherEndpoint extends MediaEndpoint {
return elements.values(); return elements.values();
} }
public GenericMediaElement getFilter() {
return this.filter;
}
/** /**
* Initializes this media endpoint for publishing media and processes the SDP * Initializes this media endpoint for publishing media and processes the SDP
* offer or answer. If the internal endpoint is an {@link WebRtcEndpoint}, it * offer or answer. If the internal endpoint is an {@link WebRtcEndpoint}, it
@ -180,7 +192,7 @@ public class PublisherEndpoint extends MediaEndpoint {
* @throws OpenViduException * @throws OpenViduException
* if thrown, the media element was not added * if thrown, the media element was not added
*/ */
public String apply(MediaElement shaper) throws OpenViduException { public String apply(GenericMediaElement shaper) throws OpenViduException {
return apply(shaper, null); return apply(shaper, null);
} }
@ -198,7 +210,7 @@ public class PublisherEndpoint extends MediaEndpoint {
* @throws OpenViduException * @throws OpenViduException
* if thrown, the media element was not added * if thrown, the media element was not added
*/ */
public synchronized String apply(MediaElement shaper, MediaType type) throws OpenViduException { public synchronized String apply(GenericMediaElement shaper, MediaType type) throws OpenViduException {
String id = shaper.getId(); String id = shaper.getId();
if (id == null) { if (id == null) {
throw new OpenViduException(Code.MEDIA_WEBRTC_ENDPOINT_ERROR_CODE, throw new OpenViduException(Code.MEDIA_WEBRTC_ENDPOINT_ERROR_CODE,
@ -222,6 +234,9 @@ public class PublisherEndpoint extends MediaEndpoint {
} }
elementIds.addFirst(id); elementIds.addFirst(id);
elements.put(id, shaper); elements.put(id, shaper);
this.filter = shaper;
elementsErrorSubscriptions.put(id, registerElemErrListener(shaper)); elementsErrorSubscriptions.put(id, registerElemErrListener(shaper));
return id; return id;
} }
@ -283,6 +298,12 @@ public class PublisherEndpoint extends MediaEndpoint {
} }
}); });
} }
this.filter = null;
}
public JsonElement execMethod(String method, JsonObject params) throws OpenViduException {
Props props = new JsonUtils().fromJsonObjectToProps(params);
return (JsonElement) ((GenericMediaElement) this.filter).invoke(method, props);
} }
public synchronized void mute(TrackType muteType) { public synchronized void mute(TrackType muteType) {
@ -488,22 +509,20 @@ public class PublisherEndpoint extends MediaEndpoint {
this.mediaOptions = mediaOptions; this.mediaOptions = mediaOptions;
} }
@SuppressWarnings("unchecked")
@Override @Override
public JSONObject toJSON() { public JsonObject toJson() {
JSONObject json = super.toJSON(); JsonObject json = super.toJson();
json.put("streamId", this.getEndpoint().getTag("name")); json.addProperty("streamId", this.getEndpoint().getTag("name"));
json.put("mediaOptions", this.mediaOptions.toJSON()); json.add("mediaOptions", this.mediaOptions.toJson());
return json; return json;
} }
@SuppressWarnings("unchecked")
@Override @Override
public JSONObject withStatsToJSON() { public JsonObject withStatsToJson() {
JSONObject json = super.withStatsToJSON(); JsonObject json = super.withStatsToJson();
JSONObject toJSON = this.toJSON(); JsonObject toJson = this.toJson();
for (Object key : toJSON.keySet()) { for (Entry<String, JsonElement> entry : toJson.entrySet()) {
json.put(key, toJSON.get(key)); json.add(entry.getKey(), entry.getValue());
} }
return json; return json;
} }

View File

@ -17,11 +17,15 @@
package io.openvidu.server.kurento.endpoint; package io.openvidu.server.kurento.endpoint;
import org.json.simple.JSONObject; import java.util.Map.Entry;
import org.kurento.client.MediaPipeline; import org.kurento.client.MediaPipeline;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.kurento.core.KurentoParticipant; import io.openvidu.server.kurento.core.KurentoParticipant;
@ -37,7 +41,8 @@ public class SubscriberEndpoint extends MediaEndpoint {
private PublisherEndpoint publisher = null; private PublisherEndpoint publisher = null;
public SubscriberEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline, OpenviduConfig openviduConfig) { public SubscriberEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline,
OpenviduConfig openviduConfig) {
super(web, owner, endpointName, pipeline, openviduConfig, log); super(web, owner, endpointName, pipeline, openviduConfig, log);
} }
@ -68,22 +73,20 @@ public class SubscriberEndpoint extends MediaEndpoint {
this.publisher = publisher; this.publisher = publisher;
} }
@SuppressWarnings("unchecked")
@Override @Override
public JSONObject toJSON() { public JsonObject toJson() {
JSONObject json = super.toJSON(); JsonObject json = super.toJson();
json.put("streamId", this.publisher.getEndpoint().getTag("name")); json.addProperty("streamId", this.publisher.getEndpoint().getTag("name"));
json.put("publisher", this.publisher.getEndpointName()); json.addProperty("publisher", this.publisher.getEndpointName());
return json; return json;
} }
@SuppressWarnings("unchecked")
@Override @Override
public JSONObject withStatsToJSON() { public JsonObject withStatsToJson() {
JSONObject json = super.withStatsToJSON(); JsonObject json = super.withStatsToJson();
JSONObject toJSON = this.toJSON(); JsonObject toJson = this.toJson();
for (Object key : toJSON.keySet()) { for (Entry<String, JsonElement> entry : toJson.entrySet()) {
json.put(key, toJSON.get(key)); json.add(entry.getKey(), entry.getValue());
} }
return json; return json;
} }

View File

@ -37,9 +37,6 @@ import java.util.stream.Collectors;
import javax.ws.rs.ProcessingException; import javax.ws.rs.ProcessingException;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -61,6 +58,8 @@ import com.github.dockerjava.core.DockerClientBuilder;
import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.command.ExecStartResultCallback; import com.github.dockerjava.core.command.ExecStartResultCallback;
import com.github.dockerjava.core.command.PullImageResultCallback; import com.github.dockerjava.core.command.PullImageResultCallback;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException;
import io.openvidu.client.OpenViduException.Code; import io.openvidu.client.OpenViduException.Code;
@ -137,9 +136,9 @@ public class ComposedRecordingService {
envs.add("VIDEO_NAME=" + properties.name()); envs.add("VIDEO_NAME=" + properties.name());
envs.add("VIDEO_FORMAT=mp4"); envs.add("VIDEO_FORMAT=mp4");
envs.add("USER_ID=" + uid); envs.add("USER_ID=" + uid);
envs.add("RECORDING_JSON=" + recording.toJson().toJSONString()); envs.add("RECORDING_JSON=" + recording.toJson().toString());
log.info(recording.toJson().toJSONString()); log.info(recording.toJson().toString());
log.info("Recorder connecting to url {}", layoutUrl); log.info("Recorder connecting to url {}", layoutUrl);
String containerId = this.runRecordingContainer(envs, "recording_" + recordingId); String containerId = this.runRecordingContainer(envs, "recording_" + recordingId);
@ -212,7 +211,7 @@ public class ComposedRecordingService {
recording.setUrl(this.openviduConfig.getFinalUrl() + "recordings/" + recording.getName() + ".mp4"); recording.setUrl(this.openviduConfig.getFinalUrl() + "recordings/" + recording.getName() + ".mp4");
} }
} catch (IOException | ParseException e) { } catch (IOException e) {
throw new OpenViduException(Code.RECORDING_REPORT_ERROR_CODE, throw new OpenViduException(Code.RECORDING_REPORT_ERROR_CODE,
"There was an error generating the metadata report file for the recording"); "There was an error generating the metadata report file for the recording");
} }
@ -396,7 +395,7 @@ public class ComposedRecordingService {
// Cannot delete an active recording // Cannot delete an active recording
return HttpStatus.CONFLICT; return HttpStatus.CONFLICT;
} }
Recording recording = getRecordingFromHost(recordingId); Recording recording = getRecordingFromHost(recordingId);
if (recording == null) { if (recording == null) {
return HttpStatus.NOT_FOUND; return HttpStatus.NOT_FOUND;
@ -417,11 +416,10 @@ public class ComposedRecordingService {
private Recording getRecordingFromEntityFile(File file) { private Recording getRecordingFromEntityFile(File file) {
if (file.isFile() && file.getName().startsWith(RECORDING_ENTITY_FILE)) { if (file.isFile() && file.getName().startsWith(RECORDING_ENTITY_FILE)) {
JSONParser parser = new JSONParser(); JsonObject json = null;
JSONObject json = null;
try { try {
json = (JSONObject) parser.parse(new FileReader(file)); json = new JsonParser().parse(new FileReader(file)).getAsJsonObject();
} catch (IOException | ParseException e) { } catch (IOException e) {
return null; return null;
} }
return new Recording(json); return new Recording(json);

View File

@ -17,7 +17,7 @@
package io.openvidu.server.recording; package io.openvidu.server.recording;
import org.json.simple.JSONObject; import com.google.gson.JsonObject;
import io.openvidu.java.client.RecordingLayout; import io.openvidu.java.client.RecordingLayout;
import io.openvidu.java.client.RecordingProperties; import io.openvidu.java.client.RecordingProperties;
@ -53,22 +53,26 @@ public class Recording {
this.recordingProperties = recordingProperties; this.recordingProperties = recordingProperties;
} }
public Recording(JSONObject json) { public Recording(JsonObject json) {
this.id = (String) json.get("id"); this.id = json.get("id").getAsString();
this.sessionId = (String) json.get("sessionId"); this.sessionId = json.get("sessionId").getAsString();
this.createdAt = (long) json.get("createdAt"); this.createdAt = json.get("createdAt").getAsLong();
this.size = (long) json.get("size"); this.size = json.get("size").getAsLong();
try { try {
this.duration = (double) json.get("duration"); this.duration = json.get("duration").getAsDouble();
} catch (Exception e) { } catch (Exception e) {
this.duration = new Long((long) json.get("duration")).doubleValue(); this.duration = new Long((long) json.get("duration").getAsLong()).doubleValue();
} }
this.url = (String) json.get("url"); if (json.get("url").isJsonNull()) {
this.hasAudio = (boolean) json.get("hasAudio"); this.url = null;
this.hasVideo = (boolean) json.get("hasVideo"); } else {
this.status = Status.valueOf((String) json.get("status")); this.url = json.get("url").getAsString();
this.recordingProperties = new RecordingProperties.Builder().name((String) json.get("name")) }
.recordingLayout(RecordingLayout.valueOf((String) json.get("recordingLayout"))).build(); this.hasAudio = json.get("hasAudio").getAsBoolean();
this.hasVideo = json.get("hasVideo").getAsBoolean();
this.status = Status.valueOf(json.get("status").getAsString());
this.recordingProperties = new RecordingProperties.Builder().name(json.get("name").getAsString())
.recordingLayout(RecordingLayout.valueOf(json.get("recordingLayout").getAsString())).build();
} }
public Status getStatus() { public Status getStatus() {
@ -155,23 +159,22 @@ public class Recording {
this.hasVideo = hasVideo; this.hasVideo = hasVideo;
} }
@SuppressWarnings("unchecked") public JsonObject toJson() {
public JSONObject toJson() { JsonObject json = new JsonObject();
JSONObject json = new JSONObject(); json.addProperty("id", this.id);
json.put("id", this.id); json.addProperty("name", this.recordingProperties.name());
json.put("name", this.recordingProperties.name()); json.addProperty("recordingLayout", this.recordingProperties.recordingLayout().name());
json.put("recordingLayout", this.recordingProperties.recordingLayout().name());
if (RecordingLayout.CUSTOM.equals(this.recordingProperties.recordingLayout())) { if (RecordingLayout.CUSTOM.equals(this.recordingProperties.recordingLayout())) {
json.put("customLayout", this.recordingProperties.customLayout()); json.addProperty("customLayout", this.recordingProperties.customLayout());
} }
json.put("sessionId", this.sessionId); json.addProperty("sessionId", this.sessionId);
json.put("createdAt", this.createdAt); json.addProperty("createdAt", this.createdAt);
json.put("size", this.size); json.addProperty("size", this.size);
json.put("duration", this.duration); json.addProperty("duration", this.duration);
json.put("url", this.url); json.addProperty("url", this.url);
json.put("hasAudio", this.hasAudio); json.addProperty("hasAudio", this.hasAudio);
json.put("hasVideo", this.hasVideo); json.addProperty("hasVideo", this.hasVideo);
json.put("status", this.status.toString()); json.addProperty("status", this.status.toString());
return json; return json;
} }

View File

@ -21,39 +21,45 @@ import java.io.FileNotFoundException;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import org.json.simple.JSONArray; import com.google.gson.JsonArray;
import org.json.simple.JSONObject; import com.google.gson.JsonIOException;
import org.json.simple.parser.JSONParser; import com.google.gson.JsonObject;
import org.json.simple.parser.ParseException; import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException;
import io.openvidu.client.OpenViduException.Code; import io.openvidu.client.OpenViduException.Code;
public class RecordingInfoUtils { public class RecordingInfoUtils {
private JSONParser parser; private JsonParser parser;
private JSONObject json; private JsonObject json;
private JSONObject jsonFormat; private JsonObject jsonFormat;
private JSONObject videoStream; private JsonObject videoStream;
private JSONObject audioStream; private JsonObject audioStream;
public RecordingInfoUtils(String fullVideoPath) public RecordingInfoUtils(String fullVideoPath) throws FileNotFoundException, IOException, OpenViduException {
throws FileNotFoundException, IOException, ParseException, OpenViduException {
this.parser = new JSONParser(); this.parser = new JsonParser();
this.json = (JSONObject) parser.parse(new FileReader(fullVideoPath));
if (json.isEmpty()) { try {
// Recording metadata from ffprobe is empty: video file is corrupted or empty this.json = parser.parse(new FileReader(fullVideoPath)).getAsJsonObject();
throw new OpenViduException(Code.RECORDING_FILE_EMPTY_ERROR, "The recording file is empty or corrupted"); } catch (JsonIOException | JsonSyntaxException e) {
// Recording metadata from ffprobe is not a JSON: video file is corrupted
throw new OpenViduException(Code.RECORDING_FILE_EMPTY_ERROR, "The recording file is corrupted");
} }
this.jsonFormat = (JSONObject) json.get("format"); if (this.json.size() == 0) {
// Recording metadata from ffprobe is an emtpy JSON
throw new OpenViduException(Code.RECORDING_FILE_EMPTY_ERROR, "The recording file is empty");
}
JSONArray streams = (JSONArray) json.get("streams"); this.jsonFormat = json.get("format").getAsJsonObject();
JsonArray streams = json.get("streams").getAsJsonArray();
for (int i = 0; i < streams.size(); i++) { for (int i = 0; i < streams.size(); i++) {
JSONObject stream = (JSONObject) streams.get(i); JsonObject stream = streams.get(i).getAsJsonObject();
if ("video".equals(stream.get("codec_type").toString())) { if ("video".equals(stream.get("codec_type").toString())) {
this.videoStream = stream; this.videoStream = stream;
} else if ("audio".equals(stream.get("codec_type").toString())) { } else if ("audio".equals(stream.get("codec_type").toString())) {
@ -64,19 +70,19 @@ public class RecordingInfoUtils {
} }
public double getDurationInSeconds() { public double getDurationInSeconds() {
return Double.parseDouble(jsonFormat.get("duration").toString()); return jsonFormat.get("duration").getAsDouble();
} }
public int getSizeInBytes() { public int getSizeInBytes() {
return Integer.parseInt(jsonFormat.get("size").toString()); return jsonFormat.get("size").getAsInt();
} }
public int getNumberOfStreams() { public int getNumberOfStreams() {
return Integer.parseInt(jsonFormat.get("nb_streams").toString()); return jsonFormat.get("nb_streams").getAsInt();
} }
public int getBitRate() { public int getBitRate() {
return (Integer.parseInt(jsonFormat.get("bit_rate").toString()) / 1000); return ((jsonFormat.get("bit_rate").getAsInt()) / 1000);
} }
public boolean hasVideo() { public boolean hasVideo() {
@ -88,11 +94,11 @@ public class RecordingInfoUtils {
} }
public int videoWidth() { public int videoWidth() {
return Integer.parseInt(videoStream.get("width").toString()); return videoStream.get("width").getAsInt();
} }
public int videoHeight() { public int videoHeight() {
return Integer.parseInt(videoStream.get("height").toString()); return videoStream.get("height").getAsInt();
} }
public int getVideoFramerate() { public int getVideoFramerate() {

View File

@ -22,10 +22,10 @@ import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
@ -35,6 +35,10 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException;
import io.openvidu.client.internal.ProtocolElements; import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.java.client.MediaMode; import io.openvidu.java.client.MediaMode;
@ -47,6 +51,7 @@ import io.openvidu.server.core.Participant;
import io.openvidu.server.core.ParticipantRole; import io.openvidu.server.core.ParticipantRole;
import io.openvidu.server.core.Session; import io.openvidu.server.core.Session;
import io.openvidu.server.core.SessionManager; import io.openvidu.server.core.SessionManager;
import io.openvidu.server.kurento.core.KurentoTokenOptions;
import io.openvidu.server.recording.ComposedRecordingService; import io.openvidu.server.recording.ComposedRecordingService;
import io.openvidu.server.recording.Recording; import io.openvidu.server.recording.Recording;
@ -68,9 +73,8 @@ public class SessionRestController {
@Autowired @Autowired
private OpenviduConfig openviduConfig; private OpenviduConfig openviduConfig;
@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<?> getSessionId(@RequestBody(required = false) Map<?, ?> params) {
SessionProperties.Builder builder = new SessionProperties.Builder(); SessionProperties.Builder builder = new SessionProperties.Builder();
String customSessionId = null; String customSessionId = null;
@ -130,45 +134,43 @@ public class SessionRestController {
} }
sessionManager.storeSessionId(sessionId, sessionProperties); sessionManager.storeSessionId(sessionId, sessionProperties);
JSONObject responseJson = new JSONObject(); JsonObject responseJson = new JsonObject();
responseJson.put("id", sessionId); responseJson.addProperty("id", sessionId);
return new ResponseEntity<>(responseJson, HttpStatus.OK); return new ResponseEntity<>(responseJson.toString(), getResponseHeaders(), HttpStatus.OK);
} }
@SuppressWarnings("unchecked")
@RequestMapping(value = "/sessions/{sessionId}", method = RequestMethod.GET) @RequestMapping(value = "/sessions/{sessionId}", method = RequestMethod.GET)
public ResponseEntity<JSONObject> getSession(@PathVariable("sessionId") String sessionId, public ResponseEntity<?> getSession(@PathVariable("sessionId") String sessionId,
@RequestParam(value = "webRtcStats", defaultValue = "false", required = false) boolean webRtcStats) { @RequestParam(value = "webRtcStats", defaultValue = "false", required = false) boolean webRtcStats) {
Session session = this.sessionManager.getSession(sessionId); Session session = this.sessionManager.getSession(sessionId);
if (session != null) { if (session != null) {
JSONObject response = (webRtcStats == true) ? session.withStatsToJSON() : session.toJSON(); JsonObject response = (webRtcStats == true) ? session.withStatsToJson() : session.toJson();
response.put("recording", this.recordingService.sessionIsBeingRecorded(sessionId)); response.addProperty("recording", this.recordingService.sessionIsBeingRecorded(sessionId));
return new ResponseEntity<>(response, HttpStatus.OK); return new ResponseEntity<>(response.toString(), getResponseHeaders(), HttpStatus.OK);
} else { } else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND); return new ResponseEntity<>(HttpStatus.NOT_FOUND);
} }
} }
@SuppressWarnings("unchecked")
@RequestMapping(value = "/sessions", method = RequestMethod.GET) @RequestMapping(value = "/sessions", method = RequestMethod.GET)
public ResponseEntity<JSONObject> listSessions( public ResponseEntity<?> listSessions(
@RequestParam(value = "webRtcStats", defaultValue = "false", required = false) boolean webRtcStats) { @RequestParam(value = "webRtcStats", defaultValue = "false", required = false) boolean webRtcStats) {
Collection<Session> sessions = this.sessionManager.getSessionObjects(); Collection<Session> sessions = this.sessionManager.getSessionObjects();
JSONObject json = new JSONObject(); JsonObject json = new JsonObject();
JSONArray jsonArray = new JSONArray(); JsonArray jsonArray = new JsonArray();
sessions.forEach(s -> { sessions.forEach(s -> {
JSONObject sessionJson = (webRtcStats == true) ? s.withStatsToJSON() : s.toJSON(); JsonObject sessionJson = (webRtcStats == true) ? s.withStatsToJson() : s.toJson();
sessionJson.put("recording", this.recordingService.sessionIsBeingRecorded(s.getSessionId())); sessionJson.addProperty("recording", this.recordingService.sessionIsBeingRecorded(s.getSessionId()));
jsonArray.add(sessionJson); jsonArray.add(sessionJson);
}); });
json.put("numberOfElements", sessions.size()); json.addProperty("numberOfElements", sessions.size());
json.put("content", jsonArray); json.add("content", jsonArray);
return new ResponseEntity<>(json, HttpStatus.OK); return new ResponseEntity<>(json.toString(), getResponseHeaders(), HttpStatus.OK);
} }
@RequestMapping(value = "/sessions/{sessionId}", method = RequestMethod.DELETE) @RequestMapping(value = "/sessions/{sessionId}", method = RequestMethod.DELETE)
public ResponseEntity<JSONObject> closeSession(@PathVariable("sessionId") String sessionId) { public ResponseEntity<?> closeSession(@PathVariable("sessionId") String sessionId) {
Session session = this.sessionManager.getSession(sessionId); Session session = this.sessionManager.getSession(sessionId);
if (session != null) { if (session != null) {
this.sessionManager.closeSession(sessionId, "sessionClosedByServer"); this.sessionManager.closeSession(sessionId, "sessionClosedByServer");
@ -179,7 +181,7 @@ public class SessionRestController {
} }
@RequestMapping(value = "/sessions/{sessionId}/connection/{connectionId}", method = RequestMethod.DELETE) @RequestMapping(value = "/sessions/{sessionId}/connection/{connectionId}", method = RequestMethod.DELETE)
public ResponseEntity<JSONObject> disconnectParticipant(@PathVariable("sessionId") String sessionId, public ResponseEntity<?> disconnectParticipant(@PathVariable("sessionId") String sessionId,
@PathVariable("connectionId") String participantPublicId) { @PathVariable("connectionId") String participantPublicId) {
Session session = this.sessionManager.getSession(sessionId); Session session = this.sessionManager.getSession(sessionId);
if (session != null) { if (session != null) {
@ -196,7 +198,7 @@ public class SessionRestController {
} }
@RequestMapping(value = "/sessions/{sessionId}/stream/{streamId}", method = RequestMethod.DELETE) @RequestMapping(value = "/sessions/{sessionId}/stream/{streamId}", method = RequestMethod.DELETE)
public ResponseEntity<JSONObject> unpublishStream(@PathVariable("sessionId") String sessionId, public ResponseEntity<?> unpublishStream(@PathVariable("sessionId") String sessionId,
@PathVariable("streamId") String streamId) { @PathVariable("streamId") String streamId) {
Session session = this.sessionManager.getSession(sessionId); Session session = this.sessionManager.getSession(sessionId);
if (session != null) { if (session != null) {
@ -210,51 +212,81 @@ public class SessionRestController {
} }
} }
/*
* @RequestMapping(value = "/sessions/{sessionId}/stream/{streamId}", method =
* RequestMethod.PUT) public ResponseEntity<JSONObject>
* muteMedia(@PathVariable("sessionId") String sessionId,
*
* @PathVariable("streamId") String streamId, @RequestBody Map<?, ?> params) { }
*/
@SuppressWarnings("unchecked")
@RequestMapping(value = "/tokens", method = RequestMethod.POST) @RequestMapping(value = "/tokens", method = RequestMethod.POST)
public ResponseEntity<JSONObject> newToken(@RequestBody Map<?, ?> params) { public ResponseEntity<String> newToken(@RequestBody Map<?, ?> params) {
try { try {
String sessionId = (String) params.get("session"); String sessionId = (String) params.get("session");
String roleString = (String) params.get("role"); String roleString = (String) params.get("role");
String metadata = (String) params.get("data"); String metadata = (String) params.get("data");
JsonObject kurentoOptions = new JsonParser().parse(params.get("kurentoOptions").toString()).getAsJsonObject();
ParticipantRole role; ParticipantRole role;
if (roleString != null) { try {
role = ParticipantRole.valueOf(roleString); if (roleString != null) {
} else { role = ParticipantRole.valueOf(roleString);
role = ParticipantRole.PUBLISHER; } else {
role = ParticipantRole.PUBLISHER;
}
} catch (IllegalArgumentException e) {
return this.generateErrorResponse("Role " + params.get("role") + " is not defined", "/api/tokens",
HttpStatus.BAD_REQUEST);
}
KurentoTokenOptions kurentoTokenOptions = null;
if (kurentoOptions != null) {
try {
kurentoTokenOptions = new KurentoTokenOptions(kurentoOptions);
} catch (Exception e) {
return this.generateErrorResponse("Error in some parameter of 'kurentoOptions'", "/api/tokens",
HttpStatus.BAD_REQUEST);
}
} }
metadata = (metadata != null) ? metadata : ""; metadata = (metadata != null) ? metadata : "";
String token = sessionManager.newToken(sessionId, role, metadata); String token = sessionManager.newToken(sessionId, role, metadata, kurentoTokenOptions);
JSONObject responseJson = new JSONObject(); JsonObject responseJson = new JsonObject();
responseJson.put("id", token); responseJson.addProperty("id", token);
responseJson.put("session", sessionId); responseJson.addProperty("session", sessionId);
responseJson.put("role", role.toString()); responseJson.addProperty("role", role.toString());
responseJson.put("data", metadata); responseJson.addProperty("data", metadata);
responseJson.put("token", token); responseJson.addProperty("token", token);
return new ResponseEntity<>(responseJson, HttpStatus.OK);
} catch (IllegalArgumentException e) { if (kurentoOptions != null) {
return this.generateErrorResponse("Role " + params.get("role") + " is not defined", "/api/tokens", JsonObject kurentoOptsResponse = new JsonObject();
HttpStatus.BAD_REQUEST); if (kurentoTokenOptions.getVideoMaxRecvBandwidth() != null) {
kurentoOptsResponse.addProperty("videoMaxRecvBandwidth",
kurentoTokenOptions.getVideoMaxRecvBandwidth());
}
if (kurentoTokenOptions.getVideoMinRecvBandwidth() != null) {
kurentoOptsResponse.addProperty("videoMinRecvBandwidth",
kurentoTokenOptions.getVideoMinRecvBandwidth());
}
if (kurentoTokenOptions.getVideoMaxSendBandwidth() != null) {
kurentoOptsResponse.addProperty("videoMaxSendBandwidth",
kurentoTokenOptions.getVideoMaxSendBandwidth());
}
if (kurentoTokenOptions.getVideoMinSendBandwidth() != null) {
kurentoOptsResponse.addProperty("videoMinSendBandwidth",
kurentoTokenOptions.getVideoMinSendBandwidth());
}
if (kurentoTokenOptions.getAllowedFilters().length > 0) {
JsonArray filters = new JsonArray();
for (String filter : kurentoTokenOptions.getAllowedFilters()) {
filters.add(filter);
}
kurentoOptsResponse.add("allowedFilters", filters);
}
responseJson.add("kurentoOptions", kurentoOptsResponse);
}
return new ResponseEntity<>(responseJson.toString(), getResponseHeaders(), HttpStatus.OK);
} catch (OpenViduException e) { } catch (OpenViduException e) {
return this.generateErrorResponse(e.getMessage(), "/api/tokens", HttpStatus.BAD_REQUEST); return this.generateErrorResponse(e.getMessage(), "/api/tokens", HttpStatus.BAD_REQUEST);
} }
} }
@RequestMapping(value = "/recordings/start", method = RequestMethod.POST) @RequestMapping(value = "/recordings/start", method = RequestMethod.POST)
public ResponseEntity<JSONObject> startRecordingSession(@RequestBody Map<?, ?> params) { public ResponseEntity<?> startRecordingSession(@RequestBody Map<?, ?> params) {
String sessionId = (String) params.get("session"); String sessionId = (String) params.get("session");
String name = (String) params.get("name"); String name = (String) params.get("name");
@ -290,8 +322,8 @@ public class SessionRestController {
RecordingLayout recordingLayout; RecordingLayout recordingLayout;
if (recordingLayoutString == null || recordingLayoutString.isEmpty()) { if (recordingLayoutString == null || recordingLayoutString.isEmpty()) {
// "recordingLayout" parameter not defined. Use global layout from // "recordingLayout" parameter not defined. Use global layout from
// SessionProperties // SessionProperties (it is always configured as it has RecordingLayout.BEST_FIT
// (it is always configured as it has RecordingLayout.BEST_FIT as default value) // as default value)
recordingLayout = session.getSessionProperties().defaultRecordingLayout(); recordingLayout = session.getSessionProperties().defaultRecordingLayout();
} else { } else {
recordingLayout = RecordingLayout.valueOf(recordingLayoutString); recordingLayout = RecordingLayout.valueOf(recordingLayoutString);
@ -301,11 +333,11 @@ public class SessionRestController {
Recording startedRecording = this.recordingService.startRecording(session, new RecordingProperties.Builder() Recording startedRecording = this.recordingService.startRecording(session, new RecordingProperties.Builder()
.name(name).recordingLayout(recordingLayout).customLayout(customLayout).build()); .name(name).recordingLayout(recordingLayout).customLayout(customLayout).build());
return new ResponseEntity<>(startedRecording.toJson(), HttpStatus.OK); return new ResponseEntity<>(startedRecording.toJson().toString(), getResponseHeaders(), HttpStatus.OK);
} }
@RequestMapping(value = "/recordings/stop/{recordingId}", method = RequestMethod.POST) @RequestMapping(value = "/recordings/stop/{recordingId}", method = RequestMethod.POST)
public ResponseEntity<JSONObject> stopRecordingSession(@PathVariable("recordingId") String recordingId) { public ResponseEntity<?> stopRecordingSession(@PathVariable("recordingId") String recordingId) {
if (recordingId == null) { if (recordingId == null) {
// "recordingId" parameter not found // "recordingId" parameter not found
@ -335,11 +367,11 @@ public class SessionRestController {
session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), null, null, session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), null, null,
"EVICT_RECORDER"); "EVICT_RECORDER");
return new ResponseEntity<>(stoppedRecording.toJson(), HttpStatus.OK); return new ResponseEntity<>(stoppedRecording.toJson().toString(), getResponseHeaders(), HttpStatus.OK);
} }
@RequestMapping(value = "/recordings/{recordingId}", method = RequestMethod.GET) @RequestMapping(value = "/recordings/{recordingId}", method = RequestMethod.GET)
public ResponseEntity<JSONObject> getRecording(@PathVariable("recordingId") String recordingId) { public ResponseEntity<?> getRecording(@PathVariable("recordingId") String recordingId) {
try { try {
Recording recording = this.recordingService.getAllRecordings().stream() Recording recording = this.recordingService.getAllRecordings().stream()
.filter(rec -> rec.getId().equals(recordingId)).findFirst().get(); .filter(rec -> rec.getId().equals(recordingId)).findFirst().get();
@ -347,18 +379,17 @@ public class SessionRestController {
&& recordingService.getStartingRecording(recording.getId()) != null) { && recordingService.getStartingRecording(recording.getId()) != null) {
recording.setStatus(Recording.Status.starting); recording.setStatus(Recording.Status.starting);
} }
return new ResponseEntity<>(recording.toJson(), HttpStatus.OK); return new ResponseEntity<>(recording.toJson().toString(), getResponseHeaders(), HttpStatus.OK);
} catch (NoSuchElementException e) { } catch (NoSuchElementException e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND); return new ResponseEntity<>(HttpStatus.NOT_FOUND);
} }
} }
@SuppressWarnings("unchecked")
@RequestMapping(value = "/recordings", method = RequestMethod.GET) @RequestMapping(value = "/recordings", method = RequestMethod.GET)
public ResponseEntity<JSONObject> getAllRecordings() { public ResponseEntity<?> getAllRecordings() {
Collection<Recording> recordings = this.recordingService.getAllRecordings(); Collection<Recording> recordings = this.recordingService.getAllRecordings();
JSONObject json = new JSONObject(); JsonObject json = new JsonObject();
JSONArray jsonArray = new JSONArray(); JsonArray jsonArray = new JsonArray();
recordings.forEach(rec -> { recordings.forEach(rec -> {
if (Recording.Status.started.equals(rec.getStatus()) if (Recording.Status.started.equals(rec.getStatus())
&& recordingService.getStartingRecording(rec.getId()) != null) { && recordingService.getStartingRecording(rec.getId()) != null) {
@ -366,24 +397,29 @@ public class SessionRestController {
} }
jsonArray.add(rec.toJson()); jsonArray.add(rec.toJson());
}); });
json.put("count", recordings.size()); json.addProperty("count", recordings.size());
json.put("items", jsonArray); json.add("items", jsonArray);
return new ResponseEntity<>(json, HttpStatus.OK); return new ResponseEntity<>(json.toString(), getResponseHeaders(), HttpStatus.OK);
} }
@RequestMapping(value = "/recordings/{recordingId}", method = RequestMethod.DELETE) @RequestMapping(value = "/recordings/{recordingId}", method = RequestMethod.DELETE)
public ResponseEntity<JSONObject> deleteRecording(@PathVariable("recordingId") String recordingId) { public ResponseEntity<?> deleteRecording(@PathVariable("recordingId") String recordingId) {
return new ResponseEntity<>(this.recordingService.deleteRecordingFromHost(recordingId)); return new ResponseEntity<>(this.recordingService.deleteRecordingFromHost(recordingId));
} }
@SuppressWarnings("unchecked") private ResponseEntity<String> generateErrorResponse(String errorMessage, String path, HttpStatus status) {
private ResponseEntity<JSONObject> generateErrorResponse(String errorMessage, String path, HttpStatus status) { JsonObject responseJson = new JsonObject();
JSONObject responseJson = new JSONObject(); responseJson.addProperty("timestamp", System.currentTimeMillis());
responseJson.put("timestamp", System.currentTimeMillis()); responseJson.addProperty("status", status.value());
responseJson.put("status", status.value()); responseJson.addProperty("error", status.getReasonPhrase());
responseJson.put("error", status.getReasonPhrase()); responseJson.addProperty("message", errorMessage);
responseJson.put("message", errorMessage); responseJson.addProperty("path", path);
responseJson.put("path", path); return new ResponseEntity<>(responseJson.toString(), getResponseHeaders(), status);
return new ResponseEntity<>(responseJson, status); }
private HttpHeaders getResponseHeaders() {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
return responseHeaders;
} }
} }

View File

@ -32,6 +32,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException;
import io.openvidu.client.OpenViduException.Code; import io.openvidu.client.OpenViduException.Code;
@ -131,6 +132,23 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
case ProtocolElements.FORCEUNPUBLISH_METHOD: case ProtocolElements.FORCEUNPUBLISH_METHOD:
forceUnpublish(rpcConnection, request); forceUnpublish(rpcConnection, request);
break; break;
case ProtocolElements.APPLYFILTER_METHOD:
applyFilter(rpcConnection, request);
break;
case ProtocolElements.EXECFILTERMETHOD_METHOD:
execFilterMethod(rpcConnection, request);
break;
case ProtocolElements.REMOVEFILTER_METHOD:
removeFilter(rpcConnection, request);
break;
/*
* case ProtocolElements.FORCEAPPLYFILTER_METHOD:
* forceApplyFilter(rpcConnection, request); break; case
* ProtocolElements.FORCEEXECFILTERMETHOD_METHOD:
* forceExecFilterMethod(rpcConnection, request); break; case
* ProtocolElements.FORCEREMOVEFILTER_METHOD: forceRemoveFilter(rpcConnection,
* request); break;
*/
default: default:
log.error("Unrecognized request {}", request); log.error("Unrecognized request {}", request);
break; break;
@ -291,6 +309,22 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
sessionManager.unpublishVideo(participant, null, request.getId(), "unpublish"); sessionManager.unpublishVideo(participant, null, request.getId(), "unpublish");
} }
private void streamPropertyChanged(RpcConnection rpcConnection, Request<JsonObject> request) {
Participant participant;
try {
participant = sanityCheckOfSession(rpcConnection, "onStreamPropertyChanged");
} catch (OpenViduException e) {
return;
}
String streamId = getStringParam(request, ProtocolElements.STREAMPROPERTYCHANGED_STREAMID_PARAM);
String property = getStringParam(request, ProtocolElements.STREAMPROPERTYCHANGED_PROPERTY_PARAM);
JsonElement newValue = getParam(request, ProtocolElements.STREAMPROPERTYCHANGED_NEWVALUE_PARAM);
String reason = getStringParam(request, ProtocolElements.STREAMPROPERTYCHANGED_REASON_PARAM);
sessionManager.streamPropertyChanged(participant, request.getId(), streamId, property, newValue, reason);
}
private void forceDisconnect(RpcConnection rpcConnection, Request<JsonObject> request) { private void forceDisconnect(RpcConnection rpcConnection, Request<JsonObject> request) {
Participant participant; Participant participant;
try { try {
@ -331,20 +365,51 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
} }
private void streamPropertyChanged(RpcConnection rpcConnection, Request<JsonObject> request) { private void applyFilter(RpcConnection rpcConnection, Request<JsonObject> request) {
Participant participant; Participant participant;
try { try {
participant = sanityCheckOfSession(rpcConnection, "onStreamPropertyChanged"); participant = sanityCheckOfSession(rpcConnection, "applyFilter");
} catch (OpenViduException e) { } catch (OpenViduException e) {
return; return;
} }
String streamId = getStringParam(request, ProtocolElements.STREAMPROPERTYCHANGED_STREAMID_PARAM); String filterType = getStringParam(request, ProtocolElements.FILTER_TYPE_PARAM);
String property = getStringParam(request, ProtocolElements.STREAMPROPERTYCHANGED_PROPERTY_PARAM); if (participant.getToken().getKurentoTokenOptions().isFilterAllowed(filterType)) {
JsonElement newValue = getParam(request, ProtocolElements.STREAMPROPERTYCHANGED_NEWVALUE_PARAM); JsonObject filterOptions = new JsonParser().parse(getStringParam(request, ProtocolElements.FILTER_OPTIONS_PARAM))
String reason = getStringParam(request, ProtocolElements.STREAMPROPERTYCHANGED_REASON_PARAM); .getAsJsonObject();
String streamId = getStringParam(request, ProtocolElements.FILTER_STREAMID_PARAM);
sessionManager.applyFilter(sessionManager.getSession(rpcConnection.getSessionId()), streamId, filterType,
filterOptions, null, request.getId(), "applyFilter");
} else {
log.error("Error: participant {} is not a moderator", participant.getParticipantPublicId());
throw new OpenViduException(Code.USER_UNAUTHORIZED_ERROR_CODE,
"Unable to apply filter. The user does not have a valid token");
}
}
sessionManager.streamPropertyChanged(participant, request.getId(), streamId, property, newValue, reason); private void execFilterMethod(RpcConnection rpcConnection, Request<JsonObject> request) {
try {
sanityCheckOfSession(rpcConnection, "applyFilter");
} catch (OpenViduException e) {
return;
}
String filterMethod = getStringParam(request, ProtocolElements.FILTER_METHOD_PARAM);
JsonObject filterParams = new JsonParser().parse(getStringParam(request, ProtocolElements.FILTER_PARAMS_PARAM))
.getAsJsonObject();
String streamId = getStringParam(request, ProtocolElements.FILTER_STREAMID_PARAM);
sessionManager.execFilterMethod(sessionManager.getSession(rpcConnection.getSessionId()), streamId, filterMethod,
filterParams, null, request.getId(), "execFilterMethod");
}
private void removeFilter(RpcConnection rpcConnection, Request<JsonObject> request) {
try {
sanityCheckOfSession(rpcConnection, "removeFilter");
} catch (OpenViduException e) {
return;
}
String streamId = getStringParam(request, ProtocolElements.FILTER_STREAMID_PARAM);
sessionManager.removeFilter(sessionManager.getSession(rpcConnection.getSessionId()), streamId, null,
request.getId(), "removeFilter");
} }
public void leaveRoomAfterConnClosed(String participantPrivateId, String reason) { public void leaveRoomAfterConnClosed(String participantPrivateId, String reason) {

View File

@ -0,0 +1,41 @@
/*
* (C) Copyright 2017-2018 OpenVidu (https://openvidu.io/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.openvidu.server.utils;
import java.util.Map.Entry;
import org.kurento.jsonrpc.Props;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
public class JsonUtils {
public Props fromJsonObjectToProps(JsonObject params) {
Props props = new Props();
for (Entry<String, JsonElement> entry : params.entrySet()) {
if (entry.getValue().isJsonPrimitive()) {
props.add(entry.getKey(), entry.getValue().getAsString());
} else if (entry.getValue().isJsonObject()) {
props.add(entry.getKey(), fromJsonObjectToProps(entry.getValue().getAsJsonObject()));
}
}
return props;
}
}