openvidu-test-e2e: enable the SRT ingress tests

The 14 srtIngress* tests had been commented out with three notes: SRT pull
does not work when the ingress runs in a container, AAC fails alongside
video, and a single OPUS stream fails. All 14 pass now.

The first note was the networking. The listener ran on the bridge network
testcontainers uses and the test handed the ingress a container IP that the
deployment's own network cannot reach. Moving it to the host network is not
enough on its own: host.docker.internal resolves to the gateway of Docker's
default bridge, while the ingress sits on another network, so a listener
bound to 0.0.0.0 answers the handshake from whatever source address routing
picks for that network, and libsrt drops a reply that comes from an address
it never contacted. TCP is immune, which is why RTSP never had the problem.
The listener now binds the very address the ingress calls, taken from the
Docker API, on a free UDP port.

The second note does not reproduce: H264 + AAC and MPEG-4 + AAC pass
unchanged, so it was a casualty of the networking problem. The third was a
real product bug, in the ingress rather than here, and is fixed there.

Both helpers now run mediamtx with RTSP over TCP and every other server off.
On the host network its defaults collide with the deployment (1935 is the
ingress RTMP port) or with another mediamtx (8000/8001 for RTP, and the MoQ
server on 8892, which 1.21 extends to 8893), and the container exits.
mediamtx moves to 1.21.0 in the same step.

The RTSP AC3 test stays disabled, with the cause it actually has: ffmpeg's
RTP muxer cannot payload AC-3, so the publisher never produces the stream.

Verified against a local deployment: the 44 tests that use the mediamtx
image, 30 RTSP and 14 SRT, all pass except that disabled one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pull/910/merge
pabloFuente 2026-09-16 18:32:58 +02:00
parent 7f6f1381d7
commit adfd19c8fa
2 changed files with 146 additions and 129 deletions

View File

