Commit f96a6c71 by hschlueter Committed by microkatz

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
(cherry picked from commit 3a966916)
parent 4102cdbc
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.transformerdemo; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.transformerdemo;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE; 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.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
...@@ -421,9 +422,28 @@ public final class TransformerActivity extends AppCompatActivity { ...@@ -421,9 +422,28 @@ public final class TransformerActivity extends AppCompatActivity {
private final class DemoDebugViewProvider implements Transformer.DebugViewProvider { 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 @Nullable
@Override @Override
public SurfaceView getDebugPreviewSurfaceView(int width, int height) { 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. // Update the UI on the main thread and wait for the output surface to be available.
CountDownLatch surfaceCreatedCountDownLatch = new CountDownLatch(1); CountDownLatch surfaceCreatedCountDownLatch = new CountDownLatch(1);
SurfaceView surfaceView = new SurfaceView(/* context= */ TransformerActivity.this); SurfaceView surfaceView = new SurfaceView(/* context= */ TransformerActivity.this);
...@@ -460,6 +480,7 @@ public final class TransformerActivity extends AppCompatActivity { ...@@ -460,6 +480,7 @@ public final class TransformerActivity extends AppCompatActivity {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
return null; return null;
} }
this.surfaceView = surfaceView;
return surfaceView; return surfaceView;
} }
} }
......
...@@ -73,6 +73,16 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso ...@@ -73,6 +73,16 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso
this.listener = listener; 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 @Override
public final boolean maybeQueueInputFrame(TextureInfo inputTexture, long presentationTimeUs) { public final boolean maybeQueueInputFrame(TextureInfo inputTexture, long presentationTimeUs) {
if (outputTextureInUse) { if (outputTextureInUse) {
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
package com.google.android.exoplayer2.transformer; package com.google.android.exoplayer2.transformer;
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 android.content.Context; import android.content.Context;
import android.media.MediaCodec; import android.media.MediaCodec;
...@@ -32,7 +31,6 @@ import com.google.common.collect.ImmutableList; ...@@ -32,7 +31,6 @@ import com.google.common.collect.ImmutableList;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.checkerframework.dataflow.qual.Pure; import org.checkerframework.dataflow.qual.Pure;
...@@ -91,8 +89,6 @@ import org.checkerframework.dataflow.qual.Pure; ...@@ -91,8 +89,6 @@ import org.checkerframework.dataflow.qual.Pure;
effectsListBuilder.add(Presentation.createForHeight(transformationRequest.outputHeight)); effectsListBuilder.add(Presentation.createForHeight(transformationRequest.outputHeight));
} }
AtomicReference<TransformationException> encoderInitializationException =
new AtomicReference<>();
encoderWrapper = encoderWrapper =
new EncoderWrapper( new EncoderWrapper(
encoderFactory, encoderFactory,
...@@ -100,9 +96,8 @@ import org.checkerframework.dataflow.qual.Pure; ...@@ -100,9 +96,8 @@ import org.checkerframework.dataflow.qual.Pure;
allowedOutputMimeTypes, allowedOutputMimeTypes,
transformationRequest, transformationRequest,
fallbackListener, fallbackListener,
encoderInitializationException); asyncErrorListener);
@Nullable FrameProcessorChain frameProcessorChain;
try { try {
frameProcessorChain = frameProcessorChain =
FrameProcessorChain.create( FrameProcessorChain.create(
...@@ -137,12 +132,6 @@ import org.checkerframework.dataflow.qual.Pure; ...@@ -137,12 +132,6 @@ import org.checkerframework.dataflow.qual.Pure;
e, TransformationException.ERROR_CODE_GL_INIT_FAILED); 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 = decoder =
decoderFactory.createForVideoDecoding( decoderFactory.createForVideoDecoding(
inputFormat, inputFormat,
...@@ -266,7 +255,7 @@ import org.checkerframework.dataflow.qual.Pure; ...@@ -266,7 +255,7 @@ import org.checkerframework.dataflow.qual.Pure;
} }
if (maxPendingFrameCount != Codec.UNLIMITED_PENDING_FRAME_COUNT if (maxPendingFrameCount != Codec.UNLIMITED_PENDING_FRAME_COUNT
&& frameProcessorChain.getPendingFrameCount() == maxPendingFrameCount) { && frameProcessorChain.getPendingInputFrameCount() == maxPendingFrameCount) {
return false; return false;
} }
...@@ -303,7 +292,7 @@ import org.checkerframework.dataflow.qual.Pure; ...@@ -303,7 +292,7 @@ import org.checkerframework.dataflow.qual.Pure;
private final List<String> allowedOutputMimeTypes; private final List<String> allowedOutputMimeTypes;
private final TransformationRequest transformationRequest; private final TransformationRequest transformationRequest;
private final FallbackListener fallbackListener; private final FallbackListener fallbackListener;
private final AtomicReference<TransformationException> encoderInitializationException; private final Transformer.AsyncErrorListener asyncErrorListener;
private @MonotonicNonNull SurfaceInfo encoderSurfaceInfo; private @MonotonicNonNull SurfaceInfo encoderSurfaceInfo;
...@@ -317,14 +306,14 @@ import org.checkerframework.dataflow.qual.Pure; ...@@ -317,14 +306,14 @@ import org.checkerframework.dataflow.qual.Pure;
List<String> allowedOutputMimeTypes, List<String> allowedOutputMimeTypes,
TransformationRequest transformationRequest, TransformationRequest transformationRequest,
FallbackListener fallbackListener, FallbackListener fallbackListener,
AtomicReference<TransformationException> encoderInitializationException) { Transformer.AsyncErrorListener asyncErrorListener) {
this.encoderFactory = encoderFactory; this.encoderFactory = encoderFactory;
this.inputFormat = inputFormat; this.inputFormat = inputFormat;
this.allowedOutputMimeTypes = allowedOutputMimeTypes; this.allowedOutputMimeTypes = allowedOutputMimeTypes;
this.transformationRequest = transformationRequest; this.transformationRequest = transformationRequest;
this.fallbackListener = fallbackListener; this.fallbackListener = fallbackListener;
this.encoderInitializationException = encoderInitializationException; this.asyncErrorListener = asyncErrorListener;
} }
@Override @Override
...@@ -365,7 +354,7 @@ import org.checkerframework.dataflow.qual.Pure; ...@@ -365,7 +354,7 @@ import org.checkerframework.dataflow.qual.Pure;
encoder = encoder =
encoderFactory.createForVideoEncoding(requestedEncoderFormat, allowedOutputMimeTypes); encoderFactory.createForVideoEncoding(requestedEncoderFormat, allowedOutputMimeTypes);
} catch (TransformationException e) { } catch (TransformationException e) {
encoderInitializationException.set(e); asyncErrorListener.onTransformationException(e);
return null; return null;
} }
Format encoderSupportedFormat = encoder.getConfigurationFormat(); Format encoderSupportedFormat = encoder.getConfigurationFormat();
......
...@@ -29,7 +29,6 @@ import com.google.android.exoplayer2.util.Clock; ...@@ -29,7 +29,6 @@ import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.ListenerSet; import com.google.android.exoplayer2.util.ListenerSet;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -52,7 +51,7 @@ public final class VideoEncoderWrapperTest { ...@@ -52,7 +51,7 @@ public final class VideoEncoderWrapperTest {
/* allowedOutputMimeTypes= */ ImmutableList.of(), /* allowedOutputMimeTypes= */ ImmutableList.of(),
emptyTransformationRequest, emptyTransformationRequest,
fallbackListener, fallbackListener,
new AtomicReference<>()); mock(Transformer.AsyncErrorListener.class));
@Before @Before
public void registerTrack() { 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