openvidu-test-e2e: refactor recording utilities to openvidu-test-browsers

pull/574/head
pabloFuente 2020-12-02 18:43:43 +01:00
parent 0561c6aab7
commit eaa1361d04
6 changed files with 361 additions and 318 deletions

View File

@ -87,11 +87,20 @@
<artifactId>unirest-java</artifactId>
<version>${version.unirest}</version>
</dependency>
<dependency>
<groupId>org.jcodec</groupId>
<artifactId>jcodec-javase</artifactId>
<version>${version.jcodec}</version>
</dependency>
<dependency>
<groupId>io.openvidu</groupId>
<artifactId>openvidu-java-client</artifactId>
<version>${version.openvidu.java.client}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${version.junit}</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -0,0 +1,264 @@
package io.openvidu.test.browsers.utils;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import javax.imageio.ImageIO;
import org.jcodec.api.FrameGrab;
import org.jcodec.api.JCodecException;
import org.jcodec.common.model.Picture;
import org.jcodec.scale.AWTUtil;
import org.junit.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.stream.JsonReader;
import io.openvidu.java.client.Recording;
public class RecordingUtils {
protected static final Logger log = LoggerFactory.getLogger(RecordingUtils.class);
public boolean recordedGreenFileFine(File file, Recording recording) throws IOException {
return this.recordedFileFine(file, recording, RecordingUtils::checkVideoAverageRgbGreen);
}
public boolean recordedRedFileFine(File file, Recording recording) throws IOException {
return this.recordedFileFine(file, recording, RecordingUtils::checkVideoAverageRgbRed);
}
private boolean recordedFileFine(File file, Recording recording,
Function<Map<String, Long>, Boolean> colorCheckFunction) throws IOException {
this.checkMultimediaFile(file, recording.hasAudio(), recording.hasVideo(), recording.getDuration(),
recording.getResolution(), "aac", "h264", true);
boolean isFine = false;
Picture frame;
try {
// Get a frame at 75% duration and check that it has the expected color
frame = FrameGrab.getFrameAtSec(file, (double) (recording.getDuration() * 0.75));
BufferedImage image = AWTUtil.toBufferedImage(frame);
Map<String, Long> colorMap = this.averageColor(image);
String realResolution = image.getWidth() + "x" + image.getHeight();
Assert.assertEquals(
"Resolution (" + recording.getResolution()
+ ") of recording entity is not equal to real video resolution (" + realResolution + ")",
recording.getResolution(), realResolution);
log.info("Recording map color: {}", colorMap.toString());
log.info("Recording frame below");
System.out.println(bufferedImageToBase64PngString(image));
isFine = colorCheckFunction.apply(colorMap);
} catch (IOException | JCodecException e) {
log.warn("Error getting frame from video recording: {}", e.getMessage());
isFine = false;
}
return isFine;
}
public static boolean checkVideoAverageRgbGreen(Map<String, Long> rgb) {
// GREEN color: {r < 15, g > 130, b <15}
return (rgb.get("r") < 15) && (rgb.get("g") > 130) && (rgb.get("b") < 15);
}
public static boolean checkVideoAverageRgbGray(Map<String, Long> 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)
&& (Math.abs(rgb.get("r") - rgb.get("g")) <= 2) && (Math.abs(rgb.get("r") - rgb.get("b")) <= 2)
&& (Math.abs(rgb.get("b") - rgb.get("g")) <= 2);
}
public static boolean checkVideoAverageRgbRed(Map<String, Long> rgb) {
// RED color: {r > 240, g < 15, b <15}
return (rgb.get("r") > 240) && (rgb.get("g") < 15) && (rgb.get("b") < 15);
}
private String bufferedImageToBase64PngString(BufferedImage image) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
String imageString = null;
try {
ImageIO.write(image, "png", bos);
byte[] imageBytes = bos.toByteArray();
imageString = "data:image/png;base64," + Base64.getEncoder().encodeToString(imageBytes);
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
return imageString;
}
public void checkIndividualRecording(String recPath, Recording recording, int numberOfVideoFiles,
String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {
// Should be only 2 files: zip and metadata
File folder = new File(recPath);
Assert.assertEquals("There are more than 2 files (ZIP and metadata) inside individual recording folder "
+ recPath + ": " + Arrays.toString(folder.listFiles()), 2, folder.listFiles().length);
File file1 = new File(recPath + recording.getName() + ".zip");
File file2 = new File(recPath + ".recording." + recording.getId());
Assert.assertTrue("File " + file1.getAbsolutePath() + " does not exist or is empty",
file1.exists() && file1.length() > 0);
Assert.assertTrue("File " + file2.getAbsolutePath() + " does not exist or is empty",
file2.exists() && file2.length() > 0);
List<File> unzippedWebmFiles = new Unzipper().unzipFile(recPath, recording.getName() + ".zip");
Assert.assertEquals("Expecting " + numberOfVideoFiles + " videos inside ZIP file but "
+ unzippedWebmFiles.size() + " found: " + unzippedWebmFiles.toString(), numberOfVideoFiles,
unzippedWebmFiles.size());
File jsonSyncFile = new File(recPath + recording.getName() + ".json");
Assert.assertTrue("JSON sync file " + jsonSyncFile.getAbsolutePath() + "does not exist or is empty",
jsonSyncFile.exists() && jsonSyncFile.length() > 0);
JsonObject jsonSyncMetadata;
try {
Gson gson = new Gson();
JsonReader reader = new JsonReader(new FileReader(jsonSyncFile));
jsonSyncMetadata = gson.fromJson(reader, JsonObject.class);
} catch (Exception e) {
log.error("Cannot read JSON sync metadata file from {}. Error: {}", jsonSyncFile.getAbsolutePath(),
e.getMessage());
Assert.fail("Cannot read JSON sync metadata file from " + jsonSyncFile.getAbsolutePath());
return;
}
long totalFileSize = 0;
JsonArray syncArray = jsonSyncMetadata.get("files").getAsJsonArray();
for (File webmFile : unzippedWebmFiles) {
totalFileSize += webmFile.length();
Assert.assertTrue("WEBM file " + webmFile.getAbsolutePath() + " does not exist or is empty",
webmFile.exists() && webmFile.length() > 0);
double durationInSeconds = 0;
boolean found = false;
for (int i = 0; i < syncArray.size(); i++) {
JsonObject j = syncArray.get(i).getAsJsonObject();
if (webmFile.getName().contains(j.get("streamId").getAsString())) {
durationInSeconds = (double) (j.get("endTimeOffset").getAsDouble()
- j.get("startTimeOffset").getAsDouble()) / 1000;
found = true;
break;
}
}
Assert.assertTrue("Couldn't find in JSON sync object information for webm file " + webmFile.getName(),
found);
log.info("Duration of {} according to sync metadata json file: {} s", webmFile.getName(),
durationInSeconds);
this.checkMultimediaFile(webmFile, recording.hasAudio(), recording.hasVideo(), durationInSeconds,
recording.getResolution(), audioDecoder, videoDecoder, checkAudio);
webmFile.delete();
}
Assert.assertEquals("Size of recording entity (" + recording.getSessionId()
+ ") is not equal to real file size (" + totalFileSize + ")", recording.getSize(), totalFileSize);
jsonSyncFile.delete();
}
public void checkMultimediaFile(File file, boolean hasAudio, boolean hasVideo, double duration, String resolution,
String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {
// Check tracks, duration, resolution, framerate and decoders
MultimediaFileMetadata metadata = new MultimediaFileMetadata(file.getAbsolutePath());
if (hasVideo) {
if (checkAudio) {
if (hasAudio) {
Assert.assertTrue("Media file " + file.getAbsolutePath() + " should have audio",
metadata.hasAudio() && metadata.hasVideo());
Assert.assertTrue(metadata.getAudioDecoder().toLowerCase().contains(audioDecoder));
} else {
Assert.assertTrue("Media file " + file.getAbsolutePath() + " should have video",
metadata.hasVideo());
Assert.assertFalse(metadata.hasAudio());
}
}
if (resolution != null) {
Assert.assertEquals(resolution, metadata.getVideoWidth() + "x" + metadata.getVideoHeight());
}
Assert.assertTrue(metadata.getVideoDecoder().toLowerCase().contains(videoDecoder));
} else if (hasAudio && checkAudio) {
Assert.assertTrue(metadata.hasAudio());
Assert.assertFalse(metadata.hasVideo());
Assert.assertTrue(metadata.getAudioDecoder().toLowerCase().contains(audioDecoder));
} else {
Assert.fail("Cannot check a file witho no audio and no video");
}
// Check duration with 1 decimal precision
DecimalFormat df = new DecimalFormat("#0.0");
df.setRoundingMode(RoundingMode.UP);
log.info("Duration of {} according to ffmpeg: {} s", file.getName(), metadata.getDuration());
log.info("Duration of {} according to 'duration' property: {} s", file.getName(), duration);
log.info("Difference in s duration: {}", Math.abs(metadata.getDuration() - duration));
final double difference = 10;
Assert.assertTrue(
"Difference between recording entity duration (" + duration + ") and real video duration ("
+ metadata.getDuration() + ") is greater than " + difference + " in file " + file.getName(),
Math.abs((metadata.getDuration() - duration)) < difference);
}
public boolean thumbnailIsFine(File file, Function<Map<String, Long>, Boolean> colorCheckFunction) {
boolean isFine = false;
BufferedImage image = null;
try {
image = ImageIO.read(file);
} catch (IOException e) {
log.error(e.getMessage());
return false;
}
log.info("Recording thumbnail dimensions: {}x{}", image.getWidth(), image.getHeight());
Map<String, Long> colorMap = this.averageColor(image);
log.info("Thumbnail map color: {}", colorMap.toString());
isFine = colorCheckFunction.apply(colorMap);
return isFine;
}
private Map<String, Long> averageColor(BufferedImage bi) {
int x0 = 0;
int y0 = 0;
int w = bi.getWidth();
int h = bi.getHeight();
int x1 = x0 + w;
int y1 = y0 + h;
long sumr = 0, sumg = 0, sumb = 0;
for (int x = x0; x < x1; x++) {
for (int y = y0; y < y1; y++) {
Color pixel = new Color(bi.getRGB(x, y));
sumr += pixel.getRed();
sumg += pixel.getGreen();
sumb += pixel.getBlue();
}
}
int num = w * h;
Map<String, Long> colorMap = new HashMap<>();
colorMap.put("r", (long) (sumr / num));
colorMap.put("g", (long) (sumg / num));
colorMap.put("b", (long) (sumb / num));
return colorMap;
}
}

View File

@ -108,12 +108,6 @@
<artifactId>gson</artifactId>
<version>${version.gson}</version>
</dependency>
<dependency>
<groupId>org.jcodec</groupId>
<artifactId>jcodec-javase</artifactId>
<version>${version.jcodec}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.mashape.unirest</groupId>
<artifactId>unirest-java</artifactId>

View File

@ -2,37 +2,18 @@ package io.openvidu.test.e2e;
import static org.openqa.selenium.OutputType.BASE64;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.RoundingMode;
import java.nio.file.Path;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.imageio.ImageIO;
import com.mashape.unirest.http.HttpMethod;
import io.openvidu.test.browsers.utils.CustomHttpClient;
import org.apache.commons.io.FileUtils;
import org.apache.http.HttpStatus;
import org.jcodec.api.FrameGrab;
import org.jcodec.api.JCodecException;
import org.jcodec.common.model.Picture;
import org.jcodec.scale.AWTUtil;
import org.junit.Assert;
import org.junit.jupiter.api.AfterEach;
import org.openqa.selenium.By;
@ -45,26 +26,22 @@ import org.openqa.selenium.support.ui.ExpectedConditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import com.mashape.unirest.http.HttpMethod;
import io.github.bonigarcia.wdm.WebDriverManager;
import io.openvidu.java.client.OpenVidu;
import io.openvidu.java.client.OpenViduHttpException;
import io.openvidu.java.client.OpenViduJavaClientException;
import io.openvidu.java.client.Recording;
import io.openvidu.java.client.VideoCodec;
import io.openvidu.test.browsers.BrowserUser;
import io.openvidu.test.browsers.ChromeAndroidUser;
import io.openvidu.test.browsers.ChromeUser;
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.MultimediaFileMetadata;
import io.openvidu.test.browsers.utils.Unzipper;
import io.openvidu.test.browsers.utils.CustomHttpClient;
import io.openvidu.test.browsers.utils.RecordingUtils;
public class AbstractOpenViduTestAppE2eTest {
@ -96,6 +73,8 @@ public class AbstractOpenViduTestAppE2eTest {
protected static OpenVidu OV;
protected RecordingUtils recordingUtils = new RecordingUtils();
protected static void checkFfmpegInstallation() {
String ffmpegOutput = commandLine.executeCommand("which ffmpeg");
if (ffmpegOutput == null || ffmpegOutput.isEmpty()) {
@ -298,24 +277,6 @@ public class AbstractOpenViduTestAppE2eTest {
};
}
protected static boolean checkVideoAverageRgbGreen(Map<String, Long> rgb) {
// GREEN color: {r < 15, g > 130, b <15}
return (rgb.get("r") < 15) && (rgb.get("g") > 130) && (rgb.get("b") < 15);
}
protected static boolean checkVideoAverageRgbGray(Map<String, Long> 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)
&& (Math.abs(rgb.get("r") - rgb.get("g")) <= 2) && (Math.abs(rgb.get("r") - rgb.get("b")) <= 2)
&& (Math.abs(rgb.get("b") - rgb.get("g")) <= 2);
}
protected static boolean checkVideoAverageRgbRed(Map<String, Long> rgb) {
// RED color: {r > 240, g < 15, b <15}
return (rgb.get("r") > 240) && (rgb.get("g") < 15) && (rgb.get("b") < 15);
}
protected void gracefullyLeaveParticipants(int numberOfParticipants) throws Exception {
int accumulatedConnectionDestroyed = 0;
for (int j = 1; j <= numberOfParticipants; j++) {
@ -333,215 +294,6 @@ public class AbstractOpenViduTestAppE2eTest {
return "data:image/png;base64," + screenshotBase64;
}
protected boolean recordedFileFine(File file, Recording recording,
Function<Map<String, Long>, Boolean> colorCheckFunction) throws IOException {
this.checkMultimediaFile(file, recording.hasAudio(), recording.hasVideo(), recording.getDuration(),
recording.getResolution(), "aac", "h264", true);
boolean isFine = false;
Picture frame;
try {
// Get a frame at 75% duration and check that it has the expected color
frame = FrameGrab.getFrameAtSec(file, (double) (recording.getDuration() * 0.75));
BufferedImage image = AWTUtil.toBufferedImage(frame);
Map<String, Long> colorMap = this.averageColor(image);
String realResolution = image.getWidth() + "x" + image.getHeight();
Assert.assertEquals(
"Resolution (" + recording.getResolution()
+ ") of recording entity is not equal to real video resolution (" + realResolution + ")",
recording.getResolution(), realResolution);
log.info("Recording map color: {}", colorMap.toString());
log.info("Recording frame below");
System.out.println(bufferedImageToBase64PngString(image));
isFine = colorCheckFunction.apply(colorMap);
} catch (IOException | JCodecException e) {
log.warn("Error getting frame from video recording: {}", e.getMessage());
isFine = false;
}
return isFine;
}
protected boolean recordedGreenFileFine(File file, Recording recording) throws IOException {
return this.recordedFileFine(file, recording, OpenViduTestAppE2eTest::checkVideoAverageRgbGreen);
}
protected boolean recordedRedFileFine(File file, Recording recording) throws IOException {
return this.recordedFileFine(file, recording, OpenViduTestAppE2eTest::checkVideoAverageRgbRed);
}
protected String bufferedImageToBase64PngString(BufferedImage image) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
String imageString = null;
try {
ImageIO.write(image, "png", bos);
byte[] imageBytes = bos.toByteArray();
imageString = "data:image/png;base64," + Base64.getEncoder().encodeToString(imageBytes);
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return imageString;
}
protected void checkIndividualRecording(String recPath, Recording recording, int numberOfVideoFiles,
String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {
// Should be only 2 files: zip and metadata
File folder = new File(recPath);
Assert.assertEquals("There are more than 2 files (ZIP and metadata) inside individual recording folder "
+ recPath + ": " + Arrays.toString(folder.listFiles()), 2, folder.listFiles().length);
File file1 = new File(recPath + recording.getName() + ".zip");
File file2 = new File(recPath + ".recording." + recording.getId());
Assert.assertTrue("File " + file1.getAbsolutePath() + " does not exist or is empty",
file1.exists() && file1.length() > 0);
Assert.assertTrue("File " + file2.getAbsolutePath() + " does not exist or is empty",
file2.exists() && file2.length() > 0);
List<File> unzippedWebmFiles = new Unzipper().unzipFile(recPath, recording.getName() + ".zip");
Assert.assertEquals("Expecting " + numberOfVideoFiles + " videos inside ZIP file but "
+ unzippedWebmFiles.size() + " found: " + unzippedWebmFiles.toString(), numberOfVideoFiles,
unzippedWebmFiles.size());
File jsonSyncFile = new File(recPath + recording.getName() + ".json");
Assert.assertTrue("JSON sync file " + jsonSyncFile.getAbsolutePath() + "does not exist or is empty",
jsonSyncFile.exists() && jsonSyncFile.length() > 0);
JsonObject jsonSyncMetadata;
try {
Gson gson = new Gson();
JsonReader reader = new JsonReader(new FileReader(jsonSyncFile));
jsonSyncMetadata = gson.fromJson(reader, JsonObject.class);
} catch (Exception e) {
log.error("Cannot read JSON sync metadata file from {}. Error: {}", jsonSyncFile.getAbsolutePath(),
e.getMessage());
Assert.fail("Cannot read JSON sync metadata file from " + jsonSyncFile.getAbsolutePath());
return;
}
long totalFileSize = 0;
JsonArray syncArray = jsonSyncMetadata.get("files").getAsJsonArray();
for (File webmFile : unzippedWebmFiles) {
totalFileSize += webmFile.length();
Assert.assertTrue("WEBM file " + webmFile.getAbsolutePath() + " does not exist or is empty",
webmFile.exists() && webmFile.length() > 0);
double durationInSeconds = 0;
boolean found = false;
for (int i = 0; i < syncArray.size(); i++) {
JsonObject j = syncArray.get(i).getAsJsonObject();
if (webmFile.getName().contains(j.get("streamId").getAsString())) {
durationInSeconds = (double) (j.get("endTimeOffset").getAsDouble()
- j.get("startTimeOffset").getAsDouble()) / 1000;
found = true;
break;
}
}
Assert.assertTrue("Couldn't find in JSON sync object information for webm file " + webmFile.getName(),
found);
log.info("Duration of {} according to sync metadata json file: {} s", webmFile.getName(),
durationInSeconds);
this.checkMultimediaFile(webmFile, recording.hasAudio(), recording.hasVideo(), durationInSeconds,
recording.getResolution(), audioDecoder, videoDecoder, checkAudio);
webmFile.delete();
}
Assert.assertEquals("Size of recording entity (" + recording.getSessionId()
+ ") is not equal to real file size (" + totalFileSize + ")", recording.getSize(), totalFileSize);
jsonSyncFile.delete();
}
protected void checkMultimediaFile(File file, boolean hasAudio, boolean hasVideo, double duration,
String resolution, String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {
// Check tracks, duration, resolution, framerate and decoders
MultimediaFileMetadata metadata = new MultimediaFileMetadata(file.getAbsolutePath());
if (hasVideo) {
if (checkAudio) {
if (hasAudio) {
Assert.assertTrue("Media file " + file.getAbsolutePath() + " should have audio",
metadata.hasAudio() && metadata.hasVideo());
Assert.assertTrue(metadata.getAudioDecoder().toLowerCase().contains(audioDecoder));
} else {
Assert.assertTrue("Media file " + file.getAbsolutePath() + " should have video",
metadata.hasVideo());
Assert.assertFalse(metadata.hasAudio());
}
}
if (resolution != null) {
Assert.assertEquals(resolution, metadata.getVideoWidth() + "x" + metadata.getVideoHeight());
}
Assert.assertTrue(metadata.getVideoDecoder().toLowerCase().contains(videoDecoder));
} else if (hasAudio && checkAudio) {
Assert.assertTrue(metadata.hasAudio());
Assert.assertFalse(metadata.hasVideo());
Assert.assertTrue(metadata.getAudioDecoder().toLowerCase().contains(audioDecoder));
} else {
Assert.fail("Cannot check a file witho no audio and no video");
}
// Check duration with 1 decimal precision
DecimalFormat df = new DecimalFormat("#0.0");
df.setRoundingMode(RoundingMode.UP);
log.info("Duration of {} according to ffmpeg: {} s", file.getName(), metadata.getDuration());
log.info("Duration of {} according to 'duration' property: {} s", file.getName(), duration);
log.info("Difference in s duration: {}", Math.abs(metadata.getDuration() - duration));
final double difference = 10;
Assert.assertTrue(
"Difference between recording entity duration (" + duration + ") and real video duration ("
+ metadata.getDuration() + ") is greater than " + difference + " in file " + file.getName(),
Math.abs((metadata.getDuration() - duration)) < difference);
}
protected boolean thumbnailIsFine(File file, Function<Map<String, Long>, Boolean> colorCheckFunction) {
boolean isFine = false;
BufferedImage image = null;
try {
image = ImageIO.read(file);
} catch (IOException e) {
log.error(e.getMessage());
return false;
}
log.info("Recording thumbnail dimensions: {}x{}", image.getWidth(), image.getHeight());
Map<String, Long> colorMap = this.averageColor(image);
log.info("Thumbnail map color: {}", colorMap.toString());
isFine = colorCheckFunction.apply(colorMap);
return isFine;
}
protected Map<String, Long> averageColor(BufferedImage bi) {
int x0 = 0;
int y0 = 0;
int w = bi.getWidth();
int h = bi.getHeight();
int x1 = x0 + w;
int y1 = y0 + h;
long sumr = 0, sumg = 0, sumb = 0;
for (int x = x0; x < x1; x++) {
for (int y = y0; y < y1; y++) {
Color pixel = new Color(bi.getRGB(x, y));
sumr += pixel.getRed();
sumg += pixel.getGreen();
sumb += pixel.getBlue();
}
}
int num = w * h;
Map<String, Long> colorMap = new HashMap<>();
colorMap.put("r", (long) (sumr / num));
colorMap.put("g", (long) (sumg / num));
colorMap.put("b", (long) (sumb / num));
return colorMap;
}
protected void startKms() {
log.info("Starting KMS");
commandLine.executeCommand("/usr/bin/kurento-media-server &>> /kms.log &");

View File

@ -168,7 +168,7 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
String recPath = "/opt/openvidu/recordings/" + sessionName + "/";
Recording recording = new OpenVidu(OpenViduTestAppE2eTest.OPENVIDU_URL, OpenViduTestAppE2eTest.OPENVIDU_SECRET)
.getRecording(sessionName);
checkIndividualRecording(recPath, recording, 4, "opus", "vp8", true);
this.recordingUtils.checkIndividualRecording(recPath, recording, 4, "opus", "vp8", true);
// Analyze INDIVIDUAL recording metadata
new Unzipper().unzipFile(recPath, recording.getName() + ".zip");

View File

@ -73,6 +73,7 @@ import io.openvidu.java.client.SessionProperties;
import io.openvidu.java.client.VideoCodec;
import io.openvidu.test.browsers.FirefoxUser;
import io.openvidu.test.browsers.utils.CustomHttpClient;
import io.openvidu.test.browsers.utils.RecordingUtils;
import io.openvidu.test.browsers.utils.layout.CustomLayoutHandler;
import io.openvidu.test.browsers.utils.webhook.CustomWebhook;
@ -1082,10 +1083,10 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
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.recordedGreenFileFine(file1,
new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName)));
Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine", this.recordingUtils
.recordedGreenFileFine(file1, new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName)));
Assert.assertTrue("Thumbnail " + file3.getAbsolutePath() + " is not fine",
this.thumbnailIsFine(file3, OpenViduTestAppE2eTest::checkVideoAverageRgbGreen));
this.recordingUtils.thumbnailIsFine(file3, RecordingUtils::checkVideoAverageRgbGreen));
// Try to get the stopped recording
user.getDriver().findElement(By.id("get-recording-btn")).click();
@ -1368,7 +1369,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
String recPath = recordingsPath + sessionName + "/";
Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(sessionName);
this.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true);
this.recordingUtils.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true);
// Try to get the stopped recording
user.getDriver().findElement(By.id("get-recording-btn")).click();
@ -1592,24 +1593,24 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
// Check video-only COMPOSED recording
String recPath = recordingsPath + SESSION_NAME + "/";
Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME);
this.checkMultimediaFile(new File(recPath + recording.getName() + ".mp4"), false, true, recording.getDuration(),
recording.getResolution(), null, "h264", true);
this.recordingUtils.checkMultimediaFile(new File(recPath + recording.getName() + ".mp4"), false, true,
recording.getDuration(), recording.getResolution(), null, "h264", true);
// Check audio-only COMPOSED recording
recPath = recordingsPath + SESSION_NAME + "-1/";
recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "-1");
this.checkMultimediaFile(new File(recPath + recording.getName() + ".webm"), true, false,
this.recordingUtils.checkMultimediaFile(new File(recPath + recording.getName() + ".webm"), true, false,
recording.getDuration(), null, "opus", null, true);
// Check video-only INDIVIDUAL recording
recPath = recordingsPath + SESSION_NAME + "-2/";
recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "-2");
this.checkIndividualRecording(recPath, recording, 3, "opus", "vp8", true);
this.recordingUtils.checkIndividualRecording(recPath, recording, 3, "opus", "vp8", true);
// Check audio-only INDIVIDUAL recording
recPath = recordingsPath + SESSION_NAME + "-3/";
recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME + "-3");
this.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true);
this.recordingUtils.checkIndividualRecording(recPath, recording, 2, "opus", "vp8", true);
user.getDriver().findElement(By.id("close-dialog-btn")).click();
Thread.sleep(500);
@ -1685,10 +1686,10 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
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("Recorded file " + file1.getAbsolutePath() + " is not fine", this.recordingUtils
.recordedRedFileFine(file1, new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(SESSION_NAME)));
Assert.assertTrue("Thumbnail " + file2.getAbsolutePath() + " is not fine",
this.thumbnailIsFine(file2, OpenViduTestAppE2eTest::checkVideoAverageRgbRed));
this.recordingUtils.thumbnailIsFine(file2, RecordingUtils::checkVideoAverageRgbRed));
// Custom layout from external URL
CountDownLatch initLatch = new CountDownLatch(1);
@ -1737,10 +1738,11 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
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("Recorded file " + file1.getAbsolutePath() + " is not fine",
this.recordingUtils.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));
this.recordingUtils.thumbnailIsFine(file2, RecordingUtils::checkVideoAverageRgbRed));
} finally {
CustomLayoutHandler.shutDown();
@ -1902,7 +1904,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
// Analyze Chrome fake video stream without gray filter (GREEN color)
Map<String, Long> rgb = user.getEventManager().getAverageRgbFromVideo(subscriberVideo);
System.out.println(rgb.toString());
Assert.assertTrue("Video is not average green", checkVideoAverageRgbGreen(rgb));
Assert.assertTrue("Video is not average green", RecordingUtils.checkVideoAverageRgbGreen(rgb));
// Try to apply none allowed filter
user.getDriver().findElement(By.cssSelector(".filter-btn")).click();
@ -1941,7 +1943,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
Thread.sleep(500);
rgb = user.getEventManager().getAverageRgbFromVideo(subscriberVideo);
System.out.println(rgb.toString());
Assert.assertTrue("Video is not average gray", checkVideoAverageRgbGray(rgb));
Assert.assertTrue("Video is not average gray", RecordingUtils.checkVideoAverageRgbGray(rgb));
// Execute filter method
WebElement filterMethodInput = user.getDriver().findElement(By.id("filter-method-field"));
@ -1959,7 +1961,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
Thread.sleep(500);
rgb = user.getEventManager().getAverageRgbFromVideo(subscriberVideo);
System.out.println(rgb.toString());
Assert.assertTrue("Video is not average green", checkVideoAverageRgbGreen(rgb));
Assert.assertTrue("Video is not average green", RecordingUtils.checkVideoAverageRgbGreen(rgb));
user.getDriver().findElement(By.id("close-dialog-btn")).click();
Thread.sleep(500);
@ -1984,7 +1986,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
subscriberVideo = user.getDriver().findElement(By.cssSelector("#openvidu-instance-1 video"));
rgb = user.getEventManager().getAverageRgbFromVideo(subscriberVideo);
System.out.println(rgb.toString());
Assert.assertTrue("Video is not average gray", checkVideoAverageRgbGray(rgb));
Assert.assertTrue("Video is not average gray", RecordingUtils.checkVideoAverageRgbGray(rgb));
// Remove filter
user.getDriver().findElement(By.cssSelector(".filter-btn")).click();
@ -1998,7 +2000,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
// Analyze Chrome fake video stream with gray filter (GREEN color)
rgb = user.getEventManager().getAverageRgbFromVideo(subscriberVideo);
System.out.println(rgb.toString());
Assert.assertTrue("Video is not average green", checkVideoAverageRgbGreen(rgb));
Assert.assertTrue("Video is not average green", RecordingUtils.checkVideoAverageRgbGreen(rgb));
user.getDriver().findElement(By.id("close-dialog-btn")).click();
Thread.sleep(500);
@ -2457,8 +2459,8 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
Assert.assertFalse("Session shouldn't be being recorded", session.isBeingRecorded());
Assert.assertFalse("OpenVidu.fetch() should return false", OV.fetch());
this.checkIndividualRecording("/opt/openvidu/recordings/" + customSessionId + "/", recording, 2, "opus", "vp8",
false);
this.recordingUtils.checkIndividualRecording("/opt/openvidu/recordings/" + customSessionId + "/", recording, 2,
"opus", "vp8", false);
user.getDriver().findElement(By.cssSelector("#openvidu-instance-0 .change-publisher-btn")).click();
user.getEventManager().waitUntilEventReaches("streamDestroyed", 4);
@ -2522,9 +2524,9 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
file3.exists() && file3.length() > 0);
Assert.assertTrue("Recorded file " + file1.getAbsolutePath() + " is not fine",
this.recordedGreenFileFine(file1, recording2));
this.recordingUtils.recordedGreenFileFine(file1, recording2));
Assert.assertTrue("Thumbnail " + file3.getAbsolutePath() + " is not fine",
this.thumbnailIsFine(file3, OpenViduTestAppE2eTest::checkVideoAverageRgbGreen));
this.recordingUtils.thumbnailIsFine(file3, RecordingUtils::checkVideoAverageRgbGreen));
try {
OV.deleteRecording("NOT_EXISTS");
@ -2614,24 +2616,20 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
session.close();
/** Test transcoding defined properties */
SessionProperties.Builder basePropertiesBuilder = new SessionProperties.Builder()
.mediaMode(MediaMode.ROUTED).recordingMode(RecordingMode.ALWAYS)
.defaultOutputMode(OutputMode.INDIVIDUAL);
SessionProperties.Builder basePropertiesBuilder = new SessionProperties.Builder().mediaMode(MediaMode.ROUTED)
.recordingMode(RecordingMode.ALWAYS).defaultOutputMode(OutputMode.INDIVIDUAL);
SessionProperties propertiesDefaultCodec = basePropertiesBuilder.build();
SessionProperties propertiesH264AllowTranscoding = basePropertiesBuilder
.forcedVideoCodec(VideoCodec.H264)
.allowTranscoding(true)
.build();
SessionProperties propertiesVP9AllowTranscoding = basePropertiesBuilder
.forcedVideoCodec(VideoCodec.VP9)
.allowTranscoding(true)
.build();
SessionProperties propertiesH264AllowTranscoding = basePropertiesBuilder.forcedVideoCodec(VideoCodec.H264)
.allowTranscoding(true).build();
SessionProperties propertiesVP9AllowTranscoding = basePropertiesBuilder.forcedVideoCodec(VideoCodec.VP9)
.allowTranscoding(true).build();
Session sessionDefaultCodec = OV.createSession(propertiesDefaultCodec);
Session sessionH264AllowTranscoding = OV.createSession(propertiesH264AllowTranscoding);
Session sessionVP9AllowTranscoding = OV.createSession(propertiesVP9AllowTranscoding);
assertTranscodingSessionProperties(sessionDefaultCodec, sessionH264AllowTranscoding, sessionVP9AllowTranscoding);
assertTranscodingSessionProperties(sessionDefaultCodec, sessionH264AllowTranscoding,
sessionVP9AllowTranscoding);
// Fetch sessions
Assert.assertFalse(sessionDefaultCodec.fetch());
@ -2639,13 +2637,15 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
Assert.assertFalse(sessionVP9AllowTranscoding.fetch());
// Check transcoding session properties
assertTranscodingSessionProperties(sessionDefaultCodec, sessionH264AllowTranscoding, sessionVP9AllowTranscoding);
assertTranscodingSessionProperties(sessionDefaultCodec, sessionH264AllowTranscoding,
sessionVP9AllowTranscoding);
// Fetch all sessions
Assert.assertFalse(OV.fetch());
// Check transcoding session properties
assertTranscodingSessionProperties(sessionDefaultCodec, sessionH264AllowTranscoding, sessionVP9AllowTranscoding);
assertTranscodingSessionProperties(sessionDefaultCodec, sessionH264AllowTranscoding,
sessionVP9AllowTranscoding);
sessionDefaultCodec.close();
sessionH264AllowTranscoding.close();
@ -3101,8 +3101,12 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
// Check all sessions data
res = restClient.rest(HttpMethod.GET, "/openvidu/api/sessions", HttpStatus.SC_OK);
Assert.assertEquals(res.get("numberOfElements").getAsInt(), 1);
Assert.assertEquals(VideoCodec.valueOf(res.get("content").getAsJsonArray().get(0).getAsJsonObject().get("forcedVideoCodec").getAsString()), defaultForcedVideoCodec);
Assert.assertEquals(res.get("content").getAsJsonArray().get(0).getAsJsonObject().get("allowTranscoding").getAsBoolean(), defaultAllowTranscoding);
Assert.assertEquals(VideoCodec.valueOf(
res.get("content").getAsJsonArray().get(0).getAsJsonObject().get("forcedVideoCodec").getAsString()),
defaultForcedVideoCodec);
Assert.assertEquals(
res.get("content").getAsJsonArray().get(0).getAsJsonObject().get("allowTranscoding").getAsBoolean(),
defaultAllowTranscoding);
// Remove session
restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID", HttpStatus.SC_NO_CONTENT);
@ -3117,8 +3121,12 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
// Check all sessions data
res = restClient.rest(HttpMethod.GET, "/openvidu/api/sessions", HttpStatus.SC_OK);
Assert.assertEquals(res.get("numberOfElements").getAsInt(), 1);
Assert.assertEquals(VideoCodec.valueOf(res.get("content").getAsJsonArray().get(0).getAsJsonObject().get("forcedVideoCodec").getAsString()), VideoCodec.H264);
Assert.assertEquals(res.get("content").getAsJsonArray().get(0).getAsJsonObject().get("allowTranscoding").getAsBoolean(), true);
Assert.assertEquals(VideoCodec.valueOf(
res.get("content").getAsJsonArray().get(0).getAsJsonObject().get("forcedVideoCodec").getAsString()),
VideoCodec.H264);
Assert.assertEquals(
res.get("content").getAsJsonArray().get(0).getAsJsonObject().get("allowTranscoding").getAsBoolean(),
true);
restClient.rest(HttpMethod.DELETE, "/openvidu/api/sessions/CUSTOM_SESSION_ID", HttpStatus.SC_NO_CONTENT);
}
@ -3248,7 +3256,8 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
Assert.assertTrue("Recording duration exceeds valid value. Expected no more than 0.2 seconds, got "
+ differenceInDuration, differenceInDuration < 0.2);
this.checkIndividualRecording("/opt/openvidu/recordings/TestSession/", rec, 1, "opus", "vp8", true);
this.recordingUtils.checkIndividualRecording("/opt/openvidu/recordings/TestSession/", rec, 1, "opus", "vp8",
true);
WebElement pubBtn = user.getDriver().findElements(By.cssSelector("#openvidu-instance-1 .pub-btn")).get(0);
pubBtn.click();
@ -3672,7 +3681,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
CustomWebhook.waitForEvent("recordingStatusChanged", 1); // Ready
Recording recording = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET).getRecording(recId);
this.checkIndividualRecording(recPath + recId + "/", recording, 1, "opus", "vp8", true);
this.recordingUtils.checkIndividualRecording(recPath + recId + "/", recording, 1, "opus", "vp8", true);
// Test IPCAM individual recording (IPCAM video only, recording audio and video)
@ -4042,8 +4051,11 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
/**
* Test to force specified codec and allowTranscoding
* @param codec codec to force. If null, default value in openvidu config will be used.
* @param allowTranscoding If true, allow transcoding. If null, default value in openvidu config will be used.
*
* @param codec codec to force. If null, default value in openvidu
* config will be used.
* @param allowTranscoding If true, allow transcoding. If null, default value in
* openvidu config will be used.
*/
private void forceCodecGenericE2eTest(VideoCodec codec, Boolean allowTranscoding) throws Exception {
CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, "OPENVIDUAPP", OPENVIDU_SECRET);
@ -4084,9 +4096,13 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
// Load properties from session object of node-client
user.getDriver().findElement(By.id("session-info-btn-0")).click();
JsonObject res = JsonParser.parseString(user.getDriver().findElement(By.id("session-text-area")).getAttribute("value")).getAsJsonObject();
VideoCodec sessionCodecNodeClient = VideoCodec.valueOf(res.get("properties").getAsJsonObject().get("forcedVideoCodec").getAsString());
boolean sessionAllowTranscodingNodeClient = res.get("properties").getAsJsonObject().get("allowTranscoding").getAsBoolean();
JsonObject res = JsonParser
.parseString(user.getDriver().findElement(By.id("session-text-area")).getAttribute("value"))
.getAsJsonObject();
VideoCodec sessionCodecNodeClient = VideoCodec
.valueOf(res.get("properties").getAsJsonObject().get("forcedVideoCodec").getAsString());
boolean sessionAllowTranscodingNodeClient = res.get("properties").getAsJsonObject().get("allowTranscoding")
.getAsBoolean();
user.getDriver().findElement(By.id("close-dialog-btn")).click();
final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size();
@ -4128,6 +4144,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
/**
* Force codec not allowed by opened browser
*
* @throws Exception
*/
private void forceNotSupportedCodec(VideoCodec codec, boolean allowTranscoding) throws Exception {
@ -4164,7 +4181,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
sessionName2.sendKeys(sessionName);
List<WebElement> joinButtons = user.getDriver().findElements(By.className("join-btn"));
for(WebElement el : joinButtons) {
for (WebElement el : joinButtons) {
Thread.sleep(5000);
el.sendKeys(Keys.ENTER);
}
@ -4193,18 +4210,25 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest {
Thread.sleep(1000);
}
private void assertTranscodingSessionProperties(Session sessionDefaultCodec, Session sessionH264AllowTranscoding, Session sessionVP9AllowTranscoding) {
private void assertTranscodingSessionProperties(Session sessionDefaultCodec, Session sessionH264AllowTranscoding,
Session sessionVP9AllowTranscoding) {
// Check session with default transcoding params
Assert.assertEquals("Wrong default forcedVideoCodec", defaultForcedVideoCodec, sessionDefaultCodec.getProperties().forcedVideoCodec());
Assert.assertEquals("Wrong default allowTranscoding", defaultAllowTranscoding, sessionDefaultCodec.getProperties().isTranscodingAllowed());
Assert.assertEquals("Wrong default forcedVideoCodec", defaultForcedVideoCodec,
sessionDefaultCodec.getProperties().forcedVideoCodec());
Assert.assertEquals("Wrong default allowTranscoding", defaultAllowTranscoding,
sessionDefaultCodec.getProperties().isTranscodingAllowed());
// Check session which use H264 and allow transcoding
Assert.assertEquals("Wrong default forcedVideoCodec", VideoCodec.H264, sessionH264AllowTranscoding.getProperties().forcedVideoCodec());
Assert.assertEquals("Wrong default allowTranscoding", true, sessionH264AllowTranscoding.getProperties().isTranscodingAllowed());
Assert.assertEquals("Wrong default forcedVideoCodec", VideoCodec.H264,
sessionH264AllowTranscoding.getProperties().forcedVideoCodec());
Assert.assertEquals("Wrong default allowTranscoding", true,
sessionH264AllowTranscoding.getProperties().isTranscodingAllowed());
// Check session which use VP9 and allow transcoding
Assert.assertEquals("Wrong default forcedVideoCodec", VideoCodec.VP9, sessionVP9AllowTranscoding.getProperties().forcedVideoCodec());
Assert.assertEquals("Wrong default allowTranscoding", true, sessionVP9AllowTranscoding.getProperties().isTranscodingAllowed());
Assert.assertEquals("Wrong default forcedVideoCodec", VideoCodec.VP9,
sessionVP9AllowTranscoding.getProperties().forcedVideoCodec());
Assert.assertEquals("Wrong default allowTranscoding", true,
sessionVP9AllowTranscoding.getProperties().isTranscodingAllowed());
}
}