diff --git a/openvidu-test-e2e/src/main/java/io/openvidu/test/e2e/OpenViduTestE2e.java b/openvidu-test-e2e/src/main/java/io/openvidu/test/e2e/OpenViduTestE2e.java index c48378de..385ad112 100644 --- a/openvidu-test-e2e/src/main/java/io/openvidu/test/e2e/OpenViduTestE2e.java +++ b/openvidu-test-e2e/src/main/java/io/openvidu/test/e2e/OpenViduTestE2e.java @@ -12,6 +12,7 @@ import java.nio.file.Paths; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -50,7 +51,8 @@ public class OpenViduTestE2e { private final static WaitStrategy waitBrowser = Wait.forLogMessage("^.*Started Selenium Standalone.*$", 1); - protected static String MEDIA_SERVER_IMAGE = "livekit-server:latest"; + protected static String RTSP_SERVER_IMAGE = "lroktu/vlc-server:latest"; + protected static String SRT_SERVER_IMAGE = "linuxserver/ffmpeg:latest"; protected static String LIVEKIT_API_KEY = "devkey"; protected static String LIVEKIT_API_SECRET = "secret"; @@ -80,8 +82,6 @@ public class OpenViduTestE2e { protected static RoomServiceClient LK; protected static IngressServiceClient LK_INGRESS; - private static boolean isMediaServerRestartTest = false; - protected static void checkFfmpegInstallation() { String ffmpegOutput = commandLine.executeCommand("which ffmpeg", 60); if (ffmpegOutput == null || ffmpegOutput.isEmpty()) { @@ -141,6 +141,37 @@ public class OpenViduTestE2e { return edge; } + public void startRtspServer(boolean withAudio, boolean withVideo) throws Exception { + GenericContainer rtspServerContainer = new GenericContainer<>(DockerImageName.parse(RTSP_SERVER_IMAGE)) + .withCommand(getFileUrl(withAudio, withVideo) + + " --loop :sout=#gather:rtp{sdp=rtsp://:8554/} :network-caching=1500 :sout-all :sout-keep"); + rtspServerContainer.setPortBindings(Arrays.asList("8554:8554")); + rtspServerContainer.start(); + containers.add(rtspServerContainer); + } + + public void startSrtServer(boolean withAudio, boolean withVideo) throws Exception { + GenericContainer srtServerContainer = new GenericContainer<>(DockerImageName.parse(SRT_SERVER_IMAGE)) + .withNetworkMode("host").withCommand( + "-i " + getFileUrl(withAudio, withVideo) + " -c:v libx264 -f mpegts srt://:8554?mode=listener"); + srtServerContainer.start(); + containers.add(srtServerContainer); + } + + private String getFileUrl(boolean withAudio, boolean withVideo) throws Exception { + String fileUrl; + if (withAudio && withVideo) { + fileUrl = "https://s3.eu-west-1.amazonaws.com/public.openvidu.io/bbb_sunflower_1080p_60fps_normal.mp4"; + } else if (!withAudio && withVideo) { + fileUrl = "https://s3.eu-west-1.amazonaws.com/public.openvidu.io/bbb_sunflower_1080p_60fps_normal_noaudio.mp4"; + } else if (withAudio) { + fileUrl = "https://s3.eu-west-1.amazonaws.com/public.openvidu.io/bbb_sunflower_1080p_60fps_normal_onlyaudio.mp3"; + } else { + throw new Exception("Must have audio or video"); + } + return fileUrl; + } + protected static void setUpLiveKitClient() throws NoSuchAlgorithmException { URI uri = null; try { @@ -209,12 +240,6 @@ public class OpenViduTestE2e { } log.info("Using api secret {} to connect to livekit-server", LIVEKIT_API_SECRET); - String mediaServerImage = System.getProperty("MEDIA_SERVER_IMAGE"); - if (mediaServerImage != null) { - MEDIA_SERVER_IMAGE = mediaServerImage; - } - log.info("Using media server {} for e2e tests", MEDIA_SERVER_IMAGE); - String chromeVersion = System.getProperty("CHROME_VERSION"); if (chromeVersion != null && !chromeVersion.isBlank()) { CHROME_VERSION = chromeVersion; @@ -366,13 +391,6 @@ public class OpenViduTestE2e { // Close all remaining Rooms this.closeAllRooms(LK); - // Reset Media Server - if (isMediaServerRestartTest) { - this.stopMediaServer(); - this.startMediaServer(); - isMediaServerRestartTest = false; - } - // Dispose all browsers users Iterator it1 = browserUsers.iterator(); while (it1.hasNext()) { @@ -458,17 +476,4 @@ public class OpenViduTestE2e { throw new Exception("File " + path.toAbsolutePath().toString() + " exists but is not readable"); } } - - protected void startMediaServer() { - String command = "docker run --rm -p 1880:7880 -e LIVEKIT_KEYS=\"" + LIVEKIT_API_KEY + " : " - + LIVEKIT_API_SECRET + "\" " + MEDIA_SERVER_IMAGE; - commandLine.executeCommand(command, 60); - } - - protected void stopMediaServer() { - isMediaServerRestartTest = true; - final String dockerRemoveCmd = "docker ps -a | awk '{ print $1,$2 }' | grep livekit-server | awk '{ print $1 }' | xargs -I {} docker rm -f {}"; - commandLine.executeCommand(dockerRemoveCmd, 60); - } - } 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 958ca2f1..3499bc2b 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 @@ -54,6 +54,8 @@ import com.google.gson.JsonParser; import io.openvidu.test.e2e.annotations.OnlyMediasoup; import io.openvidu.test.e2e.annotations.OnlyPion; +import livekit.LivekitIngress.IngressInfo; +import livekit.LivekitIngress.IngressState; /** * E2E tests for openvidu-testapp. @@ -1659,7 +1661,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { user.getEventManager().waitUntilEventReaches("connected", "RoomEvent", 1); // Try publishing H264 with 2 layer simulcast - createIngress(user, "H264_540P_25FPS_2_LAYERS", null, true); + createIngress(user, "H264_540P_25FPS_2_LAYERS", null, true, "HTTP"); user.getEventManager().waitUntilEventReaches("trackSubscribed", "ParticipantEvent", 1); user.getWaiter().until(ExpectedConditions.numberOfElementsToBe(By.tagName("video"), 1)); @@ -1688,7 +1690,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { user.getEventManager().waitUntilEventReaches("participantDisconnected", "RoomEvent", 1); // Try publishing H264 with 3 layer simulcast - createIngress(user, "H264_1080P_30FPS_3_LAYERS_HIGH_MOTION", null, true); + createIngress(user, "H264_1080P_30FPS_3_LAYERS_HIGH_MOTION", null, true, "HTTP"); user.getEventManager().waitUntilEventReaches("trackSubscribed", "ParticipantEvent", 1); user.getWaiter().until(ExpectedConditions.numberOfElementsToBe(By.tagName("video"), 1)); numberOfVideos = user.getDriver().findElements(By.tagName("video")).size(); @@ -1712,6 +1714,102 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { this.waitUntilSubscriberFrameHeightIs(user, subscriberVideo, 1080); } + @Test + @DisplayName("RTSP ingress") + void rtspIngressTest() throws Exception { + startRtspServer(true, true); + urPullCommon("RTSP", true, true); + } + + @Test + @DisplayName("RTSP ingress only video") + void rtspIngressTestOnlyVideo() throws Exception { + startRtspServer(false, true); + urPullCommon("RTSP", false, true); + } + + @Test + @DisplayName("RTSP ingress only audio") + void rtspIngressTestOnlyAudio() throws Exception { + startRtspServer(true, false); + urPullCommon("RTSP", true, false); + } + + @Test + @DisplayName("SRT ingress") + void srtIngressTest() throws Exception { + startSrtServer(true, true); + urPullCommon("SRT", true, true); + } + + @Test + @DisplayName("SRT ingress only video") + void srtIngressTestOnlyVideo() throws Exception { + startSrtServer(false, true); + urPullCommon("SRT", false, true); + } + + @Test + @DisplayName("SRT ingress only audio") + void srtIngressTestOnlyAudio() throws Exception { + startSrtServer(true, false); + urPullCommon("SRT", true, false); + } + + private void urPullCommon(String urlType, boolean withAudio, boolean withVideo) throws Exception { + OpenViduTestappUser user = setupBrowserAndConnectToOpenViduTestapp("chrome"); + this.addSubscriber(user, false); + user.getDriver().findElements(By.className("connect-btn")).forEach(el -> el.sendKeys(Keys.ENTER)); + user.getEventManager().waitUntilEventReaches("connected", "RoomEvent", 1); + createIngress(user, null, "VP8", false, urlType); + + if (withAudio && withVideo) { + user.getEventManager().waitUntilEventReaches("trackSubscribed", "ParticipantEvent", 2); + } else { + user.getEventManager().waitUntilEventReaches("trackSubscribed", "ParticipantEvent", 1); + } + + if (withAudio) { + user.getWaiter().until(ExpectedConditions.numberOfElementsToBe(By.tagName("audio"), 1)); + final int numberOfAudios = user.getDriver().findElements(By.tagName("audio")).size(); + Assertions.assertEquals(1, numberOfAudios, "Wrong number of videos"); + Assertions.assertTrue(user.getBrowserUser().assertAllElementsHaveTracks("audio", true, false), + "HTMLAudioElements were expected to have only one audio track"); + if (!withVideo) { + final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size(); + Assertions.assertEquals(0, numberOfVideos, "Wrong number of videos"); + } + } + if (withVideo) { + user.getWaiter().until(ExpectedConditions.numberOfElementsToBe(By.tagName("video"), 1)); + final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size(); + Assertions.assertEquals(1, numberOfVideos, "Wrong number of videos"); + Assertions.assertTrue(user.getBrowserUser().assertAllElementsHaveTracks("video", false, true), + "HTMLVideoElements were expected to have only one video track"); + if (!withAudio) { + final int numberOfAudios = user.getDriver().findElements(By.tagName("audio")).size(); + Assertions.assertEquals(0, numberOfAudios, "Wrong number of videos"); + } + + WebElement subscriberVideo = user.getDriver() + .findElement(By.cssSelector("#openvidu-instance-0 video.remote")); + + waitUntilVideoLayersNotEmpty(user, subscriberVideo); + long bytesReceived = this.getSubscriberVideoBytesReceived(user, subscriberVideo); + this.waitUntilSubscriberBytesReceivedIncrease(user, subscriberVideo, bytesReceived); + this.waitUntilSubscriberFramesPerSecondNotZero(user, subscriberVideo); + JsonArray json = this.getLayersAsJsonArray(user, subscriberVideo); + String subscriberCodec = json.get(0).getAsJsonObject().get("codec").getAsString(); + String expectedCodec = "video/VP8"; + Assertions.assertEquals(expectedCodec, subscriberCodec); + } + + List ingresses = LK_INGRESS.listIngress().execute().body(); + Assertions.assertEquals(1, ingresses.size()); + Assertions.assertTrue(ingresses.get(0).getUrl().startsWith(urlType.toLowerCase() + "://")); + Assertions.assertEquals(IngressState.Status.ENDPOINT_PUBLISHING, ingresses.get(0).getState().getStatus()); + } + private void ingressSimulcastTest(OpenViduTestappUser user, boolean simulcast, String codec, String preset) throws Exception { @@ -1721,7 +1819,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { user.getEventManager().waitUntilEventReaches("connected", "RoomEvent", 1); - createIngress(user, preset, codec, simulcast); + createIngress(user, preset, codec, simulcast, "HTTP"); user.getEventManager().waitUntilEventReaches("trackSubscribed", "ParticipantEvent", 1); @@ -2074,7 +2172,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { } } - private void createIngress(OpenViduTestappUser user, String preset, String codec, boolean simulcast) + private void createIngress(OpenViduTestappUser user, String preset, String codec, boolean simulcast, String urlType) throws InterruptedException { if (!user.getDriver().findElements(By.id("close-dialog-btn")).isEmpty()) { user.getDriver().findElement(By.id("close-dialog-btn")).click(); @@ -2094,6 +2192,11 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest { Thread.sleep(300); user.getDriver().findElement(By.cssSelector("#mat-option-" + codec.toUpperCase())).click(); } + if (urlType != null) { + user.getDriver().findElement(By.cssSelector("#ingress-url-type-select")).click(); + Thread.sleep(300); + user.getDriver().findElement(By.cssSelector("#mat-option-" + urlType.toUpperCase())).click(); + } user.getDriver().findElement(By.cssSelector("#create-ingress-api-btn")).click(); user.getDriver().findElement(By.cssSelector("#close-dialog-btn")).click(); Thread.sleep(300);