Commit 3a966916 by hschlueter Committed by Ian Baker

Support chaining async GlTextureProcessors in FrameProcessorChain.

After this change, FrameProcessorChain chains any GlTextureProcessors
instead of only SingleFrameGlTextureProcessors.

The GlTextureProcessors are chained in a bidirectional manner using
ChainingGlTextureProcessorListener to feed input and output related
events forward and release events backwards.

PiperOrigin-RevId: 456478414
parent 251389d7
......@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.transformerdemo;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import android.app.Activity;
import android.content.Context;
......@@ -421,9 +422,28 @@ public final class TransformerActivity extends AppCompatActivity {
private final class DemoDebugViewProvider implements Transformer.DebugViewProvider {
private @MonotonicNonNull SurfaceView surfaceView;
private int width;
private int height;
public DemoDebugViewProvider() {
width = C.LENGTH_UNSET;
height = C.LENGTH_UNSET;
}
@Nullable
@Override
public SurfaceView getDebugPreviewSurfaceView(int width, int height) {
checkState(
surfaceView == null || (this.width == width && this.height == height),
"Transformer should not change the output size mid-transformation.");
if (surfaceView != null) {
return surfaceView;
}
this.width = width;
this.height = height;
// Update the UI on the main thread and wait for the output surface to be available.
CountDownLatch surfaceCreatedCountDownLatch = new CountDownLatch(1);
SurfaceView surfaceView = new SurfaceView(/* context= */ TransformerActivity.this);
......@@ -460,6 +480,7 @@ public final class TransformerActivity extends AppCompatActivity {
Thread.currentThread().interrupt();
return null;
}
this.surfaceView = surfaceView;
return surfaceView;
}
}
......
......@@ -73,6 +73,16 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso
this.listener = listener;
}
/**
* Returns whether the {@code SingleFrameGlTextureProcessor} can accept an input frame.
*
* <p>If this method returns {@code true}, the next call to {@link #maybeQueueInputFrame(
* TextureInfo, long)} will also return {@code true}.
*/
public boolean acceptsInputFrame() {
return !outputTextureInUse;
}
@Override
public final boolean maybeQueueInputFrame(TextureInfo inputTexture, long presentationTimeUs) {
if (outputTextureInUse) {
......
......@@ -17,7 +17,6 @@
package com.google.android.exoplayer2.transformer;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
import android.content.Context;
import android.media.MediaCodec;
......@@ -32,7 +31,6 @@ import com.google.common.collect.ImmutableList;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.dataflow.qual.Pure;
......@@ -91,8 +89,6 @@ import org.checkerframework.dataflow.qual.Pure;
effectsListBuilder.add(Presentation.createForHeight(transformationRequest.outputHeight));
}
AtomicReference<TransformationException> encoderInitializationException =
new AtomicReference<>();
encoderWrapper =
new EncoderWrapper(
encoderFactory,
......@@ -100,9 +96,8 @@ import org.checkerframework.dataflow.qual.Pure;
allowedOutputMimeTypes,
transformationRequest,
fallbackListener,
encoderInitializationException);
asyncErrorListener);
@Nullable FrameProcessorChain frameProcessorChain;
try {
frameProcessorChain =
FrameProcessorChain.create(
......@@ -137,12 +132,6 @@ import org.checkerframework.dataflow.qual.Pure;
e, TransformationException.ERROR_CODE_GL_INIT_FAILED);
}
if (frameProcessorChain == null) {
// Failed to create FrameProcessorChain because the encoder could not provide a surface.
throw checkStateNotNull(encoderInitializationException.get());
}
this.frameProcessorChain = frameProcessorChain;
decoder =
decoderFactory.createForVideoDecoding(
inputFormat,
......@@ -266,7 +255,7 @@ import org.checkerframework.dataflow.qual.Pure;
}
if (maxPendingFrameCount != Codec.UNLIMITED_PENDING_FRAME_COUNT
&& frameProcessorChain.getPendingFrameCount() == maxPendingFrameCount) {
&& frameProcessorChain.getPendingInputFrameCount() == maxPendingFrameCount) {
return false;
}
......@@ -303,7 +292,7 @@ import org.checkerframework.dataflow.qual.Pure;
private final List<String> allowedOutputMimeTypes;
private final TransformationRequest transformationRequest;
private final FallbackListener fallbackListener;
private final AtomicReference<TransformationException> encoderInitializationException;
private final Transformer.AsyncErrorListener asyncErrorListener;
private @MonotonicNonNull SurfaceInfo encoderSurfaceInfo;
......@@ -317,14 +306,14 @@ import org.checkerframework.dataflow.qual.Pure;
List<String> allowedOutputMimeTypes,
TransformationRequest transformationRequest,
FallbackListener fallbackListener,
AtomicReference<TransformationException> encoderInitializationException) {
Transformer.AsyncErrorListener asyncErrorListener) {
this.encoderFactory = encoderFactory;
this.inputFormat = inputFormat;
this.allowedOutputMimeTypes = allowedOutputMimeTypes;
this.transformationRequest = transformationRequest;
this.fallbackListener = fallbackListener;
this.encoderInitializationException = encoderInitializationException;
this.asyncErrorListener = asyncErrorListener;
}
@Override
......@@ -365,7 +354,7 @@ import org.checkerframework.dataflow.qual.Pure;
encoder =
encoderFactory.createForVideoEncoding(requestedEncoderFormat, allowedOutputMimeTypes);
} catch (TransformationException e) {
encoderInitializationException.set(e);
asyncErrorListener.onTransformationException(e);
return null;
}
Format encoderSupportedFormat = encoder.getConfigurationFormat();
......
......@@ -29,7 +29,6 @@ import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.ListenerSet;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -52,7 +51,7 @@ public final class VideoEncoderWrapperTest {
/* allowedOutputMimeTypes= */ ImmutableList.of(),
emptyTransformationRequest,
fallbackListener,
new AtomicReference<>());
mock(Transformer.AsyncErrorListener.class));
@Before
public void registerTrack() {
......
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