diff --git a/demos/transformer/src/withMediaPipe/java/com/google/android/exoplayer2/transformerdemo/MediaPipeShaderProgram.java b/demos/transformer/src/withMediaPipe/java/com/google/android/exoplayer2/transformerdemo/MediaPipeShaderProgram.java index ff81086f12e..b1474fefb74 100644 --- a/demos/transformer/src/withMediaPipe/java/com/google/android/exoplayer2/transformerdemo/MediaPipeShaderProgram.java +++ b/demos/transformer/src/withMediaPipe/java/com/google/android/exoplayer2/transformerdemo/MediaPipeShaderProgram.java @@ -25,6 +25,7 @@ import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.effect.GlShaderProgram; +import com.google.android.exoplayer2.util.GlObjectsProvider; import com.google.android.exoplayer2.util.GlTextureInfo; import com.google.android.exoplayer2.util.LibraryLoader; import com.google.android.exoplayer2.util.Util; @@ -159,6 +160,9 @@ public void setErrorListener(Executor executor, ErrorListener errorListener) { () -> errorListener.onError(new VideoFrameProcessingException(error)))); } + @Override + public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {} + @Override public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) { AppTextureFrame appTextureFrame = diff --git a/library/common/src/main/java/com/google/android/exoplayer2/util/VideoFrameProcessor.java b/library/common/src/main/java/com/google/android/exoplayer2/util/VideoFrameProcessor.java index 9b77c826b3f..cd507a218f4 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/util/VideoFrameProcessor.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/util/VideoFrameProcessor.java @@ -41,6 +41,15 @@ public interface VideoFrameProcessor { /** A factory for {@link VideoFrameProcessor} instances. */ interface Factory { + + /** + * Sets the {@link GlObjectsProvider}. + * + *

Must be called before {@link #create}. + */ + Factory setGlObjectsProvider(GlObjectsProvider glObjectsProvider); + + // TODO(271433904): Turn parameters with default values into setters. /** * Creates a new {@link VideoFrameProcessor} instance. * diff --git a/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/DefaultVideoFrameProcessorVideoFrameReleaseTest.java b/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/DefaultVideoFrameProcessorVideoFrameReleaseTest.java index c7fa124c112..f662bd36a48 100644 --- a/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/DefaultVideoFrameProcessorVideoFrameReleaseTest.java +++ b/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/DefaultVideoFrameProcessorVideoFrameReleaseTest.java @@ -28,6 +28,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.util.DebugViewProvider; import com.google.android.exoplayer2.util.FrameInfo; +import com.google.android.exoplayer2.util.GlObjectsProvider; import com.google.android.exoplayer2.util.GlTextureInfo; import com.google.android.exoplayer2.util.GlUtil; import com.google.android.exoplayer2.util.SurfaceInfo; @@ -410,6 +411,9 @@ public void setOutputListener(OutputListener outputListener) { @Override public void setErrorListener(Executor executor, ErrorListener errorListener) {} + @Override + public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {} + @Override public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) { // No input is queued in these tests. The BlankFrameProducer is used to produce frames. diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/DefaultVideoFrameProcessor.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/DefaultVideoFrameProcessor.java index c7d3b62bfe0..af486b87123 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/DefaultVideoFrameProcessor.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/DefaultVideoFrameProcessor.java @@ -35,6 +35,7 @@ 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.GlObjectsProvider; import com.google.android.exoplayer2.util.GlUtil; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.SurfaceInfo; @@ -60,6 +61,20 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { /** A factory for {@link DefaultVideoFrameProcessor} instances. */ public static final class Factory implements VideoFrameProcessor.Factory { + private GlObjectsProvider glObjectsProvider = GlObjectsProvider.DEFAULT; + + /** + * {@inheritDoc} + * + *

The default value is {@link GlObjectsProvider#DEFAULT}. + */ + @Override + public DefaultVideoFrameProcessor.Factory setGlObjectsProvider( + GlObjectsProvider glObjectsProvider) { + this.glObjectsProvider = glObjectsProvider; + return this; + } + /** * {@inheritDoc} * @@ -137,7 +152,8 @@ public DefaultVideoFrameProcessor create( releaseFramesAutomatically, singleThreadExecutorService, listenerExecutor, - listener)); + listener, + glObjectsProvider)); try { return defaultVideoFrameProcessorFuture.get(); @@ -365,7 +381,8 @@ private static DefaultVideoFrameProcessor createOpenGlObjectsAndFrameProcessor( boolean releaseFramesAutomatically, ExecutorService singleThreadExecutorService, Executor executor, - Listener listener) + Listener listener, + GlObjectsProvider glObjectsProvider) throws GlUtil.GlException, VideoFrameProcessingException { checkState(Thread.currentThread().getName().equals(THREAD_NAME)); @@ -378,7 +395,8 @@ private static DefaultVideoFrameProcessor createOpenGlObjectsAndFrameProcessor( : GlUtil.EGL_CONFIG_ATTRIBUTES_RGBA_8888; int openGlVersion = ColorInfo.isTransferHdr(inputColorInfo) || ColorInfo.isTransferHdr(outputColorInfo) ? 3 : 2; - EGLContext eglContext = GlUtil.createEglContext(eglDisplay, openGlVersion, configAttributes); + EGLContext eglContext = + glObjectsProvider.createEglContext(eglDisplay, openGlVersion, configAttributes); GlUtil.createFocusedPlaceholderEglSurface(eglContext, eglDisplay, configAttributes); // Not releaseFramesAutomatically means outputting to a display surface. HDR display surfaces @@ -407,6 +425,7 @@ private static DefaultVideoFrameProcessor createOpenGlObjectsAndFrameProcessor( releaseFramesAutomatically, executor, listener); + setGlObjectProviderOnShaderPrograms(shaderPrograms, glObjectsProvider); VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor = new VideoFrameProcessingTaskExecutor(singleThreadExecutorService, listener); chainShaderProgramsWithListeners( @@ -520,6 +539,15 @@ private static ImmutableList getGlShaderProgramsForGlEffects( return shaderProgramListBuilder.build(); } + /** Sets the {@link GlObjectsProvider} on all of the {@linkplain GlShaderProgram}s provided. */ + private static void setGlObjectProviderOnShaderPrograms( + ImmutableList shaderPrograms, GlObjectsProvider glObjectsProvider) { + for (int i = 0; i < shaderPrograms.size() - 1; i++) { + GlShaderProgram shaderProgram = shaderPrograms.get(i); + shaderProgram.setGlObjectsProvider(glObjectsProvider); + } + } + /** * Chains the given {@link GlShaderProgram} instances using {@link * ChainingGlShaderProgramListener} instances. diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/FinalShaderProgramWrapper.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/FinalShaderProgramWrapper.java index 24a3acb2046..4729a137e0d 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/FinalShaderProgramWrapper.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/FinalShaderProgramWrapper.java @@ -34,6 +34,7 @@ import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.util.DebugViewProvider; +import com.google.android.exoplayer2.util.GlObjectsProvider; import com.google.android.exoplayer2.util.GlTextureInfo; import com.google.android.exoplayer2.util.GlUtil; import com.google.android.exoplayer2.util.Log; @@ -86,9 +87,11 @@ private int inputHeight; @Nullable private DefaultShaderProgram defaultShaderProgram; @Nullable private SurfaceViewWrapper debugSurfaceViewWrapper; + private GlObjectsProvider glObjectsProvider; private InputListener inputListener; private @MonotonicNonNull Size outputSizeBeforeSurfaceTransformation; @Nullable private SurfaceView debugSurfaceView; + private boolean frameProcessingStarted; private volatile boolean outputSizeOrRotationChanged; @@ -130,10 +133,19 @@ public FinalShaderProgramWrapper( textureTransformMatrix = GlUtil.create4x4IdentityMatrix(); streamOffsetUsQueue = new ConcurrentLinkedQueue<>(); + glObjectsProvider = GlObjectsProvider.DEFAULT; inputListener = new InputListener() {}; availableFrames = new ConcurrentLinkedQueue<>(); } + @Override + public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) { + checkState( + !frameProcessingStarted, + "The GlObjectsProvider cannot be set after frame processing has started."); + this.glObjectsProvider = glObjectsProvider; + } + @Override public void setInputListener(InputListener inputListener) { this.inputListener = inputListener; @@ -154,6 +166,7 @@ public void setErrorListener(Executor executor, ErrorListener errorListener) { @Override public void signalEndOfCurrentInputStream() { + frameProcessingStarted = true; checkState(!streamOffsetUsQueue.isEmpty(), "No input stream to end."); streamOffsetUsQueue.remove(); if (streamOffsetUsQueue.isEmpty()) { @@ -178,6 +191,7 @@ public void appendStream(long streamOffsetUs) { @Override public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) { + frameProcessingStarted = true; long streamOffsetUs = checkStateNotNull(streamOffsetUsQueue.peek(), "No input stream specified."); long offsetPresentationTimeUs = presentationTimeUs + streamOffsetUs; @@ -199,6 +213,7 @@ public void releaseOutputFrame(GlTextureInfo outputTexture) { } public void releaseOutputFrame(long releaseTimeNs) { + frameProcessingStarted = true; checkState(!releaseFramesAutomatically); Pair oldestAvailableFrame = availableFrames.remove(); renderFrameToSurfaces( @@ -209,6 +224,7 @@ public void releaseOutputFrame(long releaseTimeNs) { @Override public void flush() { + frameProcessingStarted = true; // Drops all frames that aren't released yet. availableFrames.clear(); if (defaultShaderProgram != null) { @@ -304,7 +320,7 @@ private synchronized void maybeRenderFrameToOutputSurface( outputEglSurface, outputSurfaceInfo.width, outputSurfaceInfo.height); - GlUtil.clearOutputFrame(); + glObjectsProvider.clearOutputFrame(); defaultShaderProgram.drawFrame(inputTexture.texId, presentationTimeUs); EGLExt.eglPresentationTimeANDROID( @@ -446,7 +462,7 @@ private void maybeRenderFrameToDebugSurface(GlTextureInfo inputTexture, long pre try { debugSurfaceViewWrapper.maybeRenderToSurfaceView( () -> { - GlUtil.clearOutputFrame(); + glObjectsProvider.clearOutputFrame(); @C.ColorTransfer int configuredColorTransfer = defaultShaderProgram.getOutputColorTransfer(); defaultShaderProgram.setOutputColorTransfer( diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/FrameCacheGlShaderProgram.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/FrameCacheGlShaderProgram.java index 24197217a8d..4b59b2f66f3 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/FrameCacheGlShaderProgram.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/FrameCacheGlShaderProgram.java @@ -19,7 +19,7 @@ import android.content.Context; import android.opengl.GLES20; -import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.util.GlObjectsProvider; import com.google.android.exoplayer2.util.GlProgram; import com.google.android.exoplayer2.util.GlTextureInfo; import com.google.android.exoplayer2.util.GlUtil; @@ -49,10 +49,12 @@ private final int capacity; private final boolean useHdr; + private GlObjectsProvider glObjectsProvider; private InputListener inputListener; private OutputListener outputListener; private ErrorListener errorListener; private Executor errorListenerExecutor; + private boolean frameProcessingStarted; /** Creates a new instance. */ public FrameCacheGlShaderProgram(Context context, int capacity, boolean useHdr) @@ -80,12 +82,21 @@ public FrameCacheGlShaderProgram(Context context, int capacity, boolean useHdr) GlUtil.getNormalizedCoordinateBounds(), GlUtil.HOMOGENEOUS_COORDINATE_VECTOR_SIZE); + glObjectsProvider = GlObjectsProvider.DEFAULT; inputListener = new InputListener() {}; outputListener = new OutputListener() {}; errorListener = videoFrameProcessingException -> {}; errorListenerExecutor = MoreExecutors.directExecutor(); } + @Override + public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) { + checkState( + !frameProcessingStarted, + "The GlObjectsProvider cannot be set after frame processing has started."); + this.glObjectsProvider = glObjectsProvider; + } + @Override public void setInputListener(InputListener inputListener) { this.inputListener = inputListener; @@ -115,6 +126,7 @@ public void setErrorListener(Executor errorListenerExecutor, ErrorListener error @Override public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) { + frameProcessingStarted = true; try { configureAllOutputTextures(inputTexture.width, inputTexture.height); @@ -125,7 +137,7 @@ public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) // Copy frame to fbo. GlUtil.focusFramebufferUsingCurrentContext( outputTexture.fboId, outputTexture.width, outputTexture.height); - GlUtil.clearOutputFrame(); + glObjectsProvider.clearOutputFrame(); drawFrame(inputTexture.texId); inputListener.onInputFrameProcessed(inputTexture); outputListener.onOutputFrameAvailable(outputTexture, presentationTimeUs); @@ -147,6 +159,7 @@ private void drawFrame(int inputTexId) throws GlUtil.GlException { @Override public void releaseOutputFrame(GlTextureInfo outputTexture) { + frameProcessingStarted = true; checkState(inUseOutputTextures.contains(outputTexture)); inUseOutputTextures.remove(outputTexture); freeOutputTextures.add(outputTexture); @@ -155,11 +168,13 @@ public void releaseOutputFrame(GlTextureInfo outputTexture) { @Override public void signalEndOfCurrentInputStream() { + frameProcessingStarted = true; outputListener.onCurrentOutputStreamEnded(); } @Override public void flush() { + frameProcessingStarted = true; freeOutputTextures.addAll(inUseOutputTextures); inUseOutputTextures.clear(); inputListener.onFlush(); @@ -170,6 +185,7 @@ public void flush() { @Override public void release() throws VideoFrameProcessingException { + frameProcessingStarted = true; try { deleteAllOutputTextures(); } catch (GlUtil.GlException e) { @@ -196,9 +212,8 @@ private void createAllOutputTextures(int width, int height) throws GlUtil.GlExce checkState(inUseOutputTextures.isEmpty()); for (int i = 0; i < capacity; i++) { int outputTexId = GlUtil.createTexture(width, height, useHdr); - int outputFboId = GlUtil.createFboForTexture(outputTexId); GlTextureInfo outputTexture = - new GlTextureInfo(outputTexId, outputFboId, /* rboId= */ C.INDEX_UNSET, width, height); + glObjectsProvider.createBuffersForTexture(outputTexId, width, height); freeOutputTextures.add(outputTexture); } } diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlShaderProgram.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlShaderProgram.java index c68d351dc8b..89307715c5c 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlShaderProgram.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlShaderProgram.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.effect; +import com.google.android.exoplayer2.util.GlObjectsProvider; import com.google.android.exoplayer2.util.GlTextureInfo; import com.google.android.exoplayer2.util.VideoFrameProcessingException; import java.util.concurrent.Executor; @@ -146,6 +147,13 @@ interface ErrorListener { */ void setErrorListener(Executor executor, ErrorListener errorListener); + /** + * Sets the {@link GlObjectsProvider}. + * + *

This method should not be called after any of the frame processing methods. + */ + void setGlObjectsProvider(GlObjectsProvider glObjectsProvider); + /** * Processes an input frame if possible. * diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/SingleFrameGlShaderProgram.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/SingleFrameGlShaderProgram.java index 3bdd716d614..3f4eb99ff76 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/SingleFrameGlShaderProgram.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/SingleFrameGlShaderProgram.java @@ -18,7 +18,7 @@ import static com.google.android.exoplayer2.util.Assertions.checkState; import androidx.annotation.CallSuper; -import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.util.GlObjectsProvider; import com.google.android.exoplayer2.util.GlTextureInfo; import com.google.android.exoplayer2.util.GlUtil; import com.google.android.exoplayer2.util.Size; @@ -42,6 +42,7 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram { private final boolean useHdr; + private GlObjectsProvider glObjectsProvider; private InputListener inputListener; private OutputListener outputListener; private ErrorListener errorListener; @@ -50,6 +51,7 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram { private int inputHeight; private @MonotonicNonNull GlTextureInfo outputTexture; private boolean outputTextureInUse; + private boolean frameProcessingStarted; /** * Creates a {@code SingleFrameGlShaderProgram} instance. @@ -59,6 +61,7 @@ public abstract class SingleFrameGlShaderProgram implements GlShaderProgram { */ public SingleFrameGlShaderProgram(boolean useHdr) { this.useHdr = useHdr; + glObjectsProvider = GlObjectsProvider.DEFAULT; inputListener = new InputListener() {}; outputListener = new OutputListener() {}; errorListener = (videoFrameProcessingException) -> {}; @@ -96,6 +99,14 @@ public abstract Size configure(int inputWidth, int inputHeight) public abstract void drawFrame(int inputTexId, long presentationTimeUs) throws VideoFrameProcessingException; + @Override + public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) { + checkState( + !frameProcessingStarted, + "The GlObjectsProvider cannot be set after frame processing has started."); + this.glObjectsProvider = glObjectsProvider; + } + @Override public final void setInputListener(InputListener inputListener) { this.inputListener = inputListener; @@ -121,7 +132,7 @@ public final void queueInputFrame(GlTextureInfo inputTexture, long presentationT !outputTextureInUse, "The shader program does not currently accept input frames. Release prior output frames" + " first."); - + frameProcessingStarted = true; try { if (outputTexture == null || inputTexture.width != inputWidth @@ -131,7 +142,7 @@ public final void queueInputFrame(GlTextureInfo inputTexture, long presentationT outputTextureInUse = true; GlUtil.focusFramebufferUsingCurrentContext( outputTexture.fboId, outputTexture.width, outputTexture.height); - GlUtil.clearOutputFrame(); + glObjectsProvider.clearOutputFrame(); drawFrame(inputTexture.texId, presentationTimeUs); inputListener.onInputFrameProcessed(inputTexture); outputListener.onOutputFrameAvailable(outputTexture, presentationTimeUs); @@ -159,25 +170,22 @@ private void configureOutputTexture(int inputWidth, int inputHeight) GlUtil.deleteFbo(outputTexture.fboId); } int outputTexId = GlUtil.createTexture(outputSize.getWidth(), outputSize.getHeight(), useHdr); - int outputFboId = GlUtil.createFboForTexture(outputTexId); outputTexture = - new GlTextureInfo( - outputTexId, - outputFboId, - /* rboId= */ C.INDEX_UNSET, - outputSize.getWidth(), - outputSize.getHeight()); + glObjectsProvider.createBuffersForTexture( + outputTexId, outputSize.getWidth(), outputSize.getHeight()); } } @Override public final void releaseOutputFrame(GlTextureInfo outputTexture) { outputTextureInUse = false; + frameProcessingStarted = true; inputListener.onReadyToAcceptInputFrame(); } @Override public final void signalEndOfCurrentInputStream() { + frameProcessingStarted = true; outputListener.onCurrentOutputStreamEnded(); } @@ -185,6 +193,7 @@ public final void signalEndOfCurrentInputStream() { @CallSuper public void flush() { outputTextureInUse = false; + frameProcessingStarted = true; inputListener.onFlush(); inputListener.onReadyToAcceptInputFrame(); } @@ -192,6 +201,7 @@ public void flush() { @Override @CallSuper public void release() throws VideoFrameProcessingException { + frameProcessingStarted = true; if (outputTexture != null) { try { GlUtil.deleteTexture(outputTexture.texId); diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Effects.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Effects.java index b553ed49ceb..fc19b147a0f 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Effects.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Effects.java @@ -19,6 +19,7 @@ import com.google.android.exoplayer2.audio.AudioProcessor; import com.google.android.exoplayer2.effect.DefaultVideoFrameProcessor; import com.google.android.exoplayer2.util.Effect; +import com.google.android.exoplayer2.util.GlObjectsProvider; import com.google.android.exoplayer2.util.VideoFrameProcessor; import com.google.common.collect.ImmutableList; import java.util.List; @@ -47,15 +48,25 @@ public final class Effects { * applying the {@code videoEffects} to the video frames. */ public final VideoFrameProcessor.Factory videoFrameProcessorFactory; + /** + * The {@link GlObjectsProvider} used to create and maintain certain GL Objects in the {@link + * VideoFrameProcessor}. + */ + public final GlObjectsProvider glObjectsProvider; /** * Creates an instance using a {@link DefaultVideoFrameProcessor.Factory}. * *

This is equivalent to calling {@link Effects#Effects(List, List, - * VideoFrameProcessor.Factory)} with a {@link DefaultVideoFrameProcessor.Factory}. + * VideoFrameProcessor.Factory, GlObjectsProvider)} with a {@link + * DefaultVideoFrameProcessor.Factory} and {@link GlObjectsProvider#DEFAULT}. */ public Effects(List audioProcessors, List videoEffects) { - this(audioProcessors, videoEffects, new DefaultVideoFrameProcessor.Factory()); + this( + audioProcessors, + videoEffects, + new DefaultVideoFrameProcessor.Factory(), + GlObjectsProvider.DEFAULT); } /** @@ -64,13 +75,16 @@ public Effects(List audioProcessors, List videoEffects) * @param audioProcessors The {@link #audioProcessors}. * @param videoEffects The {@link #videoEffects}. * @param videoFrameProcessorFactory The {@link #videoFrameProcessorFactory}. + * @param glObjectsProvider The {@link GlObjectsProvider}. */ public Effects( List audioProcessors, List videoEffects, - VideoFrameProcessor.Factory videoFrameProcessorFactory) { + VideoFrameProcessor.Factory videoFrameProcessorFactory, + GlObjectsProvider glObjectsProvider) { this.audioProcessors = ImmutableList.copyOf(audioProcessors); this.videoEffects = ImmutableList.copyOf(videoEffects); this.videoFrameProcessorFactory = videoFrameProcessorFactory; + this.glObjectsProvider = glObjectsProvider; } } diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java index 55413e8a333..9883a69c001 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java @@ -35,6 +35,7 @@ import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.DebugViewProvider; import com.google.android.exoplayer2.util.Effect; +import com.google.android.exoplayer2.util.GlObjectsProvider; import com.google.android.exoplayer2.util.HandlerWrapper; import com.google.android.exoplayer2.util.ListenerSet; import com.google.android.exoplayer2.util.MimeTypes; @@ -86,6 +87,7 @@ public static final class Builder { private ListenerSet listeners; private AssetLoader.@MonotonicNonNull Factory assetLoaderFactory; private VideoFrameProcessor.Factory videoFrameProcessorFactory; + private GlObjectsProvider glObjectsProvider; private Codec.EncoderFactory encoderFactory; private Muxer.Factory muxerFactory; private Looper looper; @@ -103,6 +105,7 @@ public Builder(Context context) { audioProcessors = ImmutableList.of(); videoEffects = ImmutableList.of(); videoFrameProcessorFactory = new DefaultVideoFrameProcessor.Factory(); + glObjectsProvider = GlObjectsProvider.DEFAULT; encoderFactory = new DefaultEncoderFactory.Builder(this.context).build(); muxerFactory = new DefaultMuxer.Factory(); looper = Util.getCurrentOrMainLooper(); @@ -122,6 +125,7 @@ private Builder(Transformer transformer) { this.listeners = transformer.listeners; this.assetLoaderFactory = transformer.assetLoaderFactory; this.videoFrameProcessorFactory = transformer.videoFrameProcessorFactory; + this.glObjectsProvider = transformer.glObjectsProvider; this.encoderFactory = transformer.encoderFactory; this.muxerFactory = transformer.muxerFactory; this.looper = transformer.looper; @@ -395,6 +399,7 @@ public Transformer build() { listeners, assetLoaderFactory, videoFrameProcessorFactory, + glObjectsProvider, encoderFactory, muxerFactory, looper, @@ -554,6 +559,7 @@ default void onFallbackApplied( private final ListenerSet listeners; private final AssetLoader.Factory assetLoaderFactory; private final VideoFrameProcessor.Factory videoFrameProcessorFactory; + private final GlObjectsProvider glObjectsProvider; private final Codec.EncoderFactory encoderFactory; private final Muxer.Factory muxerFactory; private final Looper looper; @@ -573,6 +579,7 @@ private Transformer( ListenerSet listeners, AssetLoader.Factory assetLoaderFactory, VideoFrameProcessor.Factory videoFrameProcessorFactory, + GlObjectsProvider glObjectsProvider, Codec.EncoderFactory encoderFactory, Muxer.Factory muxerFactory, Looper looper, @@ -589,6 +596,7 @@ private Transformer( this.listeners = listeners; this.assetLoaderFactory = assetLoaderFactory; this.videoFrameProcessorFactory = videoFrameProcessorFactory; + this.glObjectsProvider = glObjectsProvider; this.encoderFactory = encoderFactory; this.muxerFactory = muxerFactory; this.looper = looper; @@ -792,7 +800,9 @@ public void start(MediaItem mediaItem, String path) { .setRemoveAudio(removeAudio) .setRemoveVideo(removeVideo) .setFlattenForSlowMotion(flattenForSlowMotion) - .setEffects(new Effects(audioProcessors, videoEffects, videoFrameProcessorFactory)) + .setEffects( + new Effects( + audioProcessors, videoEffects, videoFrameProcessorFactory, glObjectsProvider)) .build(); start(editedMediaItem, path); } diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java index d4a02eaedbc..b3e9411db7e 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerInternal.java @@ -533,6 +533,7 @@ private SamplePipeline getSamplePipeline( firstEditedMediaItem.effects.videoEffects, compositionPresentation, firstEditedMediaItem.effects.videoFrameProcessorFactory, + firstEditedMediaItem.effects.glObjectsProvider, encoderFactory, muxerWrapper, /* errorConsumer= */ this::onError, diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoSamplePipeline.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoSamplePipeline.java index e89d416e19b..d4a131143b8 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoSamplePipeline.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoSamplePipeline.java @@ -37,6 +37,7 @@ 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.GlObjectsProvider; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Size; @@ -81,6 +82,7 @@ public VideoSamplePipeline( ImmutableList effects, @Nullable Presentation presentation, VideoFrameProcessor.Factory videoFrameProcessorFactory, + GlObjectsProvider glObjectsProvider, Codec.EncoderFactory encoderFactory, MuxerWrapper muxerWrapper, Consumer errorConsumer, @@ -129,52 +131,54 @@ public VideoSamplePipeline( } try { videoFrameProcessor = - videoFrameProcessorFactory.create( - context, - effectsWithPresentation, - debugViewProvider, - videoFrameProcessorInputColor, - videoFrameProcessorOutputColor, - MimeTypes.isVideo(firstInputFormat.sampleMimeType), - /* releaseFramesAutomatically= */ true, - MoreExecutors.directExecutor(), - new VideoFrameProcessor.Listener() { - private long lastProcessedFramePresentationTimeUs; - - @Override - public void onOutputSizeChanged(int width, int height) { - try { - checkNotNull(videoFrameProcessor) - .setOutputSurfaceInfo(encoderWrapper.getSurfaceInfo(width, height)); - } catch (ExportException exception) { - errorConsumer.accept(exception); - } - } - - @Override - public void onOutputFrameAvailable(long presentationTimeUs) { - // Frames are released automatically. - lastProcessedFramePresentationTimeUs = presentationTimeUs; - } - - @Override - public void onError(VideoFrameProcessingException exception) { - errorConsumer.accept( - ExportException.createForVideoFrameProcessingException( - exception, ExportException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED)); - } - - @Override - public void onEnded() { - VideoSamplePipeline.this.finalFramePresentationTimeUs = - lastProcessedFramePresentationTimeUs; - try { - encoderWrapper.signalEndOfInputStream(); - } catch (ExportException exception) { - errorConsumer.accept(exception); - } - } - }); + videoFrameProcessorFactory + .setGlObjectsProvider(glObjectsProvider) + .create( + context, + effectsWithPresentation, + debugViewProvider, + videoFrameProcessorInputColor, + videoFrameProcessorOutputColor, + MimeTypes.isVideo(firstInputFormat.sampleMimeType), + /* releaseFramesAutomatically= */ true, + MoreExecutors.directExecutor(), + new VideoFrameProcessor.Listener() { + private long lastProcessedFramePresentationTimeUs; + + @Override + public void onOutputSizeChanged(int width, int height) { + try { + checkNotNull(videoFrameProcessor) + .setOutputSurfaceInfo(encoderWrapper.getSurfaceInfo(width, height)); + } catch (ExportException exception) { + errorConsumer.accept(exception); + } + } + + @Override + public void onOutputFrameAvailable(long presentationTimeUs) { + // Frames are released automatically. + lastProcessedFramePresentationTimeUs = presentationTimeUs; + } + + @Override + public void onError(VideoFrameProcessingException exception) { + errorConsumer.accept( + ExportException.createForVideoFrameProcessingException( + exception, ExportException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED)); + } + + @Override + public void onEnded() { + VideoSamplePipeline.this.finalFramePresentationTimeUs = + lastProcessedFramePresentationTimeUs; + try { + encoderWrapper.signalEndOfInputStream(); + } catch (ExportException exception) { + errorConsumer.accept(exception); + } + } + }); } catch (VideoFrameProcessingException e) { throw ExportException.createForVideoFrameProcessingException( e, ExportException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED);