mirror of https://github.com/OpenVidu/openvidu.git
openvidu-server: filters support
parent
ccd8d7a8b9
commit
c6f80c153a
|
@ -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<CDREvent> {
|
|||
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<CDREvent> {
|
|||
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<CDREvent> {
|
|||
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<CDREvent> {
|
|||
}
|
||||
|
||||
@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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Participant> participants, String streamId, KurentoFilter filter, OpenViduException error,
|
||||
String reason) {
|
||||
boolean isRpcFromModerator = transactionId != null && moderator != null;
|
||||
boolean isRpcFromOwner = transactionId != null && moderator == null;
|
||||
|
||||
if (isRpcFromModerator) {
|
||||
if (error != null) {
|
||||
rpcNotificationService.sendErrorResponse(moderator.getParticipantPrivateId(), transactionId, null,
|
||||
error);
|
||||
return;
|
||||
}
|
||||
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject());
|
||||
}
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(ProtocolElements.STREAMPROPERTYCHANGED_CONNECTIONID_PARAM,
|
||||
participant.getParticipantPublicId());
|
||||
params.addProperty(ProtocolElements.STREAMPROPERTYCHANGED_STREAMID_PARAM, streamId);
|
||||
params.addProperty(ProtocolElements.STREAMPROPERTYCHANGED_PROPERTY_PARAM, "filter");
|
||||
JsonObject filterJson = new JsonObject();
|
||||
if (filter != null) {
|
||||
filterJson.addProperty(ProtocolElements.FILTER_TYPE_PARAM, filter.getType());
|
||||
filterJson.add(ProtocolElements.FILTER_OPTIONS_PARAM, filter.getOptions());
|
||||
if (filter.getLastExecMethod() != null) {
|
||||
filterJson.add(ProtocolElements.EXECFILTERMETHOD_LASTEXECMETHOD_PARAM,
|
||||
filter.getLastExecMethod().toJson());
|
||||
}
|
||||
}
|
||||
params.add(ProtocolElements.STREAMPROPERTYCHANGED_NEWVALUE_PARAM, filterJson);
|
||||
params.addProperty(ProtocolElements.STREAMPROPERTYCHANGED_REASON_PARAM, reason);
|
||||
|
||||
for (Participant p : participants) {
|
||||
if (p.getParticipantPrivateId().equals(participant.getParticipantPrivateId())) {
|
||||
// Send response to the affected participant
|
||||
if (!isRpcFromOwner) {
|
||||
rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
|
||||
ProtocolElements.STREAMPROPERTYCHANGED_METHOD, params);
|
||||
} else {
|
||||
if (error != null) {
|
||||
rpcNotificationService.sendErrorResponse(p.getParticipantPrivateId(), transactionId, null,
|
||||
error);
|
||||
return;
|
||||
}
|
||||
rpcNotificationService.sendResponse(p.getParticipantPrivateId(), transactionId, new JsonObject());
|
||||
}
|
||||
} else {
|
||||
// Send response to every other user in the session different than the affected
|
||||
// participant
|
||||
if (error == null) {
|
||||
rpcNotificationService.sendNotification(p.getParticipantPrivateId(),
|
||||
ProtocolElements.STREAMPROPERTYCHANGED_METHOD, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void closeRpcSession(String participantPrivateId) {
|
||||
this.rpcNotificationService.closeRpcSession(participantPrivateId);
|
||||
}
|
||||
|
|
|
@ -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<String, Token> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<MediaEndpoint, JSONObject> toJsonFunction) {
|
||||
JSONObject json = super.toJSON();
|
||||
JSONArray publisherEnpoints = new JSONArray();
|
||||
private JsonObject sharedJson(Function<MediaEndpoint, JsonObject> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<KurentoParticipant, JSONObject> 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<KurentoParticipant, JsonObject> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Participant> existingParticipants = null;
|
||||
|
@ -91,7 +96,7 @@ public class KurentoSessionManager extends SessionManager {
|
|||
log.warn("'{}' is trying to join session '{}' but it is closing", participant.getParticipantPublicId(),
|
||||
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<JsonObject> request) {
|
||||
public KurentoMediaOptions generateMediaOptions(Request<JsonObject> 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<Participant> participants = kParticipant.getSession().getParticipants();
|
||||
sessionEventsHandler.onFilterChanged(participant, moderator, transactionId, participants, streamId,
|
||||
filter, null, reason);
|
||||
} catch (OpenViduException e) {
|
||||
log.warn("PARTICIPANT {}: Error applying filter", participant.getParticipantPublicId(), e);
|
||||
sessionEventsHandler.onFilterChanged(participant, moderator, transactionId, new HashSet<>(),
|
||||
streamId, null, e, "");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("PARTICIPANT {}: Requesting to applyFilter to stream {} "
|
||||
+ "in session {} but the owner cannot be found", streamId, session.getSessionId());
|
||||
throw new OpenViduException(Code.USER_NOT_FOUND_ERROR_CODE,
|
||||
"Owner of stream '" + streamId + "' not found in session '" + session.getSessionId() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execFilterMethod(Session session, String streamId, String filterMethod, JsonObject filterParams,
|
||||
Participant moderator, Integer transactionId, String reason) {
|
||||
String participantPrivateId = ((KurentoSession) session).getParticipantPrivateIdFromStreamId(streamId);
|
||||
if (participantPrivateId != null) {
|
||||
Participant participant = this.getParticipant(participantPrivateId);
|
||||
log.debug("Request [EXEC_FILTER_MTEHOD] over stream [{}] for reason [{}]", streamId, reason);
|
||||
KurentoParticipant kParticipant = (KurentoParticipant) participant;
|
||||
if (!participant.isStreaming()) {
|
||||
log.warn(
|
||||
"PARTICIPANT {}: Requesting to execFilterMethod to user {} "
|
||||
+ "in session {} but user is not streaming media",
|
||||
moderator != null ? moderator.getParticipantPublicId() : participant.getParticipantPublicId(),
|
||||
participant.getParticipantPublicId(), session.getSessionId());
|
||||
throw new OpenViduException(Code.USER_NOT_STREAMING_ERROR_CODE,
|
||||
"User '" + participant.getParticipantPublicId() + " not streaming media in session '"
|
||||
+ session.getSessionId() + "'");
|
||||
} else if (kParticipant.getPublisher().getFilter() == null) {
|
||||
log.warn(
|
||||
"PARTICIPANT {}: Requesting to execFilterMethod to user {} "
|
||||
+ "in session {} but user does NOT have a filter",
|
||||
moderator != null ? moderator.getParticipantPublicId() : participant.getParticipantPublicId(),
|
||||
participant.getParticipantPublicId(), session.getSessionId());
|
||||
throw new OpenViduException(Code.FILTER_NOT_APPLIED_ERROR_CODE,
|
||||
"User '" + participant.getParticipantPublicId() + " has no filter applied in session '"
|
||||
+ session.getSessionId() + "'");
|
||||
} else {
|
||||
KurentoFilter updatedFilter = this.execFilterMethodInPublisher(kParticipant, filterMethod,
|
||||
filterParams);
|
||||
Set<Participant> participants = kParticipant.getSession().getParticipants();
|
||||
sessionEventsHandler.onFilterChanged(participant, moderator, transactionId, participants, streamId,
|
||||
updatedFilter, null, reason);
|
||||
}
|
||||
} else {
|
||||
log.warn("PARTICIPANT {}: Requesting to removeFilter to stream {} "
|
||||
+ "in session {} but the owner cannot be found", streamId, session.getSessionId());
|
||||
throw new OpenViduException(Code.USER_NOT_FOUND_ERROR_CODE,
|
||||
"Owner of stream '" + streamId + "' not found in session '" + session.getSessionId() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFilter(Session session, String streamId, Participant moderator, Integer transactionId,
|
||||
String reason) {
|
||||
String participantPrivateId = ((KurentoSession) session).getParticipantPrivateIdFromStreamId(streamId);
|
||||
if (participantPrivateId != null) {
|
||||
Participant participant = this.getParticipant(participantPrivateId);
|
||||
log.debug("Request [REMOVE_FILTER] over stream [{}] for reason [{}]", streamId, reason);
|
||||
KurentoParticipant kParticipant = (KurentoParticipant) participant;
|
||||
if (!participant.isStreaming()) {
|
||||
log.warn(
|
||||
"PARTICIPANT {}: Requesting to removeFilter to user {} "
|
||||
+ "in session {} but user is not streaming media",
|
||||
moderator != null ? moderator.getParticipantPublicId() : participant.getParticipantPublicId(),
|
||||
participant.getParticipantPublicId(), session.getSessionId());
|
||||
throw new OpenViduException(Code.USER_NOT_STREAMING_ERROR_CODE,
|
||||
"User '" + participant.getParticipantPublicId() + " not streaming media in session '"
|
||||
+ session.getSessionId() + "'");
|
||||
} else if (kParticipant.getPublisher().getFilter() == null) {
|
||||
log.warn(
|
||||
"PARTICIPANT {}: Requesting to removeFilter to user {} "
|
||||
+ "in session {} but user does NOT have a filter",
|
||||
moderator != null ? moderator.getParticipantPublicId() : participant.getParticipantPublicId(),
|
||||
participant.getParticipantPublicId(), session.getSessionId());
|
||||
throw new OpenViduException(Code.FILTER_NOT_APPLIED_ERROR_CODE,
|
||||
"User '" + participant.getParticipantPublicId() + " has no filter applied in session '"
|
||||
+ session.getSessionId() + "'");
|
||||
} else {
|
||||
this.removeFilterInPublisher(kParticipant);
|
||||
Set<Participant> participants = kParticipant.getSession().getParticipants();
|
||||
sessionEventsHandler.onFilterChanged(participant, moderator, transactionId, participants, streamId,
|
||||
null, null, reason);
|
||||
}
|
||||
} else {
|
||||
log.warn("PARTICIPANT {}: Requesting to removeFilter to stream {} "
|
||||
+ "in session {} but the owner cannot be found", streamId, session.getSessionId());
|
||||
throw new OpenViduException(Code.USER_NOT_FOUND_ERROR_CODE,
|
||||
"Owner of stream '" + streamId + "' not found in session '" + session.getSessionId() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private void applyFilterInPublisher(KurentoParticipant kParticipant, KurentoFilter filter)
|
||||
throws OpenViduException {
|
||||
GenericMediaElement.Builder builder = new GenericMediaElement.Builder(kParticipant.getPipeline(),
|
||||
filter.getType());
|
||||
Props props = new JsonUtils().fromJsonObjectToProps(filter.getOptions());
|
||||
props.forEach(prop -> {
|
||||
builder.withConstructorParam(prop.getName(), prop.getValue());
|
||||
});
|
||||
kParticipant.getPublisher().apply(builder.build());
|
||||
kParticipant.getPublisher().getMediaOptions().setFilter(filter);
|
||||
}
|
||||
|
||||
private KurentoFilter execFilterMethodInPublisher(KurentoParticipant kParticipant, String method,
|
||||
JsonObject params) {
|
||||
kParticipant.getPublisher().execMethod(method, params);
|
||||
KurentoFilter filter = kParticipant.getPublisher().getMediaOptions().getFilter();
|
||||
KurentoFilter updatedFilter = new KurentoFilter(filter.getType(), filter.getOptions(), method, params);
|
||||
kParticipant.getPublisher().getMediaOptions().setFilter(updatedFilter);
|
||||
return filter;
|
||||
}
|
||||
|
||||
private void removeFilterInPublisher(KurentoParticipant kParticipant) {
|
||||
kParticipant.getPublisher().revert(kParticipant.getPublisher().getFilter());
|
||||
kParticipant.getPublisher().getMediaOptions().setFilter(null);
|
||||
}
|
||||
|
||||
/*
|
||||
* private void addFilterEventListenerInPublisher(KurentoParticipant
|
||||
* kParticipant) { this.listener =
|
||||
* kParticipant.getPublisher().getFilter().addEventListener("CodeFound", event
|
||||
* -> { System.out.println(event.getData()); }); } private void
|
||||
* removeFilterEventListenerInPublisher(KurentoParticipant kParticipant) {
|
||||
* kParticipant.getPublisher().getFilter().removeEventListener(this.listener); }
|
||||
*/
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2018 OpenVidu (https://openvidu.io/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.openvidu.server.kurento.core;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class KurentoTokenOptions {
|
||||
|
||||
private Integer videoMaxRecvBandwidth;
|
||||
private Integer videoMinRecvBandwidth;
|
||||
private Integer videoMaxSendBandwidth;
|
||||
private Integer videoMinSendBandwidth;
|
||||
private Map<String, Boolean> allowedFilters = new ConcurrentHashMap<>();
|
||||
|
||||
public KurentoTokenOptions(JsonObject options) {
|
||||
if (options.has("videoMaxRecvBandwidth")) {
|
||||
this.videoMaxRecvBandwidth = options.get("videoMaxRecvBandwidth").getAsInt();
|
||||
}
|
||||
if (options.has("videoMinRecvBandwidth")) {
|
||||
this.videoMinRecvBandwidth = options.get("videoMinRecvBandwidth").getAsInt();
|
||||
}
|
||||
if (options.has("videoMaxSendBandwidth")) {
|
||||
this.videoMaxSendBandwidth = options.get("videoMaxSendBandwidth").getAsInt();
|
||||
}
|
||||
if (options.has("videoMinSendBandwidth")) {
|
||||
this.videoMinSendBandwidth = options.get("videoMinSendBandwidth").getAsInt();
|
||||
}
|
||||
if (options.has("allowedFilters")) {
|
||||
JsonArray filters = options.get("allowedFilters").getAsJsonArray();
|
||||
Iterator<JsonElement> it = filters.iterator();
|
||||
while (it.hasNext()) {
|
||||
this.allowedFilters.put(it.next().getAsString(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getVideoMaxRecvBandwidth() {
|
||||
return videoMaxRecvBandwidth;
|
||||
}
|
||||
|
||||
public Integer getVideoMinRecvBandwidth() {
|
||||
return videoMinRecvBandwidth;
|
||||
}
|
||||
|
||||
public Integer getVideoMaxSendBandwidth() {
|
||||
return videoMaxSendBandwidth;
|
||||
}
|
||||
|
||||
public Integer getVideoMinSendBandwidth() {
|
||||
return videoMinSendBandwidth;
|
||||
}
|
||||
|
||||
public String[] getAllowedFilters() {
|
||||
return allowedFilters.keySet().stream().toArray(String[]::new);
|
||||
}
|
||||
|
||||
public boolean isFilterAllowed(String filterType) {
|
||||
return this.allowedFilters.containsKey(filterType);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String, MediaElement> elements = new HashMap<String, MediaElement>();
|
||||
private LinkedList<String> elementIds = new LinkedList<String>();
|
||||
private boolean connected = false;
|
||||
|
||||
private Map<String, ListenerSubscription> elementsErrorSubscriptions = new HashMap<String, ListenerSubscription>();
|
||||
|
||||
public PublisherEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline, OpenviduConfig openviduConfig) {
|
||||
public PublisherEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline,
|
||||
OpenviduConfig openviduConfig) {
|
||||
super(web, owner, endpointName, pipeline, openviduConfig, log);
|
||||
}
|
||||
|
||||
|
@ -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<String, JsonElement> entry : toJson.entrySet()) {
|
||||
json.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
|
|
@ -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<String, JsonElement> entry : toJson.entrySet()) {
|
||||
json.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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<JSONObject> 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<JSONObject> 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<JSONObject> listSessions(
|
||||
public ResponseEntity<?> listSessions(
|
||||
@RequestParam(value = "webRtcStats", defaultValue = "false", required = false) boolean webRtcStats) {
|
||||
Collection<Session> 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<JSONObject> 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<JSONObject> 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<JSONObject> 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<JSONObject>
|
||||
* muteMedia(@PathVariable("sessionId") String sessionId,
|
||||
*
|
||||
* @PathVariable("streamId") String streamId, @RequestBody Map<?, ?> params) { }
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@RequestMapping(value = "/tokens", method = RequestMethod.POST)
|
||||
public ResponseEntity<JSONObject> newToken(@RequestBody Map<?, ?> params) {
|
||||
public ResponseEntity<String> 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<JSONObject> 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<JSONObject> 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<JSONObject> 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<JSONObject> getAllRecordings() {
|
||||
public ResponseEntity<?> getAllRecordings() {
|
||||
Collection<Recording> 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<JSONObject> deleteRecording(@PathVariable("recordingId") String recordingId) {
|
||||
public ResponseEntity<?> deleteRecording(@PathVariable("recordingId") String recordingId) {
|
||||
return new ResponseEntity<>(this.recordingService.deleteRecordingFromHost(recordingId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ResponseEntity<JSONObject> 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<String> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<JsonObject> {
|
|||
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<JsonObject> {
|
|||
sessionManager.unpublishVideo(participant, null, request.getId(), "unpublish");
|
||||
}
|
||||
|
||||
private void streamPropertyChanged(RpcConnection rpcConnection, Request<JsonObject> request) {
|
||||
Participant participant;
|
||||
try {
|
||||
participant = sanityCheckOfSession(rpcConnection, "onStreamPropertyChanged");
|
||||
} catch (OpenViduException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
String streamId = getStringParam(request, ProtocolElements.STREAMPROPERTYCHANGED_STREAMID_PARAM);
|
||||
String property = getStringParam(request, ProtocolElements.STREAMPROPERTYCHANGED_PROPERTY_PARAM);
|
||||
JsonElement newValue = getParam(request, ProtocolElements.STREAMPROPERTYCHANGED_NEWVALUE_PARAM);
|
||||
String reason = getStringParam(request, ProtocolElements.STREAMPROPERTYCHANGED_REASON_PARAM);
|
||||
|
||||
sessionManager.streamPropertyChanged(participant, request.getId(), streamId, property, newValue, reason);
|
||||
}
|
||||
|
||||
private void forceDisconnect(RpcConnection rpcConnection, Request<JsonObject> request) {
|
||||
Participant participant;
|
||||
try {
|
||||
|
@ -331,20 +365,51 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
|
|||
|
||||
}
|
||||
|
||||
private void streamPropertyChanged(RpcConnection rpcConnection, Request<JsonObject> request) {
|
||||
private void applyFilter(RpcConnection rpcConnection, Request<JsonObject> request) {
|
||||
Participant participant;
|
||||
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<JsonObject> request) {
|
||||
try {
|
||||
sanityCheckOfSession(rpcConnection, "applyFilter");
|
||||
} catch (OpenViduException e) {
|
||||
return;
|
||||
}
|
||||
String filterMethod = getStringParam(request, ProtocolElements.FILTER_METHOD_PARAM);
|
||||
JsonObject filterParams = new JsonParser().parse(getStringParam(request, ProtocolElements.FILTER_PARAMS_PARAM))
|
||||
.getAsJsonObject();
|
||||
String streamId = getStringParam(request, ProtocolElements.FILTER_STREAMID_PARAM);
|
||||
sessionManager.execFilterMethod(sessionManager.getSession(rpcConnection.getSessionId()), streamId, filterMethod,
|
||||
filterParams, null, request.getId(), "execFilterMethod");
|
||||
}
|
||||
|
||||
private void removeFilter(RpcConnection rpcConnection, Request<JsonObject> request) {
|
||||
try {
|
||||
sanityCheckOfSession(rpcConnection, "removeFilter");
|
||||
} catch (OpenViduException e) {
|
||||
return;
|
||||
}
|
||||
String streamId = getStringParam(request, ProtocolElements.FILTER_STREAMID_PARAM);
|
||||
sessionManager.removeFilter(sessionManager.getSession(rpcConnection.getSessionId()), streamId, null,
|
||||
request.getId(), "removeFilter");
|
||||
}
|
||||
|
||||
public void leaveRoomAfterConnClosed(String participantPrivateId, String reason) {
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2018 OpenVidu (https://openvidu.io/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.openvidu.server.utils;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.kurento.jsonrpc.Props;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class JsonUtils {
|
||||
|
||||
public Props fromJsonObjectToProps(JsonObject params) {
|
||||
Props props = new Props();
|
||||
for (Entry<String, JsonElement> entry : params.entrySet()) {
|
||||
if (entry.getValue().isJsonPrimitive()) {
|
||||
props.add(entry.getKey(), entry.getValue().getAsString());
|
||||
} else if (entry.getValue().isJsonObject()) {
|
||||
props.add(entry.getKey(), fromJsonObjectToProps(entry.getValue().getAsJsonObject()));
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue