mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Effect: Move FrameProcessorTestRunner to test-utils.
This is necessary in order to move HDR to SDR tone-mapping tests to transformer/mh. PiperOrigin-RevId: 504858508
This commit is contained in:
parent
4206dbe73e
commit
237a98e7fb
2 changed files with 296 additions and 267 deletions
|
|
@ -15,11 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.effect;
|
package com.google.android.exoplayer2.effect;
|
||||||
|
|
||||||
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
|
|
||||||
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE;
|
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE;
|
||||||
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.createArgb8888BitmapFromRgba8888Image;
|
|
||||||
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceArgb8888;
|
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceArgb8888;
|
||||||
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.maybeSaveTestBitmapToCacheDirectory;
|
|
||||||
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.readBitmap;
|
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.readBitmap;
|
||||||
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||||
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
|
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
|
||||||
|
|
@ -28,27 +25,15 @@ import static com.google.common.truth.Truth.assertThat;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.PixelFormat;
|
|
||||||
import android.media.Image;
|
|
||||||
import android.media.ImageReader;
|
|
||||||
import android.media.MediaFormat;
|
|
||||||
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.C;
|
||||||
import com.google.android.exoplayer2.testutil.BitmapPixelTestUtil;
|
import com.google.android.exoplayer2.testutil.BitmapPixelTestUtil;
|
||||||
import com.google.android.exoplayer2.testutil.DecodeOneFrameUtil;
|
import com.google.android.exoplayer2.testutil.FrameProcessorTestRunner;
|
||||||
import com.google.android.exoplayer2.util.DebugViewProvider;
|
|
||||||
import com.google.android.exoplayer2.util.Effect;
|
import com.google.android.exoplayer2.util.Effect;
|
||||||
import com.google.android.exoplayer2.util.FrameInfo;
|
|
||||||
import com.google.android.exoplayer2.util.FrameProcessingException;
|
import com.google.android.exoplayer2.util.FrameProcessingException;
|
||||||
import com.google.android.exoplayer2.util.FrameProcessor;
|
|
||||||
import com.google.android.exoplayer2.util.Size;
|
import com.google.android.exoplayer2.util.Size;
|
||||||
import com.google.android.exoplayer2.util.SurfaceInfo;
|
|
||||||
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 com.google.common.util.concurrent.MoreExecutors;
|
|
||||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
|
|
@ -529,257 +514,6 @@ public final class GlEffectsFrameProcessorPixelTest {
|
||||||
// TODO(b/227624622): Add a test for HDR input after BitmapPixelTestUtil can read HDR bitmaps,
|
// TODO(b/227624622): Add a test for HDR input after BitmapPixelTestUtil can read HDR bitmaps,
|
||||||
// using GlEffectWrapper to ensure usage of intermediate textures.
|
// using GlEffectWrapper to ensure usage of intermediate textures.
|
||||||
|
|
||||||
/* A test runner for {@link FrameProcessor} tests. */
|
|
||||||
private static final class FrameProcessorTestRunner {
|
|
||||||
|
|
||||||
/** A builder for {@link FrameProcessorTestRunner} instances. */
|
|
||||||
public static final class Builder {
|
|
||||||
private @MonotonicNonNull String testId;
|
|
||||||
private FrameProcessor.@MonotonicNonNull Factory frameProcessorFactory;
|
|
||||||
private @MonotonicNonNull String videoAssetPath;
|
|
||||||
private @MonotonicNonNull String outputFileLabel;
|
|
||||||
private @MonotonicNonNull ImmutableList<Effect> effects;
|
|
||||||
private float pixelWidthHeightRatio;
|
|
||||||
private @MonotonicNonNull ColorInfo inputColorInfo;
|
|
||||||
private @MonotonicNonNull ColorInfo outputColorInfo;
|
|
||||||
|
|
||||||
/** Creates a new instance with default values. */
|
|
||||||
public Builder() {
|
|
||||||
pixelWidthHeightRatio = DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the test ID, used to generate output files.
|
|
||||||
*
|
|
||||||
* <p>This is a required value.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setTestId(String testId) {
|
|
||||||
this.testId = testId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the {@link FrameProcessor.Factory}.
|
|
||||||
*
|
|
||||||
* <p>This is a required value.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setFrameProcessorFactory(FrameProcessor.Factory frameProcessorFactory) {
|
|
||||||
this.frameProcessorFactory = frameProcessorFactory;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the input video asset path.
|
|
||||||
*
|
|
||||||
* <p>This is a required value.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setVideoAssetPath(String videoAssetPath) {
|
|
||||||
this.videoAssetPath = videoAssetPath;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the output file label.
|
|
||||||
*
|
|
||||||
* <p>This value will be postfixed after the {@code testId} to generated output files.
|
|
||||||
*
|
|
||||||
* <p>The default value is an empty string.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setOutputFileLabel(String outputFileLabel) {
|
|
||||||
this.outputFileLabel = outputFileLabel;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the effects used.
|
|
||||||
*
|
|
||||||
* <p>The default value is an empty list.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setEffects(List<Effect> effects) {
|
|
||||||
this.effects = ImmutableList.copyOf(effects);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the effects used.
|
|
||||||
*
|
|
||||||
* <p>The default value is an empty list.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setEffects(Effect... effects) {
|
|
||||||
this.effects = ImmutableList.copyOf(effects);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the {@code pixelWidthHeightRatio}.
|
|
||||||
*
|
|
||||||
* <p>The default value is {@link #DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO}.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setPixelWidthHeightRatio(float pixelWidthHeightRatio) {
|
|
||||||
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the input color.
|
|
||||||
*
|
|
||||||
* <p>The default value is {@link ColorInfo#SDR_BT709_LIMITED}.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setInputColorInfo(ColorInfo inputColorInfo) {
|
|
||||||
this.inputColorInfo = inputColorInfo;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the output color.
|
|
||||||
*
|
|
||||||
* <p>The default value is {@link ColorInfo#SDR_BT709_LIMITED}.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
|
||||||
public Builder setOutputColorInfo(ColorInfo outputColorInfo) {
|
|
||||||
this.outputColorInfo = outputColorInfo;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrameProcessorTestRunner build() throws FrameProcessingException {
|
|
||||||
checkStateNotNull(testId, "testId must be set.");
|
|
||||||
checkStateNotNull(frameProcessorFactory, "frameProcessorFactory must be set.");
|
|
||||||
checkStateNotNull(videoAssetPath, "videoAssetPath must be set.");
|
|
||||||
|
|
||||||
return new FrameProcessorTestRunner(
|
|
||||||
testId,
|
|
||||||
frameProcessorFactory,
|
|
||||||
videoAssetPath,
|
|
||||||
outputFileLabel == null ? "" : outputFileLabel,
|
|
||||||
effects == null ? ImmutableList.of() : effects,
|
|
||||||
pixelWidthHeightRatio,
|
|
||||||
inputColorInfo == null ? ColorInfo.SDR_BT709_LIMITED : inputColorInfo,
|
|
||||||
outputColorInfo == null ? ColorInfo.SDR_BT709_LIMITED : outputColorInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The ratio of width over height, for each pixel in a frame. */
|
|
||||||
private static final float DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO = 1;
|
|
||||||
/**
|
|
||||||
* Time to wait for the decoded frame to populate the {@link GlEffectsFrameProcessor} instance's
|
|
||||||
* input surface and the {@link GlEffectsFrameProcessor} to finish processing the frame, in
|
|
||||||
* milliseconds.
|
|
||||||
*/
|
|
||||||
private static final int FRAME_PROCESSING_WAIT_MS = 5000;
|
|
||||||
|
|
||||||
private final String testId;
|
|
||||||
private final String videoAssetPath;
|
|
||||||
private final String outputFileLabel;
|
|
||||||
private final float pixelWidthHeightRatio;
|
|
||||||
private final AtomicReference<FrameProcessingException> frameProcessingException;
|
|
||||||
|
|
||||||
private final FrameProcessor frameProcessor;
|
|
||||||
|
|
||||||
private volatile @MonotonicNonNull ImageReader outputImageReader;
|
|
||||||
private volatile boolean frameProcessingEnded;
|
|
||||||
|
|
||||||
private FrameProcessorTestRunner(
|
|
||||||
String testId,
|
|
||||||
FrameProcessor.Factory frameProcessorFactory,
|
|
||||||
String videoAssetPath,
|
|
||||||
String outputFileLabel,
|
|
||||||
ImmutableList<Effect> effects,
|
|
||||||
float pixelWidthHeightRatio,
|
|
||||||
ColorInfo inputColorInfo,
|
|
||||||
ColorInfo outputColorInfo)
|
|
||||||
throws FrameProcessingException {
|
|
||||||
this.testId = testId;
|
|
||||||
this.videoAssetPath = videoAssetPath;
|
|
||||||
this.outputFileLabel = outputFileLabel;
|
|
||||||
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
|
||||||
frameProcessingException = new AtomicReference<>();
|
|
||||||
|
|
||||||
frameProcessor =
|
|
||||||
frameProcessorFactory.create(
|
|
||||||
getApplicationContext(),
|
|
||||||
effects,
|
|
||||||
DebugViewProvider.NONE,
|
|
||||||
inputColorInfo,
|
|
||||||
outputColorInfo,
|
|
||||||
/* releaseFramesAutomatically= */ true,
|
|
||||||
MoreExecutors.directExecutor(),
|
|
||||||
new FrameProcessor.Listener() {
|
|
||||||
@Override
|
|
||||||
public void onOutputSizeChanged(int width, int height) {
|
|
||||||
outputImageReader =
|
|
||||||
ImageReader.newInstance(
|
|
||||||
width, height, PixelFormat.RGBA_8888, /* maxImages= */ 1);
|
|
||||||
checkNotNull(frameProcessor)
|
|
||||||
.setOutputSurfaceInfo(
|
|
||||||
new SurfaceInfo(outputImageReader.getSurface(), width, height));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onOutputFrameAvailable(long presentationTimeUs) {
|
|
||||||
// Do nothing as frames are released automatically.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFrameProcessingError(FrameProcessingException exception) {
|
|
||||||
frameProcessingException.set(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFrameProcessingEnded() {
|
|
||||||
frameProcessingEnded = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap processFirstFrameAndEnd() throws Exception {
|
|
||||||
DecodeOneFrameUtil.decodeOneAssetFileFrame(
|
|
||||||
videoAssetPath,
|
|
||||||
new DecodeOneFrameUtil.Listener() {
|
|
||||||
@Override
|
|
||||||
public void onContainerExtracted(MediaFormat mediaFormat) {
|
|
||||||
frameProcessor.setInputFrameInfo(
|
|
||||||
new FrameInfo.Builder(
|
|
||||||
mediaFormat.getInteger(MediaFormat.KEY_WIDTH),
|
|
||||||
mediaFormat.getInteger(MediaFormat.KEY_HEIGHT))
|
|
||||||
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
|
|
||||||
.build());
|
|
||||||
frameProcessor.registerInputFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFrameDecoded(MediaFormat mediaFormat) {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
},
|
|
||||||
frameProcessor.getInputSurface());
|
|
||||||
frameProcessor.signalEndOfInput();
|
|
||||||
Thread.sleep(FRAME_PROCESSING_WAIT_MS);
|
|
||||||
|
|
||||||
assertThat(frameProcessingEnded).isTrue();
|
|
||||||
assertThat(frameProcessingException.get()).isNull();
|
|
||||||
|
|
||||||
Image frameProcessorOutputImage = checkNotNull(outputImageReader).acquireLatestImage();
|
|
||||||
Bitmap actualBitmap = createArgb8888BitmapFromRgba8888Image(frameProcessorOutputImage);
|
|
||||||
frameProcessorOutputImage.close();
|
|
||||||
maybeSaveTestBitmapToCacheDirectory(testId, /* bitmapLabel= */ outputFileLabel, actualBitmap);
|
|
||||||
return actualBitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void release() {
|
|
||||||
if (frameProcessor != null) {
|
|
||||||
frameProcessor.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrameProcessorTestRunner.Builder getDefaultFrameProcessorTestRunnerBuilder(String testId) {
|
public FrameProcessorTestRunner.Builder getDefaultFrameProcessorTestRunnerBuilder(String testId) {
|
||||||
return new FrameProcessorTestRunner.Builder()
|
return new FrameProcessorTestRunner.Builder()
|
||||||
.setTestId(testId)
|
.setTestId(testId)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,295 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.testutil;
|
||||||
|
|
||||||
|
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
|
||||||
|
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.createArgb8888BitmapFromRgba8888Image;
|
||||||
|
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.maybeSaveTestBitmapToCacheDirectory;
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
|
import android.media.Image;
|
||||||
|
import android.media.ImageReader;
|
||||||
|
import android.media.MediaFormat;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
import com.google.android.exoplayer2.util.DebugViewProvider;
|
||||||
|
import com.google.android.exoplayer2.util.Effect;
|
||||||
|
import com.google.android.exoplayer2.util.FrameInfo;
|
||||||
|
import com.google.android.exoplayer2.util.FrameProcessingException;
|
||||||
|
import com.google.android.exoplayer2.util.FrameProcessor;
|
||||||
|
import com.google.android.exoplayer2.util.SurfaceInfo;
|
||||||
|
import com.google.android.exoplayer2.video.ColorInfo;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
|
/** A test runner for {@link FrameProcessor} tests. */
|
||||||
|
@RequiresApi(19)
|
||||||
|
public final class FrameProcessorTestRunner {
|
||||||
|
|
||||||
|
/** A builder for {@link FrameProcessorTestRunner} instances. */
|
||||||
|
public static final class Builder {
|
||||||
|
/** The ratio of width over height, for each pixel in a frame. */
|
||||||
|
private static final float DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO = 1;
|
||||||
|
|
||||||
|
private @MonotonicNonNull String testId;
|
||||||
|
private FrameProcessor.@MonotonicNonNull Factory frameProcessorFactory;
|
||||||
|
private @MonotonicNonNull String videoAssetPath;
|
||||||
|
private @MonotonicNonNull String outputFileLabel;
|
||||||
|
private @MonotonicNonNull ImmutableList<Effect> effects;
|
||||||
|
private float pixelWidthHeightRatio;
|
||||||
|
private @MonotonicNonNull ColorInfo inputColorInfo;
|
||||||
|
private @MonotonicNonNull ColorInfo outputColorInfo;
|
||||||
|
|
||||||
|
/** Creates a new instance with default values. */
|
||||||
|
public Builder() {
|
||||||
|
pixelWidthHeightRatio = DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the test ID, used to generate output files.
|
||||||
|
*
|
||||||
|
* <p>This is a required value.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setTestId(String testId) {
|
||||||
|
this.testId = testId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link FrameProcessor.Factory}.
|
||||||
|
*
|
||||||
|
* <p>This is a required value.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setFrameProcessorFactory(FrameProcessor.Factory frameProcessorFactory) {
|
||||||
|
this.frameProcessorFactory = frameProcessorFactory;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the input video asset path.
|
||||||
|
*
|
||||||
|
* <p>This is a required value.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setVideoAssetPath(String videoAssetPath) {
|
||||||
|
this.videoAssetPath = videoAssetPath;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the output file label.
|
||||||
|
*
|
||||||
|
* <p>This value will be postfixed after the {@code testId} to generated output files.
|
||||||
|
*
|
||||||
|
* <p>The default value is an empty string.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setOutputFileLabel(String outputFileLabel) {
|
||||||
|
this.outputFileLabel = outputFileLabel;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link Effect}s used.
|
||||||
|
*
|
||||||
|
* <p>The default value is an empty list.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setEffects(List<Effect> effects) {
|
||||||
|
this.effects = ImmutableList.copyOf(effects);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link Effect}s used.
|
||||||
|
*
|
||||||
|
* <p>The default value is an empty list.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setEffects(Effect... effects) {
|
||||||
|
this.effects = ImmutableList.copyOf(effects);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@code pixelWidthHeightRatio}.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@link #DEFAULT_PIXEL_WIDTH_HEIGHT_RATIO}.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setPixelWidthHeightRatio(float pixelWidthHeightRatio) {
|
||||||
|
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the input {@link ColorInfo}.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@link ColorInfo#SDR_BT709_LIMITED}.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setInputColorInfo(ColorInfo inputColorInfo) {
|
||||||
|
this.inputColorInfo = inputColorInfo;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the output {@link ColorInfo}.
|
||||||
|
*
|
||||||
|
* <p>The default value is {@link ColorInfo#SDR_BT709_LIMITED}.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setOutputColorInfo(ColorInfo outputColorInfo) {
|
||||||
|
this.outputColorInfo = outputColorInfo;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrameProcessorTestRunner build() throws FrameProcessingException {
|
||||||
|
checkStateNotNull(testId, "testId must be set.");
|
||||||
|
checkStateNotNull(frameProcessorFactory, "frameProcessorFactory must be set.");
|
||||||
|
checkStateNotNull(videoAssetPath, "videoAssetPath must be set.");
|
||||||
|
|
||||||
|
return new FrameProcessorTestRunner(
|
||||||
|
testId,
|
||||||
|
frameProcessorFactory,
|
||||||
|
videoAssetPath,
|
||||||
|
outputFileLabel == null ? "" : outputFileLabel,
|
||||||
|
effects == null ? ImmutableList.of() : effects,
|
||||||
|
pixelWidthHeightRatio,
|
||||||
|
inputColorInfo == null ? ColorInfo.SDR_BT709_LIMITED : inputColorInfo,
|
||||||
|
outputColorInfo == null ? ColorInfo.SDR_BT709_LIMITED : outputColorInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time to wait for the decoded frame to populate the {@link FrameProcessor} instance's input
|
||||||
|
* surface and the {@link FrameProcessor} to finish processing the frame, in milliseconds.
|
||||||
|
*/
|
||||||
|
private static final int FRAME_PROCESSING_WAIT_MS = 5000;
|
||||||
|
|
||||||
|
private final String testId;
|
||||||
|
private final String videoAssetPath;
|
||||||
|
private final String outputFileLabel;
|
||||||
|
private final float pixelWidthHeightRatio;
|
||||||
|
private final AtomicReference<FrameProcessingException> frameProcessingException;
|
||||||
|
|
||||||
|
private final FrameProcessor frameProcessor;
|
||||||
|
|
||||||
|
private volatile @MonotonicNonNull ImageReader outputImageReader;
|
||||||
|
private volatile boolean frameProcessingEnded;
|
||||||
|
|
||||||
|
private FrameProcessorTestRunner(
|
||||||
|
String testId,
|
||||||
|
FrameProcessor.Factory frameProcessorFactory,
|
||||||
|
String videoAssetPath,
|
||||||
|
String outputFileLabel,
|
||||||
|
ImmutableList<Effect> effects,
|
||||||
|
float pixelWidthHeightRatio,
|
||||||
|
ColorInfo inputColorInfo,
|
||||||
|
ColorInfo outputColorInfo)
|
||||||
|
throws FrameProcessingException {
|
||||||
|
this.testId = testId;
|
||||||
|
this.videoAssetPath = videoAssetPath;
|
||||||
|
this.outputFileLabel = outputFileLabel;
|
||||||
|
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||||
|
frameProcessingException = new AtomicReference<>();
|
||||||
|
|
||||||
|
frameProcessor =
|
||||||
|
frameProcessorFactory.create(
|
||||||
|
getApplicationContext(),
|
||||||
|
effects,
|
||||||
|
DebugViewProvider.NONE,
|
||||||
|
inputColorInfo,
|
||||||
|
outputColorInfo,
|
||||||
|
/* releaseFramesAutomatically= */ true,
|
||||||
|
MoreExecutors.directExecutor(),
|
||||||
|
new FrameProcessor.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onOutputSizeChanged(int width, int height) {
|
||||||
|
outputImageReader =
|
||||||
|
ImageReader.newInstance(
|
||||||
|
width, height, PixelFormat.RGBA_8888, /* maxImages= */ 1);
|
||||||
|
checkNotNull(frameProcessor)
|
||||||
|
.setOutputSurfaceInfo(
|
||||||
|
new SurfaceInfo(outputImageReader.getSurface(), width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOutputFrameAvailable(long presentationTimeUs) {
|
||||||
|
// Do nothing as frames are released automatically.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFrameProcessingError(FrameProcessingException exception) {
|
||||||
|
frameProcessingException.set(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFrameProcessingEnded() {
|
||||||
|
frameProcessingEnded = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bitmap processFirstFrameAndEnd() throws Exception {
|
||||||
|
DecodeOneFrameUtil.decodeOneAssetFileFrame(
|
||||||
|
videoAssetPath,
|
||||||
|
new DecodeOneFrameUtil.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onContainerExtracted(MediaFormat mediaFormat) {
|
||||||
|
frameProcessor.setInputFrameInfo(
|
||||||
|
new FrameInfo.Builder(
|
||||||
|
mediaFormat.getInteger(MediaFormat.KEY_WIDTH),
|
||||||
|
mediaFormat.getInteger(MediaFormat.KEY_HEIGHT))
|
||||||
|
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
|
||||||
|
.build());
|
||||||
|
frameProcessor.registerInputFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFrameDecoded(MediaFormat mediaFormat) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
},
|
||||||
|
frameProcessor.getInputSurface());
|
||||||
|
frameProcessor.signalEndOfInput();
|
||||||
|
Thread.sleep(FRAME_PROCESSING_WAIT_MS);
|
||||||
|
|
||||||
|
assertThat(frameProcessingEnded).isTrue();
|
||||||
|
assertThat(frameProcessingException.get()).isNull();
|
||||||
|
|
||||||
|
Image frameProcessorOutputImage = checkNotNull(outputImageReader).acquireLatestImage();
|
||||||
|
Bitmap actualBitmap = createArgb8888BitmapFromRgba8888Image(frameProcessorOutputImage);
|
||||||
|
frameProcessorOutputImage.close();
|
||||||
|
maybeSaveTestBitmapToCacheDirectory(testId, /* bitmapLabel= */ outputFileLabel, actualBitmap);
|
||||||
|
return actualBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void release() {
|
||||||
|
if (frameProcessor != null) {
|
||||||
|
frameProcessor.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue