merge in nyc-release history after reset to nyc-dev
diff --git a/tests/tests/media/assets/520x360h264decodertest.mp4 b/tests/tests/media/assets/520x360h264decodertest.mp4
new file mode 100644
index 0000000..362568c
--- /dev/null
+++ b/tests/tests/media/assets/520x360h264decodertest.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/h264decodertest520x360golden.png b/tests/tests/media/res/raw/h264decodertest520x360golden.png
new file mode 100644
index 0000000..fe4663d
--- /dev/null
+++ b/tests/tests/media/res/raw/h264decodertest520x360golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/h264decodertest520x360original.png b/tests/tests/media/res/raw/h264decodertest520x360original.png
new file mode 100644
index 0000000..c0fd752
--- /dev/null
+++ b/tests/tests/media/res/raw/h264decodertest520x360original.png
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java b/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java
index 3f4dde0..0a3165b 100644
--- a/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java
+++ b/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java
@@ -19,98 +19,123 @@
import android.annotation.TargetApi;
import android.content.Context;
-import android.cts.util.MediaUtils;
import android.graphics.Bitmap;
-import android.os.Build;
-import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.View;
-import android.view.WindowManager;
-@TargetApi(16)
+@TargetApi(24)
public class DecodeAccuracyTest extends DecodeAccuracyTestBase {
private static final String TAG = DecodeAccuracyTest.class.getSimpleName();
private static final String H264_VIDEO_FILE_NAME = "480ph264decodertest.mp4";
private static final String VP9_VIDEO_FILE_NAME = "360pvp9decodertest.webm";
+ private static final String H264_CROPPED_VIDEO_FILE_NAME = "520x360h264decodertest.mp4";
private static final int ALLOWED_GREATEST_PIXEL_DIFFERENCE = 90;
private static final int OFFSET = 10;
/* <------------- Tests Using H264 -------------> */
public void testH264GLViewVideoDecode() throws Exception {
- runDecodeAccuracyTest(
+ runH264DecodeAccuracyTest(
new GLSurfaceViewFactory(),
new VideoFormat(H264_VIDEO_FILE_NAME));
}
public void testH264GLViewLargerHeightVideoDecode() throws Exception {
- runDecodeAccuracyTest(
+ runH264DecodeAccuracyTest(
new GLSurfaceViewFactory(),
getLargerHeightVideoFormat(new VideoFormat(H264_VIDEO_FILE_NAME)));
}
public void testH264GLViewLargerWidthVideoDecode() throws Exception {
- runDecodeAccuracyTest(
+ runH264DecodeAccuracyTest(
new GLSurfaceViewFactory(),
getLargerWidthVideoFormat(new VideoFormat(H264_VIDEO_FILE_NAME)));
}
public void testH264SurfaceViewVideoDecode() throws Exception {
- runDecodeAccuracyTest(
+ runH264DecodeAccuracyTest(
new SurfaceViewFactory(),
new VideoFormat(H264_VIDEO_FILE_NAME));
}
public void testH264SurfaceViewLargerHeightVideoDecode() throws Exception {
- runDecodeAccuracyTest(
+ runH264DecodeAccuracyTest(
new SurfaceViewFactory(),
getLargerHeightVideoFormat(new VideoFormat(H264_VIDEO_FILE_NAME)));
}
public void testH264SurfaceViewLargerWidthVideoDecode() throws Exception {
- runDecodeAccuracyTest(
+ runH264DecodeAccuracyTest(
new SurfaceViewFactory(),
getLargerWidthVideoFormat(new VideoFormat(H264_VIDEO_FILE_NAME)));
}
/* <------------- Tests Using VP9 -------------> */
public void testVP9GLViewVideoDecode() throws Exception {
- runDecodeAccuracyTest(
+ runVP9DecodeAccuracyTest(
new GLSurfaceViewFactory(),
new VideoFormat(VP9_VIDEO_FILE_NAME));
}
public void testVP9GLViewLargerHeightVideoDecode() throws Exception {
- runDecodeAccuracyTest(
+ runVP9DecodeAccuracyTest(
new GLSurfaceViewFactory(),
getLargerHeightVideoFormat(new VideoFormat(VP9_VIDEO_FILE_NAME)));
}
public void testVP9GLViewLargerWidthVideoDecode() throws Exception {
- runDecodeAccuracyTest(
+ runVP9DecodeAccuracyTest(
new GLSurfaceViewFactory(),
getLargerWidthVideoFormat(new VideoFormat(VP9_VIDEO_FILE_NAME)));
}
public void testVP9SurfaceViewVideoDecode() throws Exception {
- runDecodeAccuracyTest(
+ runVP9DecodeAccuracyTest(
new SurfaceViewFactory(),
new VideoFormat(VP9_VIDEO_FILE_NAME));
}
public void testVP9SurfaceViewLargerHeightVideoDecode() throws Exception {
- runDecodeAccuracyTest(
+ runVP9DecodeAccuracyTest(
new SurfaceViewFactory(),
getLargerHeightVideoFormat(new VideoFormat(VP9_VIDEO_FILE_NAME)));
}
public void testVP9SurfaceViewLargerWidthVideoDecode() throws Exception {
- runDecodeAccuracyTest(
+ runVP9DecodeAccuracyTest(
new SurfaceViewFactory(),
getLargerWidthVideoFormat(new VideoFormat(VP9_VIDEO_FILE_NAME)));
}
- private void runDecodeAccuracyTest(VideoViewFactory videoViewFactory, VideoFormat videoFormat) {
+ /* <------------- Tests H264 with cropping -------------> */
+ public void testH264GLViewCroppedVideoDecode() throws Exception {
+ runH264DecodeCroppedTest(
+ new GLSurfaceViewFactory(),
+ new VideoFormat(H264_CROPPED_VIDEO_FILE_NAME));
+ }
+
+ public void testH264SurfaceViewCroppedVideoDecode() throws Exception {
+ runH264DecodeCroppedTest(
+ new SurfaceViewFactory(),
+ new VideoFormat(H264_CROPPED_VIDEO_FILE_NAME));
+ }
+
+ private void runH264DecodeAccuracyTest(
+ VideoViewFactory videoViewFactory, VideoFormat videoFormat) {
+ runDecodeAccuracyTest(videoViewFactory, videoFormat, R.raw.h264decodertestgolden);
+ }
+
+ private void runVP9DecodeAccuracyTest(
+ VideoViewFactory videoViewFactory, VideoFormat videoFormat) {
+ runDecodeAccuracyTest(videoViewFactory, videoFormat, R.raw.vp9decodertestgolden);
+ }
+
+ private void runH264DecodeCroppedTest(
+ VideoViewFactory videoViewFactory, VideoFormat videoFormat) {
+ runDecodeAccuracyTest(videoViewFactory, videoFormat, R.raw.h264decodertest520x360golden);
+ }
+
+ private void runDecodeAccuracyTest(
+ VideoViewFactory videoViewFactory, VideoFormat videoFormat, int goldenResId) {
checkNotNull(videoViewFactory);
checkNotNull(videoFormat);
View videoView = videoViewFactory.createView(getHelper().getContext());
@@ -123,7 +148,7 @@
// so it needs to be created before start decoding.
final VideoViewSnapshot videoViewSnapshot = videoViewFactory.getVideoViewSnapshot();
decodeVideo(videoFormat, videoViewFactory);
- validateResult(videoFormat, videoViewSnapshot);
+ validateResult(videoFormat, videoViewSnapshot, goldenResId);
if (videoView != null) {
getHelper().cleanUpView(videoView);
@@ -140,20 +165,16 @@
assertTrue("Failed to decode the video.", playerResult.isSuccess());
}
- private void validateResult(VideoFormat videoFormat, VideoViewSnapshot videoViewSnapshot) {
+ private void validateResult(
+ VideoFormat videoFormat, VideoViewSnapshot videoViewSnapshot, int goldenResId) {
final Bitmap result = getHelper().generateBitmapFromVideoViewSnapshot(videoViewSnapshot);
- final Bitmap golden;
- final String mime = videoFormat.getMimeType();
- if (mime.equals(MimeTypes.VIDEO_H264)) {
- golden = getHelper().generateBitmapFromImageResourceId(R.raw.h264decodertestgolden);
- } else if (mime.equals(MimeTypes.VIDEO_VP9)) {
- golden = getHelper().generateBitmapFromImageResourceId(R.raw.vp9decodertestgolden);
- } else {
- fail("Unsupported MIME type " + mime);
- return;
- }
- final BitmapCompare.Difference difference = BitmapCompare.computeDifference(golden, result);
- assertTrue("Greatest pixel difference is "
+ final Bitmap golden = getHelper().generateBitmapFromImageResourceId(goldenResId);
+ final BitmapCompare.Difference difference = BitmapCompare.computeMinimumDifference(
+ result, golden, videoFormat.getOriginalWidth(), videoFormat.getOriginalHeight());
+ assertTrue("With the best matched border crop ("
+ + difference.bestMatchBorderCrop.first + ", "
+ + difference.bestMatchBorderCrop.second + "), "
+ + "greatest pixel difference is "
+ difference.greatestPixelDifference
+ (difference.greatestPixelDifferenceCoordinates != null
? " at (" + difference.greatestPixelDifferenceCoordinates.first + ", "
diff --git a/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java b/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java
index 3e3c866f..47029d6b 100644
--- a/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java
+++ b/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java
@@ -617,6 +617,7 @@
/**
* Factory for building a {@link SurfaceView}
*/
+@TargetApi(24)
class SurfaceViewFactory extends VideoViewFactory implements SurfaceHolder.Callback {
private static final String TAG = SurfaceViewFactory.class.getSimpleName();
@@ -1294,6 +1295,8 @@
private int height = UNSET;
private int maxWidth = UNSET;
private int maxHeight = UNSET;
+ private int originalWidth = UNSET;
+ private int originalHeight = UNSET;
public VideoFormat(String filename, Uri uri) {
this.filename = filename;
@@ -1331,6 +1334,9 @@
public void setWidth(int width) {
this.width = width;
+ if (this.originalWidth == UNSET) {
+ this.originalWidth = width;
+ }
}
public void setMaxWidth(int maxWidth) {
@@ -1345,8 +1351,15 @@
return maxWidth;
}
+ public int getOriginalWidth() {
+ return originalWidth;
+ }
+
public void setHeight(int height) {
this.height = height;
+ if (this.originalHeight == UNSET) {
+ this.originalHeight = height;
+ }
}
public void setMaxHeight(int maxHeight) {
@@ -1361,6 +1374,10 @@
return maxHeight;
}
+ public int getOriginalHeight() {
+ return originalHeight;
+ }
+
private Uri createCacheFile(Context context) {
try {
File cacheFile = new File(context.getCacheDir(), filename);
@@ -1563,22 +1580,167 @@
return (int) Math.round(Math.sqrt(result));
}
+ /**
+ * Crops the border of the array representing an image by hBorderSize
+ * pixels on the left and right borders, and by vBorderSize pixels on the
+ * top and bottom borders (so the width is 2 * hBorderSize smaller and
+ * the height is 2 * vBorderSize smaller), then scales the image up to
+ * match the original size using bilinear interpolation.
+ */
+ private static Bitmap shrinkAndScaleBilinear(
+ Bitmap input, double hBorderSize, double vBorderSize) {
+
+ int width = input.getWidth();
+ int height = input.getHeight();
+
+ // Compute the proper step sizes
+ double xInc = ((double) width - 1 - hBorderSize * 2) / (double) (width - 1);
+ double yInc = ((double) height - 1 - vBorderSize * 2) / (double) (height - 1);
+
+ // Read the input bitmap into RGB arrays.
+ int[] inputPixels = new int[width * height];
+ input.getPixels(inputPixels, 0, width, 0, 0, width, height);
+ int[][] inputRgb = new int[width * height][3];
+ for (int i = 0; i < width * height; ++i) {
+ inputRgb[i][0] = Color.red(inputPixels[i]);
+ inputRgb[i][1] = Color.green(inputPixels[i]);
+ inputRgb[i][2] = Color.blue(inputPixels[i]);
+ }
+ inputPixels = null;
+
+ // Prepare the output buffer.
+ int[] outputPixels = new int[width * height];
+
+ // Start the iteration. The first y coordinate is vBorderSize.
+ double y = vBorderSize;
+ for (int yIndex = 0; yIndex < height; ++yIndex) {
+ // The first x coordinate is hBorderSize.
+ double x = hBorderSize;
+ for (int xIndex = 0; xIndex < width; ++xIndex) {
+ // Determine the square of interest.
+ int left = (int)x; // This is floor(x).
+ int top = (int)y; // This is floor(y).
+ int right = left + 1;
+ int bottom = top + 1;
+
+ // (u, v) is the fractional part of (x, y).
+ double u = x - (double)left;
+ double v = y - (double)top;
+
+ // Precompute necessary products to save time.
+ double p00 = (1.0 - u) * (1.0 - v);
+ double p01 = (1.0 - u) * v;
+ double p10 = u * (1.0 - v);
+ double p11 = u * v;
+
+ // Clamp the indices to prevent out-of-bound that may be caused
+ // by round-off error.
+ if (left >= width) left = width - 1;
+ if (top >= height) top = height - 1;
+ if (right >= width) right = width - 1;
+ if (bottom >= height) bottom = height - 1;
+
+ // Sample RGB values from the four corners.
+ int[] rgb00 = inputRgb[top * width + left];
+ int[] rgb01 = inputRgb[bottom * width + left];
+ int[] rgb10 = inputRgb[top * width + right];
+ int[] rgb11 = inputRgb[bottom * width + right];
+
+ // Interpolate each component of RGB separately.
+ int[] mixedColor = new int[3];
+ for (int k = 0; k < 3; ++k) {
+ mixedColor[k] = (int)Math.round(
+ p00 * (double) rgb00[k] + p01 * (double) rgb01[k]
+ + p10 * (double) rgb10[k] + p11 * (double) rgb11[k]);
+ }
+ // Convert RGB to bitmap Color format and store.
+ outputPixels[yIndex * width + xIndex] = Color.rgb(
+ mixedColor[0], mixedColor[1], mixedColor[2]);
+ x += xInc;
+ }
+ y += yInc;
+ }
+ // Assemble the output buffer into a Bitmap object.
+ return Bitmap.createBitmap(outputPixels, width, height, input.getConfig());
+ }
+
+ /**
+ * Calls computeDifference on multiple cropped-and-scaled versions of
+ * bitmap2.
+ */
+ @TargetApi(12)
+ public static Difference computeMinimumDifference(
+ Bitmap bitmap1, Bitmap bitmap2, Pair<Double, Double>[] borderCrops) {
+
+ // Compute the difference with the original image (bitmap2) first.
+ Difference minDiff = computeDifference(bitmap1, bitmap2);
+ // Then go through the list of borderCrops.
+ for (Pair<Double, Double> borderCrop : borderCrops) {
+ // Compute the difference between bitmap1 and a transformed
+ // version of bitmap2.
+ Bitmap bitmap2s = shrinkAndScaleBilinear(bitmap2, borderCrop.first, borderCrop.second);
+ Difference d = computeDifference(bitmap1, bitmap2s);
+ // Keep the minimum difference.
+ if (d.greatestPixelDifference < minDiff.greatestPixelDifference) {
+ minDiff = d;
+ minDiff.bestMatchBorderCrop = borderCrop;
+ }
+ }
+ return minDiff;
+ }
+
+ /**
+ * Calls computeMinimumDifference on a default list of borderCrop.
+ */
+ @TargetApi(12)
+ public static Difference computeMinimumDifference(
+ Bitmap bitmap1, Bitmap bitmap2, int trueWidth, int trueHeight) {
+
+ double hBorder = (double) bitmap1.getWidth() / (double) trueWidth;
+ double vBorder = (double) bitmap1.getHeight() / (double) trueHeight;
+ double hBorderH = 0.5 * hBorder; // Half-texel horizontal border
+ double vBorderH = 0.5 * vBorder; // Half-texel vertical border
+ return computeMinimumDifference(
+ bitmap1,
+ bitmap2,
+ new Pair[] {
+ Pair.create(hBorderH, 0.0),
+ Pair.create(hBorderH, vBorderH),
+ Pair.create(0.0, vBorderH),
+ Pair.create(hBorder, 0.0),
+ Pair.create(hBorder, vBorder),
+ Pair.create(0.0, vBorder)
+ });
+ // This default list of borderCrop comes from the behavior of
+ // GLConsumer.computeTransformMatrix().
+ }
+
/* Describes the difference between two {@link Bitmap} instances. */
public static final class Difference {
public final int greatestPixelDifference;
public final Pair<Integer, Integer> greatestPixelDifferenceCoordinates;
+ public Pair<Double, Double> bestMatchBorderCrop;
private Difference(int greatestPixelDifference) {
- this(greatestPixelDifference, null);
+ this(greatestPixelDifference, null, Pair.create(0.0, 0.0));
}
private Difference(
int greatestPixelDifference,
Pair<Integer, Integer> greatestPixelDifferenceCoordinates) {
+ this(greatestPixelDifference, greatestPixelDifferenceCoordinates,
+ Pair.create(0.0, 0.0));
+ }
+
+ private Difference(
+ int greatestPixelDifference,
+ Pair<Integer, Integer> greatestPixelDifferenceCoordinates,
+ Pair<Double, Double> bestMatchBorderCrop) {
this.greatestPixelDifference = greatestPixelDifference;
this.greatestPixelDifferenceCoordinates = greatestPixelDifferenceCoordinates;
- }
+ this.bestMatchBorderCrop = bestMatchBorderCrop;
+ }
}
}