Commit 85512f66 by claincly Committed by Ian Baker

Add SSIM support to AndroidTestUtil.

PiperOrigin-RevId: 431479473
parent 651efecf
...@@ -58,19 +58,32 @@ public final class AndroidTestUtil { ...@@ -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 { ...@@ -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 { ...@@ -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)
......
...@@ -82,18 +82,17 @@ public final class SsimHelper { ...@@ -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();
} }
......
...@@ -17,11 +17,22 @@ package com.google.android.exoplayer2.transformer; ...@@ -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;
} }
} }
...@@ -59,7 +59,8 @@ public class TransformerEndToEndTest { ...@@ -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 { ...@@ -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());
......
...@@ -40,6 +40,7 @@ public class RemoveAudioTransformationTest { ...@@ -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);
} }
} }
...@@ -37,6 +37,7 @@ public class RemoveVideoTransformationTest { ...@@ -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);
} }
} }
...@@ -62,7 +62,8 @@ public final class RepeatedTranscodeTransformationTest { ...@@ -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 { ...@@ -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 { ...@@ -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));
} }
......
...@@ -53,6 +53,7 @@ public class SefTransformationTest { ...@@ -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);
} }
} }
...@@ -48,6 +48,7 @@ public class SetTransformationMatrixTransformationTest { ...@@ -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);
} }
} }
...@@ -23,7 +23,6 @@ import android.content.Context; ...@@ -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 { ...@@ -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);
} }
} }
...@@ -40,6 +40,7 @@ public class TransformationTest { ...@@ -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);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment