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 84c06d3e49..82ddfc3632 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 androidx.media3.common.util.LibraryLoader; import androidx.media3.common.util.Util; import androidx.media3.effect.GlTextureProcessor; import androidx.media3.effect.TextureInfo; +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/libraries/common/src/main/java/androidx/media3/common/FrameProcessor.java b/libraries/common/src/main/java/androidx/media3/common/FrameProcessor.java index fb3bfd9421..2227b8b6c3 100644 --- a/libraries/common/src/main/java/androidx/media3/common/FrameProcessor.java +++ b/libraries/common/src/main/java/androidx/media3/common/FrameProcessor.java @@ -21,6 +21,7 @@ import android.view.Surface; import androidx.annotation.Nullable; import androidx.media3.common.util.UnstableApi; import java.util.List; +import java.util.concurrent.Executor; /** * Interface for a frame processor that applies changes to individual video frames. @@ -45,6 +46,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. @@ -60,6 +62,7 @@ public interface FrameProcessor { FrameProcessor create( Context context, Listener listener, + Executor executor, List effects, DebugViewProvider debugViewProvider, ColorInfo colorInfo, @@ -70,7 +73,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/libraries/effect/src/androidTest/java/androidx/media3/effect/GlEffectsFrameProcessorFrameReleaseTest.java b/libraries/effect/src/androidTest/java/androidx/media3/effect/GlEffectsFrameProcessorFrameReleaseTest.java index a778613727..7c9caed531 100644 --- a/libraries/effect/src/androidTest/java/androidx/media3/effect/GlEffectsFrameProcessorFrameReleaseTest.java +++ b/libraries/effect/src/androidTest/java/androidx/media3/effect/GlEffectsFrameProcessorFrameReleaseTest.java @@ -32,9 +32,11 @@ import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.Util; import androidx.test.ext.junit.runners.AndroidJUnit4; 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/libraries/effect/src/androidTest/java/androidx/media3/effect/GlEffectsFrameProcessorPixelTest.java b/libraries/effect/src/androidTest/java/androidx/media3/effect/GlEffectsFrameProcessorPixelTest.java index 12dfef94be..80f49e5e2c 100644 --- a/libraries/effect/src/androidTest/java/androidx/media3/effect/GlEffectsFrameProcessorPixelTest.java +++ b/libraries/effect/src/androidTest/java/androidx/media3/effect/GlEffectsFrameProcessorPixelTest.java @@ -44,6 +44,7 @@ import androidx.media3.common.SurfaceInfo; import androidx.media3.test.utils.DecodeOneFrameUtil; import androidx.test.ext.junit.runners.AndroidJUnit4; 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/libraries/effect/src/main/java/androidx/media3/effect/FinalMatrixTextureProcessorWrapper.java b/libraries/effect/src/main/java/androidx/media3/effect/FinalMatrixTextureProcessorWrapper.java index 0b78b6e779..f7fb6d0660 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/FinalMatrixTextureProcessorWrapper.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/FinalMatrixTextureProcessorWrapper.java @@ -44,6 +44,7 @@ import androidx.media3.common.util.Util; 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/libraries/effect/src/main/java/androidx/media3/effect/GlEffectsFrameProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/GlEffectsFrameProcessor.java index 943e3eb1ff..29acf40bf7 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/GlEffectsFrameProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/GlEffectsFrameProcessor.java @@ -38,8 +38,10 @@ import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; 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; @@ -59,17 +61,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 = @@ -78,6 +84,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { createOpenGlObjectsAndFrameProcessor( context, listener, + listenerExecutor, effects, debugViewProvider, colorInfo, @@ -96,7 +103,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}. * @@ -108,7 +115,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, @@ -133,12 +141,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, @@ -164,7 +174,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) @@ -222,6 +233,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { matrixTransformationListBuilder.build(), rgbMatrixListBuilder.build(), listener, + executor, debugViewProvider, sampleFromExternalTexture, colorInfo, @@ -236,7 +248,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); @@ -246,7 +259,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/libraries/effect/src/main/java/androidx/media3/effect/GlTextureProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/GlTextureProcessor.java index c2e3f5b722..2a8d95cd14 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/GlTextureProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/GlTextureProcessor.java @@ -17,6 +17,7 @@ package androidx.media3.effect; import androidx.media3.common.FrameProcessingException; import androidx.media3.common.util.UnstableApi; +import java.util.concurrent.Executor; /** * Processes frames from one OpenGL 2D texture to another. @@ -113,14 +114,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/libraries/effect/src/main/java/androidx/media3/effect/SingleFrameGlTextureProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/SingleFrameGlTextureProcessor.java index 04b88a55e0..777e747d26 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/SingleFrameGlTextureProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/SingleFrameGlTextureProcessor.java @@ -22,6 +22,8 @@ import androidx.annotation.CallSuper; import androidx.media3.common.FrameProcessingException; import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.UnstableApi; +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; @@ -43,6 +45,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; @@ -59,6 +62,7 @@ public abstract class SingleFrameGlTextureProcessor implements GlTextureProcesso inputListener = new InputListener() {}; outputListener = new OutputListener() {}; errorListener = (frameProcessingException) -> {}; + errorListenerExecutor = MoreExecutors.directExecutor(); } /** @@ -104,7 +108,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; } @@ -129,10 +134,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/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java index 87be2f58cc..9b1821b9b2 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java @@ -42,6 +42,7 @@ import androidx.media3.decoder.DecoderInputBuffer; import androidx.media3.effect.Presentation; import androidx.media3.effect.ScaleToFitTransformation; 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.