@ -3,6 +3,7 @@ package io.openvidu.test.e2e;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
@ -32,6 +33,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.BindMode; import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network; import org.testcontainers.containers.Network;
@ -60,8 +62,7 @@ public class OpenViduTestE2e {
private final static WaitStrategy waitBrowser = Wait.forLogMessage("^.*Started Selenium Standalone.*$", 1); private final static WaitStrategy waitBrowser = Wait.forLogMessage("^.*Started Selenium Standalone.*$", 1);
protected static String RTSP_SERVER_IMAGE = "bluenviron/mediamtx:1.19.2-ffmpeg"; protected static String RTSP_SERVER_IMAGE = "bluenviron/mediamtx:1.21.0-ffmpeg";
protected static int RTSP_SRT_PORT = 8554;
// Key is the common name of the video codec. It must match the output log of // Key is the common name of the video codec. It must match the output log of
// the RTSP server when receiving it. // the RTSP server when receiving it.
@ -296,7 +297,7 @@ public class OpenViduTestE2e {
.withCreateContainerCmdModifier(cmd -> cmd.withName("rtsp-" + Math.random() * 100000)) .withCreateContainerCmdModifier(cmd -> cmd.withName("rtsp-" + Math.random() * 100000))
.withEnv(Map.of("MTX_LOGLEVEL", "info", "MTX_RTSPTRANSPORTS", "tcp", "MTX_RTSPADDRESS", ":" + rtspPort, .withEnv(Map.of("MTX_LOGLEVEL", "info", "MTX_RTSPTRANSPORTS", "tcp", "MTX_RTSPADDRESS", ":" + rtspPort,
"MTX_HLS", "no", "MTX_RTSP", "yes", "MTX_WEBRTC", "no", "MTX_SRT", "no", "MTX_RTMP", "no", "MTX_HLS", "no", "MTX_RTSP", "yes", "MTX_WEBRTC", "no", "MTX_SRT", "no", "MTX_RTMP", "no",
"MTX_API", "no")) "MTX_MOQ", "no", "MTX_API", "no"))
.withNetworkMode("host") .withNetworkMode("host")
.waitingFor(Wait.forLogMessage("^.*\\[RTSP\\] started with listeners on :" + rtspPort + ".*$", 1)); .waitingFor(Wait.forLogMessage("^.*\\[RTSP\\] started with listeners on :" + rtspPort + ".*$", 1));
@ -351,27 +352,50 @@ public class OpenViduTestE2e {
*/ */
public String startSrtServer(String videoCodec, String audioCodec) throws Exception { public String startSrtServer(String videoCodec, String audioCodec) throws Exception {
int srtPort;
try (DatagramSocket socket = new DatagramSocket(0)) {
srtPort = socket.getLocalPort();
}
String hostGatewayIp = getDockerHostGatewayIp();
int unusedRtspPort;
try (ServerSocket socket = new ServerSocket(0)) {
unusedRtspPort = socket.getLocalPort();
}
String fileUrl = getFileUrl(videoCodec != null, audioCodec != null, true); String fileUrl = getFileUrl(videoCodec != null, audioCodec != null, true);
String codecs = getCodecs(videoCodec, audioCodec); String codecs = getCodecs(videoCodec, audioCodec);
// -re: see startRtspServer. // -re: see startRtspServer.
String ffmpegCommand = "ffmpeg -re -i " + fileUrl + " " + codecs + " -strict -2 -f mpegts srt://:" String ffmpegCommand = "ffmpeg -re -i " + fileUrl + " " + codecs + " -strict -2 -f mpegts srt://"
+ RTSP_SRT_PORT + "?mode=listener"; + hostGatewayIp
+ ":" + srtPort + "?mode=listener";
// Clean adjacent white spaces or the ffmpeg command will fail // Clean adjacent white spaces or the ffmpeg command will fail
ffmpegCommand = ffmpegCommand.trim().replaceAll(" +", " "); ffmpegCommand = ffmpegCommand.trim().replaceAll(" +", " ");
GenericContainer<?> srtServerContainer = new GenericContainer<>(DockerImageName.parse(RTSP_SERVER_IMAGE)) GenericContainer<?> srtServerContainer = new GenericContainer<>(DockerImageName.parse(RTSP_SERVER_IMAGE))
.withCreateContainerCmdModifier(cmd -> cmd.withName("ffmpeg-" + Math.random() * 100000)) .withCreateContainerCmdModifier(cmd -> cmd.withName("ffmpeg-" + Math.random() * 100000))
.withEnv("MTX_PATHS_RTSP_RUNONINIT", ffmpegCommand) .withEnv(Map.ofEntries(Map.entry("MTX_LOGLEVEL", "info"), Map.entry("MTX_RTSPTRANSPORTS", "tcp"),
Map.entry("MTX_RTSPADDRESS", ":" + unusedRtspPort), Map.entry("MTX_HLS", "no"),
Map.entry("MTX_RTSP", "yes"), Map.entry("MTX_WEBRTC", "no"), Map.entry("MTX_SRT", "no"),
Map.entry("MTX_RTMP", "no"), Map.entry("MTX_MOQ", "no"), Map.entry("MTX_API", "no"),
Map.entry("MTX_PATHS_RTSP_RUNONINIT", ffmpegCommand)))
.withNetworkMode("host")
.waitingFor(Wait.forLogMessage(".*" + fileUrl + ".+", 1)); .waitingFor(Wait.forLogMessage(".*" + fileUrl + ".+", 1));
srtServerContainer.start(); srtServerContainer.start();
containers.add(srtServerContainer); containers.add(srtServerContainer);
String srtServerIp = srtServerContainer.getContainerInfo().getNetworkSettings().getIpAddress(); return "srt://host.docker.internal:" + srtPort;
}
return "srt://" + srtServerIp + ":" + RTSP_SRT_PORT; /**
* The host address that host.docker.internal resolves to inside containers
* created with host-gateway: the gateway of Docker's default bridge network.
*/
private String getDockerHostGatewayIp() {
return DockerClientFactory.instance().client().inspectNetworkCmd().withNetworkId("bridge").exec().getIpam()
.getConfig().get(0).getGateway();
} }
public void startServerSdkPublisher(String sdk, String roomName, String codec) throws Exception { public void startServerSdkPublisher(String sdk, String roomName, String codec) throws Exception {

View File

@ -4621,8 +4621,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
@Test @Test
@DisplayName("RTSP ingress AC3") @DisplayName("RTSP ingress AC3")
@Disabled // AC3 audio codec not supported through RTSP server with a single audio PCMU @Disabled // ffmpeg cannot payload AC-3 into RTP, see above
// track
void rtspIngressAC3Test() throws Exception { void rtspIngressAC3Test() throws Exception {
log.info("RTSP ingress AC3"); log.info("RTSP ingress AC3");
String rtspUri = startRtspServer(null, "AC3"); String rtspUri = startRtspServer(null, "AC3");
@ -4630,127 +4629,121 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
} }
/** /**
* NOTE 1: ingress with SRT pull does not work in the local network when ingress * NOTE: SRT ingest carries MPEG-TS, so only the codecs MPEG-TS can carry are
* process is a Docker container * tested: H264 and MPEG-4 video; AAC, AC3, MP3 and OPUS audio.
*/
/**
* NOTE 2: ingress SRT seems to support only video codecs H264 and MPEG-4
*/ */
// @Test @Test
// @DisplayName("SRT ingress H264 + AAC") @DisplayName("SRT ingress H264 + AAC")
// @Disabled // AAC audio codec stream fails if sent along a video stream void srtIngressH264_AACTest() throws Exception {
// void srtIngressTestH264_AAC() throws Exception { log.info("SRT ingress H264 + AAC");
// log.info("SRT ingress H264 + AAC"); String srtUri = startSrtServer("H264", "AAC");
// String srtUri = startSrtServer("H264", "AAC"); urPullCommon("SRT", srtUri, true, true);
// urPullCommon("SRT", srtUri, true, true); }
// }
// @Test
// @Test @DisplayName("SRT ingress H264 + AC3")
// @DisplayName("SRT ingress H264 + AC3") void srtIngressH264_AC3Test() throws Exception {
// void srtIngressTestH264_AC3() throws Exception { log.info("SRT ingress H264 + AC3");
// log.info("SRT ingress H264 + AC3"); String srtUri = startSrtServer("H264", "AC3");
// String srtUri = startSrtServer("H264", "AC3"); urPullCommon("SRT", srtUri, true, true);
// urPullCommon("SRT", srtUri, true, true); }
// }
// @Test
// @Test @DisplayName("SRT ingress H264 + OPUS")
// @DisplayName("SRT ingress H264 + OPUS") void srtIngressH264_OPUSTest() throws Exception {
// void srtIngressTestH264_OPUS() throws Exception { log.info("SRT ingress H264 + OPUS");
// log.info("SRT ingress H264 + OPUS"); String srtUri = startSrtServer("H264", "OPUS");
// String srtUri = startSrtServer("H264", "OPUS"); urPullCommon("SRT", srtUri, true, true);
// urPullCommon("SRT", srtUri, true, true); }
// }
// @Test
// @Test @DisplayName("SRT ingress H264 + MP3")
// @DisplayName("SRT ingress H264 + MP3") void srtIngressH264_MP3Test() throws Exception {
// void srtIngressTestH264_MP3() throws Exception { log.info("SRT ingress H264 + MP3");
// log.info("SRT ingress H264 + MP3"); String srtUri = startSrtServer("H264", "MP3");
// String srtUri = startSrtServer("H264", "MP3"); urPullCommon("SRT", srtUri, true, true);
// urPullCommon("SRT", srtUri, true, true); }
// }
// @Test
// @Test @DisplayName("SRT ingress MPEG-4 + AAC")
// @DisplayName("SRT ingress MPEG-4 + AAC") void srtIngressMPEG4_AACTest() throws Exception {
// @Disabled // AAC audio codec stream fails if sent along a video stream log.info("SRT ingress MPEG-4 + AAC");
// void srtIngressTestMPEG-4_AAC() throws Exception { String srtUri = startSrtServer("MPEG-4", "AAC");
// log.info("SRT ingress MPEG-4 + AAC"); urPullCommon("SRT", srtUri, true, true);
// String srtUri = startSrtServer("MPEG-4", "AAC"); }
// urPullCommon("SRT", srtUri, true, true);
// } @Test
// @DisplayName("SRT ingress MPEG-4 + AC3")
// @Test void srtIngressMPEG4_AC3Test() throws Exception {
// @DisplayName("SRT ingress MPEG-4 + AC3") log.info("SRT ingress MPEG-4 + AC3");
// void srtIngressTestMPEG-4_AC3() throws Exception { String srtUri = startSrtServer("MPEG-4", "AC3");
// log.info("SRT ingress MPEG-4 + AC3"); urPullCommon("SRT", srtUri, true, true);
// String srtUri = startSrtServer("MPEG-4", "AC3"); }
// urPullCommon("SRT", srtUri, true, true);
// } @Test
// @DisplayName("SRT ingress MPEG-4 + OPUS")
// @Test void srtIngressMPEG4_OPUSTest() throws Exception {
// @DisplayName("SRT ingress MPEG-4 + OPUS") log.info("SRT ingress MPEG-4 + OPUS");
// void srtIngressTestMPEG-4_OPUS() throws Exception { String srtUri = startSrtServer("MPEG-4", "OPUS");
// log.info("SRT ingress MPEG-4 + OPUS"); urPullCommon("SRT", srtUri, true, true);
// String srtUri = startSrtServer("MPEG-4", "OPUS"); }
// urPullCommon("SRT", srtUri, true, true);
// } @Test
// @DisplayName("SRT ingress MPEG-4 + MP3")
// @Test void srtIngressMPEG4_MP3Test() throws Exception {
// @DisplayName("SRT ingress MPEG-4 + MP3") log.info("SRT ingress MPEG-4 + MP3");
// void srtIngressTestMPEG-4_MP3() throws Exception { String srtUri = startSrtServer("MPEG-4", "MP3");
// log.info("SRT ingress MPEG-4 + MP3"); urPullCommon("SRT", srtUri, true, true);
// String srtUri = startSrtServer("MPEG-4", "MP3"); }
// urPullCommon("SRT", srtUri, true, true);
// } @Test
// @DisplayName("SRT ingress H264")
// @Test void srtIngressH264Test() throws Exception {
// @DisplayName("SRT ingress H264") log.info("SRT ingress H264");
// void srtIngressTestH264() throws Exception { String srtUri = startSrtServer("H264", null);
// log.info("SRT ingress H264"); urPullCommon("SRT", srtUri, true, false);
// String srtUri = startSrtServer("H264", null); }
// urPullCommon("SRT", srtUri, true, false);
// } @Test
// @DisplayName("SRT ingress MPEG-4")
// @Test void srtIngressMPEG4Test() throws Exception {
// @DisplayName("SRT ingress MPEG-4") log.info("SRT ingress MPEG-4");
// void srtIngressTestMPEG-4() throws Exception { String srtUri = startSrtServer("MPEG-4", null);
// log.info("SRT ingress MPEG-4"); urPullCommon("SRT", srtUri, true, false);
// String srtUri = startSrtServer("MPEG-4", null); }
// urPullCommon("SRT", srtUri, true, false);
// } @Test
// @DisplayName("SRT ingress AAC")
// @Test void srtIngressAACTest() throws Exception {
// @DisplayName("SRT ingress AAC") log.info("SRT ingress AAC");
// void srtIngressTestAAC() throws Exception { String srtUri = startSrtServer(null, "AAC");
// log.info("SRT ingress AAC"); urPullCommon("SRT", srtUri, false, true);
// String srtUri = startSrtServer(null, "AAC"); }
// urPullCommon("SRT", srtUri, false, true);
// } @Test
// @DisplayName("SRT ingress AC3")
// @Test void srtIngressAC3Test() throws Exception {
// @DisplayName("SRT ingress AC3") log.info("SRT ingress AC3");
// void srtIngressTestAC3() throws Exception { String srtUri = startSrtServer(null, "AC3");
// log.info("SRT ingress AC3"); urPullCommon("SRT", srtUri, false, true);
// String srtUri = startSrtServer(null, "AC3"); }
// urPullCommon("SRT", srtUri, false, true);
// } @Test
// @DisplayName("SRT ingress MP3")
// @Test void srtIngressMP3Test() throws Exception {
// @DisplayName("SRT ingress MP3") log.info("SRT ingress MP3");
// void srtIngressTestMP3() throws Exception { String srtUri = startSrtServer(null, "MP3");
// log.info("SRT ingress MP3"); urPullCommon("SRT", srtUri, false, true);
// String srtUri = startSrtServer(null, "MP3"); }
// urPullCommon("SRT", srtUri, false, true);
// } @Test
// @DisplayName("SRT ingress OPUS")
// @Test void srtIngressOPUSTest() throws Exception {
// @DisplayName("SRT ingress OPUS") log.info("SRT ingress OPUS");
// @Disabled // A single OPUS audio stream fails String srtUri = startSrtServer(null, "OPUS");
// void srtIngressTestOPUS() throws Exception { urPullCommon("SRT", srtUri, false, true);
// log.info("SRT ingress OPUS"); }
// String srtUri = startSrtServer(null, "OPUS");
// urPullCommon("SRT", srtUri, false, true);
// }
private void urPullCommon(String urlType, String uri, boolean withVideo, boolean withAudio) throws Exception { private void urPullCommon(String urlType, String uri, boolean withVideo, boolean withAudio) throws Exception {
OpenViduTestappUser user = setupBrowserAndConnectToOpenViduTestapp("chrome"); OpenViduTestappUser user = setupBrowserAndConnectToOpenViduTestapp("chrome");