Commit 135cf814 by huangdarwin Committed by Ian Baker

Test: Add TextureOutputListener for texture output tests

Before this CL, SurfaceTexture.onFrameAvailable was used to tell whether a frame
was available in the VideoFrameProcessor's output texture. This was incorrect, as
it would rely on having the texture be written to before the
SurfaceTexture.onFrameAvailableListener is invoked, leading to null-pointer-
exceptions on timeouts.

Instead of using DefaultVideoFrameProcessor different interfaces to set that we
want to output to a texture, and get that output texture, use one interface that
sets a listener, and renders to a texture iff that listener is set. As this
listener is executed on the GL thread, this also allows us to no longer need to
expand visibility for the GL task executor and tasks.

PiperOrigin-RevId: 522362101
parent 4ec70402
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.effect; package com.google.android.exoplayer2.effect;
import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
import static com.google.android.exoplayer2.util.Assertions.checkArgument; import static com.google.android.exoplayer2.util.Assertions.checkArgument;
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.checkState; import static com.google.android.exoplayer2.util.Assertions.checkState;
...@@ -61,12 +62,21 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -61,12 +62,21 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*/ */
public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
/** Listener interface for texture output. */
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
public interface TextureOutputListener {
/** Called when a texture has been rendered to. */
void onTextureRendered(GlTextureInfo outputTexture, long presentationTimeUs)
throws GlUtil.GlException;
}
/** A factory for {@link DefaultVideoFrameProcessor} instances. */ /** A factory for {@link DefaultVideoFrameProcessor} instances. */
public static final class Factory implements VideoFrameProcessor.Factory { public static final class Factory implements VideoFrameProcessor.Factory {
/** A builder for {@link DefaultVideoFrameProcessor.Factory} instances. */ /** A builder for {@link DefaultVideoFrameProcessor.Factory} instances. */
public static final class Builder { public static final class Builder {
private boolean enableColorTransfers; private boolean enableColorTransfers;
@Nullable private TextureOutputListener textureOutputListener;
/** Creates an instance. */ /** Creates an instance. */
public Builder() { public Builder() {
...@@ -79,25 +89,39 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { ...@@ -79,25 +89,39 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
* <p>If the input or output is HDR, this must be {@code true}. * <p>If the input or output is HDR, this must be {@code true}.
*/ */
@CanIgnoreReturnValue @CanIgnoreReturnValue
public DefaultVideoFrameProcessor.Factory.Builder setEnableColorTransfers( public Builder setEnableColorTransfers(boolean enableColorTransfers) {
boolean enableColorTransfers) {
this.enableColorTransfers = enableColorTransfers; this.enableColorTransfers = enableColorTransfers;
return this; return this;
} }
/**
* Sets the {@link TextureOutputListener}.
*
* <p>If set, the {@link VideoFrameProcessor} will output to an OpenGL texture.
*/
@VisibleForTesting
@CanIgnoreReturnValue
public Builder setOnTextureRenderedListener(TextureOutputListener textureOutputListener) {
this.textureOutputListener = textureOutputListener;
return this;
}
/** Builds an {@link DefaultVideoFrameProcessor.Factory} instance. */ /** Builds an {@link DefaultVideoFrameProcessor.Factory} instance. */
public DefaultVideoFrameProcessor.Factory build() { public DefaultVideoFrameProcessor.Factory build() {
return new DefaultVideoFrameProcessor.Factory(enableColorTransfers); return new DefaultVideoFrameProcessor.Factory(enableColorTransfers, textureOutputListener);
} }
} }
/** Whether to transfer colors to an intermediate color space when applying effects. */ /** Whether to transfer colors to an intermediate color space when applying effects. */
public final boolean enableColorTransfers; private final boolean enableColorTransfers;
@Nullable private final TextureOutputListener textureOutputListener;
private GlObjectsProvider glObjectsProvider = GlObjectsProvider.DEFAULT; private GlObjectsProvider glObjectsProvider = GlObjectsProvider.DEFAULT;
private boolean outputToTexture;
private Factory(boolean enableColorTransfers) { private Factory(
boolean enableColorTransfers, @Nullable TextureOutputListener textureOutputListener) {
this.textureOutputListener = textureOutputListener;
this.enableColorTransfers = enableColorTransfers; this.enableColorTransfers = enableColorTransfers;
} }
...@@ -107,7 +131,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { ...@@ -107,7 +131,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
*/ */
@Deprecated @Deprecated
public Factory() { public Factory() {
this(/* enableColorTransfers= */ true); this(/* enableColorTransfers= */ true, /* textureOutputListener= */ null);
} }
// TODO(276913828): Move this setter to the DefaultVideoFrameProcessor.Factory.Builder. // TODO(276913828): Move this setter to the DefaultVideoFrameProcessor.Factory.Builder.
...@@ -117,26 +141,11 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { ...@@ -117,26 +141,11 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
* <p>The default value is {@link GlObjectsProvider#DEFAULT}. * <p>The default value is {@link GlObjectsProvider#DEFAULT}.
*/ */
@Override @Override
public DefaultVideoFrameProcessor.Factory setGlObjectsProvider( public Factory setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {
GlObjectsProvider glObjectsProvider) {
this.glObjectsProvider = glObjectsProvider; this.glObjectsProvider = glObjectsProvider;
return this; return this;
} }
// TODO(276913828): Move this setter to the DefaultVideoFrameProcessor.Factory.Builder.
/**
* Sets whether to output to a texture for testing.
*
* <p>Must be called before {@link VideoFrameProcessor.Factory#create}.
*
* <p>The default value is {@code false}.
*/
@VisibleForTesting
public Factory setOutputToTexture(boolean outputToTexture) {
this.outputToTexture = outputToTexture;
return this;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
* *
...@@ -221,7 +230,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { ...@@ -221,7 +230,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
listenerExecutor, listenerExecutor,
listener, listener,
glObjectsProvider, glObjectsProvider,
outputToTexture)); textureOutputListener));
try { try {
return defaultVideoFrameProcessorFuture.get(); return defaultVideoFrameProcessorFuture.get();
...@@ -357,20 +366,6 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { ...@@ -357,20 +366,6 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
finalShaderProgramWrapper.setOutputSurfaceInfo(outputSurfaceInfo); finalShaderProgramWrapper.setOutputSurfaceInfo(outputSurfaceInfo);
} }
/**
* Gets the output {@link GlTextureInfo}.
*
* <p>Should only be called if {@code outputToTexture} is true, and after a frame is available, as
* reported by the output {@linkplain #setOutputSurfaceInfo surface}'s {@link
* SurfaceTexture#setOnFrameAvailableListener}. Returns {@code null} if an output texture is not
* yet available.
*/
@VisibleForTesting
@Nullable
public GlTextureInfo getOutputTextureInfo() {
return finalShaderProgramWrapper.getOutputTextureInfo();
}
@Override @Override
public void releaseOutputFrame(long releaseTimeNs) { public void releaseOutputFrame(long releaseTimeNs) {
checkState( checkState(
...@@ -466,7 +461,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { ...@@ -466,7 +461,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
Executor executor, Executor executor,
Listener listener, Listener listener,
GlObjectsProvider glObjectsProvider, GlObjectsProvider glObjectsProvider,
boolean outputToTexture) @Nullable TextureOutputListener textureOutputListener)
throws GlUtil.GlException, VideoFrameProcessingException { throws GlUtil.GlException, VideoFrameProcessingException {
checkState(Thread.currentThread().getName().equals(THREAD_NAME)); checkState(Thread.currentThread().getName().equals(THREAD_NAME));
...@@ -511,7 +506,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { ...@@ -511,7 +506,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
executor, executor,
listener, listener,
glObjectsProvider, glObjectsProvider,
outputToTexture); textureOutputListener);
setGlObjectProviderOnShaderPrograms(shaderPrograms, glObjectsProvider); setGlObjectProviderOnShaderPrograms(shaderPrograms, glObjectsProvider);
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor = VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor =
new VideoFrameProcessingTaskExecutor(singleThreadExecutorService, listener); new VideoFrameProcessingTaskExecutor(singleThreadExecutorService, listener);
...@@ -552,7 +547,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { ...@@ -552,7 +547,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
Executor executor, Executor executor,
Listener listener, Listener listener,
GlObjectsProvider glObjectsProvider, GlObjectsProvider glObjectsProvider,
boolean outputToTexture) @Nullable TextureOutputListener textureOutputListener)
throws VideoFrameProcessingException { throws VideoFrameProcessingException {
ImmutableList.Builder<GlShaderProgram> shaderProgramListBuilder = new ImmutableList.Builder<>(); ImmutableList.Builder<GlShaderProgram> shaderProgramListBuilder = new ImmutableList.Builder<>();
ImmutableList.Builder<GlMatrixTransformation> matrixTransformationListBuilder = ImmutableList.Builder<GlMatrixTransformation> matrixTransformationListBuilder =
...@@ -638,7 +633,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { ...@@ -638,7 +633,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
executor, executor,
listener, listener,
glObjectsProvider, glObjectsProvider,
outputToTexture)); textureOutputListener));
return shaderProgramListBuilder.build(); return shaderProgramListBuilder.build();
} }
......
...@@ -32,7 +32,6 @@ import android.view.SurfaceHolder; ...@@ -32,7 +32,6 @@ import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
import androidx.annotation.GuardedBy; import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.DebugViewProvider; import com.google.android.exoplayer2.util.DebugViewProvider;
import com.google.android.exoplayer2.util.GlObjectsProvider; import com.google.android.exoplayer2.util.GlObjectsProvider;
...@@ -84,7 +83,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -84,7 +83,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final float[] textureTransformMatrix; private final float[] textureTransformMatrix;
private final Queue<Long> streamOffsetUsQueue; private final Queue<Long> streamOffsetUsQueue;
private final Queue<Pair<GlTextureInfo, Long>> availableFrames; private final Queue<Pair<GlTextureInfo, Long>> availableFrames;
private final boolean outputToTexture; @Nullable private final DefaultVideoFrameProcessor.TextureOutputListener textureOutputListener;
private int inputWidth; private int inputWidth;
private int inputHeight; private int inputHeight;
...@@ -123,7 +122,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -123,7 +122,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Executor videoFrameProcessorListenerExecutor, Executor videoFrameProcessorListenerExecutor,
VideoFrameProcessor.Listener videoFrameProcessorListener, VideoFrameProcessor.Listener videoFrameProcessorListener,
GlObjectsProvider glObjectsProvider, GlObjectsProvider glObjectsProvider,
boolean outputToTexture) { @Nullable DefaultVideoFrameProcessor.TextureOutputListener textureOutputListener) {
this.context = context; this.context = context;
this.matrixTransformations = matrixTransformations; this.matrixTransformations = matrixTransformations;
this.rgbMatrices = rgbMatrices; this.rgbMatrices = rgbMatrices;
...@@ -139,7 +138,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -139,7 +138,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
this.videoFrameProcessorListenerExecutor = videoFrameProcessorListenerExecutor; this.videoFrameProcessorListenerExecutor = videoFrameProcessorListenerExecutor;
this.videoFrameProcessorListener = videoFrameProcessorListener; this.videoFrameProcessorListener = videoFrameProcessorListener;
this.glObjectsProvider = glObjectsProvider; this.glObjectsProvider = glObjectsProvider;
this.outputToTexture = outputToTexture; this.textureOutputListener = textureOutputListener;
textureTransformMatrix = GlUtil.create4x4IdentityMatrix(); textureTransformMatrix = GlUtil.create4x4IdentityMatrix();
streamOffsetUsQueue = new ConcurrentLinkedQueue<>(); streamOffsetUsQueue = new ConcurrentLinkedQueue<>();
...@@ -310,7 +309,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -310,7 +309,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
GlTextureInfo inputTexture, long presentationTimeUs, long releaseTimeNs) { GlTextureInfo inputTexture, long presentationTimeUs, long releaseTimeNs) {
try { try {
maybeRenderFrameToOutputSurface(inputTexture, presentationTimeUs, releaseTimeNs); maybeRenderFrameToOutputSurface(inputTexture, presentationTimeUs, releaseTimeNs);
if (outputToTexture && defaultShaderProgram != null) { if (textureOutputListener != null && defaultShaderProgram != null) {
renderFrameToOutputTexture(inputTexture, presentationTimeUs); renderFrameToOutputTexture(inputTexture, presentationTimeUs);
} }
...@@ -364,12 +363,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -364,12 +363,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
outputTexture.fboId, outputTexture.width, outputTexture.height); outputTexture.fboId, outputTexture.width, outputTexture.height);
GlUtil.clearOutputFrame(); GlUtil.clearOutputFrame();
checkNotNull(defaultShaderProgram).drawFrame(inputTexture.texId, presentationTimeUs); checkNotNull(defaultShaderProgram).drawFrame(inputTexture.texId, presentationTimeUs);
} checkNotNull(textureOutputListener).onTextureRendered(outputTexture, presentationTimeUs);
@VisibleForTesting
@Nullable
/* package */ GlTextureInfo getOutputTextureInfo() {
return outputTexture;
} }
/** /**
...@@ -444,7 +438,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -444,7 +438,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
if (defaultShaderProgram == null) { if (defaultShaderProgram == null) {
DefaultShaderProgram defaultShaderProgram = DefaultShaderProgram defaultShaderProgram =
createDefaultShaderProgramForOutputSurface(outputSurfaceInfo); createDefaultShaderProgramForOutputSurface(outputSurfaceInfo);
if (outputToTexture) { if (textureOutputListener != null) {
configureOutputTexture( configureOutputTexture(
checkNotNull(outputSizeBeforeSurfaceTransformation).getWidth(), checkNotNull(outputSizeBeforeSurfaceTransformation).getWidth(),
checkNotNull(outputSizeBeforeSurfaceTransformation).getHeight()); checkNotNull(outputSizeBeforeSurfaceTransformation).getHeight());
......
...@@ -15,9 +15,6 @@ ...@@ -15,9 +15,6 @@
*/ */
package com.google.android.exoplayer2.effect; package com.google.android.exoplayer2.effect;
import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.util.GlUtil; import com.google.android.exoplayer2.util.GlUtil;
import com.google.android.exoplayer2.util.VideoFrameProcessingException; import com.google.android.exoplayer2.util.VideoFrameProcessingException;
...@@ -25,8 +22,7 @@ import com.google.android.exoplayer2.util.VideoFrameProcessingException; ...@@ -25,8 +22,7 @@ import com.google.android.exoplayer2.util.VideoFrameProcessingException;
* Interface for tasks that may throw a {@link GlUtil.GlException} or {@link * Interface for tasks that may throw a {@link GlUtil.GlException} or {@link
* VideoFrameProcessingException}. * VideoFrameProcessingException}.
*/ */
@VisibleForTesting(otherwise = PACKAGE_PRIVATE) /* package */ interface VideoFrameProcessingTask {
public interface VideoFrameProcessingTask {
/** Runs the task. */ /** Runs the task. */
void run() throws VideoFrameProcessingException, GlUtil.GlException; void run() throws VideoFrameProcessingException, GlUtil.GlException;
} }
...@@ -15,12 +15,10 @@ ...@@ -15,12 +15,10 @@
*/ */
package com.google.android.exoplayer2.effect; package com.google.android.exoplayer2.effect;
import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import androidx.annotation.GuardedBy; import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.util.VideoFrameProcessingException; import com.google.android.exoplayer2.util.VideoFrameProcessingException;
import com.google.android.exoplayer2.util.VideoFrameProcessor; import com.google.android.exoplayer2.util.VideoFrameProcessor;
import java.util.ArrayDeque; import java.util.ArrayDeque;
...@@ -46,8 +44,7 @@ import java.util.concurrent.RejectedExecutionException; ...@@ -46,8 +44,7 @@ import java.util.concurrent.RejectedExecutionException;
* executed before {@linkplain #submit(VideoFrameProcessingTask) default priority tasks}. Tasks with * executed before {@linkplain #submit(VideoFrameProcessingTask) default priority tasks}. Tasks with
* equal priority are executed in FIFO order. * equal priority are executed in FIFO order.
*/ */
@VisibleForTesting(otherwise = PACKAGE_PRIVATE) /* package */ final class VideoFrameProcessingTaskExecutor {
public final class VideoFrameProcessingTaskExecutor {
private final ExecutorService singleThreadExecutorService; private final ExecutorService singleThreadExecutorService;
private final VideoFrameProcessor.Listener listener; private final VideoFrameProcessor.Listener listener;
......
...@@ -33,7 +33,6 @@ import com.google.android.exoplayer2.testutil.BitmapPixelTestUtil; ...@@ -33,7 +33,6 @@ 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.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.VideoFrameProcessor;
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;
import org.junit.After; import org.junit.After;
...@@ -105,13 +104,16 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest { ...@@ -105,13 +104,16 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
private VideoFrameProcessorTestRunner.Builder getDefaultFrameProcessorTestRunnerBuilder( private VideoFrameProcessorTestRunner.Builder getDefaultFrameProcessorTestRunnerBuilder(
String testId) { String testId) {
TextureBitmapReader textureBitmapReader = new TextureBitmapReader();
DefaultVideoFrameProcessor.Factory defaultVideoFrameProcessorFactory = DefaultVideoFrameProcessor.Factory defaultVideoFrameProcessorFactory =
new DefaultVideoFrameProcessor.Factory().setOutputToTexture(true); new DefaultVideoFrameProcessor.Factory.Builder()
.setOnTextureRenderedListener(textureBitmapReader::readBitmapFromTexture)
.build();
return new VideoFrameProcessorTestRunner.Builder() return new VideoFrameProcessorTestRunner.Builder()
.setTestId(testId) .setTestId(testId)
.setVideoFrameProcessorFactory(defaultVideoFrameProcessorFactory) .setVideoFrameProcessorFactory(defaultVideoFrameProcessorFactory)
.setVideoAssetPath(INPUT_SDR_MP4_ASSET_STRING) .setVideoAssetPath(INPUT_SDR_MP4_ASSET_STRING)
.setBitmapReaderFactory(new TextureBitmapReader.Factory()); .setBitmapReader(textureBitmapReader);
} }
/** /**
...@@ -122,24 +124,11 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest { ...@@ -122,24 +124,11 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
private static final class TextureBitmapReader private static final class TextureBitmapReader
implements VideoFrameProcessorTestRunner.BitmapReader { implements VideoFrameProcessorTestRunner.BitmapReader {
// TODO(b/239172735): This outputs an incorrect black output image on emulators. // TODO(b/239172735): This outputs an incorrect black output image on emulators.
public static final class Factory
implements VideoFrameProcessorTestRunner.BitmapReader.Factory {
@Override
public TextureBitmapReader create(
VideoFrameProcessor videoFrameProcessor, int width, int height) {
return new TextureBitmapReader((DefaultVideoFrameProcessor) videoFrameProcessor);
}
}
private final DefaultVideoFrameProcessor defaultVideoFrameProcessor;
private @MonotonicNonNull Bitmap outputBitmap; private @MonotonicNonNull Bitmap outputBitmap;
private TextureBitmapReader(DefaultVideoFrameProcessor defaultVideoFrameProcessor) {
this.defaultVideoFrameProcessor = defaultVideoFrameProcessor;
}
@Override @Override
public Surface getSurface() { public Surface getSurface(int width, int height) {
int texId; int texId;
try { try {
texId = GlUtil.createExternalTexture(); texId = GlUtil.createExternalTexture();
...@@ -147,7 +136,6 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest { ...@@ -147,7 +136,6 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
SurfaceTexture surfaceTexture = new SurfaceTexture(texId); SurfaceTexture surfaceTexture = new SurfaceTexture(texId);
surfaceTexture.setOnFrameAvailableListener(this::onSurfaceTextureFrameAvailable);
return new Surface(surfaceTexture); return new Surface(surfaceTexture);
} }
...@@ -156,15 +144,8 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest { ...@@ -156,15 +144,8 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
return checkStateNotNull(outputBitmap); return checkStateNotNull(outputBitmap);
} }
private void onSurfaceTextureFrameAvailable(SurfaceTexture surfaceTexture) { public void readBitmapFromTexture(GlTextureInfo outputTexture, long presentationTimeUs)
defaultVideoFrameProcessor throws GlUtil.GlException {
.getTaskExecutor()
.submitWithHighPriority(this::getBitmapFromTexture);
}
private void getBitmapFromTexture() throws GlUtil.GlException {
GlTextureInfo outputTexture = checkNotNull(defaultVideoFrameProcessor.getOutputTextureInfo());
GlUtil.focusFramebufferUsingCurrentContext( GlUtil.focusFramebufferUsingCurrentContext(
outputTexture.fboId, outputTexture.width, outputTexture.height); outputTexture.fboId, outputTexture.width, outputTexture.height);
outputBitmap = outputBitmap =
......
...@@ -56,7 +56,7 @@ public final class VideoFrameProcessorTestRunner { ...@@ -56,7 +56,7 @@ public final class VideoFrameProcessorTestRunner {
private @MonotonicNonNull String testId; private @MonotonicNonNull String testId;
private VideoFrameProcessor.@MonotonicNonNull Factory videoFrameProcessorFactory; private VideoFrameProcessor.@MonotonicNonNull Factory videoFrameProcessorFactory;
private BitmapReader.@MonotonicNonNull Factory bitmapReaderFactory; private @MonotonicNonNull BitmapReader bitmapReader;
private @MonotonicNonNull String videoAssetPath; private @MonotonicNonNull String videoAssetPath;
private @MonotonicNonNull String outputFileLabel; private @MonotonicNonNull String outputFileLabel;
private @MonotonicNonNull ImmutableList<Effect> effects; private @MonotonicNonNull ImmutableList<Effect> effects;
...@@ -97,13 +97,13 @@ public final class VideoFrameProcessorTestRunner { ...@@ -97,13 +97,13 @@ public final class VideoFrameProcessorTestRunner {
} }
/** /**
* Sets the {@link BitmapReader.Factory}. * Sets the {@link BitmapReader}.
* *
* <p>The default value is {@link SurfaceBitmapReader.Factory}. * <p>The default value is a {@link SurfaceBitmapReader} instance.
*/ */
@CanIgnoreReturnValue @CanIgnoreReturnValue
public Builder setBitmapReaderFactory(BitmapReader.Factory bitmapReaderFactory) { public Builder setBitmapReader(BitmapReader bitmapReader) {
this.bitmapReaderFactory = bitmapReaderFactory; this.bitmapReader = bitmapReader;
return this; return this;
} }
...@@ -216,7 +216,7 @@ public final class VideoFrameProcessorTestRunner { ...@@ -216,7 +216,7 @@ public final class VideoFrameProcessorTestRunner {
return new VideoFrameProcessorTestRunner( return new VideoFrameProcessorTestRunner(
testId, testId,
videoFrameProcessorFactory, videoFrameProcessorFactory,
bitmapReaderFactory == null ? new SurfaceBitmapReader.Factory() : bitmapReaderFactory, bitmapReader == null ? new SurfaceBitmapReader() : bitmapReader,
videoAssetPath, videoAssetPath,
outputFileLabel == null ? "" : outputFileLabel, outputFileLabel == null ? "" : outputFileLabel,
effects == null ? ImmutableList.of() : effects, effects == null ? ImmutableList.of() : effects,
...@@ -248,7 +248,7 @@ public final class VideoFrameProcessorTestRunner { ...@@ -248,7 +248,7 @@ public final class VideoFrameProcessorTestRunner {
private VideoFrameProcessorTestRunner( private VideoFrameProcessorTestRunner(
String testId, String testId,
VideoFrameProcessor.Factory videoFrameProcessorFactory, VideoFrameProcessor.Factory videoFrameProcessorFactory,
BitmapReader.Factory bitmapReaderFactory, BitmapReader bitmapReader,
@Nullable String videoAssetPath, @Nullable String videoAssetPath,
String outputFileLabel, String outputFileLabel,
ImmutableList<Effect> effects, ImmutableList<Effect> effects,
...@@ -259,6 +259,7 @@ public final class VideoFrameProcessorTestRunner { ...@@ -259,6 +259,7 @@ public final class VideoFrameProcessorTestRunner {
OnOutputFrameAvailableListener onOutputFrameAvailableListener) OnOutputFrameAvailableListener onOutputFrameAvailableListener)
throws VideoFrameProcessingException { throws VideoFrameProcessingException {
this.testId = testId; this.testId = testId;
this.bitmapReader = bitmapReader;
this.videoAssetPath = videoAssetPath; this.videoAssetPath = videoAssetPath;
this.outputFileLabel = outputFileLabel; this.outputFileLabel = outputFileLabel;
this.pixelWidthHeightRatio = pixelWidthHeightRatio; this.pixelWidthHeightRatio = pixelWidthHeightRatio;
...@@ -277,11 +278,9 @@ public final class VideoFrameProcessorTestRunner { ...@@ -277,11 +278,9 @@ public final class VideoFrameProcessorTestRunner {
new VideoFrameProcessor.Listener() { new VideoFrameProcessor.Listener() {
@Override @Override
public void onOutputSizeChanged(int width, int height) { public void onOutputSizeChanged(int width, int height) {
bitmapReader = Surface outputSurface = bitmapReader.getSurface(width, height);
bitmapReaderFactory.create(checkNotNull(videoFrameProcessor), width, height); checkNotNull(videoFrameProcessor)
Surface outputSurface = bitmapReader.getSurface(); .setOutputSurfaceInfo(new SurfaceInfo(outputSurface, width, height));
videoFrameProcessor.setOutputSurfaceInfo(
new SurfaceInfo(outputSurface, width, height));
} }
@Override @Override
...@@ -358,12 +357,9 @@ public final class VideoFrameProcessorTestRunner { ...@@ -358,12 +357,9 @@ public final class VideoFrameProcessorTestRunner {
/** Reads a {@link Bitmap} from {@link VideoFrameProcessor} output. */ /** Reads a {@link Bitmap} from {@link VideoFrameProcessor} output. */
public interface BitmapReader { public interface BitmapReader {
interface Factory {
BitmapReader create(VideoFrameProcessor videoFrameProcessor, int width, int height);
}
/** Returns the {@link VideoFrameProcessor} output {@link Surface}. */ /** Returns the {@link VideoFrameProcessor} output {@link Surface}. */
Surface getSurface(); Surface getSurface(int width, int height);
/** Returns the output {@link Bitmap}. */ /** Returns the output {@link Bitmap}. */
Bitmap getBitmap(); Bitmap getBitmap();
...@@ -376,26 +372,15 @@ public final class VideoFrameProcessorTestRunner { ...@@ -376,26 +372,15 @@ public final class VideoFrameProcessorTestRunner {
*/ */
public static final class SurfaceBitmapReader public static final class SurfaceBitmapReader
implements VideoFrameProcessorTestRunner.BitmapReader { implements VideoFrameProcessorTestRunner.BitmapReader {
public static final class Factory
implements VideoFrameProcessorTestRunner.BitmapReader.Factory {
@Override
public SurfaceBitmapReader create(
VideoFrameProcessor videoFrameProcessor, int width, int height) {
return new SurfaceBitmapReader(width, height);
}
}
// ImageReader only supports SDR input. // ImageReader only supports SDR input.
private final ImageReader imageReader; private @MonotonicNonNull ImageReader imageReader;
@Override
@SuppressLint("WrongConstant") @SuppressLint("WrongConstant")
private SurfaceBitmapReader(int width, int height) { public Surface getSurface(int width, int height) {
imageReader = imageReader =
ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, /* maxImages= */ 1); ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, /* maxImages= */ 1);
}
@Override
public Surface getSurface() {
return imageReader.getSurface(); return imageReader.getSurface();
} }
......
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