From 656b346e6c5210d002b1d1f02bd7c0f1ab1ebde9 Mon Sep 17 00:00:00 2001 From: claincly Date: Mon, 5 Dec 2022 16:40:34 +0000 Subject: [PATCH] Allow specifying an Executor in FrameProcessor.Factory.create() From this CL on, FrameProcessor listeners will be invoked from an Executor that is passed in when creating the FrameProcessor. GlTextureProcessor needs to invoke the ErrorListener on the said Executor too. PiperOrigin-RevId: 493018583 --- .../demo/transformer/MediaPipeProcessor.java | 33 ++++++++++++++----- .../exoplayer2/util/FrameProcessor.java | 6 +++- ...EffectsFrameProcessorFrameReleaseTest.java | 5 ++- .../GlEffectsFrameProcessorPixelTest.java | 2 ++ .../FinalMatrixTextureProcessorWrapper.java | 29 +++++++++++----- .../effect/GlEffectsFrameProcessor.java | 30 ++++++++++++----- .../exoplayer2/effect/GlTextureProcessor.java | 25 +++++++++++--- .../effect/SingleFrameGlTextureProcessor.java | 17 +++++++--- .../VideoTranscodingSamplePipeline.java | 2 ++ 9 files changed, 113 insertions(+), 36 deletions(-) diff --git a/demos/transformer/src/withMediaPipe/java/androidx/media3/demo/transformer/MediaPipeProcessor.java b/demos/transformer/src/withMediaPipe/java/androidx/media3/demo/transformer/MediaPipeProcessor.java index 9e919a68e2..86dc3b8bba 100644 --- a/demos/transformer/src/withMediaPipe/java/androidx/media3/demo/transformer/MediaPipeProcessor.java +++ b/demos/transformer/src/withMediaPipe/java/androidx/media3/demo/transformer/MediaPipeProcessor.java @@ -29,6 +29,7 @@ import com.google.android.exoplayer2.effect.TextureInfo; import com.google.android.exoplayer2.util.FrameProcessingException; import com.google.android.exoplayer2.util.LibraryLoader; import com.google.android.exoplayer2.util.Util; +import com.google.common.util.concurrent.MoreExecutors; import com.google.mediapipe.components.FrameProcessor; import com.google.mediapipe.framework.AppTextureFrame; import com.google.mediapipe.framework.TextureFrame; @@ -37,6 +38,7 @@ import java.util.ArrayDeque; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -74,6 +76,7 @@ import java.util.concurrent.Future; private InputListener inputListener; private OutputListener outputListener; private ErrorListener errorListener; + private Executor errorListenerExecutor; private boolean acceptedFrame; /** @@ -110,6 +113,7 @@ import java.util.concurrent.Future; inputListener = new InputListener() {}; outputListener = new OutputListener() {}; errorListener = (frameProcessingException) -> {}; + errorListenerExecutor = MoreExecutors.directExecutor(); EglManager eglManager = new EglManager(EGL14.eglGetCurrentContext()); frameProcessor = new FrameProcessor( @@ -145,10 +149,13 @@ import java.util.concurrent.Future; } @Override - public void setErrorListener(ErrorListener errorListener) { + public void setErrorListener(Executor executor, ErrorListener errorListener) { + this.errorListenerExecutor = executor; this.errorListener = errorListener; frameProcessor.setAsynchronousErrorListener( - error -> errorListener.onFrameProcessingError(new FrameProcessingException(error))); + error -> + errorListenerExecutor.execute( + () -> errorListener.onFrameProcessingError(new FrameProcessingException(error)))); } @Override @@ -183,7 +190,8 @@ import java.util.concurrent.Future; appTextureFrame.waitUntilReleasedWithGpuSync(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - errorListener.onFrameProcessingError(new FrameProcessingException(e)); + errorListenerExecutor.execute( + () -> errorListener.onFrameProcessingError(new FrameProcessingException(e))); } if (acceptedFrame) { inputListener.onInputFrameProcessed(inputTexture); @@ -204,7 +212,10 @@ import java.util.concurrent.Future; } catch (InterruptedException e) { Thread.currentThread().interrupt(); if (errorListener != null) { - errorListener.onFrameProcessingError(new FrameProcessingException(e)); + errorListenerExecutor.execute( + () -> + errorListener.onFrameProcessingError( + new FrameProcessingException(e))); } } } @@ -236,11 +247,15 @@ import java.util.concurrent.Future; singleThreadExecutorService.shutdown(); try { if (!singleThreadExecutorService.awaitTermination(RELEASE_WAIT_TIME_MS, MILLISECONDS)) { - errorListener.onFrameProcessingError(new FrameProcessingException("Release timed out")); + errorListenerExecutor.execute( + () -> + errorListener.onFrameProcessingError( + new FrameProcessingException("Release timed out"))); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); - errorListener.onFrameProcessingError(new FrameProcessingException(e)); + errorListenerExecutor.execute( + () -> errorListener.onFrameProcessingError(new FrameProcessingException(e))); } frameProcessor.close(); @@ -272,10 +287,12 @@ import java.util.concurrent.Future; try { futures.remove().get(); } catch (ExecutionException e) { - errorListener.onFrameProcessingError(new FrameProcessingException(e)); + errorListenerExecutor.execute( + () -> errorListener.onFrameProcessingError(new FrameProcessingException(e))); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - errorListener.onFrameProcessingError(new FrameProcessingException(e)); + errorListenerExecutor.execute( + () -> errorListener.onFrameProcessingError(new FrameProcessingException(e))); } } } diff --git a/library/common/src/main/java/com/google/android/exoplayer2/util/FrameProcessor.java b/library/common/src/main/java/com/google/android/exoplayer2/util/FrameProcessor.java index 23ba3e7ab4..0c074b278f 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/util/FrameProcessor.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/util/FrameProcessor.java @@ -21,6 +21,7 @@ import android.view.Surface; import androidx.annotation.Nullable; import com.google.android.exoplayer2.video.ColorInfo; import java.util.List; +import java.util.concurrent.Executor; /** * Interface for a frame processor that applies changes to individual video frames. @@ -44,6 +45,7 @@ public interface FrameProcessor { * * @param context A {@link Context}. * @param listener A {@link Listener}. + * @param executor The {@link Executor} on which the {@code listener} is invoked. * @param effects The {@link Effect} instances to apply to each frame. * @param debugViewProvider A {@link DebugViewProvider}. * @param colorInfo The {@link ColorInfo} for input and output frames. @@ -59,6 +61,7 @@ public interface FrameProcessor { FrameProcessor create( Context context, Listener listener, + Executor executor, List effects, DebugViewProvider debugViewProvider, ColorInfo colorInfo, @@ -69,7 +72,8 @@ public interface FrameProcessor { /** * Listener for asynchronous frame processing events. * - *

All listener methods must be called from the same thread. + *

All listener methods must be called from the {@link Executor} passed in at {@linkplain + * Factory#create creation}. */ interface Listener { diff --git a/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessorFrameReleaseTest.java b/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessorFrameReleaseTest.java index 4b2290931b..b7f197b7d7 100644 --- a/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessorFrameReleaseTest.java +++ b/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessorFrameReleaseTest.java @@ -32,9 +32,11 @@ import com.google.android.exoplayer2.util.SurfaceInfo; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.ColorInfo; import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.MoreExecutors; import java.util.ArrayList; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; @@ -313,6 +315,7 @@ public final class GlEffectsFrameProcessorFrameReleaseTest { @Override public void onFrameProcessingEnded() {} }, + MoreExecutors.directExecutor(), ImmutableList.of( (GlEffect) (context, useHdr) -> @@ -362,7 +365,7 @@ public final class GlEffectsFrameProcessorFrameReleaseTest { } @Override - public void setErrorListener(ErrorListener errorListener) {} + public void setErrorListener(Executor executor, ErrorListener errorListener) {} @Override public void queueInputFrame(TextureInfo inputTexture, long presentationTimeUs) { diff --git a/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessorPixelTest.java b/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessorPixelTest.java index d8e92887d7..c611275009 100644 --- a/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessorPixelTest.java +++ b/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessorPixelTest.java @@ -44,6 +44,7 @@ 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 java.util.List; import java.util.concurrent.atomic.AtomicReference; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -441,6 +442,7 @@ public final class GlEffectsFrameProcessorPixelTest { frameProcessingEnded = true; } }, + MoreExecutors.directExecutor(), effects, DebugViewProvider.NONE, ColorInfo.SDR_BT709_LIMITED, diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/FinalMatrixTextureProcessorWrapper.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/FinalMatrixTextureProcessorWrapper.java index ac7b669c1e..f93a6b8879 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/FinalMatrixTextureProcessorWrapper.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/FinalMatrixTextureProcessorWrapper.java @@ -44,6 +44,7 @@ import com.google.android.exoplayer2.video.ColorInfo; import com.google.common.collect.ImmutableList; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -69,6 +70,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private final EGLContext eglContext; private final DebugViewProvider debugViewProvider; private final FrameProcessor.Listener frameProcessorListener; + private final Executor frameProcessorListenerExecutor; private final boolean sampleFromExternalTexture; private final ColorInfo colorInfo; private final boolean releaseFramesAutomatically; @@ -101,6 +103,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ImmutableList matrixTransformations, ImmutableList rgbMatrices, FrameProcessor.Listener frameProcessorListener, + Executor frameProcessorListenerExecutor, DebugViewProvider debugViewProvider, boolean sampleFromExternalTexture, ColorInfo colorInfo, @@ -112,6 +115,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; this.eglContext = eglContext; this.debugViewProvider = debugViewProvider; this.frameProcessorListener = frameProcessorListener; + this.frameProcessorListenerExecutor = frameProcessorListenerExecutor; this.sampleFromExternalTexture = sampleFromExternalTexture; this.colorInfo = colorInfo; this.releaseFramesAutomatically = releaseFramesAutomatically; @@ -135,7 +139,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } @Override - public void setErrorListener(ErrorListener errorListener) { + public void setErrorListener(Executor executor, ErrorListener errorListener) { // The FrameProcessor.Listener passed to the constructor is used for errors. throw new UnsupportedOperationException(); } @@ -145,7 +149,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; long streamOffsetUs = checkStateNotNull(streamOffsetUsQueue.peek(), "No input stream specified."); long offsetPresentationTimeUs = presentationTimeUs + streamOffsetUs; - frameProcessorListener.onOutputFrameAvailable(offsetPresentationTimeUs); + frameProcessorListenerExecutor.execute( + () -> frameProcessorListener.onOutputFrameAvailable(offsetPresentationTimeUs)); if (releaseFramesAutomatically) { renderFrameToSurfaces( inputTexture, presentationTimeUs, /* releaseTimeNs= */ offsetPresentationTimeUs * 1000); @@ -177,7 +182,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; streamOffsetUsQueue.remove(); if (streamOffsetUsQueue.isEmpty()) { - frameProcessorListener.onFrameProcessingEnded(); + frameProcessorListenerExecutor.execute(frameProcessorListener::onFrameProcessingEnded); } } @@ -234,7 +239,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; try { GlUtil.destroyEglSurface(eglDisplay, outputEglSurface); } catch (GlUtil.GlException e) { - frameProcessorListener.onFrameProcessingError(FrameProcessingException.from(e)); + frameProcessorListenerExecutor.execute( + () -> + frameProcessorListener.onFrameProcessingError(FrameProcessingException.from(e))); } this.outputEglSurface = null; } @@ -253,8 +260,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; try { maybeRenderFrameToOutputSurface(inputTexture, presentationTimeUs, releaseTimeNs); } catch (FrameProcessingException | GlUtil.GlException e) { - frameProcessorListener.onFrameProcessingError( - FrameProcessingException.from(e, presentationTimeUs)); + frameProcessorListenerExecutor.execute( + () -> + frameProcessorListener.onFrameProcessingError( + FrameProcessingException.from(e, presentationTimeUs))); } maybeRenderFrameToDebugSurface(inputTexture, presentationTimeUs); inputListener.onInputFrameProcessed(inputTexture); @@ -306,9 +315,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; if (!Util.areEqual( this.outputSizeBeforeSurfaceTransformation, outputSizeBeforeSurfaceTransformation)) { this.outputSizeBeforeSurfaceTransformation = outputSizeBeforeSurfaceTransformation; - frameProcessorListener.onOutputSizeChanged( - outputSizeBeforeSurfaceTransformation.first, - outputSizeBeforeSurfaceTransformation.second); + frameProcessorListenerExecutor.execute( + () -> + frameProcessorListener.onOutputSizeChanged( + outputSizeBeforeSurfaceTransformation.first, + outputSizeBeforeSurfaceTransformation.second)); } } diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessor.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessor.java index 4d5ad5ee0e..3c068c0c04 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessor.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlEffectsFrameProcessor.java @@ -37,8 +37,10 @@ import com.google.android.exoplayer2.util.SurfaceInfo; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.ColorInfo; import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.MoreExecutors; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -57,17 +59,21 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { *

All {@link Effect} instances must be {@link GlEffect} instances. * *

Using HDR requires the {@code EXT_YUV_target} OpenGL extension. + * + *

Pass a {@link MoreExecutors#directExecutor() direct listenerExecutor} if invoking the + * {@code listener} on {@link GlEffectsFrameProcessor}'s internal thread is desired. */ @Override public GlEffectsFrameProcessor create( Context context, - FrameProcessor.Listener listener, + Listener listener, + Executor listenerExecutor, List effects, DebugViewProvider debugViewProvider, ColorInfo colorInfo, boolean releaseFramesAutomatically) throws FrameProcessingException { - + // TODO(b/261188041) Add tests to verify the Listener is invoked on the given Executor. ExecutorService singleThreadExecutorService = Util.newSingleThreadExecutor(THREAD_NAME); Future glFrameProcessorFuture = @@ -76,6 +82,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { createOpenGlObjectsAndFrameProcessor( context, listener, + listenerExecutor, effects, debugViewProvider, colorInfo, @@ -94,7 +101,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { } /** - * Creates the OpenGL context, surfaces, textures, and framebuffers, initializes {@link + * Creates the OpenGL context, surfaces, textures, and frame buffers, initializes {@link * GlTextureProcessor} instances corresponding to the {@link GlEffect} instances, and returns a * new {@code GlEffectsFrameProcessor}. * @@ -106,7 +113,8 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { @WorkerThread private static GlEffectsFrameProcessor createOpenGlObjectsAndFrameProcessor( Context context, - FrameProcessor.Listener listener, + Listener listener, + Executor executor, List effects, DebugViewProvider debugViewProvider, ColorInfo colorInfo, @@ -131,12 +139,14 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { eglDisplay, eglContext, listener, + executor, debugViewProvider, colorInfo, releaseFramesAutomatically); FrameProcessingTaskExecutor frameProcessingTaskExecutor = new FrameProcessingTaskExecutor(singleThreadExecutorService, listener); - chainTextureProcessorsWithListeners(textureProcessors, frameProcessingTaskExecutor, listener); + chainTextureProcessorsWithListeners( + textureProcessors, frameProcessingTaskExecutor, listener, executor); return new GlEffectsFrameProcessor( eglDisplay, @@ -162,7 +172,8 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { List effects, EGLDisplay eglDisplay, EGLContext eglContext, - FrameProcessor.Listener listener, + Listener listener, + Executor executor, DebugViewProvider debugViewProvider, ColorInfo colorInfo, boolean releaseFramesAutomatically) @@ -220,6 +231,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { matrixTransformationListBuilder.build(), rgbMatrixListBuilder.build(), listener, + executor, debugViewProvider, sampleFromExternalTexture, colorInfo, @@ -234,7 +246,8 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { private static void chainTextureProcessorsWithListeners( ImmutableList textureProcessors, FrameProcessingTaskExecutor frameProcessingTaskExecutor, - FrameProcessor.Listener frameProcessorListener) { + Listener frameProcessorListener, + Executor frameProcessorListenerExecutor) { for (int i = 0; i < textureProcessors.size() - 1; i++) { GlTextureProcessor producingGlTextureProcessor = textureProcessors.get(i); GlTextureProcessor consumingGlTextureProcessor = textureProcessors.get(i + 1); @@ -244,7 +257,8 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { consumingGlTextureProcessor, frameProcessingTaskExecutor); producingGlTextureProcessor.setOutputListener(chainingGlTextureProcessorListener); - producingGlTextureProcessor.setErrorListener(frameProcessorListener::onFrameProcessingError); + producingGlTextureProcessor.setErrorListener( + frameProcessorListenerExecutor, frameProcessorListener::onFrameProcessingError); consumingGlTextureProcessor.setInputListener(chainingGlTextureProcessorListener); } } diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlTextureProcessor.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlTextureProcessor.java index 5302be764d..a6fea91fbf 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlTextureProcessor.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/GlTextureProcessor.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.effect; import com.google.android.exoplayer2.util.FrameProcessingException; +import java.util.concurrent.Executor; /** * Processes frames from one OpenGL 2D texture to another. @@ -111,14 +112,30 @@ public interface GlTextureProcessor { void onFrameProcessingError(FrameProcessingException e); } - /** Sets the {@link InputListener}. */ + /** + * Sets the {@link InputListener}. + * + *

The {@link InputListener} should be invoked on the thread that owns the parent OpenGL + * context. For example, {@link GlEffectsFrameProcessor} invokes the {@link InputListener} methods + * on its internal thread. + */ void setInputListener(InputListener inputListener); - /** Sets the {@link OutputListener}. */ + /** + * Sets the {@link OutputListener}. + * + *

The {@link OutputListener} should be invoked on the thread that owns the parent OpenGL + * context. For example, {@link GlEffectsFrameProcessor} invokes the {@link OutputListener} + * methods on its internal thread. + */ void setOutputListener(OutputListener outputListener); - /** Sets the {@link ErrorListener}. */ - void setErrorListener(ErrorListener errorListener); + /** + * Sets the {@link ErrorListener}. + * + *

The {@link ErrorListener} is invoked on the provided {@link Executor}. + */ + void setErrorListener(Executor executor, ErrorListener errorListener); /** * Processes an input frame if possible. diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/SingleFrameGlTextureProcessor.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/SingleFrameGlTextureProcessor.java index eda1dffa08..5cb3fc6530 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/SingleFrameGlTextureProcessor.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/SingleFrameGlTextureProcessor.java @@ -21,6 +21,8 @@ import android.util.Pair; import androidx.annotation.CallSuper; import com.google.android.exoplayer2.util.FrameProcessingException; import com.google.android.exoplayer2.util.GlUtil; +import com.google.common.util.concurrent.MoreExecutors; +import java.util.concurrent.Executor; import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -41,6 +43,7 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso private InputListener inputListener; private OutputListener outputListener; private ErrorListener errorListener; + private Executor errorListenerExecutor; private int inputWidth; private int inputHeight; private @MonotonicNonNull TextureInfo outputTexture; @@ -57,6 +60,7 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso inputListener = new InputListener() {}; outputListener = new OutputListener() {}; errorListener = (frameProcessingException) -> {}; + errorListenerExecutor = MoreExecutors.directExecutor(); } /** @@ -102,7 +106,8 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso } @Override - public final void setErrorListener(ErrorListener errorListener) { + public final void setErrorListener(Executor errorListenerExecutor, ErrorListener errorListener) { + this.errorListenerExecutor = errorListenerExecutor; this.errorListener = errorListener; } @@ -127,10 +132,12 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso inputListener.onInputFrameProcessed(inputTexture); outputListener.onOutputFrameAvailable(outputTexture, presentationTimeUs); } catch (FrameProcessingException | GlUtil.GlException | RuntimeException e) { - errorListener.onFrameProcessingError( - e instanceof FrameProcessingException - ? (FrameProcessingException) e - : new FrameProcessingException(e)); + errorListenerExecutor.execute( + () -> + errorListener.onFrameProcessingError( + e instanceof FrameProcessingException + ? (FrameProcessingException) e + : new FrameProcessingException(e))); } } diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoTranscodingSamplePipeline.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoTranscodingSamplePipeline.java index 40d3aa804a..db93d36f95 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoTranscodingSamplePipeline.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/VideoTranscodingSamplePipeline.java @@ -42,6 +42,7 @@ import com.google.android.exoplayer2.util.SurfaceInfo; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.ColorInfo; import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.MoreExecutors; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -195,6 +196,7 @@ import org.checkerframework.dataflow.qual.Pure; } } }, + MoreExecutors.directExecutor(), effectsListBuilder.build(), debugViewProvider, // HDR colors are only used if the MediaCodec encoder supports FEATURE_HdrEditing.