diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java index 5ab5588e..12b0564d 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java @@ -220,7 +220,7 @@ public class ComposedQuickStartRecordingService extends ComposedRecordingService containers.remove(containerId); sessionsContainers.remove(session.getSessionId()); } - log.error("Error while launchig container for COMPOSED_QUICK_START: ({})", e.getMessage()); + log.error("Error while launching container for COMPOSED_QUICK_START: ({})", e.getMessage()); throw e; } return containerId; diff --git a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/layout/CustomLayoutHandler.java b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/layout/CustomLayoutHandler.java new file mode 100644 index 00000000..a1ca17b1 --- /dev/null +++ b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/layout/CustomLayoutHandler.java @@ -0,0 +1,41 @@ +package io.openvidu.test.browsers.utils.layout; + +import java.util.concurrent.CountDownLatch; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.event.EventListener; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@SpringBootApplication +public class CustomLayoutHandler extends WebSecurityConfigurerAdapter implements WebMvcConfigurer { + + private static ConfigurableApplicationContext context; + public static CountDownLatch initLatch; + + public static void main(String[] args, CountDownLatch initLatch) { + CustomLayoutHandler.initLatch = initLatch; + CustomLayoutHandler.context = new SpringApplicationBuilder(CustomLayoutHandler.class) + .properties("spring.config.location:classpath:aplication-pro-layout-handler.properties").build() + .run(args); + } + + @Override + protected void configure(HttpSecurity security) throws Exception { + security.httpBasic().disable(); + } + + @EventListener(ApplicationReadyEvent.class) + public void afterStartup() { + CustomLayoutHandler.initLatch.countDown(); + } + + public static void shutDown() { + CustomLayoutHandler.context.close(); + } + +} diff --git a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomWebhook.java b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/webhook/CustomWebhook.java similarity index 96% rename from openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomWebhook.java rename to openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/webhook/CustomWebhook.java index 0ecf7aca..3cfbbbd6 100644 --- a/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/CustomWebhook.java +++ b/openvidu-test-browsers/src/main/java/io/openvidu/test/browsers/utils/webhook/CustomWebhook.java @@ -15,7 +15,7 @@ * */ -package io.openvidu.test.browsers.utils; +package io.openvidu.test.browsers.utils.webhook; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; @@ -24,7 +24,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; -import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ConfigurableApplicationContext; @@ -39,7 +38,6 @@ import org.springframework.web.bind.annotation.RestController; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -@SpringBootApplication public class CustomWebhook { private static ConfigurableApplicationContext context; diff --git a/openvidu-test-browsers/src/main/resources/aplication-pro-layout-handler.properties b/openvidu-test-browsers/src/main/resources/aplication-pro-layout-handler.properties new file mode 100644 index 00000000..10fc87dd --- /dev/null +++ b/openvidu-test-browsers/src/main/resources/aplication-pro-layout-handler.properties @@ -0,0 +1,3 @@ +server.port=5555 +server.ssl.enabled=false +security.basic.enabled=false \ No newline at end of file diff --git a/openvidu-test-browsers/src/main/resources/static/index.html b/openvidu-test-browsers/src/main/resources/static/index.html new file mode 100644 index 00000000..ca45b3f7 --- /dev/null +++ b/openvidu-test-browsers/src/main/resources/static/index.html @@ -0,0 +1,55 @@ + + + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/openvidu-test-e2e/jenkins/Jenkinsfile b/openvidu-test-e2e/jenkins/Jenkinsfile index 37073784..f2b5a606 100644 --- a/openvidu-test-e2e/jenkins/Jenkinsfile +++ b/openvidu-test-e2e/jenkins/Jenkinsfile @@ -6,6 +6,7 @@ node('container') { sh 'rm -rf /opt/openvidu/* || true' sh 'wget https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/barcode.y4m -P /opt/openvidu' sh 'wget https://github.com/OpenVidu/openvidu/raw/master/openvidu-test-e2e/docker/fakeaudio.wav -P /opt/openvidu' + sh 'wget --directory-prefix=/opt/openvidu/test-layouts/layout1 https://github.com/OpenVidu/openvidu/blob/master/openvidu-test-e2e/docker/my-custom-layout/index.html' docker.image('selenium/standalone-firefox:latest').withRun('-p 6667:4444 --name firefox --shm-size=1g') { d -> def mycontainer = docker.image('openvidu/openvidu-test-e2e:$DISTRO') mycontainer.pull() @@ -93,10 +94,10 @@ node('container') { sh(script: '''#!/bin/bash if [ "$DOCKER_RECORDING_VERSION" != "default" ]; then echo "Using custom openvidu-recording tag: $DOCKER_RECORDING_VERSION" - java -jar -DDOMAIN_OR_PUBLIC_IP=172.17.0.1 -DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true -DOPENVIDU_RECORDING_VERSION=$DOCKER_RECORDING_VERSION -DOPENVIDU_WEBHOOK=true -DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook /opt/openvidu/openvidu-server-*.jar &> openvidu-server.log & + java -jar -DDOMAIN_OR_PUBLIC_IP=172.17.0.1 -DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true -DOPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/test-layouts -DOPENVIDU_RECORDING_VERSION=$DOCKER_RECORDING_VERSION -DOPENVIDU_WEBHOOK=true -DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook /opt/openvidu/openvidu-server-*.jar &> openvidu-server.log & else echo "Using default openvidu-recording tag" - java -jar -DDOMAIN_OR_PUBLIC_IP=172.17.0.1 -DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true -DOPENVIDU_WEBHOOK=true -DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook /opt/openvidu/openvidu-server-*.jar &> openvidu-server.log & + java -jar -DDOMAIN_OR_PUBLIC_IP=172.17.0.1 -DOPENVIDU_SECRET=MY_SECRET -DHTTPS_PORT=4443 -DOPENVIDU_RECORDING=true -DOPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/test-layouts -DOPENVIDU_WEBHOOK=true -DOPENVIDU_WEBHOOK_ENDPOINT=http://127.0.0.1:7777/webhook /opt/openvidu/openvidu-server-*.jar &> openvidu-server.log & fi '''.stripIndent()) sh 'until $(curl --insecure --output /dev/null --silent --head --fail https://OPENVIDUAPP:MY_SECRET@localhost:4443/); do echo "Waiting for openvidu-server..."; sleep 2; done' 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 1b36cab7..d2a2514c 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 @@ -42,6 +42,7 @@ import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import javax.imageio.ImageIO; @@ -103,9 +104,10 @@ import io.openvidu.test.browsers.FirefoxUser; import io.openvidu.test.browsers.OperaUser; import io.openvidu.test.browsers.utils.CommandLineExecutor; import io.openvidu.test.browsers.utils.CustomHttpClient; -import io.openvidu.test.browsers.utils.CustomWebhook; import io.openvidu.test.browsers.utils.MultimediaFileMetadata; import io.openvidu.test.browsers.utils.Unzipper; +import io.openvidu.test.browsers.utils.layout.CustomLayoutHandler; +import io.openvidu.test.browsers.utils.webhook.CustomWebhook; /** * E2E tests for openvidu-testapp. @@ -1208,9 +1210,10 @@ public class OpenViduTestAppE2eTest { Assert.assertTrue("File " + file3.getAbsolutePath() + " does not exist or is empty", file3.exists() && file3.length() > 0); - Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine", - this.recordedFileFine(file1, new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName))); - Assert.assertTrue("Thumbnail " + file3.getAbsolutePath() + " is not fine", this.thumbnailIsFine(file3)); + Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine", this.recordedGreenFileFine(file1, + new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName))); + Assert.assertTrue("Thumbnail " + file3.getAbsolutePath() + " is not fine", + this.thumbnailIsFine(file3, OpenViduTestAppE2eTest::checkVideoAverageRgbGreen)); // Try to get the stopped recording user.getDriver().findElement(By.id("get-recording-btn")).click(); @@ -1247,7 +1250,7 @@ public class OpenViduTestAppE2eTest { log.info("Composed quick start record"); CountDownLatch initLatch = new CountDownLatch(1); - io.openvidu.test.browsers.utils.CustomWebhook.main(new String[0], initLatch); + io.openvidu.test.browsers.utils.webhook.CustomWebhook.main(new String[0], initLatch); try { @@ -1520,7 +1523,7 @@ public class OpenViduTestAppE2eTest { @Test @DisplayName("Record cross-browser audio-only and video-only") - void recordAudioOnlyVideoOnlyTest() throws Exception { + void audioOnlyVideoOnlyRecordTest() throws Exception { isRecordingTest = true; setupBrowser("chromeAlternateScreenShare"); @@ -1750,6 +1753,128 @@ public class OpenViduTestAppE2eTest { } } + @Test + @DisplayName("Custom layout recording") + void customLayoutRecordTest() throws Exception { + isRecordingTest = true; + + setupBrowser("chrome"); + + log.info("Custom layout recording"); + + final String SESSION_NAME = "CUSTOM_LAYOUT_SESSION"; + + user.getDriver().findElement(By.id("add-user-btn")).click(); + user.getDriver().findElement(By.id("session-name-input-0")).clear(); + user.getDriver().findElement(By.id("session-name-input-0")).sendKeys(SESSION_NAME); + + // Custom layout from local storage + user.getDriver().findElement(By.id("session-settings-btn-0")).click(); + Thread.sleep(1000); + user.getDriver().findElement(By.id("recording-mode-select")).click(); + Thread.sleep(500); + user.getDriver().findElement(By.id("option-ALWAYS")).click(); + Thread.sleep(500); + user.getDriver().findElement(By.id("recording-layout-select")).click(); + Thread.sleep(500); + user.getDriver().findElement(By.id("option-CUSTOM")).click(); + Thread.sleep(500); + WebElement tokeInput = user.getDriver().findElement(By.id("default-custom-layout-input")); + tokeInput.clear(); + tokeInput.sendKeys("layout1"); + user.getDriver().findElement(By.id("save-btn")).click(); + Thread.sleep(1000); + + user.getDriver().findElement(By.className("join-btn")).sendKeys(Keys.ENTER); + + user.getEventManager().waitUntilEventReaches("connectionCreated", 1); + user.getEventManager().waitUntilEventReaches("accessAllowed", 1); + user.getEventManager().waitUntilEventReaches("streamCreated", 1); + user.getEventManager().waitUntilEventReaches("streamPlaying", 1); + user.getEventManager().waitUntilEventReaches("recordingStarted", 1); + + Thread.sleep(4000); + + user.getDriver().findElement(By.id("session-api-btn-0")).click(); + Thread.sleep(1000); + + user.getDriver().findElement(By.id("recording-id-field")).clear(); + user.getDriver().findElement(By.id("recording-id-field")).sendKeys(SESSION_NAME); + user.getDriver().findElement(By.id("stop-recording-btn")).click(); + user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value", + "Recording stopped [" + SESSION_NAME + "]")); + user.getEventManager().waitUntilEventReaches("recordingStopped", 1); + user.getDriver().findElement(By.id("close-session-btn")).click(); + user.getEventManager().waitUntilEventReaches("streamDestroyed", 1); + user.getEventManager().waitUntilEventReaches("sessionDisconnected", 1); + user.getDriver().findElement(By.id("close-dialog-btn")).click(); + + String recordingsPath = "/opt/openvidu/recordings/" + SESSION_NAME + "/"; + File file1 = new File(recordingsPath + SESSION_NAME + ".mp4"); + File file2 = new File(recordingsPath + SESSION_NAME + ".jpg"); + + Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine", this.recordedRedFileFine(file1, + new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME))); + Assert.assertTrue("Thumbnail " + file2.getAbsolutePath() + " is not fine", + this.thumbnailIsFine(file2, OpenViduTestAppE2eTest::checkVideoAverageRgbRed)); + + // Custom layout from external URL + CountDownLatch initLatch = new CountDownLatch(1); + CustomLayoutHandler.main(new String[0], initLatch); + try { + + if (!initLatch.await(30, TimeUnit.SECONDS)) { + Assert.fail("Timeout waiting for webhook springboot app to start"); + CustomLayoutHandler.shutDown(); + return; + } + + user.getDriver().findElement(By.id("session-settings-btn-0")).click(); + Thread.sleep(1000); + tokeInput = user.getDriver().findElement(By.id("default-custom-layout-input")); + tokeInput.clear(); + tokeInput.sendKeys("http://localhost:5555?sessionId=CUSTOM_LAYOUT_SESSION&secret=MY_SECRET"); + user.getDriver().findElement(By.id("save-btn")).click(); + Thread.sleep(1000); + + user.getDriver().findElement(By.className("join-btn")).sendKeys(Keys.ENTER); + + user.getEventManager().waitUntilEventReaches("connectionCreated", 2); + user.getEventManager().waitUntilEventReaches("accessAllowed", 2); + user.getEventManager().waitUntilEventReaches("streamCreated", 2); + user.getEventManager().waitUntilEventReaches("streamPlaying", 2); + user.getEventManager().waitUntilEventReaches("recordingStarted", 2); + + Thread.sleep(4000); + + user.getDriver().findElement(By.id("session-api-btn-0")).click(); + Thread.sleep(1000); + + user.getDriver().findElement(By.id("recording-id-field")).clear(); + user.getDriver().findElement(By.id("recording-id-field")).sendKeys(SESSION_NAME + "-1"); + user.getDriver().findElement(By.id("stop-recording-btn")).click(); + user.getWaiter().until(ExpectedConditions.attributeToBe(By.id("api-response-text-area"), "value", + "Recording stopped [" + SESSION_NAME + "-1]")); + user.getEventManager().waitUntilEventReaches("recordingStopped", 2); + user.getDriver().findElement(By.id("close-session-btn")).click(); + user.getEventManager().waitUntilEventReaches("streamDestroyed", 2); + user.getEventManager().waitUntilEventReaches("sessionDisconnected", 2); + user.getDriver().findElement(By.id("close-dialog-btn")).click(); + + recordingsPath = "/opt/openvidu/recordings/" + SESSION_NAME + "-1/"; + file1 = new File(recordingsPath + SESSION_NAME + "-1.mp4"); + file2 = new File(recordingsPath + SESSION_NAME + "-1.jpg"); + + Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine", this.recordedRedFileFine( + file1, new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "-1"))); + Assert.assertTrue("Thumbnail " + file2.getAbsolutePath() + " is not fine", + this.thumbnailIsFine(file2, OpenViduTestAppE2eTest::checkVideoAverageRgbRed)); + + } finally { + CustomLayoutHandler.shutDown(); + } + } + @Test @DisplayName("REST API: Fetch all, fetch one, force disconnect, force unpublish, close session") void restApiFetchForce() throws Exception { @@ -2490,8 +2615,9 @@ public class OpenViduTestAppE2eTest { file3.exists() && file3.length() > 0); Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine", - this.recordedFileFine(file1, recording2)); - Assert.assertTrue("Thumbnail " + file3.getAbsolutePath() + " is not fine", this.thumbnailIsFine(file3)); + this.recordedGreenFileFine(file1, recording2)); + Assert.assertTrue("Thumbnail " + file3.getAbsolutePath() + " is not fine", + this.thumbnailIsFine(file3, OpenViduTestAppE2eTest::checkVideoAverageRgbGreen)); try { OV.deleteRecording("NOT_EXISTS"); @@ -2506,12 +2632,12 @@ public class OpenViduTestAppE2eTest { try { session.forceUnpublish("NOT_EXISTS"); } catch (OpenViduHttpException e) { - Assert.assertEquals("Wrong HTTP status on Session.fetch()", 404, e.getStatus()); + Assert.assertEquals("Wrong HTTP status on Session.forceUnpublish()", 404, e.getStatus()); } try { session.forceDisconnect("NOT_EXISTS"); } catch (OpenViduHttpException e) { - Assert.assertEquals("Wrong HTTP status on Session.fetch()", 404, e.getStatus()); + Assert.assertEquals("Wrong HTTP status on Session.forceDisconnect()", 404, e.getStatus()); } if (OpenViduRole.MODERATOR.equals(session.getActiveConnections().get(0).getRole())) { @@ -3051,7 +3177,7 @@ public class OpenViduTestAppE2eTest { log.info("Webhook test"); CountDownLatch initLatch = new CountDownLatch(1); - io.openvidu.test.browsers.utils.CustomWebhook.main(new String[0], initLatch); + io.openvidu.test.browsers.utils.webhook.CustomWebhook.main(new String[0], initLatch); try { @@ -3204,7 +3330,7 @@ public class OpenViduTestAppE2eTest { log.info("IP camera test"); CountDownLatch initLatch = new CountDownLatch(1); - io.openvidu.test.browsers.utils.CustomWebhook.main(new String[0], initLatch); + io.openvidu.test.browsers.utils.webhook.CustomWebhook.main(new String[0], initLatch); try { @@ -3464,12 +3590,12 @@ public class OpenViduTestAppE2eTest { }; } - private boolean checkVideoAverageRgbGreen(Map rgb) { + private static boolean checkVideoAverageRgbGreen(Map rgb) { // GREEN color: {r < 15, g > 130, b <15} return (rgb.get("r") < 15) && (rgb.get("g") > 130) && (rgb.get("b") < 15); } - private boolean checkVideoAverageRgbGray(Map rgb) { + private static boolean checkVideoAverageRgbGray(Map rgb) { // GRAY color: {r < 50, g < 50, b < 50} and the absolute difference between them // not greater than 2 return (rgb.get("r") < 50) && (rgb.get("g") < 50) && (rgb.get("b") < 50) @@ -3477,6 +3603,11 @@ public class OpenViduTestAppE2eTest { && (Math.abs(rgb.get("b") - rgb.get("g")) <= 2); } + private static boolean checkVideoAverageRgbRed(Map rgb) { + // RED color: {r > 240, g < 15, b <15} + return (rgb.get("r") > 240) && (rgb.get("g") < 15) && (rgb.get("b") < 15); + } + private void gracefullyLeaveParticipants(int numberOfParticipants) throws Exception { int accumulatedConnectionDestroyed = 0; for (int j = 1; j <= numberOfParticipants; j++) { @@ -3494,7 +3625,8 @@ public class OpenViduTestAppE2eTest { return "data:image/png;base64," + screenshotBase64; } - private boolean recordedFileFine(File file, Recording recording) throws IOException { + private boolean recordedFileFine(File file, Recording recording, + Function, Boolean> colorCheckFunction) throws IOException { this.checkMultimediaFile(file, recording.hasAudio(), recording.hasVideo(), recording.getDuration(), recording.getResolution(), "aac", "h264", true); @@ -3515,7 +3647,7 @@ public class OpenViduTestAppE2eTest { log.info("Recording map color: {}", colorMap.toString()); log.info("Recording frame below"); System.out.println(bufferedImageToBase64PngString(image)); - isFine = this.checkVideoAverageRgbGreen(colorMap); + isFine = colorCheckFunction.apply(colorMap); } catch (IOException | JCodecException e) { log.warn("Error getting frame from video recording: {}", e.getMessage()); isFine = false; @@ -3523,6 +3655,14 @@ public class OpenViduTestAppE2eTest { return isFine; } + private boolean recordedGreenFileFine(File file, Recording recording) throws IOException { + return this.recordedFileFine(file, recording, OpenViduTestAppE2eTest::checkVideoAverageRgbGreen); + } + + private boolean recordedRedFileFine(File file, Recording recording) throws IOException { + return this.recordedFileFine(file, recording, OpenViduTestAppE2eTest::checkVideoAverageRgbRed); + } + private String bufferedImageToBase64PngString(BufferedImage image) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); String imageString = null; @@ -3654,7 +3794,7 @@ public class OpenViduTestAppE2eTest { Math.abs((metadata.getDuration() - duration)) < difference); } - private boolean thumbnailIsFine(File file) { + private boolean thumbnailIsFine(File file, Function, Boolean> colorCheckFunction) { boolean isFine = false; BufferedImage image = null; try { @@ -3666,7 +3806,7 @@ public class OpenViduTestAppE2eTest { log.info("Recording thumbnail dimensions: {}x{}", image.getWidth(), image.getHeight()); Map colorMap = this.averageColor(image); log.info("Thumbnail map color: {}", colorMap.toString()); - isFine = this.checkVideoAverageRgbGreen(colorMap); + isFine = colorCheckFunction.apply(colorMap); return isFine; } 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 de5608d5..7ca258ce 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 @@ -27,16 +27,17 @@ - + - {{ enumerator }} + {{ enumerator }} + [(ngModel)]="sessionProperties.defaultCustomLayout" id="default-custom-layout-input">