From 34d9c197d8c51ae7cf9767e26b76516f1ca0b975 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Thu, 5 Jul 2018 17:38:07 +0200 Subject: [PATCH 01/13] openvidu-testapp: when unpublished hide publisher buttons --- .../main/java/io/openvidu/server/core/Participant.java | 2 +- .../src/main/java/io/openvidu/server/core/Session.java | 10 +++++----- .../src/app/components/video/video.component.html | 8 ++++---- .../src/app/components/video/video.component.ts | 4 ++++ 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java b/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java index c119e42e..6fc89fcd 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java @@ -93,7 +93,7 @@ public class Participant { this.streaming = streaming; } - public String getPublisherStremId() { + public String getPublisherStreamId() { return null; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Session.java b/openvidu-server/src/main/java/io/openvidu/server/core/Session.java index d9a80e30..a5f5d346 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/Session.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/Session.java @@ -26,13 +26,13 @@ import io.openvidu.java.client.SessionProperties; public interface Session { String getSessionId(); - + SessionProperties getSessionProperties(); void join(Participant participant); - + void leave(String participantPrivateId, String reason); - + boolean close(String reason); boolean isClosed(); @@ -44,9 +44,9 @@ public interface Session { Participant getParticipantByPublicId(String participantPublicId); int getActivePublishers(); - + JSONObject toJSON(); - + JSONObject withStatsToJSON(); } diff --git a/openvidu-testapp/src/app/components/video/video.component.html b/openvidu-testapp/src/app/components/video/video.component.html index 3c1099da..b410d664 100644 --- a/openvidu-testapp/src/app/components/video/video.component.html +++ b/openvidu-testapp/src/app/components/video/video.component.html @@ -10,16 +10,16 @@ - - - - diff --git a/openvidu-testapp/src/app/components/video/video.component.ts b/openvidu-testapp/src/app/components/video/video.component.ts index 407a598e..ea0a0179 100644 --- a/openvidu-testapp/src/app/components/video/video.component.ts +++ b/openvidu-testapp/src/app/components/video/video.component.ts @@ -450,6 +450,10 @@ export class VideoComponent implements OnInit, OnDestroy { event: 'streamDestroyed', content: e.stream.streamId }); + if (e.reason.indexOf('forceUnpublish') !== -1) { + this.unpublished = !this.unpublished; + this.unpublished ? this.pubSubIcon = 'play_arrow' : this.pubSubIcon = 'stop'; + } }); } } else { From d011c5b88b6193e7889b6723ef8210121f98d3c5 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Thu, 5 Jul 2018 17:49:35 +0200 Subject: [PATCH 02/13] openvidu-server: REST API update (DELETE connection and DELETE stream) --- .../server/core/SessionEventsHandler.java | 24 ++++++++++---- .../openvidu/server/core/SessionManager.java | 2 ++ .../kurento/core/KurentoParticipant.java | 11 +++++-- .../server/kurento/core/KurentoSession.java | 14 ++++---- .../kurento/core/KurentoSessionManager.java | 20 +++++++++-- .../server/rest/SessionRestController.java | 33 +++++++++++++++++++ 6 files changed, 85 insertions(+), 19 deletions(-) diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java index 29e9d22a..656f422d 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java @@ -96,7 +96,7 @@ public class SessionEventsHandler { JsonObject stream = new JsonObject(); stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMID_PARAM, - existingParticipant.getPublisherStremId()); + existingParticipant.getPublisherStreamId()); stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMHASAUDIO_PARAM, kParticipant.getPublisherMediaOptions().hasAudio); stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMHASVIDEO_PARAM, @@ -216,11 +216,15 @@ public class SessionEventsHandler { public void onUnpublishMedia(Participant participant, Set participants, Integer transactionId, OpenViduException error, String reason) { - if (error != null) { - rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error); - return; + boolean force = reason.contains("force") || transactionId == null; + if (!force) { + if (error != null) { + rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, + error); + return; + } + rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject()); } - rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject()); JsonObject params = new JsonObject(); params.addProperty(ProtocolElements.PARTICIPANTUNPUBLISHED_NAME_PARAM, participant.getParticipantPublicId()); @@ -228,7 +232,12 @@ public class SessionEventsHandler { for (Participant p : participants) { if (p.getParticipantPrivateId().equals(participant.getParticipantPrivateId())) { - continue; + if (force) { + rpcNotificationService.sendNotification(p.getParticipantPrivateId(), + ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params); + } else { + continue; + } } else { rpcNotificationService.sendNotification(p.getParticipantPrivateId(), ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params); @@ -351,7 +360,8 @@ public class SessionEventsHandler { public void onParticipantEvicted(Participant participant, String reason) { JsonObject params = new JsonObject(); - params.addProperty(ProtocolElements.PARTICIPANTEVICTED_CONNECTIONID_PARAM, participant.getParticipantPublicId()); + params.addProperty(ProtocolElements.PARTICIPANTEVICTED_CONNECTIONID_PARAM, + participant.getParticipantPublicId()); params.addProperty(ProtocolElements.PARTICIPANTEVICTED_REASON_PARAM, reason); rpcNotificationService.sendNotification(participant.getParticipantPrivateId(), ProtocolElements.PARTICIPANTEVICTED_METHOD, params); diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java index ab36a20a..fa90596b 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java @@ -94,6 +94,8 @@ 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, String reason); + /** * Application-originated request to remove a participant from a session.
* Side effects: The session event handler should notify the diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java index 1194b72c..57066d98 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java @@ -107,6 +107,9 @@ public class KurentoParticipant extends Participant { + RandomStringUtils.random(5, true, false).toUpperCase(); this.publisher.getEndpoint().addTag("name", publisherStreamId); addEndpointListeners(this.publisher); + + // Remove streamId from publisher's map + this.session.publishedStreamIds.putIfAbsent(this.getPublisherStreamId(), this.getParticipantPrivateId()); CDR.recordNewPublisher(this, this.session.getSessionId(), mediaOptions); @@ -300,7 +303,7 @@ public class KurentoParticipant extends Participant { throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE, "Unable to create subscriber endpoint"); } - String subscriberStreamId = this.getParticipantPublicId() + "_" + kSender.getPublisherStremId(); + String subscriberStreamId = this.getParticipantPublicId() + "_" + kSender.getPublisherStreamId(); subscriber.getEndpoint().addTag("name", subscriberStreamId); @@ -468,6 +471,10 @@ public class KurentoParticipant extends Participant { private void releasePublisherEndpoint(String reason) { if (publisher != null && publisher.getEndpoint() != null) { + + // Store streamId from publisher's map + this.session.publishedStreamIds.remove(this.getPublisherStreamId()); + publisher.unregisterErrorListeners(); for (MediaElement el : publisher.getMediaElements()) { releaseElement(getParticipantPublicId(), el); @@ -695,7 +702,7 @@ public class KurentoParticipant extends Participant { } @Override - public String getPublisherStremId() { + public String getPublisherStreamId() { return this.publisher.getEndpoint().getTag("name"); } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java index 4d88122a..96203336 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java @@ -25,7 +25,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; -import java.util.stream.Collectors; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -45,8 +44,6 @@ import io.openvidu.java.client.SessionProperties; import io.openvidu.server.cdr.CallDetailRecord; import io.openvidu.server.core.Participant; import io.openvidu.server.core.Session; -import io.openvidu.server.kurento.endpoint.PublisherEndpoint; -import io.openvidu.server.kurento.endpoint.SubscriberEndpoint; /** * @author Pablo Fuente (pablofuenteperez@gmail.com) @@ -77,6 +74,8 @@ public class KurentoSession implements Session { private boolean destroyKurentoClient; private CallDetailRecord CDR; + + public final ConcurrentHashMap publishedStreamIds = new ConcurrentHashMap<>(); public KurentoSession(String sessionId, SessionProperties sessionProperties, KurentoClient kurentoClient, KurentoSessionEventsHandler kurentoSessionHandler, boolean destroyKurentoClient, CallDetailRecord CDR) { @@ -199,11 +198,6 @@ public class KurentoSession implements Session { return null; } - public Set getAllSubscribersForPublisher(PublisherEndpoint publisher) { - return this.participants.values().stream().flatMap(kp -> kp.getConnectedSubscribedEndpoints(publisher).stream()) - .collect(Collectors.toSet()); - } - @Override public boolean close(String reason) { if (!closed) { @@ -392,4 +386,8 @@ public class KurentoSession implements Session { return json; } + public String getParticipantPrivateIdFromStreamId(String streamId) { + return this.publishedStreamIds.get(streamId); + } + } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java index 8956910d..e3dc1b80 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java @@ -261,7 +261,7 @@ public class KurentoSessionManager extends SessionManager { participants = kurentoParticipant.getSession().getParticipants(); if (sdpAnswer != null) { - sessionEventsHandler.onPublishMedia(participant, participant.getPublisherStremId(), session.getSessionId(), + sessionEventsHandler.onPublishMedia(participant, participant.getPublisherStreamId(), session.getSessionId(), mediaOptions, sdpAnswer, participants, transactionId, null); } } @@ -373,7 +373,7 @@ public class KurentoSessionManager extends SessionManager { public void streamPropertyChanged(Participant participant, Integer transactionId, String streamId, String property, JsonElement newValue, String reason) { KurentoParticipant kParticipant = (KurentoParticipant) participant; - streamId = kParticipant.getPublisherStremId(); + streamId = kParticipant.getPublisherStreamId(); MediaOptions streamProperties = kParticipant.getPublisherMediaOptions(); Boolean hasAudio = streamProperties.hasAudio(); @@ -509,4 +509,20 @@ public class KurentoSessionManager extends SessionManager { typeOfVideo, frameRate, videoDimensions, doLoopback); } + @Override + public boolean unpublishStream(Session session, String streamId, String reason) { + String participantPrivateId = ((KurentoSession) session).getParticipantPrivateIdFromStreamId(streamId); + if (participantPrivateId != null) { + Participant participant = this.getParticipant(participantPrivateId); + if (participant != null) { + this.unpublishVideo(participant, null, reason); + return true; + } else { + return false; + } + } else { + return false; + } + } + } diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java index caf0c7ff..4c626a40 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java @@ -43,6 +43,7 @@ import io.openvidu.java.client.RecordingMode; import io.openvidu.java.client.RecordingProperties; import io.openvidu.java.client.SessionProperties; import io.openvidu.server.config.OpenviduConfig; +import io.openvidu.server.core.Participant; import io.openvidu.server.core.ParticipantRole; import io.openvidu.server.core.Session; import io.openvidu.server.core.SessionManager; @@ -177,6 +178,38 @@ public class SessionRestController { } } + @RequestMapping(value = "/sessions/{sessionId}/connection/{connectionId}", method = RequestMethod.DELETE) + public ResponseEntity disconnectParticipant(@PathVariable("sessionId") String sessionId, + @PathVariable("connectionId") String participantPublicId) { + Session session = this.sessionManager.getSession(sessionId); + if (session != null) { + Participant participant = session.getParticipantByPublicId(participantPublicId); + if (participant != null) { + this.sessionManager.evictParticipant(participant.getParticipantPrivateId(), "forceDisconnectByServer"); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } else { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + } + + @RequestMapping(value = "/sessions/{sessionId}/stream/{streamId}", method = RequestMethod.DELETE) + public ResponseEntity unpublishStream(@PathVariable("sessionId") String sessionId, + @PathVariable("streamId") String streamId) { + Session session = this.sessionManager.getSession(sessionId); + if (session != null) { + if (this.sessionManager.unpublishStream(session, streamId, "forceUnpublishByServer")) { + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } else { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + } + @SuppressWarnings("unchecked") @RequestMapping(value = "/tokens", method = RequestMethod.POST) public ResponseEntity newToken(@RequestBody Map params) { From cb59dbcc4d0198149ef91a6468cfe34a928e9803 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Thu, 5 Jul 2018 17:50:49 +0200 Subject: [PATCH 03/13] openvidu-browser: RPC listeners updated to support forced operations --- openvidu-browser/src/OpenVidu/Publisher.ts | 2 +- openvidu-browser/src/OpenVidu/Session.ts | 53 +++++++++++++--------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/openvidu-browser/src/OpenVidu/Publisher.ts b/openvidu-browser/src/OpenVidu/Publisher.ts index e6bcbb10..c5ee097f 100644 --- a/openvidu-browser/src/OpenVidu/Publisher.ts +++ b/openvidu-browser/src/OpenVidu/Publisher.ts @@ -76,7 +76,7 @@ export class Publisher extends StreamManager { this.properties = properties; this.openvidu = openvidu; - this.stream.ee.on('local-stream-destroyed-by-disconnect', (reason: string) => { + this.stream.ee.on('local-stream-destroyed', (reason: string) => { const streamEvent = new StreamEvent(true, this, 'streamDestroyed', this.stream, reason); this.emitEvent('streamDestroyed', [streamEvent]); streamEvent.callDefaultBehavior(); diff --git a/openvidu-browser/src/OpenVidu/Session.ts b/openvidu-browser/src/OpenVidu/Session.ts index 41b788ce..b38d9560 100644 --- a/openvidu-browser/src/OpenVidu/Session.ts +++ b/openvidu-browser/src/OpenVidu/Session.ts @@ -630,23 +630,28 @@ export class Session implements EventDispatcher { * @hidden */ onParticipantUnpublished(msg): void { - this.getRemoteConnection(msg.connectionId, "Remote connection '" + msg.connectionId + "' unknown when 'onParticipantUnpublished'. " + - 'Existing remote connections: ' + JSON.stringify(Object.keys(this.remoteConnections))) + if (msg.connectionId === this.connection.connectionId) { + // Your stream has been forcedly unpublished from the session + this.stopPublisherStream(msg.reason); + } else { + this.getRemoteConnection(msg.connectionId, "Remote connection '" + msg.connectionId + "' unknown when 'onParticipantUnpublished'. " + + 'Existing remote connections: ' + JSON.stringify(Object.keys(this.remoteConnections))) - .then(connection => { + .then(connection => { - const streamEvent = new StreamEvent(true, this, 'streamDestroyed', connection.stream, msg.reason); - this.ee.emitEvent('streamDestroyed', [streamEvent]); - streamEvent.callDefaultBehavior(); + const streamEvent = new StreamEvent(true, this, 'streamDestroyed', connection.stream, msg.reason); + this.ee.emitEvent('streamDestroyed', [streamEvent]); + streamEvent.callDefaultBehavior(); - // Deleting the remote stream - const streamId: string = connection.stream.streamId; - delete this.remoteStreamsCreated[streamId]; - connection.removeStream(streamId); - }) - .catch(openViduError => { - console.error(openViduError); - }); + // Deleting the remote stream + const streamId: string = connection.stream.streamId; + delete this.remoteStreamsCreated[streamId]; + connection.removeStream(streamId); + }) + .catch(openViduError => { + console.error(openViduError); + }); + } } /** @@ -860,14 +865,7 @@ export class Session implements EventDispatcher { this.openvidu.closeWs(); } - if (!!this.connection.stream) { - // Dispose Publisher's local stream - this.connection.stream.disposeWebRtcPeer(); - if (this.connection.stream.isLocalStreamPublished) { - // Make Publisher object dispatch 'streamDestroyed' event if the Stream was published - this.connection.stream.ee.emitEvent('local-stream-destroyed-by-disconnect', [reason]); - } - } + this.stopPublisherStream(reason); if (!this.connection.disposed) { // Make Session object dispatch 'sessionDisconnected' event (if it is not already disposed) @@ -951,6 +949,17 @@ export class Session implements EventDispatcher { }); } + private stopPublisherStream(reason: string) { + if (!!this.connection.stream) { + // Dispose Publisher's local stream + this.connection.stream.disposeWebRtcPeer(); + if (this.connection.stream.isLocalStreamPublished) { + // Make Publisher object dispatch 'streamDestroyed' event if the Stream was published + this.connection.stream.ee.emitEvent('local-stream-destroyed', [reason]); + } + } + } + private stringClientMetadata(metadata: any): string { if (typeof metadata !== 'string') { return JSON.stringify(metadata); From 2571a8a33cc55b3810b77c02430983ecc17dfc6a Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Fri, 6 Jul 2018 11:31:41 +0200 Subject: [PATCH 04/13] openvidu-server: KMS track muting refactoring --- .../{MutedMediaType.java => TrackType.java} | 2 +- .../kurento/core/KurentoParticipant.java | 56 +++---------------- .../kurento/core/KurentoSessionManager.java | 11 ++-- .../kurento/endpoint/MediaEndpoint.java | 47 ---------------- .../kurento/endpoint/PublisherEndpoint.java | 25 ++++++--- .../kurento/endpoint/SubscriberEndpoint.java | 28 ---------- .../server/rest/SessionRestController.java | 8 +++ .../io/openvidu/server/rpc/RpcHandler.java | 10 ++-- 8 files changed, 42 insertions(+), 145 deletions(-) rename openvidu-server/src/main/java/io/openvidu/server/kurento/{MutedMediaType.java => TrackType.java} (95%) diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/MutedMediaType.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/TrackType.java similarity index 95% rename from openvidu-server/src/main/java/io/openvidu/server/kurento/MutedMediaType.java rename to openvidu-server/src/main/java/io/openvidu/server/kurento/TrackType.java index 508c0cb8..f39648b2 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/MutedMediaType.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/TrackType.java @@ -17,6 +17,6 @@ package io.openvidu.server.kurento; -public enum MutedMediaType { +public enum TrackType { ALL, VIDEO, AUDIO; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java index 57066d98..b840d64f 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java @@ -49,7 +49,7 @@ import io.openvidu.server.config.InfoHandler; import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.core.MediaOptions; import io.openvidu.server.core.Participant; -import io.openvidu.server.kurento.MutedMediaType; +import io.openvidu.server.kurento.TrackType; import io.openvidu.server.kurento.endpoint.KmsEvent; import io.openvidu.server.kurento.endpoint.MediaEndpoint; import io.openvidu.server.kurento.endpoint.PublisherEndpoint; @@ -107,7 +107,7 @@ public class KurentoParticipant extends Participant { + RandomStringUtils.random(5, true, false).toUpperCase(); this.publisher.getEndpoint().addTag("name", publisherStreamId); addEndpointListeners(this.publisher); - + // Remove streamId from publisher's map this.session.publishedStreamIds.putIfAbsent(this.getPublisherStreamId(), this.getParticipantPrivateId()); @@ -353,54 +353,12 @@ public class KurentoParticipant extends Participant { } } - public void mutePublishedMedia(MutedMediaType muteType) { - if (muteType == null) { - throw new OpenViduException(Code.MEDIA_MUTE_ERROR_CODE, "Mute type cannot be null"); - } - this.getPublisher().mute(muteType); + public void mutePublishedMedia(TrackType trackType) { + this.getPublisher().mute(trackType); } - public void unmutePublishedMedia() { - if (this.getPublisher().getMuteType() == null) { - log.warn("PARTICIPANT {}: Trying to unmute published media. " + "But media is not muted.", - this.getParticipantPublicId()); - } else { - this.getPublisher().unmute(); - } - } - - public void muteSubscribedMedia(Participant sender, MutedMediaType muteType) { - if (muteType == null) { - throw new OpenViduException(Code.MEDIA_MUTE_ERROR_CODE, "Mute type cannot be null"); - } - String senderName = sender.getParticipantPublicId(); - SubscriberEndpoint subscriberEndpoint = subscribers.get(senderName); - if (subscriberEndpoint == null || subscriberEndpoint.getEndpoint() == null) { - log.warn("PARTICIPANT {}: Trying to mute incoming media from user {}. " - + "But there is no such subscriber endpoint.", this.getParticipantPublicId(), senderName); - } else { - log.debug("PARTICIPANT {}: Mute subscriber endpoint linked to user {}", this.getParticipantPublicId(), - senderName); - subscriberEndpoint.mute(muteType); - } - } - - public void unmuteSubscribedMedia(Participant sender) { - String senderName = sender.getParticipantPublicId(); - SubscriberEndpoint subscriberEndpoint = subscribers.get(senderName); - if (subscriberEndpoint == null || subscriberEndpoint.getEndpoint() == null) { - log.warn("PARTICIPANT {}: Trying to unmute incoming media from user {}. " - + "But there is no such subscriber endpoint.", this.getParticipantPublicId(), senderName); - } else { - if (subscriberEndpoint.getMuteType() == null) { - log.warn("PARTICIPANT {}: Trying to unmute incoming media from user {}. " + "But media is not muted.", - this.getParticipantPublicId(), senderName); - } else { - log.debug("PARTICIPANT {}: Unmute subscriber endpoint linked to user {}", this.getParticipantPublicId(), - senderName); - subscriberEndpoint.unmute(); - } - } + public void unmutePublishedMedia(TrackType trackType) { + this.getPublisher().unmute(trackType); } public void close(String reason) { @@ -471,7 +429,7 @@ public class KurentoParticipant extends Participant { private void releasePublisherEndpoint(String reason) { if (publisher != null && publisher.getEndpoint() != null) { - + // Store streamId from publisher's map this.session.publishedStreamIds.remove(this.getPublisherStreamId()); diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java index e3dc1b80..babced90 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java @@ -36,20 +36,20 @@ import com.google.gson.JsonSyntaxException; import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; import io.openvidu.client.internal.ProtocolElements; +import io.openvidu.java.client.MediaMode; import io.openvidu.java.client.RecordingLayout; import io.openvidu.java.client.RecordingMode; import io.openvidu.java.client.RecordingProperties; -import io.openvidu.java.client.MediaMode; import io.openvidu.java.client.SessionProperties; +import io.openvidu.server.core.MediaOptions; +import io.openvidu.server.core.Participant; +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.OpenViduKurentoClientSessionInfo; import io.openvidu.server.kurento.endpoint.SdpType; import io.openvidu.server.rpc.RpcHandler; -import io.openvidu.server.core.MediaOptions; -import io.openvidu.server.core.Participant; -import io.openvidu.server.core.Session; public class KurentoSessionManager extends SessionManager { @@ -105,7 +105,8 @@ public class KurentoSessionManager extends SessionManager { } @Override - public synchronized void leaveRoom(Participant participant, Integer transactionId, String reason, boolean closeWebSocket) { + public synchronized void leaveRoom(Participant participant, Integer transactionId, String reason, + boolean closeWebSocket) { log.debug("Request [LEAVE_ROOM] ({})", participant.getParticipantPublicId()); KurentoParticipant kParticipant = (KurentoParticipant) participant; diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java index 471115a2..a62930ab 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java @@ -45,7 +45,6 @@ import org.slf4j.LoggerFactory; import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; import io.openvidu.server.core.Participant; -import io.openvidu.server.kurento.MutedMediaType; import io.openvidu.server.kurento.core.KurentoParticipant; /** @@ -73,8 +72,6 @@ public abstract class MediaEndpoint { private final List receivedCandidateList = new LinkedList(); private LinkedList candidates = new LinkedList(); - private MutedMediaType muteType; - public Map flowInMedia = new ConcurrentHashMap<>(); public Map flowOutMedia = new ConcurrentHashMap<>(); @@ -204,50 +201,6 @@ public abstract class MediaEndpoint { unregisterElementErrListener(endpoint, endpointSubscription); } - /** - * Mute the media stream. - * - * @param muteType - * which type of leg to disconnect (audio, video or both) - */ - public abstract void mute(MutedMediaType muteType); - - /** - * Reconnect the muted media leg(s). - */ - public abstract void unmute(); - - public void setMuteType(MutedMediaType muteType) { - this.muteType = muteType; - } - - public MutedMediaType getMuteType() { - return this.muteType; - } - - protected void resolveCurrentMuteType(MutedMediaType newMuteType) { - MutedMediaType prev = this.getMuteType(); - if (prev != null) { - switch (prev) { - case AUDIO: - if (muteType.equals(MutedMediaType.VIDEO)) { - this.setMuteType(MutedMediaType.ALL); - return; - } - break; - case VIDEO: - if (muteType.equals(MutedMediaType.AUDIO)) { - this.setMuteType(MutedMediaType.ALL); - return; - } - break; - case ALL: - return; - } - } - this.setMuteType(newMuteType); - } - /** * Creates the endpoint (RTP or WebRTC) and any other additional elements (if * needed). diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java index 31adeb76..c27eb813 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java @@ -37,7 +37,7 @@ import org.slf4j.LoggerFactory; import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; import io.openvidu.server.core.MediaOptions; -import io.openvidu.server.kurento.MutedMediaType; +import io.openvidu.server.kurento.TrackType; import io.openvidu.server.kurento.core.KurentoParticipant; /** @@ -46,6 +46,7 @@ import io.openvidu.server.kurento.core.KurentoParticipant; * @author Radu Tom Vlad */ public class PublisherEndpoint extends MediaEndpoint { + private final static Logger log = LoggerFactory.getLogger(PublisherEndpoint.class); protected MediaOptions mediaOptions; @@ -283,8 +284,7 @@ public class PublisherEndpoint extends MediaEndpoint { } } - @Override - public synchronized void mute(MutedMediaType muteType) { + public synchronized void mute(TrackType muteType) { MediaElement sink = passThru; if (!elements.isEmpty()) { String sinkId = elementIds.peekLast(); @@ -308,11 +308,9 @@ public class PublisherEndpoint extends MediaEndpoint { internalSinkDisconnect(this.getEndpoint(), sink, MediaType.VIDEO); break; } - resolveCurrentMuteType(muteType); } - @Override - public synchronized void unmute() { + public synchronized void unmute(TrackType muteType) { MediaElement sink = passThru; if (!elements.isEmpty()) { String sinkId = elementIds.peekLast(); @@ -325,8 +323,17 @@ public class PublisherEndpoint extends MediaEndpoint { } else { log.debug("Will unmute connection of WebRTC and PassThrough (no other elems)"); } - internalSinkConnect(this.getEndpoint(), sink); - setMuteType(null); + switch (muteType) { + case ALL: + internalSinkConnect(this.getEndpoint(), sink); + break; + case AUDIO: + internalSinkConnect(this.getEndpoint(), sink, MediaType.AUDIO); + break; + case VIDEO: + internalSinkConnect(this.getEndpoint(), sink, MediaType.VIDEO); + break; + } } private String getNext(String uid) { @@ -466,7 +473,7 @@ public class PublisherEndpoint extends MediaEndpoint { }); } } - + @Override public PublisherEndpoint getPublisher() { return this; diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java index f0c82205..6d79e3b4 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java @@ -19,12 +19,9 @@ package io.openvidu.server.kurento.endpoint; import org.json.simple.JSONObject; import org.kurento.client.MediaPipeline; -import org.kurento.client.MediaType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openvidu.client.OpenViduException; -import io.openvidu.client.OpenViduException.Code; import io.openvidu.server.kurento.core.KurentoParticipant; /** @@ -70,31 +67,6 @@ public class SubscriberEndpoint extends MediaEndpoint { this.publisher = publisher; } - @Override - public synchronized void mute(io.openvidu.server.kurento.MutedMediaType muteType) { - if (this.publisher == null) { - throw new OpenViduException(Code.MEDIA_MUTE_ERROR_CODE, "Publisher endpoint not found"); - } - switch (muteType) { - case ALL: - this.publisher.disconnectFrom(this.getEndpoint()); - break; - case AUDIO: - this.publisher.disconnectFrom(this.getEndpoint(), MediaType.AUDIO); - break; - case VIDEO: - this.publisher.disconnectFrom(this.getEndpoint(), MediaType.VIDEO); - break; - } - resolveCurrentMuteType(muteType); - } - - @Override - public synchronized void unmute() { - this.publisher.connect(this.getEndpoint()); - setMuteType(null); - } - @SuppressWarnings("unchecked") @Override public JSONObject toJSON() { diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java index 4c626a40..c126dd2a 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java @@ -210,6 +210,14 @@ public class SessionRestController { } } + /* + * @RequestMapping(value = "/sessions/{sessionId}/stream/{streamId}", method = + * RequestMethod.PUT) public ResponseEntity + * muteMedia(@PathVariable("sessionId") String sessionId, + * + * @PathVariable("streamId") String streamId, @RequestBody Map params) { } + */ + @SuppressWarnings("unchecked") @RequestMapping(value = "/tokens", method = RequestMethod.POST) public ResponseEntity newToken(@RequestBody Map params) { diff --git a/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java b/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java index 22032a99..854c5ede 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java @@ -56,7 +56,6 @@ public class RpcHandler extends DefaultJsonRpcHandler { RpcNotificationService notificationService; private ConcurrentMap webSocketEOFTransportError = new ConcurrentHashMap<>(); - // private ConcurrentMap webSocketBrokenPipeTransportError = new ConcurrentHashMap<>(); @Override public void handleRequest(Transaction transaction, Request request) throws Exception { @@ -286,17 +285,17 @@ public class RpcHandler extends DefaultJsonRpcHandler { sessionManager.unpublishVideo(participant, request.getId(), "unpublish"); } - + public void streamPropertyChanged(RpcConnection rpcConnection, Request request) { String participantPrivateId = rpcConnection.getParticipantPrivateId(); String sessionId = rpcConnection.getSessionId(); Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId); - + 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); } @@ -360,7 +359,6 @@ public class RpcHandler extends DefaultJsonRpcHandler { if ("IOException".equals(exception.getClass().getSimpleName()) && "Broken pipe".equals(exception.getCause().getMessage())) { log.warn("Parcipant with private id {} unexpectedly closed the websocket", rpcSession.getSessionId()); - // this.webSocketBrokenPipeTransportError.put(rpcSession.getSessionId(), true); } if ("EOFException".equals(exception.getClass().getSimpleName())) { // Store WebSocket connection interrupted exception for this web socket to @@ -402,7 +400,7 @@ public class RpcHandler extends DefaultJsonRpcHandler { } return request.getParams().get(key).getAsBoolean(); } - + public static JsonElement getParam(Request request, String key) { if (request.getParams() == null || request.getParams().get(key) == null) { throw new RuntimeException("Request element '" + key + "' is missing in method '" + request.getMethod() From e615ff7ec4cba43aea02240d0fd8f0aabd672280 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Fri, 6 Jul 2018 13:18:34 +0200 Subject: [PATCH 05/13] openvidu-server: configuration parameters for max and min video send and recv (kbps) --- .../server/config/OpenviduConfig.java | 42 +++++++++++++--- .../kurento/core/KurentoParticipant.java | 16 ++++--- .../server/kurento/core/KurentoSession.java | 11 +++-- .../kurento/core/KurentoSessionManager.java | 2 +- .../kurento/endpoint/MediaEndpoint.java | 23 +++++++-- .../kurento/endpoint/PublisherEndpoint.java | 5 +- .../kurento/endpoint/SubscriberEndpoint.java | 5 +- ...itional-spring-configuration-metadata.json | 48 +++++++++++++++++++ .../src/main/resources/application.properties | 16 +++++-- 9 files changed, 136 insertions(+), 32 deletions(-) diff --git a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java index 1876131f..b52e361a 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java +++ b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java @@ -44,19 +44,28 @@ public class OpenviduConfig { private String openviduRecordingPath; @Value("${openvidu.recording.public-access}") - boolean openviduRecordingPublicAccess; + private boolean openviduRecordingPublicAccess; @Value("${openvidu.recording.notification}") - String openviduRecordingNotification; + private String openviduRecordingNotification; @Value("${openvidu.recording.custom-layout}") - String openviduRecordingCustomLayout; + private String openviduRecordingCustomLayout; @Value("${openvidu.recording.version}") - String openviduRecordingVersion; + private String openviduRecordingVersion; - @Value("#{'${spring.profiles.active:}'.length() > 0 ? '${spring.profiles.active:}'.split(',') : \"default\"}") - private String springProfile; + @Value("${openvidu.streams.video.max-recv-bandwidth}") + private int openviduStreamsVideoMaxRecvBandwidth; + + @Value("${openvidu.streams.video.min-recv-bandwidth}") + private int openviduStreamsVideoMinRecvBandwidth; + + @Value("${openvidu.streams.video.max-send-bandwidth}") + private int openviduStreamsVideoMaxSendBandwidth; + + @Value("${openvidu.streams.video.min-send-bandwidth}") + private int openviduStreamsVideoMinSendBandwidth; @Value("${coturn.redis.ip}") private String coturnRedisIp; @@ -70,6 +79,9 @@ public class OpenviduConfig { @Value("${coturn.redis.connect-timeout}") private String coturnRedisConnectTimeout; + @Value("#{'${spring.profiles.active:}'.length() > 0 ? '${spring.profiles.active:}'.split(',') : \"default\"}") + private String springProfile; + private String finalUrl; public String getOpenViduPublicUrl() { @@ -132,11 +144,27 @@ public class OpenviduConfig { return springProfile; } + public int getVideoMaxRecvBandwidth() { + return this.openviduStreamsVideoMaxRecvBandwidth; + } + + public int getVideoMinRecvBandwidth() { + return this.openviduStreamsVideoMinRecvBandwidth; + } + + public int getVideoMaxSendBandwidth() { + return this.openviduStreamsVideoMaxSendBandwidth; + } + + public int getVideoMinSendBandwidth() { + return this.openviduStreamsVideoMinSendBandwidth; + } + public String getCoturnDatabaseString() { return "\"ip=" + this.coturnRedisIp + " dbname=" + this.coturnRedisDbname + " password=" + this.coturnRedisPassword + " connect_timeout=" + this.coturnRedisConnectTimeout + "\""; } - + public String getCoturnDatabaseDbname() { return this.coturnRedisDbname; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java index b840d64f..3a71a25b 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java @@ -39,7 +39,6 @@ import org.kurento.client.SdpEndpoint; import org.kurento.client.internal.server.KurentoServerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import io.openvidu.client.OpenViduException; import io.openvidu.client.OpenViduException.Code; @@ -60,8 +59,7 @@ public class KurentoParticipant extends Participant { private static final Logger log = LoggerFactory.getLogger(KurentoParticipant.class); - @Autowired - protected OpenviduConfig openviduConfig; + private OpenviduConfig openviduConfig; private InfoHandler infoHandler; private CallDetailRecord CDR; @@ -78,12 +76,14 @@ public class KurentoParticipant extends Participant { private final ConcurrentMap subscribers = new ConcurrentHashMap(); public KurentoParticipant(Participant participant, KurentoSession kurentoSession, MediaPipeline pipeline, - InfoHandler infoHandler, CallDetailRecord CDR) { + InfoHandler infoHandler, CallDetailRecord CDR, OpenviduConfig openviduConfig) { super(participant.getParticipantPrivateId(), participant.getParticipantPublicId(), participant.getToken(), participant.getClientMetadata()); + this.openviduConfig = openviduConfig; this.session = kurentoSession; this.pipeline = pipeline; - this.publisher = new PublisherEndpoint(webParticipant, this, participant.getParticipantPublicId(), pipeline); + this.publisher = new PublisherEndpoint(webParticipant, this, participant.getParticipantPublicId(), pipeline, + this.openviduConfig); for (Participant other : session.getParticipants()) { if (!other.getParticipantPublicId().equals(this.getParticipantPublicId())) { @@ -250,7 +250,8 @@ public class KurentoParticipant extends Participant { log.info("PARTICIPANT {}: unpublishing media stream from room {}", this.getParticipantPublicId(), this.session.getSessionId()); releasePublisherEndpoint(reason); - this.publisher = new PublisherEndpoint(webParticipant, this, this.getParticipantPublicId(), pipeline); + this.publisher = new PublisherEndpoint(webParticipant, this, this.getParticipantPublicId(), pipeline, + this.openviduConfig); log.info("PARTICIPANT {}: released publisher endpoint and left it initialized (ready for future streaming)", this.getParticipantPublicId()); } @@ -394,7 +395,8 @@ public class KurentoParticipant extends Participant { */ public SubscriberEndpoint getNewOrExistingSubscriber(String senderPublicId) { - SubscriberEndpoint sendingEndpoint = new SubscriberEndpoint(webParticipant, this, senderPublicId, pipeline); + SubscriberEndpoint sendingEndpoint = new SubscriberEndpoint(webParticipant, this, senderPublicId, pipeline, + this.openviduConfig); SubscriberEndpoint existingSendingEndpoint = this.subscribers.putIfAbsent(senderPublicId, sendingEndpoint); if (existingSendingEndpoint != null) { diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java index 96203336..15ebec7a 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSession.java @@ -42,6 +42,7 @@ import io.openvidu.client.OpenViduException.Code; import io.openvidu.client.internal.ProtocolElements; import io.openvidu.java.client.SessionProperties; import io.openvidu.server.cdr.CallDetailRecord; +import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.core.Participant; import io.openvidu.server.core.Session; @@ -53,6 +54,8 @@ public class KurentoSession implements Session { private final static Logger log = LoggerFactory.getLogger(Session.class); public static final int ASYNC_LATCH_TIMEOUT = 30; + private OpenviduConfig openviduConfig; + private final ConcurrentMap participants = new ConcurrentHashMap<>(); private String sessionId; private SessionProperties sessionProperties; @@ -74,17 +77,19 @@ public class KurentoSession implements Session { private boolean destroyKurentoClient; private CallDetailRecord CDR; - + public final ConcurrentHashMap publishedStreamIds = new ConcurrentHashMap<>(); public KurentoSession(String sessionId, SessionProperties sessionProperties, KurentoClient kurentoClient, - KurentoSessionEventsHandler kurentoSessionHandler, boolean destroyKurentoClient, CallDetailRecord CDR) { + KurentoSessionEventsHandler kurentoSessionHandler, boolean destroyKurentoClient, CallDetailRecord CDR, + OpenviduConfig openviduConfig) { this.sessionId = sessionId; this.sessionProperties = sessionProperties; this.kurentoClient = kurentoClient; this.destroyKurentoClient = destroyKurentoClient; this.kurentoSessionHandler = kurentoSessionHandler; this.CDR = CDR; + this.openviduConfig = openviduConfig; log.debug("New SESSION instance with id '{}'", sessionId); } @@ -104,7 +109,7 @@ public class KurentoSession implements Session { createPipeline(); KurentoParticipant kurentoParticipant = new KurentoParticipant(participant, this, getPipeline(), - kurentoSessionHandler.getInfoHandler(), this.CDR); + kurentoSessionHandler.getInfoHandler(), this.CDR, this.openviduConfig); participants.put(participant.getParticipantPrivateId(), kurentoParticipant); filterStates.forEach((filterId, state) -> { diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java index babced90..6d4235f1 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java @@ -441,7 +441,7 @@ public class KurentoSessionManager extends SessionManager { } KurentoClient kurentoClient = kcProvider.getKurentoClient(kcSessionInfo); session = new KurentoSession(sessionId, sessionProperties, kurentoClient, kurentoSessionEventsHandler, - kcProvider.destroyWhenUnused(), this.CDR); + kcProvider.destroyWhenUnused(), this.CDR, this.openviduConfig); KurentoSession oldSession = (KurentoSession) sessions.putIfAbsent(sessionId, session); if (oldSession != null) { diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java index a62930ab..2d021c39 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/MediaEndpoint.java @@ -44,6 +44,7 @@ import org.slf4j.LoggerFactory; 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; @@ -57,12 +58,18 @@ import io.openvidu.server.kurento.core.KurentoParticipant; */ public abstract class MediaEndpoint { private static Logger log; + private OpenviduConfig openviduConfig; private boolean web = false; private WebRtcEndpoint webEndpoint = null; private RtpEndpoint endpoint = null; + private final int maxRecvKbps; + private final int minRecvKbps; + private final int maxSendKbps; + private final int minSendKbps; + private KurentoParticipant owner; private String endpointName; @@ -89,7 +96,7 @@ public abstract class MediaEndpoint { * @param log */ public MediaEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline, - Logger log) { + OpenviduConfig openviduConfig, Logger log) { if (log == null) { MediaEndpoint.log = LoggerFactory.getLogger(MediaEndpoint.class); } else { @@ -99,6 +106,12 @@ public abstract class MediaEndpoint { this.owner = owner; this.setEndpointName(endpointName); 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(); } public boolean isWeb() { @@ -218,10 +231,10 @@ public abstract class MediaEndpoint { public void onSuccess(WebRtcEndpoint result) throws Exception { webEndpoint = result; - webEndpoint.setMaxVideoRecvBandwidth(600); - webEndpoint.setMinVideoRecvBandwidth(300); - webEndpoint.setMaxVideoSendBandwidth(600); - webEndpoint.setMinVideoSendBandwidth(300); + webEndpoint.setMaxVideoRecvBandwidth(maxRecvKbps); + webEndpoint.setMinVideoRecvBandwidth(minRecvKbps); + webEndpoint.setMaxVideoSendBandwidth(maxSendKbps); + webEndpoint.setMinVideoSendBandwidth(minSendKbps); endpointLatch.countDown(); log.trace("EP {}: Created a new WebRtcEndpoint", endpointName); diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java index c27eb813..36bd5afa 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/PublisherEndpoint.java @@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory; 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; @@ -60,8 +61,8 @@ public class PublisherEndpoint extends MediaEndpoint { private Map elementsErrorSubscriptions = new HashMap(); - public PublisherEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline) { - super(web, owner, endpointName, pipeline, log); + public PublisherEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline, OpenviduConfig openviduConfig) { + super(web, owner, endpointName, pipeline, openviduConfig, log); } @Override diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java index 6d79e3b4..11facb7c 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/endpoint/SubscriberEndpoint.java @@ -22,6 +22,7 @@ import org.kurento.client.MediaPipeline; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.kurento.core.KurentoParticipant; /** @@ -36,8 +37,8 @@ public class SubscriberEndpoint extends MediaEndpoint { private PublisherEndpoint publisher = null; - public SubscriberEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline) { - super(web, owner, endpointName, pipeline, log); + public SubscriberEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline, OpenviduConfig openviduConfig) { + super(web, owner, endpointName, pipeline, openviduConfig, log); } public synchronized String subscribe(String sdpOffer, PublisherEndpoint publisher) { diff --git a/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 9a6240b5..963698f0 100644 --- a/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/openvidu-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -64,6 +64,54 @@ "type": "java.lang.String", "description": "Path to COTURN sqlite database to add and remove TURN user credentials", "defaultValue": "/opt/openvidu/coturn/turndb" + }, + { + "name": "openvidu.streams.video.max-recv-bandwidth", + "type": "java.lang.Integer", + "description": "Maximum video bandwith sent from clients to OpenVidu Server, in kbps. 0 means unconstrained", + "defaultValue": 600 + }, + { + "name": "openvidu.streams.video.min-recv-bandwidth", + "type": "java.lang.Integer", + "description": "Minimum video bandwith sent from clients to OpenVidu Server, in kbps. 0 means unconstrained", + "defaultValue": 300 + }, + { + "name": "openvidu.streams.video.max-send-bandwidth", + "type": "java.lang.Integer", + "description": "Maximum video bandwith sent from OpenVidu Server to clients, in kbps. 0 means unconstrained", + "defaultValue": 600 + }, + { + "name": "openvidu.streams.video.min-send-bandwidth", + "type": "java.lang.Integer", + "description": "Minimum video bandwith sent from OpenVidu Server to clients, in kbps. 0 means unconstrained", + "defaultValue": 300 + }, + { + "name": "coturn.redis.ip", + "type": "java.lang.String", + "description": "Redis IP where OpenVidu Server should connect to store TURN credentials", + "defaultValue": "127.0.0.1" + }, + { + "name": "coturn.redis.dbname", + "type": "java.lang.String", + "description": "Redis database where to store TURN credentials", + "defaultValue": "0" + }, + { + "name": "coturn.redis.password", + "type": "java.lang.String", + "description": "Password to connect OpenVidu Server to Redis database to store TURN credentials", + "defaultValue": "turn" + }, + { + "name": "coturn.redis.connect-timeout", + "type": "java.lang.Integer", + "description": "Timeout in seconds when OpenVidu Server is connecting to Redis database to store TURN credentials", + "defaultValue": 30 } ] } \ No newline at end of file diff --git a/openvidu-server/src/main/resources/application.properties b/openvidu-server/src/main/resources/application.properties index 17555c22..c909cf51 100644 --- a/openvidu-server/src/main/resources/application.properties +++ b/openvidu-server/src/main/resources/application.properties @@ -13,15 +13,21 @@ server.ssl.key-alias: openvidu-selfsigned openvidu.secret: MY_SECRET openvidu.publicurl: local openvidu.cdr: false + openvidu.recording: false openvidu.recording.path: /opt/openvidu/recordings openvidu.recording.public-access: false openvidu.recording.notification: publisher_moderator openvidu.recording.custom-layout: /opt/openvidu/custom-layout -kms.uris=[\"ws://localhost:8888/kurento\"] +openvidu.streams.video.max-recv-bandwidth: 600 +openvidu.streams.video.min-recv-bandwidth: 300 +openvidu.streams.video.max-send-bandwidth: 600 +openvidu.streams.video.min-send-bandwidth: 300 -coturn.redis.ip=127.0.0.1 -coturn.redis.dbname=0 -coturn.redis.password=turn -coturn.redis.connect-timeout=30 +kms.uris: [\"ws://localhost:8888/kurento\"] + +coturn.redis.ip: 127.0.0.1 +coturn.redis.dbname: 0 +coturn.redis.password: turn +coturn.redis.connect-timeout: 30 From d5958ec906a2dbd9f60dadaac66c22fa71f94b25 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Mon, 9 Jul 2018 15:45:20 +0200 Subject: [PATCH 06/13] openvidu-browser: Session.forceUnpublish and Session.forceDisconnect --- openvidu-browser/src/OpenVidu/Publisher.ts | 1 + openvidu-browser/src/OpenVidu/Session.ts | 109 ++++++++++++++---- openvidu-browser/src/OpenVidu/Stream.ts | 12 +- .../OpenViduInternal/Enums/OpenViduError.ts | 3 +- .../Events/ConnectionEvent.ts | 2 + .../OpenViduInternal/Events/RecordingEvent.ts | 4 +- .../Events/SessionDisconnectedEvent.ts | 3 + .../OpenViduInternal/Events/StreamEvent.ts | 5 + .../Interfaces/Public/Capabilities.ts | 10 ++ .../Interfaces/Public/StreamManagerVideo.ts | 8 +- 10 files changed, 126 insertions(+), 31 deletions(-) diff --git a/openvidu-browser/src/OpenVidu/Publisher.ts b/openvidu-browser/src/OpenVidu/Publisher.ts index c5ee097f..99c91774 100644 --- a/openvidu-browser/src/OpenVidu/Publisher.ts +++ b/openvidu-browser/src/OpenVidu/Publisher.ts @@ -77,6 +77,7 @@ export class Publisher extends StreamManager { this.openvidu = openvidu; this.stream.ee.on('local-stream-destroyed', (reason: string) => { + this.stream.isLocalStreamPublished = false; const streamEvent = new StreamEvent(true, this, 'streamDestroyed', this.stream, reason); this.emitEvent('streamDestroyed', [streamEvent]); streamEvent.callDefaultBehavior(); diff --git a/openvidu-browser/src/OpenVidu/Session.ts b/openvidu-browser/src/OpenVidu/Session.ts index b38d9560..8914082f 100644 --- a/openvidu-browser/src/OpenVidu/Session.ts +++ b/openvidu-browser/src/OpenVidu/Session.ts @@ -330,7 +330,7 @@ export class Session implements EventDispatcher { publisher.session = this; publisher.stream.session = this; - if (!publisher.stream.isLocalStreamPublished) { + if (!publisher.stream.publishedOnce) { // 'Session.unpublish(Publisher)' has NOT been called this.connection.addStream(publisher.stream); publisher.stream.publish() @@ -413,6 +413,88 @@ export class Session implements EventDispatcher { } + /** + * Forces some user to leave the session + * + * #### Events dispatched + * + * The behavior is the same as when some user calls [[Session.disconnect]], but `reason` property in all events will be `"forceDisconnectByUser"`. + * + * The local [[Session]] object will dispatch: + * - A `streamDestroyed` event if the evicted user was publishing a stream, with property `reason` set to `"forceDisconnectByUser"` + * - A `connectionDestroyed` event for the evicted user, with property `reason` set to `"forceDisconnectByUser"` + * + * The remote [[Session]] object of every other participant will dispatch: + * - A `streamDestroyed` event if the evicted user was publishing a stream, with property `reason` set to `"forceDisconnectByUser"` + * - A `connectionDestroyed` event for the evicted user, with property `reason` set to `"forceDisconnectByUser"` + * + * If any, the [[Publisher]] object of the evicted participant will also dispatch a `streamDestroyed` event with property `reason` set to `"forceDisconnectByUser"` + * + * @returns A Promise (to which you can optionally subscribe to) that is resolved only after the participant has been successfully evicted from the session and rejected with an Error object if not + */ + forceDisconnect(connection: Connection): Promise { + return new Promise((resolve, reject) => { + console.info('Forcing disconnect for connection ' + connection.connectionId); + this.openvidu.sendRequest( + 'forceDisconnect', + { connectionId: connection.connectionId }, + (error, response) => { + if (error) { + console.error('Error forcing disconnect for Connection ' + connection.connectionId, error); + if (error.code === 401) { + reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to force a disconnection")); + } else { + reject(error); + } + } else { + console.info('Forcing disconnect correctly for Connection ' + connection.connectionId); + resolve(); + } + } + ); + }); + } + + + /** + * Forces some user to unpublish a Stream + * + * #### Events dispatched + * + * The behavior is the same as when some user calls [[Session.unpublish]], but `reason` property in all events will be `"forceUnpublishByUser"`. + * + * The local [[Session]] object will dispatch a `streamDestroyed` event with property `reason` set to `"forceUnpublishByUser"` + * + * The remote [[Session]] object of every other participant will dispatch a `streamDestroyed` event with property `reason` set to `"forceDisconnectByUser"` + * + * The [[Publisher]] object of the affected participant will also dispatch a `streamDestroyed` event with property `reason` set to `"forceDisconnectByUser"` + * + * @returns A Promise (to which you can optionally subscribe to) that is resolved only after the remote Stream has been successfully unpublished from the session and rejected with an Error object if not + */ + forceUnpublish(stream: Stream): Promise { + return new Promise((resolve, reject) => { + console.info('Forcing unpublish for stream ' + stream.streamId); + this.openvidu.sendRequest( + 'forceUnpublish', + { streamId: stream.streamId }, + (error, response) => { + if (error) { + console.error('Error forcing unpublish for Stream ' + stream.streamId, error); + if (error.code === 401) { + reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to force an unpublishing")); + } else { + reject(error); + } + } else { + console.info('Forcing unpublish correctly for Stream ' + stream.streamId); + resolve(); + } + } + ); + }); + } + + /** * Sends one signal. `signal` object has the following optional properties: * ```json @@ -663,27 +745,6 @@ export class Session implements EventDispatcher { if (!!this.sessionId && !this.connection.disposed) { this.leave(true, msg.reason); } - } else { - // Other user has been evicted from the session - this.getRemoteConnection(msg.connectionId, 'Remote connection ' + msg.connectionId + " unknown when 'onParticipantEvicted'. " + - 'Existing remote connections: ' + JSON.stringify(Object.keys(this.remoteConnections))) - - .then(connection => { - if (!!connection.stream) { - const stream = connection.stream; - - const streamEvent = new StreamEvent(true, this, 'streamDestroyed', stream, msg.reason); - this.ee.emitEvent('streamDestroyed', [streamEvent]); - streamEvent.callDefaultBehavior(); - - delete this.remoteStreamsCreated[stream.streamId]; - } - delete this.remoteConnections[connection.connectionId]; - this.ee.emitEvent('connectionDestroyed', [new ConnectionEvent(false, this, 'connectionDestroyed', connection, msg.reason)]); - }) - .catch(openViduError => { - console.error(openViduError); - }); } } @@ -904,7 +965,9 @@ export class Session implements EventDispatcher { // Initialize capabilities object with the role this.capabilities = { subscribe: true, - publish: this.openvidu.role !== 'SUBSCRIBER' + publish: this.openvidu.role !== 'SUBSCRIBER', + forceUnpublish: this.openvidu.role === 'MODERATOR', + forceDisconnect: this.openvidu.role === 'MODERATOR' }; // Initialize local Connection object with values returned by openvidu-server diff --git a/openvidu-browser/src/OpenVidu/Stream.ts b/openvidu-browser/src/OpenVidu/Stream.ts index 58cf4bf3..1a46bf85 100644 --- a/openvidu-browser/src/OpenVidu/Stream.ts +++ b/openvidu-browser/src/OpenVidu/Stream.ts @@ -26,6 +26,7 @@ import { PublisherSpeakingEvent } from '../OpenViduInternal/Events/PublisherSpea import EventEmitter = require('wolfy87-eventemitter'); import hark = require('hark'); +import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError'; /** @@ -118,6 +119,10 @@ export class Stream { * @hidden */ isLocalStreamPublished = false; + /** + * @hidden + */ + publishedOnce = false; /** * @hidden */ @@ -465,12 +470,17 @@ export class Stream { videoDimensions: JSON.stringify(this.videoDimensions) }, (error, response) => { if (error) { - reject('Error on publishVideo: ' + JSON.stringify(error)); + if (error.code === 401) { + reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to publish")); + } else { + reject('Error on publishVideo: ' + JSON.stringify(error)); + } } else { this.webRtcPeer.processAnswer(response.sdpAnswer) .then(() => { this.streamId = response.id; this.isLocalStreamPublished = true; + this.publishedOnce = true; if (this.displayMyRemote()) { this.remotePeerSuccessfullyEstablished(); } diff --git a/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts b/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts index 26f5a1e5..5c64469b 100644 --- a/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts +++ b/openvidu-browser/src/OpenViduInternal/Enums/OpenViduError.ts @@ -82,7 +82,8 @@ export enum OpenViduErrorName { PUBLISHER_PROPERTIES_ERROR = 'PUBLISHER_PROPERTIES_ERROR', /** - * _Not in use yet_ + * The client tried to call a method without the required permissions. This can occur for methods [[Session.publish]], + * [[Session.forceUnpublish]] and [[Session.forceDisconnect]] */ OPENVIDU_PERMISSION_DENIED = 'OPENVIDU_PERMISSION_DENIED', diff --git a/openvidu-browser/src/OpenViduInternal/Events/ConnectionEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/ConnectionEvent.ts index bc0040c6..28d8c868 100644 --- a/openvidu-browser/src/OpenViduInternal/Events/ConnectionEvent.ts +++ b/openvidu-browser/src/OpenViduInternal/Events/ConnectionEvent.ts @@ -35,6 +35,8 @@ export class ConnectionEvent extends Event { /** * For 'connectionDestroyed' event: * - "disconnect": the remote user has called `Session.disconnect()` + * - "forceDisconnectByUser": the remote user has been evicted from the Session by other user calling `Session.forceDisconnect()` + * - "forceDisconnectByServer": the remote user has been evicted from the Session by the application * - "networkDisconnect": the remote user network connection has dropped * * For 'connectionCreated' empty string diff --git a/openvidu-browser/src/OpenViduInternal/Events/RecordingEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/RecordingEvent.ts index 6b1ff6d8..1d88bb11 100644 --- a/openvidu-browser/src/OpenViduInternal/Events/RecordingEvent.ts +++ b/openvidu-browser/src/OpenViduInternal/Events/RecordingEvent.ts @@ -34,8 +34,8 @@ export class RecordingEvent extends Event { /** * The recording name you supplied to openvidu-server. For example, to name your recording file MY_RECORDING: * - With **API REST**: POST to `/api/recordings/start` passing JSON body `{"session":"sessionId","name":"MY_RECORDING"}` - * - With **openvidu-java-client**: `OpenVidu.startRecording(sessionId, MY_RECORDING)` or `OpenVidu.startRecording(sessionId, new RecordingProperties.Builder().name(MY_RECORDING).build())` - * - With **openvidu-node-client**: `OpenVidu.startRecording(sessionId, MY_RECORDING)` or `OpenVidu.startRecording(sessionId, new RecordingProperties.Builder().name(MY_RECORDING).build())` + * - With **openvidu-java-client**: `OpenVidu.startRecording(sessionId, "MY_RECORDING")` or `OpenVidu.startRecording(sessionId, new RecordingProperties.Builder().name("MY_RECORDING").build())` + * - With **openvidu-node-client**: `OpenVidu.startRecording(sessionId, "MY_RECORDING")` or `OpenVidu.startRecording(sessionId, {name: "MY_RECORDING"})` * * If no name is supplied, this property will be undefined and the recorded file will be named after property [[id]] */ diff --git a/openvidu-browser/src/OpenViduInternal/Events/SessionDisconnectedEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/SessionDisconnectedEvent.ts index 49933af6..0887f7ca 100644 --- a/openvidu-browser/src/OpenViduInternal/Events/SessionDisconnectedEvent.ts +++ b/openvidu-browser/src/OpenViduInternal/Events/SessionDisconnectedEvent.ts @@ -26,6 +26,9 @@ export class SessionDisconnectedEvent extends Event { /** * - "disconnect": you have called `Session.disconnect()` + * - "forceDisconnectByUser": you have been evicted from the Session by other user calling `Session.forceDisconnect()` + * - "forceDisconnectByServer": you have been evicted from the Session by the application + * - "sessionClosedByServer": the Session has been closed by the application * - "networkDisconnect": your network connection has dropped */ reason: string; diff --git a/openvidu-browser/src/OpenViduInternal/Events/StreamEvent.ts b/openvidu-browser/src/OpenViduInternal/Events/StreamEvent.ts index 90b6186b..a0408aa6 100644 --- a/openvidu-browser/src/OpenViduInternal/Events/StreamEvent.ts +++ b/openvidu-browser/src/OpenViduInternal/Events/StreamEvent.ts @@ -37,6 +37,11 @@ export class StreamEvent extends Event { * For 'streamDestroyed' event: * - "unpublish": method `Session.unpublish()` has been called * - "disconnect": method `Session.disconnect()` has been called + * - "forceUnpublishByUser": some user has called `Session.forceUnpublish()` over the Stream + * - "forceDisconnectByUser": some user has called `Session.forceDisconnect()` over the Stream + * - "forceUnpublishByServer": the user's stream has been unpublished from the Session by the application + * - "forceDisconnectByServer": the user has been evicted from the Session by the application + * - "sessionClosedByServer": the Session has been closed by the application * - "networkDisconnect": the user's network connection has dropped * * For 'streamCreated' empty string diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Capabilities.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Capabilities.ts index e55c805c..1112735e 100644 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Capabilities.ts +++ b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/Capabilities.ts @@ -20,6 +20,16 @@ */ export interface Capabilities { + /** + * `true` if the client can call [[Session.forceDisconnect]], `false` if not + */ + forceDisconnect: boolean; + + /** + * `true` if the client can call [[Session.forceUnpublish]], `false` if not + */ + forceUnpublish: boolean; + /** * `true` if the client can call [[Session.publish]], `false` if not */ diff --git a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/StreamManagerVideo.ts b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/StreamManagerVideo.ts index adbf93e8..3e936471 100644 --- a/openvidu-browser/src/OpenViduInternal/Interfaces/Public/StreamManagerVideo.ts +++ b/openvidu-browser/src/OpenViduInternal/Interfaces/Public/StreamManagerVideo.ts @@ -35,22 +35,22 @@ export interface StreamManagerVideo { /** * The DOM HTMLElement assigned as target element when creating a video for the StreamManager. This property is defined when: * - [[OpenVidu.initPublisher]] or [[Session.subscribe]] methods have been called passing a valid `targetElement` parameter. - * - [[SessionManager.createVideoElement]] has been called. + * - [[StreamManager.createVideoElement]] has been called. * * This property is undefined when: * - [[OpenVidu.initPublisher]] or [[Session.subscribe]] methods have been called passing *null* or *undefined* as `targetElement` parameter. - * - [[SessionManager.addVideoElement]] has been called. + * - [[StreamManager.addVideoElement]] has been called. */ targetElement?: HTMLElement; /** * How the DOM video element should be inserted with respect to `targetElement`. This property is defined when: * - [[OpenVidu.initPublisher]] or [[Session.subscribe]] methods have been called passing a valid `targetElement` parameter. - * - [[SessionManager.createVideoElement]] has been called. + * - [[StreamManager.createVideoElement]] has been called. * * This property is undefined when: * - [[OpenVidu.initPublisher]] or [[Session.subscribe]] methods have been called passing *null* or *undefined* as `targetElement` parameter. - * - [[SessionManager.addVideoElement]] has been called. + * - [[StreamManager.addVideoElement]] has been called. */ insertMode?: VideoInsertMode; From ff05bb190656db480ba1358df56bdac99eddc474 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Mon, 9 Jul 2018 15:49:27 +0200 Subject: [PATCH 07/13] openvidu-server: RPC Session.forceUnpublish and Session.forceDisconnect --- .../server/config/OpenviduConfig.java | 3 + .../server/core/SessionEventsHandler.java | 59 ++++-- .../openvidu/server/core/SessionManager.java | 36 ++-- .../kurento/core/KurentoSessionManager.java | 49 ++--- .../server/rest/SessionRestController.java | 9 +- .../io/openvidu/server/rpc/RpcHandler.java | 184 +++++++++++++----- 6 files changed, 227 insertions(+), 113 deletions(-) diff --git a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java index b52e361a..80abae51 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java +++ b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java @@ -175,6 +175,9 @@ public class OpenviduConfig { case "none": roles = new ParticipantRole[0]; break; + case "moderator": + roles = new ParticipantRole[] { ParticipantRole.MODERATOR }; + break; case "publisher_moderator": roles = new ParticipantRole[] { ParticipantRole.PUBLISHER, ParticipantRole.MODERATOR }; break; diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java index 656f422d..a15d10ff 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionEventsHandler.java @@ -214,16 +214,18 @@ public class SessionEventsHandler { } } - public void onUnpublishMedia(Participant participant, Set participants, Integer transactionId, - OpenViduException error, String reason) { - boolean force = reason.contains("force") || transactionId == null; - if (!force) { + public void onUnpublishMedia(Participant participant, Set participants, Participant moderator, + Integer transactionId, OpenViduException error, String reason) { + boolean isRpcFromModerator = transactionId != null && moderator != null; + boolean isRpcFromOwner = transactionId != null && moderator == null; + + if (isRpcFromModerator) { if (error != null) { - rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, + rpcNotificationService.sendErrorResponse(moderator.getParticipantPrivateId(), transactionId, null, error); return; } - rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject()); + rpcNotificationService.sendResponse(moderator.getParticipantPrivateId(), transactionId, new JsonObject()); } JsonObject params = new JsonObject(); @@ -232,21 +234,28 @@ public class SessionEventsHandler { for (Participant p : participants) { if (p.getParticipantPrivateId().equals(participant.getParticipantPrivateId())) { - if (force) { + if (!isRpcFromOwner) { rpcNotificationService.sendNotification(p.getParticipantPrivateId(), ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params); } else { - continue; + if (error != null) { + rpcNotificationService.sendErrorResponse(p.getParticipantPrivateId(), transactionId, null, + error); + return; + } + rpcNotificationService.sendResponse(p.getParticipantPrivateId(), transactionId, new JsonObject()); } } else { - rpcNotificationService.sendNotification(p.getParticipantPrivateId(), - ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params); + if (error == null) { + rpcNotificationService.sendNotification(p.getParticipantPrivateId(), + ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD, params); + } } } } - public void onSubscribe(Participant participant, Session session, String senderName, String sdpAnswer, - Integer transactionId, OpenViduException error) { + public void onSubscribe(Participant participant, Session session, String sdpAnswer, Integer transactionId, + OpenViduException error) { if (error != null) { rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error); return; @@ -269,8 +278,7 @@ public class SessionEventsHandler { } } - public void onUnsubscribe(Participant participant, String senderName, Integer transactionId, - OpenViduException error) { + public void onUnsubscribe(Participant participant, Integer transactionId, OpenViduException error) { if (error != null) { rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error); return; @@ -358,13 +366,30 @@ public class SessionEventsHandler { rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, new JsonObject()); } - public void onParticipantEvicted(Participant participant, String reason) { + public void onForceDisconnect(Participant moderator, Participant evictedParticipant, Set participants, + Integer transactionId, OpenViduException error, String reason) { + + boolean isRpcCall = transactionId != null; + if (isRpcCall) { + if (error != null) { + rpcNotificationService.sendErrorResponse(moderator.getParticipantPrivateId(), transactionId, null, + error); + return; + } + rpcNotificationService.sendResponse(moderator.getParticipantPrivateId(), transactionId, new JsonObject()); + } + JsonObject params = new JsonObject(); params.addProperty(ProtocolElements.PARTICIPANTEVICTED_CONNECTIONID_PARAM, - participant.getParticipantPublicId()); + evictedParticipant.getParticipantPublicId()); params.addProperty(ProtocolElements.PARTICIPANTEVICTED_REASON_PARAM, reason); - rpcNotificationService.sendNotification(participant.getParticipantPrivateId(), + + rpcNotificationService.sendNotification(evictedParticipant.getParticipantPrivateId(), ProtocolElements.PARTICIPANTEVICTED_METHOD, params); + for (Participant p : participants) { + rpcNotificationService.sendNotification(p.getParticipantPrivateId(), + ProtocolElements.PARTICIPANTEVICTED_METHOD, params); + } } public void sendRecordingStartedNotification(Session session, Recording recording) { diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java index fa90596b..224837f7 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java @@ -22,7 +22,6 @@ import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.stream.Collectors; import javax.annotation.PreDestroy; @@ -80,7 +79,7 @@ public abstract class SessionManager { public abstract void publishVideo(Participant participant, MediaOptions mediaOptions, Integer transactionId); - public abstract void unpublishVideo(Participant participant, 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); @@ -94,17 +93,10 @@ 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, String reason); + public abstract boolean unpublishStream(Session session, String streamId, Participant moderator, Integer transactionId, String reason); - /** - * Application-originated request to remove a participant from a session.
- * Side effects: The session event handler should notify the - * participant that she has been evicted. Should also send notifications to all - * other participants about the one that's just been evicted. - * - */ - public void evictParticipant(String participantPrivateId, String reason) throws OpenViduException { - } + public abstract void evictParticipant(Participant evictedParticipant, Participant moderator, Integer transactionId, + String reason); /** * Returns a Session given its id @@ -287,6 +279,18 @@ public abstract class SessionManager { } } + public boolean isModeratorInSession(String sessionId, Participant participant) { + if (!this.isInsecureParticipant(participant.getParticipantPrivateId())) { + if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null) { + return ParticipantRole.MODERATOR.equals(participant.getToken().getRole()); + } else { + return false; + } + } else { + return true; + } + } + public boolean isInsecureParticipant(String participantPrivateId) { if (this.insecureUsers.containsKey(participantPrivateId)) { log.info("The user with private id {} is an INSECURE user", participantPrivateId); @@ -409,13 +413,11 @@ public abstract class SessionManager { throw new OpenViduException(Code.ROOM_CLOSED_ERROR_CODE, "Session '" + sessionId + "' already closed"); } Set participants = getParticipants(sessionId); - // copy the ids as they will be removed from the map - Set pids = participants.stream().map(Participant::getParticipantPrivateId).collect(Collectors.toSet()); - for (String pid : pids) { + for (Participant p : participants) { try { - this.evictParticipant(pid, reason); + this.evictParticipant(p, null, null, reason); } catch (OpenViduException e) { - log.warn("Error evicting participant with id '{}' from session '{}'", pid, sessionId, e); + log.warn("Error evicting participant '{}' from session '{}'", p.getParticipantPublicId(), sessionId, e); } } diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java index 6d4235f1..1a38d8d0 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java @@ -172,8 +172,8 @@ public class KurentoSessionManager extends SessionManager { log.info("Last participant left. Stopping recording for session {}", sessionId); recordingService.stopRecording(session); - evictParticipant(session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID) - .getParticipantPrivateId(), "EVICT_RECORDER"); + evictParticipant(session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), null, + null, "EVICT_RECORDER"); } // Finally close websocket session if required @@ -268,7 +268,7 @@ public class KurentoSessionManager extends SessionManager { } @Override - public void unpublishVideo(Participant participant, Integer transactionId, String reason) { + public void unpublishVideo(Participant participant, Participant moderator, Integer transactionId, String reason) { try { KurentoParticipant kParticipant = (KurentoParticipant) participant; KurentoSession session = kParticipant.getSession(); @@ -283,11 +283,11 @@ public class KurentoSessionManager extends SessionManager { Set participants = session.getParticipants(); - sessionEventsHandler.onUnpublishMedia(participant, participants, transactionId, null, reason); + sessionEventsHandler.onUnpublishMedia(participant, participants, moderator, transactionId, null, reason); } catch (OpenViduException e) { log.warn("PARTICIPANT {}: Error unpublishing media", participant.getParticipantPublicId(), e); - sessionEventsHandler.onUnpublishMedia(participant, null, transactionId, e, ""); + sessionEventsHandler.onUnpublishMedia(participant, null, moderator, transactionId, e, ""); } } @@ -328,10 +328,10 @@ public class KurentoSessionManager extends SessionManager { } } catch (OpenViduException e) { log.error("PARTICIPANT {}: Error subscribing to {}", participant.getParticipantPublicId(), senderName, e); - sessionEventsHandler.onSubscribe(participant, session, senderName, null, transactionId, e); + sessionEventsHandler.onSubscribe(participant, session, null, transactionId, e); } if (sdpAnswer != null) { - sessionEventsHandler.onSubscribe(participant, session, senderName, sdpAnswer, transactionId, null); + sessionEventsHandler.onSubscribe(participant, session, sdpAnswer, transactionId, null); } } @@ -354,7 +354,7 @@ public class KurentoSessionManager extends SessionManager { kParticipant.cancelReceivingMedia(senderName, "unsubscribe"); - sessionEventsHandler.onUnsubscribe(participant, senderName, transactionId, null); + sessionEventsHandler.onUnsubscribe(participant, transactionId, null); } @Override @@ -457,19 +457,24 @@ public class KurentoSessionManager extends SessionManager { sessionEventsHandler.onSessionCreated(sessionId); } - /** - * Application-originated request to remove a participant from a session.
- * Side effects: The session event handler should notify the - * participant that she has been evicted. Should also send notifications to all - * other participants about the one that's just been evicted. - * - */ @Override - public void evictParticipant(String participantPrivateId, String reason) throws OpenViduException { - Participant participant = this.getParticipant(participantPrivateId); - this.leaveRoom(participant, null, reason, false); - sessionEventsHandler.onParticipantEvicted(participant, reason); - sessionEventsHandler.closeRpcSession(participantPrivateId); + public void evictParticipant(Participant evictedParticipant, Participant moderator, Integer transactionId, + String reason) throws OpenViduException { + if (evictedParticipant != null) { + KurentoParticipant kParticipant = (KurentoParticipant) evictedParticipant; + Set participants = kParticipant.getSession().getParticipants(); + this.leaveRoom(kParticipant, null, reason, false); + this.sessionEventsHandler.onForceDisconnect(moderator, evictedParticipant, participants, transactionId, + null, reason); + sessionEventsHandler.closeRpcSession(evictedParticipant.getParticipantPrivateId()); + } else { + if (moderator != null && transactionId != null) { + this.sessionEventsHandler.onForceDisconnect(moderator, evictedParticipant, null, transactionId, + new OpenViduException(Code.USER_NOT_FOUND_ERROR_CODE, + "Connection not found when calling 'forceDisconnect'"), + ""); + } + } } @Override @@ -511,12 +516,12 @@ public class KurentoSessionManager extends SessionManager { } @Override - public boolean unpublishStream(Session session, String streamId, String reason) { + public boolean unpublishStream(Session session, String streamId, Participant moderator, Integer transactionId, String reason) { String participantPrivateId = ((KurentoSession) session).getParticipantPrivateIdFromStreamId(streamId); if (participantPrivateId != null) { Participant participant = this.getParticipant(participantPrivateId); if (participant != null) { - this.unpublishVideo(participant, null, reason); + this.unpublishVideo(participant, moderator, transactionId, reason); return true; } else { return false; diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java index c126dd2a..49b5af8c 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java @@ -185,7 +185,7 @@ public class SessionRestController { if (session != null) { Participant participant = session.getParticipantByPublicId(participantPublicId); if (participant != null) { - this.sessionManager.evictParticipant(participant.getParticipantPrivateId(), "forceDisconnectByServer"); + this.sessionManager.evictParticipant(participant, null, null, "forceDisconnectByServer"); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -200,7 +200,7 @@ public class SessionRestController { @PathVariable("streamId") String streamId) { Session session = this.sessionManager.getSession(sessionId); if (session != null) { - if (this.sessionManager.unpublishStream(session, streamId, "forceUnpublishByServer")) { + if (this.sessionManager.unpublishStream(session, streamId, null, null, "forceUnpublishByServer")) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -331,8 +331,9 @@ public class SessionRestController { Recording stoppedRecording = this.recordingService.stopRecording(session); - sessionManager.evictParticipant(session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID) - .getParticipantPrivateId(), "EVICT_RECORDER"); + sessionManager.evictParticipant( + session.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), null, null, + "EVICT_RECORDER"); return new ResponseEntity<>(stoppedRecording.toJson(), HttpStatus.OK); } diff --git a/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java b/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java index 854c5ede..0382dd60 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rpc/RpcHandler.java @@ -125,6 +125,12 @@ public class RpcHandler extends DefaultJsonRpcHandler { case ProtocolElements.STREAMPROPERTYCHANGED_METHOD: streamPropertyChanged(rpcConnection, request); break; + case ProtocolElements.FORCEDISCONNECT_METHOD: + forceDisconnect(rpcConnection, request); + break; + case ProtocolElements.FORCEUNPUBLISH_METHOD: + forceUnpublish(rpcConnection, request); + break; default: log.error("Unrecognized request {}", request); break; @@ -189,36 +195,27 @@ public class RpcHandler extends DefaultJsonRpcHandler { } private void leaveRoom(RpcConnection rpcConnection, Request request) { - - String participantPrivateId = rpcConnection.getParticipantPrivateId(); - String sessionId = rpcConnection.getSessionId(); - - if (sessionId == null) { // null when afterConnectionClosed - log.warn("No session information found for participant with privateId {}. " - + "Using the admin method to evict the user.", participantPrivateId); - leaveRoomAfterConnClosed(participantPrivateId, ""); - } else { - // Sanity check: don't call leaveRoom unless the id checks out - Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId); - if (participant != null) { - log.info("Participant {} is leaving session {}", participant.getParticipantPublicId(), sessionId); - sessionManager.leaveRoom(participant, request.getId(), "disconnect", true); - log.info("Participant {} has left session {}", participant.getParticipantPublicId(), sessionId); - } else { - log.warn("Participant with private id {} not found in session {}. " - + "Using the admin method to evict the user.", participantPrivateId, sessionId); - leaveRoomAfterConnClosed(participantPrivateId, ""); - } + Participant participant; + try { + participant = sanityCheckOfSession(rpcConnection, "disconnect"); + } catch (OpenViduException e) { + return; } + + sessionManager.leaveRoom(participant, request.getId(), "disconnect", true); + log.info("Participant {} has left session {}", participant.getParticipantPublicId(), + rpcConnection.getSessionId()); } private void publishVideo(RpcConnection rpcConnection, Request request) { + Participant participant; + try { + participant = sanityCheckOfSession(rpcConnection, "publish"); + } catch (OpenViduException e) { + return; + } - String participantPrivateId = rpcConnection.getParticipantPrivateId(); - String sessionId = rpcConnection.getSessionId(); - Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId); - - if (sessionManager.isPublisherInSession(sessionId, participant)) { + if (sessionManager.isPublisherInSession(rpcConnection.getSessionId(), participant)) { MediaOptions options = sessionManager.generateMediaOptions(request); sessionManager.publishVideo(participant, options, request.getId()); } else { @@ -229,10 +226,12 @@ public class RpcHandler extends DefaultJsonRpcHandler { } private void receiveVideoFrom(RpcConnection rpcConnection, Request request) { - - String participantPrivateId = rpcConnection.getParticipantPrivateId(); - String sessionId = rpcConnection.getSessionId(); - Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId); + Participant participant; + try { + participant = sanityCheckOfSession(rpcConnection, "subscribe"); + } catch (OpenViduException e) { + return; + } String senderName = getStringParam(request, ProtocolElements.RECEIVEVIDEO_SENDER_PARAM); senderName = senderName.substring(0, senderName.indexOf("_")); @@ -242,21 +241,24 @@ public class RpcHandler extends DefaultJsonRpcHandler { } private void unsubscribeFromVideo(RpcConnection rpcConnection, Request request) { - - String participantPrivateId = rpcConnection.getParticipantPrivateId(); - String sessionId = rpcConnection.getSessionId(); - Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId); + Participant participant; + try { + participant = sanityCheckOfSession(rpcConnection, "unsubscribe"); + } catch (OpenViduException e) { + return; + } String senderName = getStringParam(request, ProtocolElements.UNSUBSCRIBEFROMVIDEO_SENDER_PARAM); - sessionManager.unsubscribe(participant, senderName, request.getId()); } private void onIceCandidate(RpcConnection rpcConnection, Request request) { - - String participantPrivateId = rpcConnection.getParticipantPrivateId(); - String sessionId = rpcConnection.getSessionId(); - Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId); + Participant participant; + try { + participant = sanityCheckOfSession(rpcConnection, "onIceCandidate"); + } catch (OpenViduException e) { + return; + } String endpointName = getStringParam(request, ProtocolElements.ONICECANDIDATE_EPNAME_PARAM); String candidate = getStringParam(request, ProtocolElements.ONICECANDIDATE_CANDIDATE_PARAM); @@ -267,29 +269,75 @@ public class RpcHandler extends DefaultJsonRpcHandler { } private void sendMessage(RpcConnection rpcConnection, Request request) { - - String participantPrivateId = rpcConnection.getParticipantPrivateId(); - String sessionId = rpcConnection.getSessionId(); - Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId); + Participant participant; + try { + participant = sanityCheckOfSession(rpcConnection, "signal"); + } catch (OpenViduException e) { + return; + } String message = getStringParam(request, ProtocolElements.SENDMESSAGE_MESSAGE_PARAM); - sessionManager.sendMessage(participant, message, request.getId()); } private void unpublishVideo(RpcConnection rpcConnection, Request request) { + Participant participant; + try { + participant = sanityCheckOfSession(rpcConnection, "unpublish"); + } catch (OpenViduException e) { + return; + } - String participantPrivateId = rpcConnection.getParticipantPrivateId(); - String sessionId = rpcConnection.getSessionId(); - Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId); - - sessionManager.unpublishVideo(participant, request.getId(), "unpublish"); + sessionManager.unpublishVideo(participant, null, request.getId(), "unpublish"); } - public void streamPropertyChanged(RpcConnection rpcConnection, Request request) { - String participantPrivateId = rpcConnection.getParticipantPrivateId(); - String sessionId = rpcConnection.getSessionId(); - Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId); + private void forceDisconnect(RpcConnection rpcConnection, Request request) { + Participant participant; + try { + participant = sanityCheckOfSession(rpcConnection, "forceDisconnect"); + } catch (OpenViduException e) { + return; + } + + if (sessionManager.isModeratorInSession(rpcConnection.getSessionId(), participant)) { + String connectionId = getStringParam(request, ProtocolElements.FORCEDISCONNECT_CONNECTIONID_PARAM); + sessionManager.evictParticipant( + sessionManager.getSession(rpcConnection.getSessionId()).getParticipantByPublicId(connectionId), + participant, request.getId(), "forceDisconnectByUser"); + } else { + log.error("Error: participant {} is not a moderator", participant.getParticipantPublicId()); + throw new OpenViduException(Code.USER_UNAUTHORIZED_ERROR_CODE, + "Unable to force disconnect. The user does not have a valid token"); + } + } + + private void forceUnpublish(RpcConnection rpcConnection, Request request) { + Participant participant; + try { + participant = sanityCheckOfSession(rpcConnection, "forceUnpublish"); + } catch (OpenViduException e) { + return; + } + + if (sessionManager.isModeratorInSession(rpcConnection.getSessionId(), participant)) { + String streamId = getStringParam(request, ProtocolElements.FORCEUNPUBLISH_STREAMID_PARAM); + sessionManager.unpublishStream(sessionManager.getSession(rpcConnection.getSessionId()), streamId, + participant, request.getId(), "forceUnpublishByUser"); + } else { + log.error("Error: participant {} is not a moderator", participant.getParticipantPublicId()); + throw new OpenViduException(Code.USER_UNAUTHORIZED_ERROR_CODE, + "Unable to force unpublish. The user does not have a valid token"); + } + + } + + private void streamPropertyChanged(RpcConnection rpcConnection, Request request) { + Participant participant; + try { + participant = sanityCheckOfSession(rpcConnection, "onStreamPropertyChanged"); + } catch (OpenViduException e) { + return; + } String streamId = getStringParam(request, ProtocolElements.STREAMPROPERTYCHANGED_STREAMID_PARAM); String property = getStringParam(request, ProtocolElements.STREAMPROPERTYCHANGED_PROPERTY_PARAM); @@ -301,7 +349,8 @@ public class RpcHandler extends DefaultJsonRpcHandler { public void leaveRoomAfterConnClosed(String participantPrivateId, String reason) { try { - sessionManager.evictParticipant(participantPrivateId, reason); + sessionManager.evictParticipant(this.sessionManager.getParticipant(participantPrivateId), null, null, + reason); log.info("Evicted participant with privateId {}", participantPrivateId); } catch (OpenViduException e) { log.warn("Unable to evict: {}", e.getMessage()); @@ -409,4 +458,33 @@ public class RpcHandler extends DefaultJsonRpcHandler { return request.getParams().get(key); } + private Participant sanityCheckOfSession(RpcConnection rpcConnection, String methodName) throws OpenViduException { + String participantPrivateId = rpcConnection.getParticipantPrivateId(); + String sessionId = rpcConnection.getSessionId(); + String errorMsg; + + if (sessionId == null) { // null when afterConnectionClosed + errorMsg = "No session information found for participant with privateId " + participantPrivateId + + ". Using the admin method to evict the user."; + log.warn(errorMsg); + leaveRoomAfterConnClosed(participantPrivateId, ""); + throw new OpenViduException(Code.GENERIC_ERROR_CODE, errorMsg); + } else { + // Sanity check: don't call RPC method unless the id checks out + Participant participant = sessionManager.getParticipant(sessionId, participantPrivateId); + if (participant != null) { + errorMsg = "Participant " + participant.getParticipantPublicId() + " is calling method '" + methodName + + "' in session " + sessionId; + log.info(errorMsg); + return participant; + } else { + errorMsg = "Participant with private id " + participantPrivateId + " not found in session " + sessionId + + ". Using the admin method to evict the user."; + log.warn(errorMsg); + leaveRoomAfterConnClosed(participantPrivateId, ""); + throw new OpenViduException(Code.GENERIC_ERROR_CODE, errorMsg); + } + } + } + } From b4903eaca025b1a5c689333b51082e78b8bf069f Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Mon, 9 Jul 2018 16:04:49 +0200 Subject: [PATCH 08/13] openvidu-client: Session.forceUnpublish and Session.forceDisconnect params --- .../io/openvidu/client/internal/ProtocolElements.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java b/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java index fffed01d..f2cc5846 100644 --- a/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java +++ b/openvidu-client/src/main/java/io/openvidu/client/internal/ProtocolElements.java @@ -87,7 +87,13 @@ public class ProtocolElements { public static final String STREAMPROPERTYCHANGED_PROPERTY_PARAM = "property"; public static final String STREAMPROPERTYCHANGED_NEWVALUE_PARAM = "newValue"; public static final String STREAMPROPERTYCHANGED_REASON_PARAM = "reason"; - + + public static final String FORCEDISCONNECT_METHOD = "forceDisconnect"; + public static final String FORCEDISCONNECT_CONNECTIONID_PARAM = "connectionId"; + + public static final String FORCEUNPUBLISH_METHOD = "forceUnpublish"; + public static final String FORCEUNPUBLISH_STREAMID_PARAM = "streamId"; + // ---------------------------- SERVER RESPONSES & EVENTS ----------------- public static final String PARTICIPANTJOINED_METHOD = "participantJoined"; From a3849098a35605bb1032b6dddbe4b2a54f080ed0 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Mon, 9 Jul 2018 16:20:27 +0200 Subject: [PATCH 09/13] openvidu-server: RPC error response fix --- .../server/kurento/core/KurentoSessionManager.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java index 1a38d8d0..019b6af3 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoSessionManager.java @@ -17,7 +17,9 @@ package io.openvidu.server.kurento.core; +import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.Set; import org.kurento.client.IceCandidate; @@ -287,7 +289,8 @@ public class KurentoSessionManager extends SessionManager { } catch (OpenViduException e) { log.warn("PARTICIPANT {}: Error unpublishing media", participant.getParticipantPublicId(), e); - sessionEventsHandler.onUnpublishMedia(participant, null, moderator, transactionId, e, ""); + sessionEventsHandler.onUnpublishMedia(participant, new HashSet<>(Arrays.asList(participant)), moderator, + transactionId, e, ""); } } @@ -469,7 +472,8 @@ public class KurentoSessionManager extends SessionManager { sessionEventsHandler.closeRpcSession(evictedParticipant.getParticipantPrivateId()); } else { if (moderator != null && transactionId != null) { - this.sessionEventsHandler.onForceDisconnect(moderator, evictedParticipant, null, transactionId, + this.sessionEventsHandler.onForceDisconnect(moderator, evictedParticipant, + new HashSet<>(Arrays.asList(moderator)), transactionId, new OpenViduException(Code.USER_NOT_FOUND_ERROR_CODE, "Connection not found when calling 'forceDisconnect'"), ""); @@ -516,7 +520,8 @@ public class KurentoSessionManager extends SessionManager { } @Override - public boolean unpublishStream(Session session, String streamId, Participant moderator, Integer transactionId, String reason) { + public boolean unpublishStream(Session session, String streamId, Participant moderator, Integer transactionId, + String reason) { String participantPrivateId = ((KurentoSession) session).getParticipantPrivateIdFromStreamId(streamId); if (participantPrivateId != null) { Participant participant = this.getParticipant(participantPrivateId); From c67e98aec5dc3c88e4786bb757cc377f0852de3c Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Mon, 9 Jul 2018 16:29:21 +0200 Subject: [PATCH 10/13] openvidu-testapp: Session.forceUnpublish and Session.forceDisconnect update --- .../session-properties-dialog.component.css | 12 ++++++++---- .../session-properties-dialog.component.html | 17 ++++++++++++++--- .../session-properties-dialog.component.ts | 2 ++ .../openvidu-instance.component.html | 2 +- .../openvidu-instance.component.ts | 17 ++++++++++++----- .../app/components/video/video.component.html | 8 ++++++++ .../src/app/components/video/video.component.ts | 8 ++++++++ 7 files changed, 53 insertions(+), 13 deletions(-) diff --git a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.css b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.css index b0ee64f6..4728e295 100644 --- a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.css +++ b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.css @@ -7,7 +7,7 @@ mat-radio-button:first-child { margin-left: 0; } -#turn-conf-label { +.label { display: block; font-size: 12px; color: rgba(0, 0, 0, 0.54); @@ -15,9 +15,8 @@ mat-radio-button:first-child { margin-bottom: 5px; } -.not-manual { - padding-top: 6px; - padding-bottom: 15px; +#turn-div { + padding-bottom: 1.25em; } #manual-turn-div { @@ -25,4 +24,9 @@ mat-radio-button:first-child { padding: 5px; border: 1px solid #00000026; border-radius: 3px; +} + +#role-div { + padding-top: 6px; + padding-bottom: 15px; } \ No newline at end of file diff --git a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html index 38bce2fc..e945fc76 100644 --- a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html +++ b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.html @@ -28,8 +28,8 @@ - -
+ +
Auto Freeice @@ -47,9 +47,20 @@
+ + +
+ + SUB + PUB + MOD + +
+ + - + \ No newline at end of file diff --git a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.ts b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.ts index 5ddb9d3c..189eef69 100644 --- a/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.ts +++ b/openvidu-testapp/src/app/components/dialogs/session-properties-dialog/session-properties-dialog.component.ts @@ -13,6 +13,7 @@ export class SessionPropertiesDialogComponent { sessionProperties: SessionProperties; turnConf: string; manualTurnConf: RTCIceServer = {}; + participantRole: string; mediaMode = MediaMode; recordingMode = RecordingMode; @@ -23,6 +24,7 @@ export class SessionPropertiesDialogComponent { this.sessionProperties = data.sessionProperties; this.turnConf = data.turnConf; this.manualTurnConf = data.manualTurnConf; + this.participantRole = data.participantRole; } enumToArray(enumerator: any) { diff --git a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.html b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.html index f3fdd6e7..18059538 100644 --- a/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.html +++ b/openvidu-testapp/src/app/components/openvidu-instance/openvidu-instance.component.html @@ -78,7 +78,7 @@
+ + + + diff --git a/openvidu-testapp/src/app/components/video/video.component.ts b/openvidu-testapp/src/app/components/video/video.component.ts index ea0a0179..1fae7b53 100644 --- a/openvidu-testapp/src/app/components/video/video.component.ts +++ b/openvidu-testapp/src/app/components/video/video.component.ts @@ -613,4 +613,12 @@ export class VideoComponent implements OnInit, OnDestroy { } } + forceUnpublish() { + this.OV.session.forceUnpublish(this.streamManager.stream); + } + + forceDisconnect() { + this.OV.session.forceDisconnect(this.streamManager.stream.connection); + } + } From 7f41f0ccad830b4e9e6865ff777e401c47b19e76 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Mon, 9 Jul 2018 16:33:47 +0200 Subject: [PATCH 11/13] openvidu-node-client: TokenOptions properties optional --- openvidu-node-client/src/TokenOptions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openvidu-node-client/src/TokenOptions.ts b/openvidu-node-client/src/TokenOptions.ts index 66be778d..d84a7fd9 100644 --- a/openvidu-node-client/src/TokenOptions.ts +++ b/openvidu-node-client/src/TokenOptions.ts @@ -25,10 +25,10 @@ export interface TokenOptions { * - If you have provided some data when calling `Session.connect(TOKEN, DATA)` (`DATA` defined), then `Connection.data` will have the following structure: `"CLIENT_DATA%/%SERVER_DATA"`, being `CLIENT_DATA` the second * parameter passed in OpenVidu Browser in method `Session.connect` and `SERVER_DATA` this [[TokenOptions.data]] property. */ - data: string; + data?: string; /** * The role assigned to this token */ - role: OpenViduRole; + role?: OpenViduRole; } \ No newline at end of file From 844e7f2999d11fa66382deb4dd8318bbdd7e35c2 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Mon, 9 Jul 2018 17:21:11 +0200 Subject: [PATCH 12/13] openvidu backend clients OpenViduRole JavaDoc updated (MODERATOR) --- .../main/java/io/openvidu/java/client/OpenViduRole.java | 9 ++++----- openvidu-node-client/src/OpenViduRole.ts | 7 ++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenViduRole.java b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenViduRole.java index 574ebb68..c47bb5fd 100644 --- a/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenViduRole.java +++ b/openvidu-java-client/src/main/java/io/openvidu/java/client/OpenViduRole.java @@ -20,19 +20,18 @@ package io.openvidu.java.client; public enum OpenViduRole { /** - * Can subscribe to published streams of other users + * Can subscribe to published Streams of other users */ SUBSCRIBER, /** - * SUBSCRIBER permissions + can publish their own streams + * SUBSCRIBER permissions + can publish their own Streams (call Session.publish()) */ PUBLISHER, /** - * (not available yet) SUBSCRIBER and PUBLIHSER permissions + can force - * unpublish() and disconnect() over a third-party - * stream or user + * SUBSCRIBER + PUBLISHER permissions + can force the unpublishing or disconnection over a third-party Stream or Connection + * (call Session.forceUnpublish() and Session.forceDisconnect()) */ MODERATOR; } diff --git a/openvidu-node-client/src/OpenViduRole.ts b/openvidu-node-client/src/OpenViduRole.ts index 2ebdf49b..4c0e2091 100644 --- a/openvidu-node-client/src/OpenViduRole.ts +++ b/openvidu-node-client/src/OpenViduRole.ts @@ -18,17 +18,18 @@ export enum OpenViduRole { /** - * Can subscribe to published streams of other users + * Can subscribe to published Streams of other users */ SUBSCRIBER = 'SUBSCRIBER', /** - * SUBSCRIBER permissions + can publish their own streams + * SUBSCRIBER permissions + can publish their own Streams (call `Session.publish()`) */ PUBLISHER = 'PUBLISHER', /** - * _(not available yet)_ SUBSCRIBER + PUBLIHSER permissions + can force `unpublish()` and `disconnect()` over a third-party stream or user + * SUBSCRIBER + PUBLISHER permissions + can force the unpublishing or disconnection over a third-party Stream or Connection + * (call `Session.forceUnpublish()` and `Session.forceDisconnect()`) */ MODERATOR = 'MODERATOR' } \ No newline at end of file From de27312d1efd3082eb53b99fd4c6db8827456abf Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Mon, 9 Jul 2018 18:12:26 +0200 Subject: [PATCH 13/13] openvidu-server: SessionEventsHandler generic provider --- .../src/main/java/io/openvidu/server/OpenViduServer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java b/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java index 70fac139..ac00111b 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java +++ b/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java @@ -48,6 +48,7 @@ import com.google.gson.JsonParser; import io.openvidu.server.cdr.CDRLoggerFile; import io.openvidu.server.cdr.CallDetailRecord; import io.openvidu.server.config.OpenviduConfig; +import io.openvidu.server.core.SessionEventsHandler; import io.openvidu.server.core.SessionManager; import io.openvidu.server.coturn.CoturnCredentialsService; import io.openvidu.server.coturn.CoturnCredentialsServiceFactory; @@ -126,7 +127,7 @@ public class OpenViduServer implements JsonRpcConfigurer { @Bean @ConditionalOnMissingBean - public KurentoSessionEventsHandler kurentoSessionEventsHandler() { + public SessionEventsHandler sessionEventsHandler() { return new KurentoSessionEventsHandler(); }