diff --git a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREvent.java b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREvent.java index 7370ca3a..70c41b0e 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREvent.java +++ b/openvidu-server/src/main/java/io/openvidu/server/cdr/CDREvent.java @@ -17,7 +17,7 @@ package io.openvidu.server.cdr; -import org.json.simple.JSONObject; +import com.google.gson.JsonObject; import io.openvidu.java.client.RecordingLayout; import io.openvidu.server.core.MediaOptions; @@ -35,7 +35,7 @@ public class CDREvent implements Comparable { private MediaOptions mediaOptions; private String receivingFrom; private String reason; - + // Recording events private Long size; private String id; @@ -45,12 +45,14 @@ public class CDREvent implements Comparable { private RecordingLayout recordingLayout; 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); } - + 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); } @@ -64,7 +66,7 @@ public class CDREvent implements Comparable { this.timeStamp = System.currentTimeMillis(); this.startTime = this.timeStamp; } - + public CDREvent(CDREventName eventName, String sessionId, Recording recording, String reason) { this.eventName = eventName; if ((sessionId.indexOf('/')) != -1) { @@ -112,60 +114,59 @@ public class CDREvent implements Comparable { } @Override - @SuppressWarnings("unchecked") public String toString() { - JSONObject json = new JSONObject(); - json.put("sessionId", this.sessionId); - json.put("timestamp", this.timeStamp); + JsonObject json = new JsonObject(); + json.addProperty("sessionId", this.sessionId); + json.addProperty("timestamp", this.timeStamp); if (this.participant != null) { - json.put("participantId", this.participant.getParticipantPublicId()); + json.addProperty("participantId", this.participant.getParticipantPublicId()); } if (this.mediaOptions != null) { - json.put("connection", this.receivingFrom != null ? "INBOUND" : "OUTBOUND"); - json.put("audioEnabled", this.mediaOptions.hasAudio()); - json.put("videoEnabled", this.mediaOptions.hasVideo()); + json.addProperty("connection", this.receivingFrom != null ? "INBOUND" : "OUTBOUND"); + json.addProperty("audioEnabled", this.mediaOptions.hasAudio()); + json.addProperty("videoEnabled", this.mediaOptions.hasVideo()); if (this.mediaOptions.hasVideo()) { - json.put("videoSource", this.mediaOptions.getTypeOfVideo()); - json.put("videoFramerate", this.mediaOptions.getFrameRate()); + json.addProperty("videoSource", this.mediaOptions.getTypeOfVideo()); + json.addProperty("videoFramerate", this.mediaOptions.getFrameRate()); } if (this.receivingFrom != null) { - json.put("receivingFrom", this.receivingFrom); + json.addProperty("receivingFrom", this.receivingFrom); } } if (this.startTime != null && this.duration != null) { - json.put("startTime", this.startTime); - json.put("endTime", this.timeStamp); - json.put("duration", (this.timeStamp - this.startTime) / 1000); + json.addProperty("startTime", this.startTime); + json.addProperty("endTime", this.timeStamp); + json.addProperty("duration", (this.timeStamp - this.startTime) / 1000); } else if (this.duration != null) { - json.put("duration", duration); + json.addProperty("duration", duration); } if (this.reason != null) { - json.put("reason", this.reason); + json.addProperty("reason", this.reason); } if (this.id != null) { - json.put("id", this.id); + json.addProperty("id", this.id); } if (this.name != null) { - json.put("name", this.name); + json.addProperty("name", this.name); } if (this.size != null) { - json.put("size", this.size); + json.addProperty("size", this.size); } if (this.hasAudio != null) { - json.put("hasAudio", this.hasAudio); + json.addProperty("hasAudio", this.hasAudio); } if (this.hasVideo != null) { - json.put("hasVideo", this.hasVideo); + json.addProperty("hasVideo", this.hasVideo); } if (this.recordingLayout != null) { - json.put("recordingLayout", this.recordingLayout.name()); + json.addProperty("recordingLayout", this.recordingLayout.name()); } - JSONObject root = new JSONObject(); - root.put(this.eventName.name(), json); + JsonObject root = new JsonObject(); + root.add(this.eventName.name(), json); - return root.toJSONString(); + return root.toString(); } @Override diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/MediaOptions.java b/openvidu-server/src/main/java/io/openvidu/server/core/MediaOptions.java index 5017cb88..c953bfea 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/MediaOptions.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/MediaOptions.java @@ -17,7 +17,9 @@ package io.openvidu.server.core; -import org.json.simple.JSONObject; +import com.google.gson.JsonObject; + +import io.openvidu.server.kurento.KurentoFilter; public class MediaOptions { @@ -28,9 +30,10 @@ public class MediaOptions { protected String typeOfVideo; protected Integer frameRate; protected String videoDimensions; + protected KurentoFilter filter; 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.hasVideo = hasVideo; this.audioActive = audioActive; @@ -38,6 +41,7 @@ public class MediaOptions { this.typeOfVideo = typeOfVideo; this.frameRate = frameRate; this.videoDimensions = videoDimensions; + this.filter = filter; } public boolean hasAudio() { @@ -67,19 +71,32 @@ public class MediaOptions { public String getVideoDimensions() { return this.videoDimensions; } - - @SuppressWarnings("unchecked") - public JSONObject toJSON() { - JSONObject json = new JSONObject(); - json.put("hasAudio", this.hasAudio); + + public KurentoFilter getFilter() { + return this.filter; + } + + public void setFilter(KurentoFilter filter) { + this.filter = filter; + } + + public JsonObject toJson() { + JsonObject json = new JsonObject(); + json.addProperty("hasAudio", this.hasAudio); if (hasAudio) - json.put("audioActive", this.audioActive); - json.put("hasVideo", this.hasVideo); + json.addProperty("audioActive", this.audioActive); + json.addProperty("hasVideo", this.hasVideo); if (hasVideo) { - json.put("videoActive", this.videoActive); - json.put("typeOfVideo", this.typeOfVideo); - json.put("frameRate", this.frameRate); - json.put("videoDimensions", this.videoDimensions); + json.addProperty("videoActive", this.videoActive); + json.addProperty("typeOfVideo", this.typeOfVideo); + json.addProperty("frameRate", this.frameRate); + 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; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java b/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java index 6fc89fcd..23002643 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java @@ -17,7 +17,7 @@ package io.openvidu.server.core; -import org.json.simple.JSONObject; +import com.google.gson.JsonObject; public class Participant { @@ -163,14 +163,13 @@ public class Participant { return builder.toString(); } - @SuppressWarnings("unchecked") - public JSONObject toJSON() { - JSONObject json = new JSONObject(); - json.put("connectionId", this.participantPublicId); - json.put("token", this.token.getToken()); - json.put("role", this.token.getRole().name()); - json.put("serverData", this.serverMetadata); - json.put("clientData", this.clientMetadata); + public JsonObject toJson() { + JsonObject json = new JsonObject(); + json.addProperty("connectionId", this.participantPublicId); + json.addProperty("token", this.token.getToken()); + json.addProperty("role", this.token.getRole().name()); + json.addProperty("serverData", this.serverMetadata); + json.addProperty("clientData", this.clientMetadata); return json; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Session.java b/openvidu-server/src/main/java/io/openvidu/server/core/Session.java index a5f5d346..71fa6249 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/Session.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/Session.java @@ -19,7 +19,7 @@ package io.openvidu.server.core; import java.util.Set; -import org.json.simple.JSONObject; +import com.google.gson.JsonObject; import io.openvidu.java.client.SessionProperties; @@ -45,8 +45,8 @@ public interface Session { int getActivePublishers(); - JSONObject toJSON(); + JsonObject toJson(); - JSONObject withStatsToJSON(); + JsonObject withStatsToJson(); } diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java index ec07ddd1..ecf851f8 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java @@ -39,6 +39,7 @@ import io.openvidu.client.internal.ProtocolElements; import io.openvidu.server.cdr.CallDetailRecord; import io.openvidu.server.config.InfoHandler; import io.openvidu.server.config.OpenviduConfig; +import io.openvidu.server.kurento.KurentoFilter; import io.openvidu.server.kurento.core.KurentoParticipant; import io.openvidu.server.recording.Recording; import io.openvidu.server.rpc.RpcNotificationService; @@ -113,6 +114,10 @@ public class SessionEventsHandler { kParticipant.getPublisherMediaOptions().frameRate); stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMVIDEODIMENSIONS_PARAM, 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(); streamsArray.add(stream); @@ -199,6 +204,8 @@ public class SessionEventsHandler { stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_TYPEOFVIDEO_PARAM, mediaOptions.typeOfVideo); stream.addProperty(ProtocolElements.PARTICIPANTPUBLISHED_FRAMERATE_PARAM, mediaOptions.frameRate); 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(); streamsArray.add(stream); @@ -234,6 +241,7 @@ public class SessionEventsHandler { for (Participant p : participants) { if (p.getParticipantPrivateId().equals(participant.getParticipantPrivateId())) { + // Send response to the affected participant if (!isRpcFromOwner) { rpcNotificationService.sendNotification(p.getParticipantPrivateId(), ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params); @@ -247,6 +255,8 @@ public class SessionEventsHandler { } } else { if (error == null) { + // Send response to every other user in the session different than the affected + // participant rpcNotificationService.sendNotification(p.getParticipantPrivateId(), ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params); } @@ -440,6 +450,63 @@ public class SessionEventsHandler { } } + public void onFilterChanged(Participant participant, Participant moderator, Integer transactionId, + Set 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) { this.rpcNotificationService.closeRpcSession(participantPrivateId); } diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java index 2e7d4567..c4173739 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java @@ -43,6 +43,7 @@ import io.openvidu.server.cdr.CallDetailRecord; import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.coturn.CoturnCredentialsService; import io.openvidu.server.coturn.TurnCredentials; +import io.openvidu.server.kurento.core.KurentoTokenOptions; import io.openvidu.server.recording.ComposedRecordingService; 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 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); @@ -93,11 +95,21 @@ public abstract class SessionManager { public abstract void onIceCandidate(Participant participant, String endpointName, String candidate, 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, 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 * @@ -200,7 +212,8 @@ public abstract class SessionManager { 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 map = this.sessionidTokenTokenobj.putIfAbsent(sessionId, new ConcurrentHashMap<>()); @@ -224,7 +237,7 @@ public abstract class SessionManager { 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); showTokens(); @@ -252,7 +265,8 @@ public abstract class SessionManager { new Token(token, ParticipantRole.PUBLISHER, "", this.coturnCredentialsService.isCoturnAvailable() ? this.coturnCredentialsService.createUser() - : null)); + : null, + null)); return true; } } diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Token.java b/openvidu-server/src/main/java/io/openvidu/server/core/Token.java index 279622c9..ad550648 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/Token.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/Token.java @@ -17,9 +17,8 @@ package io.openvidu.server.core; -import org.json.simple.JSONObject; - import io.openvidu.server.coturn.TurnCredentials; +import io.openvidu.server.kurento.core.KurentoTokenOptions; public class Token { @@ -28,15 +27,19 @@ public class Token { private String serverMetadata = ""; private TurnCredentials turnCredentials; + private KurentoTokenOptions kurentoTokenOptions; + public Token(String 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.role = role; this.serverMetadata = serverMetadata; this.turnCredentials = turnCredentials; + this.kurentoTokenOptions = kurentoTokenOptions; } public String getToken() { @@ -55,6 +58,10 @@ public class Token { return turnCredentials; } + public KurentoTokenOptions getKurentoTokenOptions() { + return kurentoTokenOptions; + } + @Override public String toString() { if (this.role != null) diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/KurentoFilter.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/KurentoFilter.java new file mode 100644 index 00000000..404f553a --- /dev/null +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/KurentoFilter.java @@ -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; + } + +} diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoMediaOptions.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoMediaOptions.java index 5c2486e0..48b424f5 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoMediaOptions.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoMediaOptions.java @@ -21,6 +21,7 @@ import org.kurento.client.MediaElement; import org.kurento.client.MediaType; import io.openvidu.server.core.MediaOptions; +import io.openvidu.server.kurento.KurentoFilter; public class KurentoMediaOptions extends MediaOptions { @@ -33,9 +34,9 @@ public class KurentoMediaOptions extends MediaOptions { public KurentoMediaOptions(boolean isOffer, String sdpOffer, MediaElement loopbackAlternativeSrc, MediaType loopbackConnectionType, Boolean hasAudio, Boolean hasVideo, Boolean audioActive, - Boolean videoActive, String typeOfVideo, Integer frameRate, String videoDimensions, boolean doLoopback, - MediaElement... mediaElements) { - super(hasAudio, hasVideo, audioActive, videoActive, typeOfVideo, frameRate, videoDimensions); + Boolean videoActive, String typeOfVideo, Integer frameRate, String videoDimensions, KurentoFilter filter, + boolean doLoopback, MediaElement... mediaElements) { + super(hasAudio, hasVideo, audioActive, videoActive, typeOfVideo, frameRate, videoDimensions, filter); this.isOffer = isOffer; this.sdpOffer = sdpOffer; this.loopbackAlternativeSrc = loopbackAlternativeSrc; diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java index 3a71a25b..a4924722 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java @@ -26,11 +26,10 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; 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.ErrorEvent; import org.kurento.client.Filter; +import org.kurento.client.GenericMediaElement; import org.kurento.client.IceCandidate; import org.kurento.client.MediaElement; import org.kurento.client.MediaPipeline; @@ -40,6 +39,9 @@ import org.kurento.client.internal.server.KurentoServerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; 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) { this.publisher.apply(element); } else { @@ -127,38 +129,25 @@ public class KurentoParticipant extends Participant { return filters.get(id); } - public synchronized void addFilterElement(String id, Filter filter) { - filters.put(id, filter); - shapePublisherMedia(filter, null); - } - - public synchronized void disableFilterelement(String filterID, boolean releaseElement) { - Filter filter = getFilterElement(filterID); - - if (filter != null) { - try { - publisher.revert(filter, releaseElement); - } catch (OpenViduException e) { - // Ignore error - } - } - } - - 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 addFilterElement(String id, Filter filter) { + * filters.put(id, filter); shapePublisherMedia(filter, null); } + * + * public synchronized void disableFilterelement(String filterID, boolean + * releaseElement) { Filter filter = getFilterElement(filterID); + * + * if (filter != null) { try { publisher.revert(filter, releaseElement); } catch + * (OpenViduException e) { // Ignore error } } } + * + * 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) { Filter filter = getFilterElement(id); - filters.remove(id); if (filter != null) { publisher.revert(filter); @@ -166,10 +155,11 @@ public class KurentoParticipant extends Participant { } public synchronized void releaseAllFilters() { - // Check this, mutable array? - filters.forEach((s, filter) -> removeFilterElement(s)); + if(this.publisher.getFilter() != null) { + this.publisher.revert(this.publisher.getFilter()); + } } public PublisherEndpoint getPublisher() { @@ -544,8 +534,9 @@ public class KurentoParticipant extends Participant { * event.getPadName() + " | MEDIATYPE: " + event.getMediaType() + * " | TIMESTAMP: " + System.currentTimeMillis(); * - * endpoint.flowOutMedia.put(event.getSource().getName() + "/" + - * event.getMediaType(), event.getSource()); + * endpoint.flowOutMedia. @SuppressWarnings("unchecked") + * put(event.getSource().getName() + "/" + event.getMediaType(), + * event.getSource()); * * String msg2; * @@ -661,35 +652,38 @@ public class KurentoParticipant extends Participant { }); } + public MediaPipeline getPipeline() { + return this.pipeline; + } + @Override public String getPublisherStreamId() { return this.publisher.getEndpoint().getTag("name"); } @Override - public JSONObject toJSON() { - return this.sharedJSON(MediaEndpoint::toJSON); + public JsonObject toJson() { + return this.sharedJson(MediaEndpoint::toJson); } - public JSONObject withStatsToJSON() { - return this.sharedJSON(MediaEndpoint::withStatsToJSON); + public JsonObject withStatsToJson() { + return this.sharedJson(MediaEndpoint::withStatsToJson); } - @SuppressWarnings("unchecked") - private JSONObject sharedJSON(Function toJsonFunction) { - JSONObject json = super.toJSON(); - JSONArray publisherEnpoints = new JSONArray(); + private JsonObject sharedJson(Function toJsonFunction) { + JsonObject json = super.toJson(); + JsonArray publisherEnpoints = new JsonArray(); if (this.streaming && this.publisher.getEndpoint() != null) { publisherEnpoints.add(toJsonFunction.apply(this.publisher)); } - JSONArray subscriberEndpoints = new JSONArray(); + JsonArray subscriberEndpoints = new JsonArray(); for (MediaEndpoint sub : this.subscribers.values()) { if (sub.getEndpoint() != null) { subscriberEndpoints.add(toJsonFunction.apply(sub)); } } - json.put("publishers", publisherEnpoints); - json.put("subscribers", subscriberEndpoints); + json.add("publishers", publisherEnpoints); + json.add("subscribers", subscriberEndpoints); return json; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java index b52f5887..ebb08f9c 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java @@ -26,8 +26,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; import org.kurento.client.Continuation; import org.kurento.client.ErrorEvent; import org.kurento.client.EventListener; @@ -37,6 +35,9 @@ import org.kurento.client.MediaPipeline; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; import io.openvidu.client.internal.ProtocolElements; @@ -361,35 +362,34 @@ public class KurentoSession implements Session { } @Override - public JSONObject toJSON() { - return this.sharedJSON(KurentoParticipant::toJSON); + public JsonObject toJson() { + return this.sharedJson(KurentoParticipant::toJson); } @Override - public JSONObject withStatsToJSON() { - return this.sharedJSON(KurentoParticipant::withStatsToJSON); + public JsonObject withStatsToJson() { + return this.sharedJson(KurentoParticipant::withStatsToJson); } - @SuppressWarnings("unchecked") - private JSONObject sharedJSON(Function toJsonFunction) { - JSONObject json = new JSONObject(); - json.put("sessionId", this.sessionId); - json.put("mediaMode", this.sessionProperties.mediaMode().name()); - json.put("recordingMode", this.sessionProperties.recordingMode().name()); - json.put("defaultRecordingLayout", this.sessionProperties.defaultRecordingLayout().name()); + private JsonObject sharedJson(Function toJsonFunction) { + JsonObject json = new JsonObject(); + json.addProperty("sessionId", this.sessionId); + json.addProperty("mediaMode", this.sessionProperties.mediaMode().name()); + json.addProperty("recordingMode", this.sessionProperties.recordingMode().name()); + json.addProperty("defaultRecordingLayout", this.sessionProperties.defaultRecordingLayout().name()); if (RecordingLayout.CUSTOM.equals(this.sessionProperties.defaultRecordingLayout())) { - json.put("defaultCustomLayout", this.sessionProperties.defaultCustomLayout()); + json.addProperty("defaultCustomLayout", this.sessionProperties.defaultCustomLayout()); } - JSONObject connections = new JSONObject(); - JSONArray participants = new JSONArray(); + JsonObject connections = new JsonObject(); + JsonArray participants = new JsonArray(); this.participants.values().forEach(p -> { if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(p.getParticipantPublicId())) { participants.add(toJsonFunction.apply(p)); } }); - connections.put("numberOfElements", participants.size()); - connections.put("content", participants); - json.put("connections", connections); + connections.addProperty("numberOfElements", participants.size()); + connections.add("content", participants); + json.add("connections", connections); return json; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java index ba3a0323..095f6e15 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java @@ -22,9 +22,10 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import org.kurento.client.GenericMediaElement; import org.kurento.client.IceCandidate; import org.kurento.client.KurentoClient; -import org.kurento.client.MediaElement; +import org.kurento.jsonrpc.Props; import org.kurento.jsonrpc.message.Request; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,9 +50,11 @@ import io.openvidu.server.core.Session; import io.openvidu.server.core.SessionManager; import io.openvidu.server.kurento.KurentoClientProvider; import io.openvidu.server.kurento.KurentoClientSessionInfo; +import io.openvidu.server.kurento.KurentoFilter; import io.openvidu.server.kurento.OpenViduKurentoClientSessionInfo; import io.openvidu.server.kurento.endpoint.SdpType; import io.openvidu.server.rpc.RpcHandler; +import io.openvidu.server.utils.JsonUtils; public class KurentoSessionManager extends SessionManager { @@ -63,6 +66,8 @@ public class KurentoSessionManager extends SessionManager { @Autowired private KurentoSessionEventsHandler kurentoSessionEventsHandler; + private KurentoClient kurentoClient; + @Override public synchronized void joinRoom(Participant participant, String sessionId, Integer transactionId) { Set existingParticipants = null; @@ -91,7 +96,7 @@ public class KurentoSessionManager extends SessionManager { log.warn("'{}' is trying to join session '{}' but it is closing", participant.getParticipantPublicId(), sessionId); 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); session.join(participant); @@ -216,7 +221,7 @@ public class KurentoSessionManager extends SessionManager { String sdpAnswer = null; KurentoMediaOptions kurentoOptions = (KurentoMediaOptions) mediaOptions; - KurentoParticipant kurentoParticipant = (KurentoParticipant) participant; + KurentoParticipant kParticipant = (KurentoParticipant) participant; log.debug( "Request [PUBLISH_MEDIA] isOffer={} sdp={} " @@ -226,15 +231,22 @@ public class KurentoSessionManager extends SessionManager { participant.getParticipantPublicId()); 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); if (sdpAnswer == null) { @@ -261,7 +273,7 @@ public class KurentoSessionManager extends SessionManager { session.newPublisher(participant); - participants = kurentoParticipant.getSession().getParticipants(); + participants = kParticipant.getSession().getParticipants(); if (sdpAnswer != null) { sessionEventsHandler.onPublishMedia(participant, participant.getPublisherStreamId(), session.getSessionId(), @@ -277,6 +289,11 @@ public class KurentoSessionManager extends SessionManager { log.debug("Request [UNPUBLISH_MEDIA] ({})", participant.getParticipantPublicId()); 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, "Participant '" + participant.getParticipantPublicId() + "' is not streaming media"); } @@ -309,18 +326,18 @@ public class KurentoSessionManager extends SessionManager { if (senderParticipant == null) { log.warn( "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()); 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()) { log.warn( "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()); 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); @@ -349,10 +366,10 @@ public class KurentoSessionManager extends SessionManager { if (sender == null) { log.warn( "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()); 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"); @@ -363,9 +380,9 @@ public class KurentoSessionManager extends SessionManager { @Override public void sendMessage(Participant participant, String message, Integer transactionId) { try { - JsonObject messageJSON = new JsonParser().parse(message).getAsJsonObject(); + JsonObject messageJson = new JsonParser().parse(message).getAsJsonObject(); KurentoParticipant kParticipant = (KurentoParticipant) participant; - sessionEventsHandler.onSendMessage(participant, messageJSON, + sessionEventsHandler.onSendMessage(participant, messageJson, getParticipants(kParticipant.getSession().getSessionId()), transactionId, null); } catch (JsonSyntaxException | IllegalStateException e) { throw new OpenViduException(Code.SIGNAL_FORMAT_INVALID_ERROR_CODE, @@ -387,6 +404,7 @@ public class KurentoSessionManager extends SessionManager { String typeOfVideo = streamProperties.getTypeOfVideo(); Integer frameRate = streamProperties.getFrameRate(); String videoDimensions = streamProperties.getVideoDimensions(); + KurentoFilter filter = streamProperties.getFilter(); switch (property) { case "audioActive": @@ -401,7 +419,7 @@ public class KurentoSessionManager extends SessionManager { } kParticipant.setPublisherMediaOptions(new MediaOptions(hasAudio, hasVideo, audioActive, videoActive, - typeOfVideo, frameRate, videoDimensions)); + typeOfVideo, frameRate, videoDimensions, filter)); sessionEventsHandler.onStreamPropertyChanged(participant, transactionId, 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, "Session '" + sessionId + "' already exists"); } - KurentoClient kurentoClient = kcProvider.getKurentoClient(kcSessionInfo); + this.kurentoClient = kcProvider.getKurentoClient(kcSessionInfo); session = new KurentoSession(sessionId, sessionProperties, kurentoClient, kurentoSessionEventsHandler, kcProvider.destroyWhenUnused(), this.CDR, this.openviduConfig); @@ -482,7 +500,7 @@ public class KurentoSessionManager extends SessionManager { } @Override - public MediaOptions generateMediaOptions(Request request) { + public KurentoMediaOptions generateMediaOptions(Request request) { String sdpOffer = RpcHandler.getStringParam(request, ProtocolElements.PUBLISHVIDEO_SDPOFFER_PARAM); boolean hasAudio = RpcHandler.getBooleanParam(request, ProtocolElements.PUBLISHVIDEO_HASAUDIO_PARAM); @@ -491,6 +509,7 @@ public class KurentoSessionManager extends SessionManager { Boolean audioActive = null, videoActive = null; String typeOfVideo = null, videoDimensions = null; Integer frameRate = null; + KurentoFilter kurentoFilter = null; try { 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); } 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); return new KurentoMediaOptions(true, sdpOffer, null, null, hasAudio, hasVideo, audioActive, videoActive, - typeOfVideo, frameRate, videoDimensions, doLoopback); + typeOfVideo, frameRate, videoDimensions, kurentoFilter, doLoopback); } @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 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 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 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); } + */ + } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoTokenOptions.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoTokenOptions.java new file mode 100644 index 00000000..190b124e --- /dev/null +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoTokenOptions.java @@ -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 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 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); + } + +} diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java index 2d021c39..4189bcaf 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java @@ -25,8 +25,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; import org.kurento.client.Continuation; import org.kurento.client.ErrorEvent; import org.kurento.client.EventListener; @@ -42,11 +40,16 @@ import org.kurento.client.WebRtcEndpoint; import org.slf4j.Logger; 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.Code; import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.core.Participant; import io.openvidu.server.kurento.core.KurentoParticipant; +import io.openvidu.server.kurento.core.KurentoTokenOptions; /** * {@link WebRtcEndpoint} wrapper that supports buffering of @@ -108,10 +111,27 @@ public abstract class MediaEndpoint { this.setMediaPipeline(pipeline); this.openviduConfig = openviduConfig; - this.maxRecvKbps = this.openviduConfig.getVideoMaxRecvBandwidth(); - this.minRecvKbps = this.openviduConfig.getVideoMinRecvBandwidth(); - this.maxSendKbps = this.openviduConfig.getVideoMaxSendBandwidth(); - this.minSendKbps = this.openviduConfig.getVideoMinSendBandwidth(); + + KurentoTokenOptions kurentoTokenOptions = this.owner.getToken().getKurentoTokenOptions(); + if (kurentoTokenOptions != null) { + 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() { @@ -464,28 +484,26 @@ public abstract class MediaEndpoint { public abstract PublisherEndpoint getPublisher(); - public JSONObject toJSON() { - JSONObject json = new JSONObject(); + public JsonObject toJson() { + JsonObject json = new JsonObject(); return json; } - @SuppressWarnings("unchecked") - public JSONObject withStatsToJSON() { - JSONObject json = new JSONObject(); - json.put("webrtcTagName", this.getEndpoint().getTag("name")); - json.put("receivedCandidates", this.receivedCandidateList); - json.put("localCandidate", this.selectedLocalIceCandidate); - json.put("remoteCandidate", this.selectedRemoteIceCandidate); - - JSONArray jsonArray = new JSONArray(); + public JsonObject withStatsToJson() { + JsonObject json = new JsonObject(); + json.addProperty("webrtcTagName", this.getEndpoint().getTag("name")); + json.add("receivedCandidates", new GsonBuilder().create().toJsonTree(this.receivedCandidateList)); + json.addProperty("localCandidate", this.selectedLocalIceCandidate); + json.addProperty("remoteCandidate", this.selectedRemoteIceCandidate); + JsonArray jsonArray = new JsonArray(); for (KmsEvent event : this.kmsEvents) { - JSONObject jsonKmsEvent = new JSONObject(); - jsonKmsEvent.put(event.event.getType(), event.timestamp); + JsonObject jsonKmsEvent = new JsonObject(); + jsonKmsEvent.addProperty(event.event.getType(), event.timestamp); jsonArray.add(jsonKmsEvent); } + json.add("events", jsonArray); - json.put("events", jsonArray); return json; } } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java index 36bd5afa..ee3569f3 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java @@ -21,25 +21,31 @@ import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; +import java.util.Map.Entry; import java.util.concurrent.CountDownLatch; -import org.json.simple.JSONObject; import org.kurento.client.Continuation; +import org.kurento.client.GenericMediaElement; import org.kurento.client.ListenerSubscription; import org.kurento.client.MediaElement; import org.kurento.client.MediaPipeline; import org.kurento.client.MediaType; import org.kurento.client.PassThrough; import org.kurento.client.WebRtcEndpoint; +import org.kurento.jsonrpc.Props; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.core.MediaOptions; import io.openvidu.server.kurento.TrackType; import io.openvidu.server.kurento.core.KurentoParticipant; +import io.openvidu.server.utils.JsonUtils; /** * Publisher aspect of the {@link MediaEndpoint}. @@ -55,13 +61,15 @@ public class PublisherEndpoint extends MediaEndpoint { private PassThrough passThru = null; private ListenerSubscription passThruSubscription = null; + private GenericMediaElement filter; private Map elements = new HashMap(); private LinkedList elementIds = new LinkedList(); private boolean connected = false; private Map elementsErrorSubscriptions = new HashMap(); - 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); } @@ -92,6 +100,10 @@ public class PublisherEndpoint extends MediaEndpoint { return elements.values(); } + public GenericMediaElement getFilter() { + return this.filter; + } + /** * Initializes this media endpoint for publishing media and processes the SDP * offer or answer. If the internal endpoint is an {@link WebRtcEndpoint}, it @@ -180,7 +192,7 @@ public class PublisherEndpoint extends MediaEndpoint { * @throws OpenViduException * 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); } @@ -198,7 +210,7 @@ public class PublisherEndpoint extends MediaEndpoint { * @throws OpenViduException * 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(); if (id == null) { throw new OpenViduException(Code.MEDIA_WEBRTC_ENDPOINT_ERROR_CODE, @@ -222,6 +234,9 @@ public class PublisherEndpoint extends MediaEndpoint { } elementIds.addFirst(id); elements.put(id, shaper); + + this.filter = shaper; + elementsErrorSubscriptions.put(id, registerElemErrListener(shaper)); 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) { @@ -488,22 +509,20 @@ public class PublisherEndpoint extends MediaEndpoint { this.mediaOptions = mediaOptions; } - @SuppressWarnings("unchecked") @Override - public JSONObject toJSON() { - JSONObject json = super.toJSON(); - json.put("streamId", this.getEndpoint().getTag("name")); - json.put("mediaOptions", this.mediaOptions.toJSON()); + public JsonObject toJson() { + JsonObject json = super.toJson(); + json.addProperty("streamId", this.getEndpoint().getTag("name")); + json.add("mediaOptions", this.mediaOptions.toJson()); return json; } - @SuppressWarnings("unchecked") @Override - public JSONObject withStatsToJSON() { - JSONObject json = super.withStatsToJSON(); - JSONObject toJSON = this.toJSON(); - for (Object key : toJSON.keySet()) { - json.put(key, toJSON.get(key)); + public JsonObject withStatsToJson() { + JsonObject json = super.withStatsToJson(); + JsonObject toJson = this.toJson(); + for (Entry entry : toJson.entrySet()) { + json.add(entry.getKey(), entry.getValue()); } return json; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java index 11facb7c..49b3201e 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java @@ -17,11 +17,15 @@ package io.openvidu.server.kurento.endpoint; -import org.json.simple.JSONObject; +import java.util.Map.Entry; + import org.kurento.client.MediaPipeline; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.kurento.core.KurentoParticipant; @@ -37,7 +41,8 @@ public class SubscriberEndpoint extends MediaEndpoint { 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); } @@ -68,22 +73,20 @@ public class SubscriberEndpoint extends MediaEndpoint { this.publisher = publisher; } - @SuppressWarnings("unchecked") @Override - public JSONObject toJSON() { - JSONObject json = super.toJSON(); - json.put("streamId", this.publisher.getEndpoint().getTag("name")); - json.put("publisher", this.publisher.getEndpointName()); + public JsonObject toJson() { + JsonObject json = super.toJson(); + json.addProperty("streamId", this.publisher.getEndpoint().getTag("name")); + json.addProperty("publisher", this.publisher.getEndpointName()); return json; } - @SuppressWarnings("unchecked") @Override - public JSONObject withStatsToJSON() { - JSONObject json = super.withStatsToJSON(); - JSONObject toJSON = this.toJSON(); - for (Object key : toJSON.keySet()) { - json.put(key, toJSON.get(key)); + public JsonObject withStatsToJson() { + JsonObject json = super.withStatsToJson(); + JsonObject toJson = this.toJson(); + for (Entry entry : toJson.entrySet()) { + json.add(entry.getKey(), entry.getValue()); } return json; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/ComposedRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/ComposedRecordingService.java index ea4b6304..e885688d 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/ComposedRecordingService.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/ComposedRecordingService.java @@ -37,9 +37,6 @@ import java.util.stream.Collectors; import javax.ws.rs.ProcessingException; 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.LoggerFactory; 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.command.ExecStartResultCallback; 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.Code; @@ -137,9 +136,9 @@ public class ComposedRecordingService { envs.add("VIDEO_NAME=" + properties.name()); envs.add("VIDEO_FORMAT=mp4"); 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); String containerId = this.runRecordingContainer(envs, "recording_" + recordingId); @@ -212,7 +211,7 @@ public class ComposedRecordingService { recording.setUrl(this.openviduConfig.getFinalUrl() + "recordings/" + recording.getName() + ".mp4"); } - } catch (IOException | ParseException e) { + } catch (IOException e) { throw new OpenViduException(Code.RECORDING_REPORT_ERROR_CODE, "There was an error generating the metadata report file for the recording"); } @@ -396,7 +395,7 @@ public class ComposedRecordingService { // Cannot delete an active recording return HttpStatus.CONFLICT; } - + Recording recording = getRecordingFromHost(recordingId); if (recording == null) { return HttpStatus.NOT_FOUND; @@ -417,11 +416,10 @@ public class ComposedRecordingService { private Recording getRecordingFromEntityFile(File file) { if (file.isFile() && file.getName().startsWith(RECORDING_ENTITY_FILE)) { - JSONParser parser = new JSONParser(); - JSONObject json = null; + JsonObject json = null; try { - json = (JSONObject) parser.parse(new FileReader(file)); - } catch (IOException | ParseException e) { + json = new JsonParser().parse(new FileReader(file)).getAsJsonObject(); + } catch (IOException e) { return null; } return new Recording(json); diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java b/openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java index 299e4088..c04bd72e 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/Recording.java @@ -17,7 +17,7 @@ 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.RecordingProperties; @@ -53,22 +53,26 @@ public class Recording { this.recordingProperties = recordingProperties; } - public Recording(JSONObject json) { - this.id = (String) json.get("id"); - this.sessionId = (String) json.get("sessionId"); - this.createdAt = (long) json.get("createdAt"); - this.size = (long) json.get("size"); + public Recording(JsonObject json) { + this.id = json.get("id").getAsString(); + this.sessionId = json.get("sessionId").getAsString(); + this.createdAt = json.get("createdAt").getAsLong(); + this.size = json.get("size").getAsLong(); try { - this.duration = (double) json.get("duration"); + this.duration = json.get("duration").getAsDouble(); } 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"); - this.hasAudio = (boolean) json.get("hasAudio"); - this.hasVideo = (boolean) json.get("hasVideo"); - this.status = Status.valueOf((String) json.get("status")); - this.recordingProperties = new RecordingProperties.Builder().name((String) json.get("name")) - .recordingLayout(RecordingLayout.valueOf((String) json.get("recordingLayout"))).build(); + if (json.get("url").isJsonNull()) { + this.url = null; + } else { + this.url = json.get("url").getAsString(); + } + 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() { @@ -155,23 +159,22 @@ public class Recording { this.hasVideo = hasVideo; } - @SuppressWarnings("unchecked") - public JSONObject toJson() { - JSONObject json = new JSONObject(); - json.put("id", this.id); - json.put("name", this.recordingProperties.name()); - json.put("recordingLayout", this.recordingProperties.recordingLayout().name()); + public JsonObject toJson() { + JsonObject json = new JsonObject(); + json.addProperty("id", this.id); + json.addProperty("name", this.recordingProperties.name()); + json.addProperty("recordingLayout", this.recordingProperties.recordingLayout().name()); 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.put("createdAt", this.createdAt); - json.put("size", this.size); - json.put("duration", this.duration); - json.put("url", this.url); - json.put("hasAudio", this.hasAudio); - json.put("hasVideo", this.hasVideo); - json.put("status", this.status.toString()); + json.addProperty("sessionId", this.sessionId); + json.addProperty("createdAt", this.createdAt); + json.addProperty("size", this.size); + json.addProperty("duration", this.duration); + json.addProperty("url", this.url); + json.addProperty("hasAudio", this.hasAudio); + json.addProperty("hasVideo", this.hasVideo); + json.addProperty("status", this.status.toString()); return json; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingInfoUtils.java b/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingInfoUtils.java index 51e0770b..27847a36 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingInfoUtils.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/RecordingInfoUtils.java @@ -21,39 +21,45 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; +import com.google.gson.JsonArray; +import com.google.gson.JsonIOException; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; public class RecordingInfoUtils { - private JSONParser parser; - private JSONObject json; - private JSONObject jsonFormat; - private JSONObject videoStream; - private JSONObject audioStream; + private JsonParser parser; + private JsonObject json; + private JsonObject jsonFormat; + private JsonObject videoStream; + private JsonObject audioStream; - public RecordingInfoUtils(String fullVideoPath) - throws FileNotFoundException, IOException, ParseException, OpenViduException { + public RecordingInfoUtils(String fullVideoPath) throws FileNotFoundException, IOException, OpenViduException { - this.parser = new JSONParser(); - this.json = (JSONObject) parser.parse(new FileReader(fullVideoPath)); + this.parser = new JsonParser(); - if (json.isEmpty()) { - // Recording metadata from ffprobe is empty: video file is corrupted or empty - throw new OpenViduException(Code.RECORDING_FILE_EMPTY_ERROR, "The recording file is empty or corrupted"); + try { + this.json = parser.parse(new FileReader(fullVideoPath)).getAsJsonObject(); + } 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++) { - JSONObject stream = (JSONObject) streams.get(i); + JsonObject stream = streams.get(i).getAsJsonObject(); if ("video".equals(stream.get("codec_type").toString())) { this.videoStream = stream; } else if ("audio".equals(stream.get("codec_type").toString())) { @@ -64,19 +70,19 @@ public class RecordingInfoUtils { } public double getDurationInSeconds() { - return Double.parseDouble(jsonFormat.get("duration").toString()); + return jsonFormat.get("duration").getAsDouble(); } public int getSizeInBytes() { - return Integer.parseInt(jsonFormat.get("size").toString()); + return jsonFormat.get("size").getAsInt(); } public int getNumberOfStreams() { - return Integer.parseInt(jsonFormat.get("nb_streams").toString()); + return jsonFormat.get("nb_streams").getAsInt(); } public int getBitRate() { - return (Integer.parseInt(jsonFormat.get("bit_rate").toString()) / 1000); + return ((jsonFormat.get("bit_rate").getAsInt()) / 1000); } public boolean hasVideo() { @@ -88,11 +94,11 @@ public class RecordingInfoUtils { } public int videoWidth() { - return Integer.parseInt(videoStream.get("width").toString()); + return videoStream.get("width").getAsInt(); } public int videoHeight() { - return Integer.parseInt(videoStream.get("height").toString()); + return videoStream.get("height").getAsInt(); } public int getVideoFramerate() { diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java index 87541bd4..e4916e6b 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java @@ -22,10 +22,10 @@ import java.util.Map; import java.util.NoSuchElementException; 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.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; 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.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.internal.ProtocolElements; 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.Session; import io.openvidu.server.core.SessionManager; +import io.openvidu.server.kurento.core.KurentoTokenOptions; import io.openvidu.server.recording.ComposedRecordingService; import io.openvidu.server.recording.Recording; @@ -68,9 +73,8 @@ public class SessionRestController { @Autowired private OpenviduConfig openviduConfig; - @SuppressWarnings("unchecked") @RequestMapping(value = "/sessions", method = RequestMethod.POST) - public ResponseEntity getSessionId(@RequestBody(required = false) Map params) { + public ResponseEntity getSessionId(@RequestBody(required = false) Map params) { SessionProperties.Builder builder = new SessionProperties.Builder(); String customSessionId = null; @@ -130,45 +134,43 @@ public class SessionRestController { } sessionManager.storeSessionId(sessionId, sessionProperties); - JSONObject responseJson = new JSONObject(); - responseJson.put("id", sessionId); + JsonObject responseJson = new JsonObject(); + 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) - public ResponseEntity getSession(@PathVariable("sessionId") String sessionId, + public ResponseEntity getSession(@PathVariable("sessionId") String sessionId, @RequestParam(value = "webRtcStats", defaultValue = "false", required = false) boolean webRtcStats) { Session session = this.sessionManager.getSession(sessionId); if (session != null) { - JSONObject response = (webRtcStats == true) ? session.withStatsToJSON() : session.toJSON(); - response.put("recording", this.recordingService.sessionIsBeingRecorded(sessionId)); - return new ResponseEntity<>(response, HttpStatus.OK); + JsonObject response = (webRtcStats == true) ? session.withStatsToJson() : session.toJson(); + response.addProperty("recording", this.recordingService.sessionIsBeingRecorded(sessionId)); + return new ResponseEntity<>(response.toString(), getResponseHeaders(), HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } - @SuppressWarnings("unchecked") @RequestMapping(value = "/sessions", method = RequestMethod.GET) - public ResponseEntity listSessions( + public ResponseEntity listSessions( @RequestParam(value = "webRtcStats", defaultValue = "false", required = false) boolean webRtcStats) { Collection sessions = this.sessionManager.getSessionObjects(); - JSONObject json = new JSONObject(); - JSONArray jsonArray = new JSONArray(); + JsonObject json = new JsonObject(); + JsonArray jsonArray = new JsonArray(); sessions.forEach(s -> { - JSONObject sessionJson = (webRtcStats == true) ? s.withStatsToJSON() : s.toJSON(); - sessionJson.put("recording", this.recordingService.sessionIsBeingRecorded(s.getSessionId())); + JsonObject sessionJson = (webRtcStats == true) ? s.withStatsToJson() : s.toJson(); + sessionJson.addProperty("recording", this.recordingService.sessionIsBeingRecorded(s.getSessionId())); jsonArray.add(sessionJson); }); - json.put("numberOfElements", sessions.size()); - json.put("content", jsonArray); - return new ResponseEntity<>(json, HttpStatus.OK); + json.addProperty("numberOfElements", sessions.size()); + json.add("content", jsonArray); + return new ResponseEntity<>(json.toString(), getResponseHeaders(), HttpStatus.OK); } @RequestMapping(value = "/sessions/{sessionId}", method = RequestMethod.DELETE) - public ResponseEntity closeSession(@PathVariable("sessionId") String sessionId) { + public ResponseEntity closeSession(@PathVariable("sessionId") String sessionId) { Session session = this.sessionManager.getSession(sessionId); if (session != null) { this.sessionManager.closeSession(sessionId, "sessionClosedByServer"); @@ -179,7 +181,7 @@ public class SessionRestController { } @RequestMapping(value = "/sessions/{sessionId}/connection/{connectionId}", method = RequestMethod.DELETE) - public ResponseEntity disconnectParticipant(@PathVariable("sessionId") String sessionId, + public ResponseEntity disconnectParticipant(@PathVariable("sessionId") String sessionId, @PathVariable("connectionId") String participantPublicId) { Session session = this.sessionManager.getSession(sessionId); if (session != null) { @@ -196,7 +198,7 @@ public class SessionRestController { } @RequestMapping(value = "/sessions/{sessionId}/stream/{streamId}", method = RequestMethod.DELETE) - public ResponseEntity unpublishStream(@PathVariable("sessionId") String sessionId, + public ResponseEntity unpublishStream(@PathVariable("sessionId") String sessionId, @PathVariable("streamId") String streamId) { Session session = this.sessionManager.getSession(sessionId); if (session != null) { @@ -210,51 +212,81 @@ public class SessionRestController { } } - /* - * @RequestMapping(value = "/sessions/{sessionId}/stream/{streamId}", method = - * RequestMethod.PUT) public ResponseEntity - * muteMedia(@PathVariable("sessionId") String sessionId, - * - * @PathVariable("streamId") String streamId, @RequestBody Map params) { } - */ - - @SuppressWarnings("unchecked") @RequestMapping(value = "/tokens", method = RequestMethod.POST) - public ResponseEntity newToken(@RequestBody Map params) { + public ResponseEntity newToken(@RequestBody Map params) { try { - String sessionId = (String) params.get("session"); String roleString = (String) params.get("role"); String metadata = (String) params.get("data"); + JsonObject kurentoOptions = new JsonParser().parse(params.get("kurentoOptions").toString()).getAsJsonObject(); ParticipantRole role; - if (roleString != null) { - role = ParticipantRole.valueOf(roleString); - } else { - role = ParticipantRole.PUBLISHER; + try { + if (roleString != null) { + role = ParticipantRole.valueOf(roleString); + } 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 : ""; - String token = sessionManager.newToken(sessionId, role, metadata); - JSONObject responseJson = new JSONObject(); - responseJson.put("id", token); - responseJson.put("session", sessionId); - responseJson.put("role", role.toString()); - responseJson.put("data", metadata); - responseJson.put("token", token); - return new ResponseEntity<>(responseJson, HttpStatus.OK); + String token = sessionManager.newToken(sessionId, role, metadata, kurentoTokenOptions); + JsonObject responseJson = new JsonObject(); + responseJson.addProperty("id", token); + responseJson.addProperty("session", sessionId); + responseJson.addProperty("role", role.toString()); + responseJson.addProperty("data", metadata); + responseJson.addProperty("token", token); - } catch (IllegalArgumentException e) { - return this.generateErrorResponse("Role " + params.get("role") + " is not defined", "/api/tokens", - HttpStatus.BAD_REQUEST); + if (kurentoOptions != null) { + JsonObject kurentoOptsResponse = new JsonObject(); + 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) { return this.generateErrorResponse(e.getMessage(), "/api/tokens", HttpStatus.BAD_REQUEST); } } @RequestMapping(value = "/recordings/start", method = RequestMethod.POST) - public ResponseEntity startRecordingSession(@RequestBody Map params) { + public ResponseEntity startRecordingSession(@RequestBody Map params) { String sessionId = (String) params.get("session"); String name = (String) params.get("name"); @@ -290,8 +322,8 @@ public class SessionRestController { RecordingLayout recordingLayout; if (recordingLayoutString == null || recordingLayoutString.isEmpty()) { // "recordingLayout" parameter not defined. Use global layout from - // SessionProperties - // (it is always configured as it has RecordingLayout.BEST_FIT as default value) + // SessionProperties (it is always configured as it has RecordingLayout.BEST_FIT + // as default value) recordingLayout = session.getSessionProperties().defaultRecordingLayout(); } else { recordingLayout = RecordingLayout.valueOf(recordingLayoutString); @@ -301,11 +333,11 @@ public class SessionRestController { Recording startedRecording = this.recordingService.startRecording(session, new RecordingProperties.Builder() .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) - public ResponseEntity stopRecordingSession(@PathVariable("recordingId") String recordingId) { + public ResponseEntity stopRecordingSession(@PathVariable("recordingId") String recordingId) { if (recordingId == null) { // "recordingId" parameter not found @@ -335,11 +367,11 @@ public class SessionRestController { session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), null, null, "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) - public ResponseEntity getRecording(@PathVariable("recordingId") String recordingId) { + public ResponseEntity getRecording(@PathVariable("recordingId") String recordingId) { try { Recording recording = this.recordingService.getAllRecordings().stream() .filter(rec -> rec.getId().equals(recordingId)).findFirst().get(); @@ -347,18 +379,17 @@ public class SessionRestController { && recordingService.getStartingRecording(recording.getId()) != null) { recording.setStatus(Recording.Status.starting); } - return new ResponseEntity<>(recording.toJson(), HttpStatus.OK); + return new ResponseEntity<>(recording.toJson().toString(), getResponseHeaders(), HttpStatus.OK); } catch (NoSuchElementException e) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } - @SuppressWarnings("unchecked") @RequestMapping(value = "/recordings", method = RequestMethod.GET) - public ResponseEntity getAllRecordings() { + public ResponseEntity getAllRecordings() { Collection recordings = this.recordingService.getAllRecordings(); - JSONObject json = new JSONObject(); - JSONArray jsonArray = new JSONArray(); + JsonObject json = new JsonObject(); + JsonArray jsonArray = new JsonArray(); recordings.forEach(rec -> { if (Recording.Status.started.equals(rec.getStatus()) && recordingService.getStartingRecording(rec.getId()) != null) { @@ -366,24 +397,29 @@ public class SessionRestController { } jsonArray.add(rec.toJson()); }); - json.put("count", recordings.size()); - json.put("items", jsonArray); - return new ResponseEntity<>(json, HttpStatus.OK); + json.addProperty("count", recordings.size()); + json.add("items", jsonArray); + return new ResponseEntity<>(json.toString(), getResponseHeaders(), HttpStatus.OK); } @RequestMapping(value = "/recordings/{recordingId}", method = RequestMethod.DELETE) - public ResponseEntity deleteRecording(@PathVariable("recordingId") String recordingId) { + public ResponseEntity deleteRecording(@PathVariable("recordingId") String recordingId) { return new ResponseEntity<>(this.recordingService.deleteRecordingFromHost(recordingId)); } - @SuppressWarnings("unchecked") - private ResponseEntity generateErrorResponse(String errorMessage, String path, HttpStatus status) { - JSONObject responseJson = new JSONObject(); - responseJson.put("timestamp", System.currentTimeMillis()); - responseJson.put("status", status.value()); - responseJson.put("error", status.getReasonPhrase()); - responseJson.put("message", errorMessage); - responseJson.put("path", path); - return new ResponseEntity<>(responseJson, status); + private ResponseEntity generateErrorResponse(String errorMessage, String path, HttpStatus status) { + JsonObject responseJson = new JsonObject(); + responseJson.addProperty("timestamp", System.currentTimeMillis()); + responseJson.addProperty("status", status.value()); + responseJson.addProperty("error", status.getReasonPhrase()); + responseJson.addProperty("message", errorMessage); + responseJson.addProperty("path", path); + return new ResponseEntity<>(responseJson.toString(), getResponseHeaders(), status); + } + + private HttpHeaders getResponseHeaders() { + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.setContentType(MediaType.APPLICATION_JSON); + return responseHeaders; } } diff --git a/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java b/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java index 0382dd60..dd02ba08 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java @@ -32,6 +32,7 @@ import org.springframework.beans.factory.annotation.Autowired; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; @@ -131,6 +132,23 @@ public class RpcHandler extends DefaultJsonRpcHandler { case ProtocolElements.FORCEUNPUBLISH_METHOD: forceUnpublish(rpcConnection, request); 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: log.error("Unrecognized request {}", request); break; @@ -291,6 +309,22 @@ public class RpcHandler extends DefaultJsonRpcHandler { sessionManager.unpublishVideo(participant, null, request.getId(), "unpublish"); } + private void streamPropertyChanged(RpcConnection rpcConnection, Request 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 request) { Participant participant; try { @@ -331,20 +365,51 @@ public class RpcHandler extends DefaultJsonRpcHandler { } - private void streamPropertyChanged(RpcConnection rpcConnection, Request request) { + private void applyFilter(RpcConnection rpcConnection, Request request) { Participant participant; try { - participant = sanityCheckOfSession(rpcConnection, "onStreamPropertyChanged"); + participant = sanityCheckOfSession(rpcConnection, "applyFilter"); } 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); + String filterType = getStringParam(request, ProtocolElements.FILTER_TYPE_PARAM); + if (participant.getToken().getKurentoTokenOptions().isFilterAllowed(filterType)) { + JsonObject filterOptions = new JsonParser().parse(getStringParam(request, ProtocolElements.FILTER_OPTIONS_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 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 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) { diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/JsonUtils.java b/openvidu-server/src/main/java/io/openvidu/server/utils/JsonUtils.java new file mode 100644 index 00000000..a5f51c73 --- /dev/null +++ b/openvidu-server/src/main/java/io/openvidu/server/utils/JsonUtils.java @@ -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 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; + } + +}