Commit 934ac13c by huangdarwin Committed by Rohit Singh

HDR: Add HDR passthrough effect pixel tests.

A passthrough effect allows for testing having an intermediate
effect injected, which uses different OpenGL shaders from having no
effects.

PiperOrigin-RevId: 524276991
parent 9f53ce66
...@@ -34,11 +34,13 @@ import android.graphics.Bitmap; ...@@ -34,11 +34,13 @@ import android.graphics.Bitmap;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.view.Surface; import android.view.Surface;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.effect.BitmapOverlay; import com.google.android.exoplayer2.effect.BitmapOverlay;
import com.google.android.exoplayer2.effect.DefaultVideoFrameProcessor; import com.google.android.exoplayer2.effect.DefaultVideoFrameProcessor;
import com.google.android.exoplayer2.effect.GlEffect;
import com.google.android.exoplayer2.effect.GlShaderProgram;
import com.google.android.exoplayer2.effect.OverlayEffect; import com.google.android.exoplayer2.effect.OverlayEffect;
import com.google.android.exoplayer2.effect.ScaleAndRotateTransformation;
import com.google.android.exoplayer2.testutil.BitmapPixelTestUtil; import com.google.android.exoplayer2.testutil.BitmapPixelTestUtil;
import com.google.android.exoplayer2.testutil.VideoFrameProcessorTestRunner; import com.google.android.exoplayer2.testutil.VideoFrameProcessorTestRunner;
import com.google.android.exoplayer2.transformer.AndroidTestUtil; import com.google.android.exoplayer2.transformer.AndroidTestUtil;
...@@ -46,6 +48,7 @@ import com.google.android.exoplayer2.transformer.EncoderUtil; ...@@ -46,6 +48,7 @@ import com.google.android.exoplayer2.transformer.EncoderUtil;
import com.google.android.exoplayer2.util.GlTextureInfo; import com.google.android.exoplayer2.util.GlTextureInfo;
import com.google.android.exoplayer2.util.GlUtil; import com.google.android.exoplayer2.util.GlUtil;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.util.VideoFrameProcessingException;
import com.google.android.exoplayer2.video.ColorInfo; import com.google.android.exoplayer2.video.ColorInfo;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...@@ -81,6 +84,9 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest { ...@@ -81,6 +84,9 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
/** Input HLG video of which we only use the first frame. */ /** Input HLG video of which we only use the first frame. */
private static final String INPUT_HLG10_MP4_ASSET_STRING = "media/mp4/hlg-1080p.mp4"; private static final String INPUT_HLG10_MP4_ASSET_STRING = "media/mp4/hlg-1080p.mp4";
private static final GlEffect NO_OP_EFFECT =
new GlEffectWrapper(new ScaleAndRotateTransformation.Builder().build());
private @MonotonicNonNull VideoFrameProcessorTestRunner videoFrameProcessorTestRunner; private @MonotonicNonNull VideoFrameProcessorTestRunner videoFrameProcessorTestRunner;
@After @After
...@@ -152,17 +158,47 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest { ...@@ -152,17 +158,47 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
context, testId, /* inputFormat= */ format, /* outputFormat= */ null)) { context, testId, /* inputFormat= */ format, /* outputFormat= */ null)) {
return; return;
} }
ColorInfo hlg10ColorInfo = ColorInfo colorInfo = checkNotNull(format.colorInfo);
new ColorInfo.Builder() videoFrameProcessorTestRunner =
.setColorSpace(C.COLOR_SPACE_BT2020) getDefaultFrameProcessorTestRunnerBuilder(testId)
.setColorRange(C.COLOR_RANGE_LIMITED) .setInputColorInfo(colorInfo)
.setColorTransfer(C.COLOR_TRANSFER_HLG) .setOutputColorInfo(colorInfo)
.setVideoAssetPath(INPUT_HLG10_MP4_ASSET_STRING)
.build(); .build();
Bitmap expectedBitmap = readBitmap(ORIGINAL_HLG10_PNG_ASSET_PATH);
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
float averagePixelAbsoluteDifference =
BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceFp16(
expectedBitmap, actualBitmap);
assertThat(averagePixelAbsoluteDifference)
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE_FP16);
}
// A passthrough effect allows for testing having an intermediate effect injected, which uses
// different OpenGL shaders from having no effects.
@Test
public void noOpEffect_hlg10Input_matchesGoldenFile() throws Exception {
String testId = "noOpEffect_hlg10Input_matchesGoldenFile";
Context context = getApplicationContext();
Format format = MP4_ASSET_1080P_5_SECOND_HLG10_FORMAT;
if (!deviceSupportsHdrEditing(format)) {
recordTestSkipped(context, testId, "No HLG editing support");
return;
}
if (AndroidTestUtil.skipAndLogIfFormatsUnsupported(
context, testId, /* inputFormat= */ format, /* outputFormat= */ null)) {
return;
}
ColorInfo colorInfo = checkNotNull(format.colorInfo);
videoFrameProcessorTestRunner = videoFrameProcessorTestRunner =
getDefaultFrameProcessorTestRunnerBuilder(testId) getDefaultFrameProcessorTestRunnerBuilder(testId)
.setInputColorInfo(hlg10ColorInfo) .setInputColorInfo(colorInfo)
.setOutputColorInfo(hlg10ColorInfo) .setOutputColorInfo(colorInfo)
.setVideoAssetPath(INPUT_HLG10_MP4_ASSET_STRING) .setVideoAssetPath(INPUT_HLG10_MP4_ASSET_STRING)
.setEffects(NO_OP_EFFECT)
.build(); .build();
Bitmap expectedBitmap = readBitmap(ORIGINAL_HLG10_PNG_ASSET_PATH); Bitmap expectedBitmap = readBitmap(ORIGINAL_HLG10_PNG_ASSET_PATH);
...@@ -189,17 +225,47 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest { ...@@ -189,17 +225,47 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
context, testId, /* inputFormat= */ format, /* outputFormat= */ null)) { context, testId, /* inputFormat= */ format, /* outputFormat= */ null)) {
return; return;
} }
ColorInfo hdr10ColorInfo = ColorInfo colorInfo = checkNotNull(format.colorInfo);
new ColorInfo.Builder() videoFrameProcessorTestRunner =
.setColorSpace(C.COLOR_SPACE_BT2020) getDefaultFrameProcessorTestRunnerBuilder(testId)
.setColorRange(C.COLOR_RANGE_LIMITED) .setInputColorInfo(colorInfo)
.setColorTransfer(C.COLOR_TRANSFER_ST2084) .setOutputColorInfo(colorInfo)
.setVideoAssetPath(INPUT_PQ_MP4_ASSET_STRING)
.build(); .build();
Bitmap expectedBitmap = readBitmap(ORIGINAL_HDR10_PNG_ASSET_PATH);
Bitmap actualBitmap = videoFrameProcessorTestRunner.processFirstFrameAndEnd();
// TODO(b/207848601): Switch to using proper tooling for testing against golden data.
float averagePixelAbsoluteDifference =
BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceFp16(
expectedBitmap, actualBitmap);
assertThat(averagePixelAbsoluteDifference)
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE_FP16);
}
// A passthrough effect allows for testing having an intermediate effect injected, which uses
// different OpenGL shaders from having no effects.
@Test
public void noOpEffect_hdr10Input_matchesGoldenFile() throws Exception {
String testId = "noOpEffect_hdr10Input_matchesGoldenFile";
Context context = getApplicationContext();
Format format = MP4_ASSET_720P_4_SECOND_HDR10_FORMAT;
if (!deviceSupportsHdrEditing(format)) {
recordTestSkipped(context, testId, "No HLG editing support");
return;
}
if (AndroidTestUtil.skipAndLogIfFormatsUnsupported(
context, testId, /* inputFormat= */ format, /* outputFormat= */ null)) {
return;
}
ColorInfo colorInfo = checkNotNull(format.colorInfo);
videoFrameProcessorTestRunner = videoFrameProcessorTestRunner =
getDefaultFrameProcessorTestRunnerBuilder(testId) getDefaultFrameProcessorTestRunnerBuilder(testId)
.setInputColorInfo(hdr10ColorInfo) .setInputColorInfo(colorInfo)
.setOutputColorInfo(hdr10ColorInfo) .setOutputColorInfo(colorInfo)
.setVideoAssetPath(INPUT_PQ_MP4_ASSET_STRING) .setVideoAssetPath(INPUT_PQ_MP4_ASSET_STRING)
.setEffects(NO_OP_EFFECT)
.build(); .build();
Bitmap expectedBitmap = readBitmap(ORIGINAL_HDR10_PNG_ASSET_PATH); Bitmap expectedBitmap = readBitmap(ORIGINAL_HDR10_PNG_ASSET_PATH);
...@@ -281,4 +347,27 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest { ...@@ -281,4 +347,27 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
checkNotNull(checkNotNull(format).sampleMimeType), format.colorInfo) checkNotNull(checkNotNull(format).sampleMimeType), format.colorInfo)
.isEmpty(); .isEmpty();
} }
/**
* Wraps a {@link GlEffect} to prevent the {@link DefaultVideoFrameProcessor} from detecting its
* class and optimizing it.
*
* <p>This ensures that {@link DefaultVideoFrameProcessor} uses a separate {@link GlShaderProgram}
* for the wrapped {@link GlEffect} rather than merging it with preceding or subsequent {@link
* GlEffect} instances and applying them in one combined {@link GlShaderProgram}.
*/
private static final class GlEffectWrapper implements GlEffect {
private final GlEffect effect;
public GlEffectWrapper(GlEffect effect) {
this.effect = effect;
}
@Override
public GlShaderProgram toGlShaderProgram(Context context, boolean useHdr)
throws VideoFrameProcessingException {
return effect.toGlShaderProgram(context, useHdr);
}
}
} }
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