Update PixelCopySurfaceCapturerV24, uses only 1 copy step.

Currently, we are using 2 copy steps for PixelCopySurfaceCapturerV24:
- Copy from the source surface to a similarly sized bitmap (using PixelCopy API).
- Copy from the bitmap to a scaled bitmap, based on the given output size, using scaledBitmap API.
This CL merges the 2 steps and uses PixelCopy API to perform copy directly from
the source surface to the bitmap of the given output size.
However, since our test uses scaledBitmap API to create reference bitmap from original image, due to various Bitmap copy optimizations (filtering, antialiasing etc...) that are different between PixelCopy API and scaledBitmap API implementations, the result images cannot be the same. We can perform the our tests by asserting that their PSNR value is very high, which means they are very similar.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=199472005
This commit is contained in:
hoangtc 2018-06-06 08:50:07 -07:00 committed by Oliver Woodman
parent 50c50a4635
commit a0b4e58312
2 changed files with 53 additions and 84 deletions

View File

@ -1,84 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.surfacecapturer;
import android.graphics.Bitmap;
import android.view.Surface;
/**
* A surface capturer, which captures image drawn into its surface as bitmaps.
*
* <p>It constructs a {@link Surface}, which can be used as the output surface for an image producer
* to draw images to. As images are being drawn into this surface, this capturer will capture these
* images, and return them via {@link Callback}. The output images will have a fixed frame size of
* (width, height), and any image drawn into the surface will be stretched to fit this frame size.
*/
public abstract class SurfaceCapturer {
/** The callback to be notified of the image capturing result. */
public interface Callback {
/**
* Called when the surface capturer has been able to capture its surface into a {@link Bitmap}.
* This will happen whenever the producer updates the image on the wrapped surface.
*/
void onSurfaceCaptured(Bitmap bitmap);
/** Called when the surface capturer couldn't capture its surface due to an error. */
void onSurfaceCaptureError(Exception e);
}
/** The callback to be notified of the image capturing result. */
private final Callback callback;
/** The width of the output images. */
private final int outputWidth;
/** The height of the output images. */
private final int outputHeight;
/**
* Constructs a new instance.
*
* @param callback See {@link #callback}.
* @param outputWidth See {@link #outputWidth}.
* @param outputHeight See {@link #outputHeight}.
*/
protected SurfaceCapturer(Callback callback, int outputWidth, int outputHeight) {
this.callback = callback;
this.outputWidth = outputWidth;
this.outputHeight = outputHeight;
}
/** Returns the callback to be notified of the image capturing result. */
protected Callback getCallback() {
return callback;
}
/** Returns the width of the output images. */
public int getOutputWidth() {
return outputWidth;
}
/** Returns the height of the output images. */
public int getOutputHeight() {
return outputHeight;
}
/** Returns a {@link Surface} that image producers (camera, video codec etc...) can draw to. */
public abstract Surface getSurface();
/** Releases all kept resources. This instance cannot be used after this call. */
public abstract void release();
}

View File

@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException;
@ -166,4 +168,55 @@ public class TestUtil {
}
}
/**
* Asserts whether actual bitmap is very similar to the expected bitmap at some quality level.
*
* <p>This is defined as their PSNR value is greater than or equal to the threshold. The higher
* the threshold, the more similar they are.
*
* @param expectedBitmap The expected bitmap.
* @param actualBitmap The actual bitmap.
* @param psnrThresholdDb The PSNR threshold (in dB), at or above which bitmaps are considered
* very similar.
*/
public static void assertBitmapsAreSimilar(
Bitmap expectedBitmap, Bitmap actualBitmap, double psnrThresholdDb) {
assertThat(getPsnr(expectedBitmap, actualBitmap) >= psnrThresholdDb).isTrue();
}
/**
* Calculates the Peak-Signal-to-Noise-Ratio value for 2 bitmaps.
*
* <p>It is calculated as the logarithmic decibel(dB) value of the ratio between square of peak
* R/G/B values (255.0 * 255.0), and the average mean-squared-error of R/G/B values from the two
* bitmaps. The higher the value, the more similar they are.
*
* @param firstBitmap The first bitmap.
* @param secondBitmap The second bitmap.
* @return The PSNR value calculated from these 2 bitmaps.
*/
private static double getPsnr(Bitmap firstBitmap, Bitmap secondBitmap) {
assertThat(firstBitmap.getWidth()).isEqualTo(secondBitmap.getWidth());
assertThat(firstBitmap.getHeight()).isEqualTo(secondBitmap.getHeight());
double mse = 0;
for (int i = 0; i < firstBitmap.getWidth(); i++) {
for (int j = 0; j < firstBitmap.getHeight(); j++) {
int firstColorInt = firstBitmap.getPixel(i, j);
double firstRed = Color.red(firstColorInt);
double firstGreen = Color.green(firstColorInt);
double firstBlue = Color.blue(firstColorInt);
int secondColorInt = secondBitmap.getPixel(i, j);
double secondRed = Color.red(secondColorInt);
double secondGreen = Color.green(secondColorInt);
double secondBlue = Color.blue(secondColorInt);
mse +=
((firstRed - secondRed) * (firstRed - secondRed)
+ (firstGreen - secondGreen) * (firstGreen - secondGreen)
+ (firstBlue - secondBlue) * (firstBlue - secondBlue))
/ 3.0;
}
}
mse = mse / (firstBitmap.getWidth() * firstBitmap.getHeight());
return 10 * Math.log10(255.0 * 255.0 / mse);
}
}