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 transformer The {@link Transformer} that performs the transformation.
* @param uriString The uri (as a {@link String}) that will be transformed. * @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 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}. * @return The {@link TestTransformationResult}.
* @throws Exception The cause of the transformation not completing. * @throws Exception The cause of the transformation not completing.
*/ */
public static TestTransformationResult runTransformer( 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 { throws Exception {
JSONObject resultJson = new JSONObject(); JSONObject resultJson = new JSONObject();
try { try {
TestTransformationResult testTransformationResult = TestTransformationResult testTransformationResult =
runTransformerInternal(context, testId, transformer, uriString, timeoutSeconds); runTransformerInternal(
context, testId, transformer, uriString, timeoutSeconds, calculateSsim);
resultJson.put( resultJson.put(
"transformationResult", "transformationResult",
getTransformationResultJson(testTransformationResult.transformationResult)); getTransformationResultJson(testTransformationResult.transformationResult));
if (testTransformationResult.ssim != TestTransformationResult.SSIM_UNSET) {
resultJson.put("ssim", testTransformationResult.ssim);
}
return testTransformationResult; return testTransformationResult;
} catch (Exception e) { } catch (Exception e) {
resultJson.put("exception", getExceptionJson(e)); resultJson.put("exception", getExceptionJson(e));
@ -81,7 +94,12 @@ public final class AndroidTestUtil {
} }
private static TestTransformationResult runTransformerInternal( 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 { throws Exception {
AtomicReference<@NullableType TransformationException> transformationExceptionReference = AtomicReference<@NullableType TransformationException> transformationExceptionReference =
new AtomicReference<>(); new AtomicReference<>();
@ -150,7 +168,15 @@ public final class AndroidTestUtil {
.setFileSizeBytes(outputVideoFile.length()) .setFileSizeBytes(outputVideoFile.length())
.build(); .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) 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. * 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 context The {@link Context}.
* @param expectedVideoPath The path to the expected video file, must be in {@link * @param expectedVideoPath The path to the expected video file, must be in {@link
* Context#getAssets() Assets}. * Context#getAssets() Assets}.
* @param actualVideoPath The path to the actual video file. * @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. * @throws IOException When unable to open the provided video paths.
*/ */
public static double calculate( public static double calculate(Context context, String expectedVideoPath, String actualVideoPath)
Context context, String expectedVideoPath, String actualVideoPath, int comparisonInterval)
throws IOException, InterruptedException { throws IOException, InterruptedException {
return new SsimHelper(context, expectedVideoPath, actualVideoPath, comparisonInterval) return new SsimHelper(context, expectedVideoPath, actualVideoPath, DEFAULT_COMPARISON_INTERVAL)
.startCalculation(); .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}. */ /** A test only class for holding additional details alongside a {@link TransformationResult}. */
public class TestTransformationResult { public class TestTransformationResult {
/** Represents an unset or unknown SSIM score. */
public static final double SSIM_UNSET = -1.0d;
public final TransformationResult transformationResult; public final TransformationResult transformationResult;
public final String filePath; 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) { public TestTransformationResult(TransformationResult transformationResult, String filePath) {
this(transformationResult, filePath, /* ssim= */ SSIM_UNSET);
}
public TestTransformationResult(
TransformationResult transformationResult, String filePath, double ssim) {
this.transformationResult = transformationResult; this.transformationResult = transformationResult;
this.filePath = filePath; this.filePath = filePath;
this.ssim = ssim;
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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