Trigger recordingStarted event for new connected participants

pull/730/head
pabloFuente 2022-05-27 11:13:12 +02:00
parent df7bd5f202
commit 8d3fe2b681
10 changed files with 102 additions and 30 deletions

View File

@ -1436,6 +1436,10 @@ export class Session extends EventDispatcher {
this.ee.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', stream, '')]); this.ee.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', stream, '')]);
}); });
if (!!response.recordingId && !!response.recordingName) {
this.ee.emitEvent('recordingStarted', [new RecordingEvent(this, 'recordingStarted', response.recordingId, response.recordingName)]);
}
return resolve(); return resolve();
} }
}); });

View File

@ -36,5 +36,7 @@ export interface LocalConnectionOptions {
mediaServer: string; mediaServer: string;
videoSimulcast: boolean; videoSimulcast: boolean;
life: number; life: number;
customIceServers?: IceServerProperties[] customIceServers?: IceServerProperties[];
recordingId?: string; // Defined if the session is being recorded and the client must be notified
recordingName?: string; // Defined if the session is being recorded and the client must be notified
} }

View File

@ -166,6 +166,8 @@ public class ProtocolElements {
public static final String PARTICIPANTJOINED_CUSTOM_ICE_SERVERS = "customIceServers"; public static final String PARTICIPANTJOINED_CUSTOM_ICE_SERVERS = "customIceServers";
public static final String PARTICIPANTJOINED_TURNUSERNAME_PARAM = "turnUsername"; public static final String PARTICIPANTJOINED_TURNUSERNAME_PARAM = "turnUsername";
public static final String PARTICIPANTJOINED_TURNCREDENTIAL_PARAM = "turnCredential"; public static final String PARTICIPANTJOINED_TURNCREDENTIAL_PARAM = "turnCredential";
public static final String PARTICIPANTJOINED_RECORDINGID_PARAM = "recordingId";
public static final String PARTICIPANTJOINED_RECORDINGNAME_PARAM = "recordingName";
public static final String PARTICIPANTLEFT_METHOD = "participantLeft"; public static final String PARTICIPANTLEFT_METHOD = "participantLeft";
public static final String PARTICIPANTLEFT_NAME_PARAM = "connectionId"; public static final String PARTICIPANTLEFT_NAME_PARAM = "connectionId";

View File

@ -32,10 +32,11 @@ import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.Set;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -413,23 +414,26 @@ public class OpenviduConfig {
return this.mediaNodesPublicIps; return this.mediaNodesPublicIps;
} }
public OpenViduRole[] getRolesFromRecordingNotification() { public Set<OpenViduRole> getRolesFromRecordingNotification() {
OpenViduRole[] roles; Set<OpenViduRole> roles = new HashSet<>();
switch (this.openviduRecordingNotification) { switch (this.openviduRecordingNotification) {
case none: case none:
roles = new OpenViduRole[0];
break; break;
case moderator: case moderator:
roles = new OpenViduRole[] { OpenViduRole.MODERATOR }; roles.add(OpenViduRole.MODERATOR);
break; break;
case publisher_moderator: case publisher_moderator:
roles = new OpenViduRole[] { OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR }; roles.add(OpenViduRole.PUBLISHER);
roles.add(OpenViduRole.MODERATOR);
break; break;
case all: case all:
roles = new OpenViduRole[] { OpenViduRole.SUBSCRIBER, OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR }; roles.add(OpenViduRole.SUBSCRIBER);
roles.add(OpenViduRole.PUBLISHER);
roles.add(OpenViduRole.MODERATOR);
break; break;
default: default:
roles = new OpenViduRole[] { OpenViduRole.PUBLISHER, OpenViduRole.MODERATOR }; roles.add(OpenViduRole.PUBLISHER);
roles.add(OpenViduRole.MODERATOR);
} }
return roles; return roles;
} }
@ -547,7 +551,8 @@ public class OpenviduConfig {
protected List<String> getNonUserProperties() { protected List<String> getNonUserProperties() {
return Arrays.asList("server.port", "SERVER_PORT", "DOTENV_PATH", "COTURN_IP", "COTURN_PORT", return Arrays.asList("server.port", "SERVER_PORT", "DOTENV_PATH", "COTURN_IP", "COTURN_PORT",
"COTURN_INTERNAL_RELAY", "COTURN_SHARED_SECRET_KEY", "OPENVIDU_RECORDING_IMAGE", "OPENVIDU_RECORDING_ENABLE_GPU"); "COTURN_INTERNAL_RELAY", "COTURN_SHARED_SECRET_KEY", "OPENVIDU_RECORDING_IMAGE",
"OPENVIDU_RECORDING_ENABLE_GPU");
} }
protected List<String> getNonPrintablePropertiesIfEmpty() { protected List<String> getNonPrintablePropertiesIfEmpty() {

View File

@ -74,8 +74,8 @@ public class SessionEventsHandler {
CDR.recordSessionDestroyed(session, reason); CDR.recordSessionDestroyed(session, reason);
} }
public void onParticipantJoined(Participant participant, String sessionId, String coturnIp, Set<Participant> existingParticipants, public void onParticipantJoined(Participant participant, Recording recording, String coturnIp,
Integer transactionId, OpenViduException error) { Set<Participant> existingParticipants, Integer transactionId, OpenViduException error) {
if (error != null) { if (error != null) {
rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error); rpcNotificationService.sendErrorResponse(participant.getParticipantPrivateId(), transactionId, null, error);
return; return;
@ -179,7 +179,9 @@ public class SessionEventsHandler {
} }
if (participant.getToken() != null) { if (participant.getToken() != null) {
result.addProperty(ProtocolElements.PARTICIPANTJOINED_RECORD_PARAM, participant.getToken().record()); result.addProperty(ProtocolElements.PARTICIPANTJOINED_RECORD_PARAM, participant.getToken().record());
if (participant.getToken().getRole() != null) { if (participant.getToken().getRole() != null) {
result.addProperty(ProtocolElements.PARTICIPANTJOINED_ROLE_PARAM, result.addProperty(ProtocolElements.PARTICIPANTJOINED_ROLE_PARAM,
participant.getToken().getRole().name()); participant.getToken().getRole().name());
@ -198,6 +200,11 @@ public class SessionEventsHandler {
result.addProperty(ProtocolElements.PARTICIPANTJOINED_TURNCREDENTIAL_PARAM, result.addProperty(ProtocolElements.PARTICIPANTJOINED_TURNCREDENTIAL_PARAM,
participant.getToken().getTurnCredentials().getCredential()); participant.getToken().getTurnCredentials().getCredential());
} }
if (recording != null) {
result.addProperty(ProtocolElements.PARTICIPANTJOINED_RECORDINGID_PARAM, recording.getId());
result.addProperty(ProtocolElements.PARTICIPANTJOINED_RECORDINGNAME_PARAM, recording.getName());
}
} }
rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, result); rpcNotificationService.sendResponse(participant.getParticipantPrivateId(), transactionId, result);
@ -680,18 +687,12 @@ public class SessionEventsHandler {
recordingsToSendClientEvents.put(recording.getSessionId(), recording); recordingsToSendClientEvents.put(recording.getSessionId(), recording);
} }
protected Set<Participant> filterParticipantsByRole(OpenViduRole[] roles, Set<Participant> participants) { protected Set<Participant> filterParticipantsByRole(Set<OpenViduRole> roles, Set<Participant> participants) {
return participants.stream().filter(part -> { return participants.stream().filter(part -> {
if (ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(part.getParticipantPublicId())) { if (ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(part.getParticipantPublicId())) {
return false; return false;
} }
boolean isRole = false; return roles.contains(part.getToken().getRole());
for (OpenViduRole role : roles) {
isRole = role.equals(part.getToken().getRole());
if (isRole)
break;
}
return isRole;
}).collect(Collectors.toSet()); }).collect(Collectors.toSet());
} }

View File

@ -136,7 +136,7 @@ public class KurentoSessionManager extends SessionManager {
String error = "Timeout of " + KmsManager.MAX_SECONDS_LOCK_WAIT String error = "Timeout of " + KmsManager.MAX_SECONDS_LOCK_WAIT
+ " seconds waiting to acquire lock"; + " seconds waiting to acquire lock";
log.error(error); log.error(error);
sessionEventsHandler.onParticipantJoined(participant, sessionId, null, null, transactionId, sessionEventsHandler.onParticipantJoined(participant, null, null, null, transactionId,
new OpenViduException(Code.ROOM_CANNOT_BE_CREATED_ERROR_CODE, error)); new OpenViduException(Code.ROOM_CANNOT_BE_CREATED_ERROR_CODE, error));
return; return;
} }
@ -169,7 +169,11 @@ public class KurentoSessionManager extends SessionManager {
existingParticipants = getParticipants(sessionId); existingParticipants = getParticipants(sessionId);
kSession.join(participant); kSession.join(participant);
String coturnIp = openviduConfig.getCoturnIp(kSession.getKms().getUri()); String coturnIp = openviduConfig.getCoturnIp(kSession.getKms().getUri());
sessionEventsHandler.onParticipantJoined(participant, sessionId, coturnIp, existingParticipants,
io.openvidu.server.recording.Recording recording = getActiveRecordingIfAllowedByParticipantRole(
participant);
sessionEventsHandler.onParticipantJoined(participant, recording, coturnIp, existingParticipants,
transactionId, null); transactionId, null);
} finally { } finally {
kSession.joinLeaveLock.unlock(); kSession.joinLeaveLock.unlock();
@ -178,21 +182,21 @@ public class KurentoSessionManager extends SessionManager {
log.error( log.error(
"Timeout waiting for join-leave Session lock to be available for participant {} of session {} in joinRoom", "Timeout waiting for join-leave Session lock to be available for participant {} of session {} in joinRoom",
participant.getParticipantPublicId(), sessionId); participant.getParticipantPublicId(), sessionId);
sessionEventsHandler.onParticipantJoined(participant, sessionId, null, null, transactionId, sessionEventsHandler.onParticipantJoined(participant, null, null, null, transactionId,
new OpenViduException(Code.GENERIC_ERROR_CODE, "Timeout waiting for Session lock")); new OpenViduException(Code.GENERIC_ERROR_CODE, "Timeout waiting for Session lock"));
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
log.error( log.error(
"InterruptedException waiting for join-leave Session lock to be available for participant {} of session {} in joinRoom", "InterruptedException waiting for join-leave Session lock to be available for participant {} of session {} in joinRoom",
participant.getParticipantPublicId(), sessionId); participant.getParticipantPublicId(), sessionId);
sessionEventsHandler.onParticipantJoined(participant, sessionId, null,null, transactionId, sessionEventsHandler.onParticipantJoined(participant, null, null, null, transactionId,
new OpenViduException(Code.GENERIC_ERROR_CODE, new OpenViduException(Code.GENERIC_ERROR_CODE,
"InterruptedException waiting for Session lock")); "InterruptedException waiting for Session lock"));
} }
} catch (OpenViduException e) { } catch (OpenViduException e) {
log.error("PARTICIPANT {}: Error joining/creating session {}", participant.getParticipantPublicId(), log.error("PARTICIPANT {}: Error joining/creating session {}", participant.getParticipantPublicId(),
sessionId, e); sessionId, e);
sessionEventsHandler.onParticipantJoined(participant, sessionId, null,null, transactionId, e); sessionEventsHandler.onParticipantJoined(participant, null, null, null, transactionId, e);
} }
} }
@ -1406,6 +1410,16 @@ public class KurentoSessionManager extends SessionManager {
return lessLoadedKms; return lessLoadedKms;
} }
private io.openvidu.server.recording.Recording getActiveRecordingIfAllowedByParticipantRole(
Participant participant) {
io.openvidu.server.recording.Recording recording = null;
if (participant.getToken() != null && this.recordingManager.sessionIsBeingRecorded(participant.getSessionId())
&& this.openviduConfig.getRolesFromRecordingNotification().contains(participant.getToken().getRole())) {
recording = this.recordingManager.getActiveRecordingForSession(participant.getSessionId());
}
return recording;
}
@PreDestroy @PreDestroy
@Override @Override
public void close() { public void close() {

View File

@ -492,6 +492,14 @@ public class RecordingManager {
|| this.sessionsRecordingsStarting.get(sessionId) != null); || this.sessionsRecordingsStarting.get(sessionId) != null);
} }
public Recording getActiveRecordingForSession(String sessionId) {
Recording recording = this.sessionsRecordings.get(sessionId);
if (recording == null) {
recording = this.sessionsRecordingsStarting.get(sessionId);
}
return recording;
}
public boolean sessionIsBeingRecordedIndividual(String sessionId) { public boolean sessionIsBeingRecordedIndividual(String sessionId) {
if (!sessionIsBeingRecorded(sessionId)) { if (!sessionIsBeingRecorded(sessionId)) {
return false; return false;

View File

@ -167,8 +167,8 @@ public class WebhookIntegrationTest {
// Client should have already received "connectionCreated" RPC response // Client should have already received "connectionCreated" RPC response
// nonetheless // nonetheless
verify(sessionEventsHandler, times(1)).onParticipantJoined(refEq(participant), anyString(), anyString(), anySet(), verify(sessionEventsHandler, times(1)).onParticipantJoined(refEq(participant), refEq(null), anyString(),
anyInt(), refEq(null)); anySet(), anyInt(), refEq(null));
// Now webhook response for event "participantJoined" should be received // Now webhook response for event "participantJoined" should be received
CustomWebhook.waitForEvent("participantJoined", 1000, TimeUnit.MILLISECONDS); CustomWebhook.waitForEvent("participantJoined", 1000, TimeUnit.MILLISECONDS);

View File

@ -255,7 +255,7 @@ public class OpenViduEventManager {
return success; return success;
} }
private AtomicInteger getNumEvents(String eventName) { public AtomicInteger getNumEvents(String eventName) {
return this.eventNumbers.computeIfAbsent(eventName, k -> new AtomicInteger(0)); return this.eventNumbers.computeIfAbsent(eventName, k -> new AtomicInteger(0));
} }

View File

@ -1185,7 +1185,43 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
user.getEventManager().waitUntilEventReaches("recordingStarted", 1); user.getEventManager().waitUntilEventReaches("recordingStarted", 1);
Thread.sleep(5000); Thread.sleep(3000);
user.getDriver().findElement(By.id("close-dialog-btn")).click();
Thread.sleep(500);
// A new user with PUBLISHER role should trigger recordingStarted event
user.getDriver().findElement(By.id("add-user-btn")).click();
user.getDriver().findElement(By.id("session-name-input-1")).clear();
user.getDriver().findElement(By.id("session-name-input-1")).sendKeys(sessionName);
user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .publish-checkbox")).click();
user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .subscribe-checkbox")).click();
user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 .join-btn")).click();
user.getEventManager().waitUntilEventReaches("connectionCreated", 4);
user.getEventManager().waitUntilEventReaches("streamCreated", 2);
user.getEventManager().waitUntilEventReaches("recordingStarted", 2);
// A new user with SUBSCRIBER role should not trigger recordingStarted event
user.getDriver().findElement(By.id("add-user-btn")).click();
user.getDriver().findElement(By.id("session-name-input-2")).clear();
user.getDriver().findElement(By.id("session-name-input-2")).sendKeys(sessionName);
user.getDriver().findElement(By.cssSelector("#openvidu-instance-2 .publish-checkbox")).click();
user.getDriver().findElement(By.cssSelector("#openvidu-instance-2 .subscribe-checkbox")).click();
user.getDriver().findElement(By.id("session-settings-btn-2")).click();
Thread.sleep(500);
user.getDriver().findElement(By.id("radio-btn-sub")).click();
user.getDriver().findElement(By.id("save-btn")).click();
Thread.sleep(500);
user.getDriver().findElement(By.cssSelector("#openvidu-instance-2 .join-btn")).click();
user.getEventManager().waitUntilEventReaches("connectionCreated", 9);
user.getEventManager().waitUntilEventReaches("streamCreated", 3);
// No third recordingStarted event should be triggered for the SUBSCRIBER user
Thread.sleep(3000);
Assert.assertEquals(user.getEventManager().getNumEvents("recordingStarted").intValue(), 2);
user.getDriver().findElement(By.id("session-api-btn-0")).click();
Thread.sleep(500);
user.getDriver().findElement(By.id("recording-id-field")).clear(); user.getDriver().findElement(By.id("recording-id-field")).clear();
user.getDriver().findElement(By.id("recording-id-field")).sendKeys(sessionName); user.getDriver().findElement(By.id("recording-id-field")).sendKeys(sessionName);
@ -1256,7 +1292,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
user.getDriver().findElement(By.id("close-dialog-btn")).click(); user.getDriver().findElement(By.id("close-dialog-btn")).click();
Thread.sleep(500); Thread.sleep(500);
gracefullyLeaveParticipants(user, 1); gracefullyLeaveParticipants(user, 3);
} }
@Test @Test