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 ee2d8cbe..19419208 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java +++ b/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java @@ -50,7 +50,6 @@ import io.openvidu.server.config.OpenviduConfig.Error; import io.openvidu.server.core.SessionEventsHandler; import io.openvidu.server.core.SessionManager; import io.openvidu.server.core.TokenGenerator; -import io.openvidu.server.core.TokenGeneratorDefault; import io.openvidu.server.coturn.CoturnCredentialsService; import io.openvidu.server.coturn.CoturnCredentialsServiceFactory; import io.openvidu.server.kurento.core.KurentoParticipantEndpointConfig; @@ -156,7 +155,7 @@ public class OpenViduServer implements JsonRpcConfigurer { @ConditionalOnMissingBean @DependsOn("openviduConfig") public TokenGenerator tokenGenerator() { - return new TokenGeneratorDefault(); + return new TokenGenerator(); } @Bean 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 7edf339c..8a9c7b54 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 @@ -60,6 +60,7 @@ public class Session implements SessionInterface { protected volatile boolean closed = false; protected AtomicInteger activePublishers = new AtomicInteger(0); + protected AtomicInteger activeIndividualRecordedPublishers = new AtomicInteger(0); /** * This lock protects the following operations with read lock: [REST API](POST @@ -145,12 +146,22 @@ public class Session implements SessionInterface { return activePublishers.get(); } - public void registerPublisher() { - this.activePublishers.incrementAndGet(); + public int getActiveIndividualRecordedPublishers() { + return activeIndividualRecordedPublishers.get(); } - public void deregisterPublisher() { + public void registerPublisher(Participant participant) { + this.activePublishers.incrementAndGet(); + if (participant.getToken().record()) { + activeIndividualRecordedPublishers.incrementAndGet(); + } + } + + public void deregisterPublisher(Participant participant) { this.activePublishers.decrementAndGet(); + if (participant.getToken().record()) { + activeIndividualRecordedPublishers.decrementAndGet(); + } } public void storeToken(Token token) { 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 91dd8fd6..c4120af9 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 @@ -295,13 +295,13 @@ public abstract class SessionManager { return sessionNotActive; } - public String newToken(Session session, OpenViduRole role, String serverMetadata, + public String newToken(Session session, OpenViduRole role, String serverMetadata, boolean record, KurentoTokenOptions kurentoTokenOptions) throws Exception { if (!formatChecker.isServerMetadataFormatCorrect(serverMetadata)) { log.error("Data invalid format"); throw new OpenViduException(Code.GENERIC_ERROR_CODE, "Data invalid format"); } - Token tokenObj = tokenGenerator.generateToken(session.getSessionId(), role, serverMetadata, + Token tokenObj = tokenGenerator.generateToken(session.getSessionId(), role, serverMetadata, record, kurentoTokenOptions); session.storeToken(tokenObj); session.showTokens("Token created"); @@ -310,7 +310,8 @@ public abstract class SessionManager { public Token newTokenForInsecureUser(Session session, String token, String serverMetadata) throws Exception { Token tokenObj = new Token(token, OpenViduRole.PUBLISHER, serverMetadata != null ? serverMetadata : "", - this.openviduConfig.isTurnadminAvailable() ? this.coturnCredentialsService.createUser() : null, null); + this.openviduConfig.isTurnadminAvailable() ? this.coturnCredentialsService.createUser() : null, true, + null); session.storeToken(tokenObj); session.showTokens("Token created for insecure user"); return tokenObj; diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Token.java b/openvidu-server/src/main/java/io/openvidu/server/core/Token.java index feeb99af..1124afea 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/Token.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/Token.java @@ -27,6 +27,7 @@ public class Token { private OpenViduRole role; private String serverMetadata = ""; private TurnCredentials turnCredentials; + private boolean record = true; private KurentoTokenOptions kurentoTokenOptions; @@ -35,11 +36,12 @@ public class Token { } public Token(String token, OpenViduRole role, String serverMetadata, TurnCredentials turnCredentials, - KurentoTokenOptions kurentoTokenOptions) { + boolean record, KurentoTokenOptions kurentoTokenOptions) { this.token = token; this.role = role; this.serverMetadata = serverMetadata; this.turnCredentials = turnCredentials; + this.record = record; this.kurentoTokenOptions = kurentoTokenOptions; } @@ -50,7 +52,7 @@ public class Token { public void setToken(String token) { this.token = token; } - + public OpenViduRole getRole() { return role; } @@ -63,6 +65,10 @@ public class Token { return turnCredentials; } + public boolean record() { + return record; + } + public KurentoTokenOptions getKurentoTokenOptions() { return kurentoTokenOptions; } diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java b/openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java index 472f4ee8..13149582 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/TokenGenerator.java @@ -17,12 +17,45 @@ package io.openvidu.server.core; +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.beans.factory.annotation.Autowired; + import io.openvidu.java.client.OpenViduRole; +import io.openvidu.server.OpenViduServer; +import io.openvidu.server.config.OpenviduBuildInfo; +import io.openvidu.server.config.OpenviduConfig; +import io.openvidu.server.coturn.CoturnCredentialsService; +import io.openvidu.server.coturn.TurnCredentials; import io.openvidu.server.kurento.core.KurentoTokenOptions; -public interface TokenGenerator { +public class TokenGenerator { - public Token generateToken(String sessionId, OpenViduRole role, String serverMetadata, - KurentoTokenOptions kurentoTokenOptions) throws Exception; + @Autowired + private CoturnCredentialsService coturnCredentialsService; + @Autowired + protected OpenviduConfig openviduConfig; + + @Autowired + protected OpenviduBuildInfo openviduBuildConfig; + + public Token generateToken(String sessionId, OpenViduRole role, String serverMetadata, boolean record, + KurentoTokenOptions kurentoTokenOptions) throws Exception { + String token = OpenViduServer.wsUrl; + token += "?sessionId=" + sessionId; + token += "&token=" + IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase() + + RandomStringUtils.randomAlphanumeric(15); + token += "&role=" + role.name(); + token += "&version=" + openviduBuildConfig.getOpenViduServerVersion(); + TurnCredentials turnCredentials = null; + if (this.openviduConfig.isTurnadminAvailable()) { + turnCredentials = coturnCredentialsService.createUser(); + if (turnCredentials != null) { + token += "&coturnIp=" + openviduConfig.getCoturnIp(); + token += "&turnUsername=" + turnCredentials.getUsername(); + token += "&turnCredential=" + turnCredentials.getCredential(); + } + } + return new Token(token, role, serverMetadata, turnCredentials, record, kurentoTokenOptions); + } } diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/TokenGeneratorDefault.java b/openvidu-server/src/main/java/io/openvidu/server/core/TokenGeneratorDefault.java deleted file mode 100644 index eeb46563..00000000 --- a/openvidu-server/src/main/java/io/openvidu/server/core/TokenGeneratorDefault.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * (C) Copyright 2017-2020 OpenVidu (https://openvidu.io) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package io.openvidu.server.core; - -import org.apache.commons.lang3.RandomStringUtils; -import org.springframework.beans.factory.annotation.Autowired; - -import io.openvidu.java.client.OpenViduRole; -import io.openvidu.server.OpenViduServer; -import io.openvidu.server.config.OpenviduBuildInfo; -import io.openvidu.server.config.OpenviduConfig; -import io.openvidu.server.coturn.CoturnCredentialsService; -import io.openvidu.server.coturn.TurnCredentials; -import io.openvidu.server.kurento.core.KurentoTokenOptions; - -public class TokenGeneratorDefault implements TokenGenerator { - - @Autowired - private CoturnCredentialsService coturnCredentialsService; - - @Autowired - protected OpenviduConfig openviduConfig; - - @Autowired - protected OpenviduBuildInfo openviduBuildConfig; - - @Override - public Token generateToken(String sessionId, OpenViduRole role, String serverMetadata, - KurentoTokenOptions kurentoTokenOptions) throws Exception { - String token = OpenViduServer.wsUrl; - token += "?sessionId=" + sessionId; - token += "&token=" + IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase() - + RandomStringUtils.randomAlphanumeric(15); - token += "&role=" + role.name(); - token += "&version=" + openviduBuildConfig.getOpenViduServerVersion(); - TurnCredentials turnCredentials = null; - if (this.openviduConfig.isTurnadminAvailable()) { - turnCredentials = coturnCredentialsService.createUser(); - if (turnCredentials != null) { - token += "&coturnIp=" + openviduConfig.getCoturnIp(); - token += "&turnUsername=" + turnCredentials.getUsername(); - token += "&turnCredential=" + turnCredentials.getCredential(); - } - } - return new Token(token, role, serverMetadata, turnCredentials, kurentoTokenOptions); - } -} 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 05c795b3..2a639818 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 @@ -182,7 +182,7 @@ public class KurentoParticipant extends Participant { this.session.getSessionId()); if (this.openviduConfig.isRecordingModuleEnabled() - && this.recordingManager.sessionIsBeingRecorded(session.getSessionId())) { + && this.recordingManager.sessionIsBeingRecorded(session.getSessionId()) && this.token.record()) { this.recordingManager.startOneIndividualStreamRecording(session, null, null, this); } @@ -461,7 +461,7 @@ public class KurentoParticipant extends Participant { } releaseElement(getParticipantPublicId(), publisher.getEndpoint()); this.streaming = false; - this.session.deregisterPublisher(); + this.session.deregisterPublisher(this); endpointConfig.getCdr().stopPublisher(this.getParticipantPublicId(), publisher.getStreamId(), reason); publisher = 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 e0ad8cb4..64bd7349 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 @@ -88,7 +88,7 @@ public class KurentoSession extends Session { } public void newPublisher(Participant participant) { - registerPublisher(); + registerPublisher(participant); log.debug("SESSION {}: Virtually subscribed other participants {} to new publisher {}", sessionId, participants.values(), participant.getParticipantPublicId()); } diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java index 57d41d1b..14654f68 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java @@ -279,8 +279,7 @@ public class RecordingManager { this.sessionHandler.sendRecordingStartedNotification(session, recording); } if (session.getActivePublishers() == 0) { - // Init automatic recording stop if there are now publishers when starting - // recording + // Init automatic recording stop if no publishers when starting the recording log.info("No publisher in session {}. Starting {} seconds countdown for stopping recording", session.getSessionId(), this.openviduConfig.getOpenviduRecordingAutostopTimeout()); this.initAutomaticRecordingStopThread(session); diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java index 404ca521..30846616 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/SingleStreamRecordingService.java @@ -98,11 +98,11 @@ public class SingleStreamRecordingService extends RecordingService { activeRecorders.put(session.getSessionId(), new ConcurrentHashMap()); storedRecorders.put(session.getSessionId(), new ConcurrentHashMap()); - final int activePublishers = session.getActivePublishers(); - final CountDownLatch recordingStartedCountdown = new CountDownLatch(activePublishers); + int activePublishersToRecord = session.getActiveIndividualRecordedPublishers(); + final CountDownLatch recordingStartedCountdown = new CountDownLatch(activePublishersToRecord); for (Participant p : session.getParticipants()) { - if (p.isStreaming()) { + if (p.isStreaming() && p.getToken().record()) { MediaProfileSpecType profile = null; try { 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 91472a44..8d4f7ca4 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 @@ -330,10 +330,12 @@ public class SessionRestController { String sessionId; String roleString; String metadata; + Boolean record; try { sessionId = (String) params.get("session"); roleString = (String) params.get("role"); metadata = (String) params.get("data"); + record = (Boolean) params.get("record"); } catch (ClassCastException e) { return this.generateErrorResponse("Type error in some parameter", "/api/tokens", HttpStatus.BAD_REQUEST); } @@ -383,17 +385,19 @@ public class SessionRestController { } metadata = (metadata != null) ? metadata : ""; + record = (record != null) ? record : true; // While closing a session tokens can't be generated if (session.closingLock.readLock().tryLock()) { try { - String token = sessionManager.newToken(session, role, metadata, kurentoTokenOptions); + String token = sessionManager.newToken(session, role, metadata, record, kurentoTokenOptions); JsonObject responseJson = new JsonObject(); responseJson.addProperty("id", token); responseJson.addProperty("session", sessionId); responseJson.addProperty("role", role.toString()); responseJson.addProperty("data", metadata); + responseJson.addProperty("record", record); responseJson.addProperty("token", token); if (kurentoOptions != null) { diff --git a/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java b/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java index 6b1527c8..a5e2e2e9 100644 --- a/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java +++ b/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java @@ -115,7 +115,7 @@ public class SessionGarbageCollectorIntegrationTest { } private void joinParticipant(String sessionId, String token) { - Token t = new Token(token, OpenViduRole.PUBLISHER, "SERVER_METADATA", null, null); + Token t = new Token(token, OpenViduRole.PUBLISHER, "SERVER_METADATA", null, true, null); String uuid = UUID.randomUUID().toString(); String participantPrivateId = "PARTICIPANT_PRIVATE_ID_" + uuid; String finalUserId = "FINAL_USER_ID_" + uuid; diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java index d114b130..2ae83957 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java @@ -1349,8 +1349,15 @@ public class OpenViduTestAppE2eTest { final String sessionName = "TestSession"; final String recordingName = "CUSTOM_NAME"; - user.getDriver().findElement(By.id("auto-join-checkbox")).click(); + // Connect 2 users. One of them not recorded user.getDriver().findElement(By.id("one2one-btn")).click(); + user.getDriver().findElement(By.id("session-settings-btn-0")).click(); + Thread.sleep(1000); + user.getDriver().findElement(By.id("record-checkbox")).click(); + user.getDriver().findElement(By.id("save-btn")).click(); + Thread.sleep(1000); + + user.getDriver().findElements(By.className("join-btn")).forEach(el -> el.sendKeys(Keys.ENTER)); user.getEventManager().waitUntilEventReaches("connectionCreated", 4); user.getEventManager().waitUntilEventReaches("accessAllowed", 2); @@ -1418,7 +1425,7 @@ public class OpenViduTestAppE2eTest { String recPath = recordingsPath + sessionName + "/"; Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName); - this.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true); + this.checkIndividualRecording(recPath, recording, 1, "opus", "vp8", true); // Try to get the stopped recording user.getDriver().findElement(By.id("get-recording-btn")).click();