mirror of https://github.com/OpenVidu/openvidu.git
openvidu-test-e2e: add framesDecoded over time assertion to detect frozen videos
parent
439d314c6d
commit
bb5b91e5f8
|
|
@ -106,6 +106,12 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
|
|
||||||
private static final long WAIT_UNTIL_MAX_MILLIS = 20000;
|
private static final long WAIT_UNTIL_MAX_MILLIS = 20000;
|
||||||
|
|
||||||
|
// Minimum average frame rate that a subscriber video must sustain over a
|
||||||
|
// window of at least MIN_FRAMES_DECODED_WINDOW_MILLIS to be considered
|
||||||
|
// properly decoded and played
|
||||||
|
private static final long MIN_FRAMES_DECODED_FPS = 4;
|
||||||
|
private static final long MIN_FRAMES_DECODED_WINDOW_MILLIS = 2000;
|
||||||
|
|
||||||
@BeforeAll()
|
@BeforeAll()
|
||||||
protected static void setupAll() throws Exception {
|
protected static void setupAll() throws Exception {
|
||||||
checkFfmpegInstallation();
|
checkFfmpegInstallation();
|
||||||
|
|
@ -4277,7 +4283,9 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
.orElseThrow(() -> new AssertionError("No layers in " + trackInfo));
|
.orElseThrow(() -> new AssertionError("No layers in " + trackInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Width of the highest-quality published layer, from the server's TrackInfo. */
|
/**
|
||||||
|
* Width of the highest-quality published layer, from the server's TrackInfo.
|
||||||
|
*/
|
||||||
private int highestLayerWidth(TrackInfo trackInfo) {
|
private int highestLayerWidth(TrackInfo trackInfo) {
|
||||||
return trackInfo.getLayersList().stream().mapToInt(VideoLayer::getWidth).max()
|
return trackInfo.getLayersList().stream().mapToInt(VideoLayer::getWidth).max()
|
||||||
.orElseThrow(() -> new AssertionError("No layers in " + trackInfo));
|
.orElseThrow(() -> new AssertionError("No layers in " + trackInfo));
|
||||||
|
|
@ -4956,18 +4964,34 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A subscriber video is only properly received AND played if its decoder keeps
|
// A subscriber video is only properly received AND played if its decoder keeps
|
||||||
// producing new frames. Receiving bytes is not enough: a subscriber may receive
|
// producing new frames at a sustained rate. Receiving bytes is not enough: a
|
||||||
// media that it is not able to decode at all
|
// subscriber may receive media that it is not able to decode at all. And a
|
||||||
|
// single new decoded frame is not enough either: a video that only decodes one
|
||||||
|
// or two frames over a timespan of several seconds is a frozen video, not a
|
||||||
|
// playing one, and must fail the test. So framesDecoded is required to grow at
|
||||||
|
// MIN_FRAMES_DECODED_FPS or more, averaged over a window of at least
|
||||||
|
// MIN_FRAMES_DECODED_WINDOW_MILLIS
|
||||||
private void waitUntilSubscriberFramesDecodedIncrease(OpenViduTestappUser user, WebElement videoElement) {
|
private void waitUntilSubscriberFramesDecodedIncrease(OpenViduTestappUser user, WebElement videoElement) {
|
||||||
this.waitUntilSubscriberFramesDecodedIncrease(user, videoElement,
|
final long initialFramesDecoded = this.getSubscriberVideoFramesDecoded(user, videoElement);
|
||||||
this.getSubscriberVideoFramesDecoded(user, videoElement));
|
final long windowStart = System.currentTimeMillis();
|
||||||
}
|
// Last sample taken by the loop, only to report it if the wait times out
|
||||||
|
final java.util.concurrent.atomic.AtomicLong lastFramesDecoded = new java.util.concurrent.atomic.AtomicLong();
|
||||||
private void waitUntilSubscriberFramesDecodedIncrease(OpenViduTestappUser user, WebElement videoElement,
|
final java.util.concurrent.atomic.AtomicLong lastWindowMillis = new java.util.concurrent.atomic.AtomicLong();
|
||||||
final long previousFramesDecoded) {
|
|
||||||
this.waitUntilAux(user, videoElement, () -> {
|
this.waitUntilAux(user, videoElement, () -> {
|
||||||
return this.getSubscriberVideoFramesDecoded(user, videoElement) > previousFramesDecoded;
|
long framesDecoded = this.getSubscriberVideoFramesDecoded(user, videoElement) - initialFramesDecoded;
|
||||||
}, "Timeout waiting for subscriber track to increase its framesDecoded from " + previousFramesDecoded);
|
long windowMillis = System.currentTimeMillis() - windowStart;
|
||||||
|
lastFramesDecoded.set(framesDecoded);
|
||||||
|
lastWindowMillis.set(windowMillis);
|
||||||
|
// The window keeps growing while waiting, so a video that decodes a
|
||||||
|
// frame every now and then falls further behind the required rate
|
||||||
|
// instead of eventually satisfying it
|
||||||
|
return windowMillis >= MIN_FRAMES_DECODED_WINDOW_MILLIS
|
||||||
|
&& framesDecoded * 1000 >= MIN_FRAMES_DECODED_FPS * windowMillis;
|
||||||
|
}, () -> "Timeout waiting for subscriber track to decode video at a sustained frame rate: only "
|
||||||
|
+ lastFramesDecoded.get() + " frame(s) decoded in " + lastWindowMillis.get() + " ms ("
|
||||||
|
+ String.format("%.2f", lastFramesDecoded.get() * 1000d / Math.max(1, lastWindowMillis.get()))
|
||||||
|
+ " fps), while at least " + MIN_FRAMES_DECODED_FPS
|
||||||
|
+ " fps are required. Such a subscriber video is a frozen video");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitUntilPublisherBytesSentIncrease(OpenViduTestappUser user, WebElement videoElement, String rid,
|
private void waitUntilPublisherBytesSentIncrease(OpenViduTestappUser user, WebElement videoElement, String rid,
|
||||||
|
|
@ -4994,6 +5018,13 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
|
|
||||||
private void waitUntilAux(OpenViduTestappUser user, WebElement videoElement,
|
private void waitUntilAux(OpenViduTestappUser user, WebElement videoElement,
|
||||||
Callable<Boolean> breakFromLoopFunction, String errMsg) {
|
Callable<Boolean> breakFromLoopFunction, String errMsg) {
|
||||||
|
this.waitUntilAux(user, videoElement, breakFromLoopFunction, () -> errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same as above, but building the error message only if the wait times out, so
|
||||||
|
// that it can report the values actually observed by the last iteration
|
||||||
|
private void waitUntilAux(OpenViduTestappUser user, WebElement videoElement,
|
||||||
|
Callable<Boolean> breakFromLoopFunction, java.util.function.Supplier<String> errMsg) {
|
||||||
try {
|
try {
|
||||||
final long intervalWait = 250;
|
final long intervalWait = 250;
|
||||||
final long deadline = System.currentTimeMillis() + WAIT_UNTIL_MAX_MILLIS;
|
final long deadline = System.currentTimeMillis() + WAIT_UNTIL_MAX_MILLIS;
|
||||||
|
|
@ -5015,7 +5046,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!breakFromLoop) {
|
if (!breakFromLoop) {
|
||||||
Assertions.fail(errMsg);
|
Assertions.fail(errMsg.get());
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// Best-effort close of the info dialog
|
// Best-effort close of the info dialog
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue