Add SSIM support to AndroidTestUtil.

PiperOrigin-RevId: 431479473
This commit is contained in:
claincly 2022-02-28 19:08:46 +00:00 committed by Ian Baker
parent 651efecf75
commit 85512f66e6
11 changed files with 68 additions and 28 deletions

View File

@ -58,19 +58,32 @@ public final class AndroidTestUtil {
* @param transformer The {@link Transformer} that performs the transformation.
* @param uriString The uri (as a {@link String}) that will be transformed.
* @param timeoutSeconds The transformer timeout. An exception is thrown if this is exceeded.
* @param calculateSsim Whether to include SSIM in the {@link TestTransformationResult}. The
* calculation involves decoding and comparing both the input and the output video.
* Consequently this calculation is not cost-free. Requires the input and output video to be
* the same size.
* @return The {@link TestTransformationResult}.
* @throws Exception The cause of the transformation not completing.
*/
public static TestTransformationResult runTransformer(
Context context, String testId, Transformer transformer, String uriString, int timeoutSeconds)
Context context,
String testId,
Transformer transformer,
String uriString,
int timeoutSeconds,
boolean calculateSsim)
throws Exception {
JSONObject resultJson = new JSONObject();
try {
TestTransformationResult testTransformationResult =
runTransformerInternal(context, testId, transformer, uriString, timeoutSeconds);
runTransformerInternal(
context, testId, transformer, uriString, timeoutSeconds, calculateSsim);
resultJson.put(
"transformationResult",
getTransformationResultJson(testTransformationResult.transformationResult));
if (testTransformationResult.ssim != TestTransformationResult.SSIM_UNSET) {
resultJson.put("ssim", testTransformationResult.ssim);
}
return testTransformationResult;
} catch (Exception e) {
resultJson.put("exception", getExceptionJson(e));
@ -81,7 +94,12 @@ public final class AndroidTestUtil {
}
private static TestTransformationResult runTransformerInternal(
Context context, String testId, Transformer transformer, String uriString, int timeoutSeconds)
Context context,
String testId,
Transformer transformer,
String uriString,
int timeoutSeconds,
boolean calculateSsim)
throws Exception {
AtomicReference<@NullableType TransformationException> transformationExceptionReference =
new AtomicReference<>();
@ -150,7 +168,15 @@ public final class AndroidTestUtil {
.setFileSizeBytes(outputVideoFile.length())
.build();
return new TestTransformationResult(transformationResult, outputVideoFile.getPath());
if (calculateSsim) {
return new TestTransformationResult(
transformationResult,
outputVideoFile.getPath(),
SsimHelper.calculate(
context, /* expectedVideoPath= */ uriString, outputVideoFile.getPath()));
} else {
return new TestTransformationResult(transformationResult, outputVideoFile.getPath());
}
}
private static void writeTestSummaryToFile(Context context, String testId, JSONObject resultJson)

View File

@ -82,18 +82,17 @@ public final class SsimHelper {
/**
* Returns the mean SSIM score between the expected and the actual video.
*
* <p>The method compares every {@link #DEFAULT_COMPARISON_INTERVAL n-th} frame from both videos.
*
* @param context The {@link Context}.
* @param expectedVideoPath The path to the expected video file, must be in {@link
* Context#getAssets() Assets}.
* @param actualVideoPath The path to the actual video file.
* @param comparisonInterval The number of frames between the frames selected for comparison by
* SSIM.
* @throws IOException When unable to open the provided video paths.
*/
public static double calculate(
Context context, String expectedVideoPath, String actualVideoPath, int comparisonInterval)
public static double calculate(Context context, String expectedVideoPath, String actualVideoPath)
throws IOException, InterruptedException {
return new SsimHelper(context, expectedVideoPath, actualVideoPath, comparisonInterval)
return new SsimHelper(context, expectedVideoPath, actualVideoPath, DEFAULT_COMPARISON_INTERVAL)
.startCalculation();
}

View File

@ -17,11 +17,22 @@ package com.google.android.exoplayer2.transformer;
/** A test only class for holding additional details alongside a {@link TransformationResult}. */
public class TestTransformationResult {
/** Represents an unset or unknown SSIM score. */
public static final double SSIM_UNSET = -1.0d;
public final TransformationResult transformationResult;
public final String filePath;
/** The SSIM score of the transformation, {@link #SSIM_UNSET} if unavailable. */
public final double ssim;
public TestTransformationResult(TransformationResult transformationResult, String filePath) {
this(transformationResult, filePath, /* ssim= */ SSIM_UNSET);
}
public TestTransformationResult(
TransformationResult transformationResult, String filePath, double ssim) {
this.transformationResult = transformationResult;
this.filePath = filePath;
this.ssim = ssim;
}
}

View File

@ -59,7 +59,8 @@ public class TransformerEndToEndTest {
/* testId= */ "videoTranscoding_completesWithConsistentFrameCount",
transformer,
VP9_VIDEO_URI_STRING,
/* timeoutSeconds= */ 120);
/* timeoutSeconds= */ 120,
/* calculateSsim= */ false);
FrameCountingMuxer frameCountingMuxer =
checkNotNull(muxerFactory.getLastFrameCountingMuxerCreated());
@ -92,7 +93,8 @@ public class TransformerEndToEndTest {
/* testId= */ "videoEditing_completesWithConsistentFrameCount",
transformer,
AVC_VIDEO_URI_STRING,
/* timeoutSeconds= */ 120);
/* timeoutSeconds= */ 120,
/* calculateSsim= */ false);
FrameCountingMuxer frameCountingMuxer =
checkNotNull(muxerFactory.getLastFrameCountingMuxerCreated());

View File

@ -40,6 +40,7 @@ public class RemoveAudioTransformationTest {
/* testId= */ "removeAudioTransform",
transformer,
MP4_ASSET_WITH_INCREASING_TIMESTAMPS_URI_STRING,
/* timeoutSeconds= */ 120);
/* timeoutSeconds= */ 120,
/* calculateSsim= */ false);
}
}

View File

@ -37,6 +37,7 @@ public class RemoveVideoTransformationTest {
/* testId= */ "removeVideoTransform",
transformer,
MP4_ASSET_URI_STRING,
/* timeoutSeconds= */ 120);
/* timeoutSeconds= */ 120,
/* calculateSsim= */ false);
}
}

View File

@ -62,7 +62,8 @@ public final class RepeatedTranscodeTransformationTest {
/* testId= */ "repeatedTranscode_givesConsistentLengthOutput_" + i,
transformer,
AndroidTestUtil.REMOTE_MP4_10_SECONDS_H264_MP3_URI_STRING,
/* timeoutSeconds= */ 120);
/* timeoutSeconds= */ 120,
/* calculateSsim= */ false);
differentOutputSizesBytes.add(checkNotNull(testResult.transformationResult.fileSizeBytes));
}
@ -96,7 +97,8 @@ public final class RepeatedTranscodeTransformationTest {
/* testId= */ "repeatedTranscodeNoAudio_givesConsistentLengthOutput_" + i,
transformer,
AndroidTestUtil.REMOTE_MP4_10_SECONDS_H264_MP3_URI_STRING,
/* timeoutSeconds= */ 120);
/* timeoutSeconds= */ 120,
/* calculateSsim= */ false);
differentOutputSizesBytes.add(checkNotNull(testResult.transformationResult.fileSizeBytes));
}
@ -125,7 +127,8 @@ public final class RepeatedTranscodeTransformationTest {
/* testId= */ "repeatedTranscodeNoVideo_givesConsistentLengthOutput_" + i,
transformer,
AndroidTestUtil.REMOTE_MP4_10_SECONDS_H264_MP3_URI_STRING,
/* timeoutSeconds= */ 120);
/* timeoutSeconds= */ 120,
/* calculateSsim= */ false);
differentOutputSizesBytes.add(checkNotNull(testResult.transformationResult.fileSizeBytes));
}

View File

@ -53,6 +53,7 @@ public class SefTransformationTest {
/* testId = */ "sefTransform",
transformer,
SEF_ASSET_URI_STRING,
/* timeoutSeconds= */ 120);
/* timeoutSeconds= */ 120,
/* calculateSsim= */ false);
}
}

View File

@ -48,6 +48,7 @@ public class SetTransformationMatrixTransformationTest {
/* testId= */ "setTransformationMatrixTransform",
transformer,
REMOTE_MP4_10_SECONDS_URI_STRING,
/* timeoutSeconds= */ 120);
/* timeoutSeconds= */ 120,
/* calculateSsim= */ false);
}
}

View File

@ -23,7 +23,6 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.transformer.AndroidTestUtil;
import com.google.android.exoplayer2.transformer.SsimHelper;
import com.google.android.exoplayer2.transformer.TestTransformationResult;
import com.google.android.exoplayer2.transformer.TransformationRequest;
import com.google.android.exoplayer2.transformer.Transformer;
@ -52,14 +51,9 @@ public final class TranscodeQualityTest {
/* testId= */ "singleTranscode_ssim",
transformer,
AndroidTestUtil.MP4_ASSET_URI_STRING,
/* timeoutSeconds= */ 120);
/* timeoutSeconds= */ 120,
/* calculateSsim= */ true);
assertThat(
SsimHelper.calculate(
context,
AndroidTestUtil.MP4_ASSET_URI_STRING,
result.filePath,
SsimHelper.DEFAULT_COMPARISON_INTERVAL))
.isGreaterThan(0.95);
assertThat(result.ssim).isGreaterThan(0.95);
}
}

View File

@ -40,6 +40,7 @@ public class TransformationTest {
/* testId= */ "transform",
transformer,
MP4_ASSET_WITH_INCREASING_TIMESTAMPS_URI_STRING,
/* timeoutSeconds= */ 120);
/* timeoutSeconds= */ 120,
/* calculateSsim= */ false);
}
}