openvidu-server: filters support

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

View File

@ -17,7 +17,7 @@
package io.openvidu.server.cdr;
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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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)

View File

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

View File

@ -21,6 +21,7 @@ import org.kurento.client.MediaElement;
import org.kurento.client.MediaType;
import 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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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); }
*/
}

View File

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

View File

@ -25,8 +25,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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() {

View File

@ -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;
}
}

View File

@ -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) {

View File

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