diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java index 230fc2e03b..b817109c2b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java @@ -171,25 +171,6 @@ public class DefaultRenderersFactory implements RenderersFactory { return this; } - /** - * Enable calling {@link MediaCodec#start} immediately after {@link MediaCodec#flush} on the - * playback thread, when operating the codec in asynchronous mode. If disabled, {@link - * MediaCodec#start} will be called by the callback thread after pending callbacks are handled. - * - *

By default, this feature is disabled. - * - *

This method is experimental, and will be renamed or removed in a future release. - * - * @param enabled Whether {@link MediaCodec#start} will be called on the playback thread - * immediately after {@link MediaCodec#flush}. - * @return This factory, for convenience. - */ - public DefaultRenderersFactory experimentalSetImmediateCodecStartAfterFlushEnabled( - boolean enabled) { - codecAdapterFactory.experimentalSetImmediateCodecStartAfterFlushEnabled(enabled); - return this; - } - /** * Sets whether to enable fallback to lower-priority decoders if decoder initialization fails. * This may result in using a decoder that is less efficient or slower than the primary decoder. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java index e013c4b990..91aa86ca06 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java @@ -54,7 +54,6 @@ import java.nio.ByteBuffer; private final Supplier callbackThreadSupplier; private final Supplier queueingThreadSupplier; private final boolean synchronizeCodecInteractionsWithQueueing; - private final boolean enableImmediateCodecStartAfterFlush; /** * Creates an factory for {@link AsynchronousMediaCodecAdapter} instances. @@ -66,29 +65,23 @@ import java.nio.ByteBuffer; * interactions will wait until all input buffers pending queueing wil be submitted to the * {@link MediaCodec}. */ - public Factory( - @C.TrackType int trackType, - boolean synchronizeCodecInteractionsWithQueueing, - boolean enableImmediateCodecStartAfterFlush) { + public Factory(@C.TrackType int trackType, boolean synchronizeCodecInteractionsWithQueueing) { this( /* callbackThreadSupplier= */ () -> new HandlerThread(createCallbackThreadLabel(trackType)), /* queueingThreadSupplier= */ () -> new HandlerThread(createQueueingThreadLabel(trackType)), - synchronizeCodecInteractionsWithQueueing, - enableImmediateCodecStartAfterFlush); + synchronizeCodecInteractionsWithQueueing); } @VisibleForTesting /* package */ Factory( Supplier callbackThreadSupplier, Supplier queueingThreadSupplier, - boolean synchronizeCodecInteractionsWithQueueing, - boolean enableImmediateCodecStartAfterFlush) { + boolean synchronizeCodecInteractionsWithQueueing) { this.callbackThreadSupplier = callbackThreadSupplier; this.queueingThreadSupplier = queueingThreadSupplier; this.synchronizeCodecInteractionsWithQueueing = synchronizeCodecInteractionsWithQueueing; - this.enableImmediateCodecStartAfterFlush = enableImmediateCodecStartAfterFlush; } @Override @@ -105,8 +98,7 @@ import java.nio.ByteBuffer; codec, callbackThreadSupplier.get(), queueingThreadSupplier.get(), - synchronizeCodecInteractionsWithQueueing, - enableImmediateCodecStartAfterFlush); + synchronizeCodecInteractionsWithQueueing); TraceUtil.endSection(); codecAdapter.initialize( configuration.mediaFormat, @@ -139,7 +131,6 @@ import java.nio.ByteBuffer; private final AsynchronousMediaCodecCallback asynchronousMediaCodecCallback; private final AsynchronousMediaCodecBufferEnqueuer bufferEnqueuer; private final boolean synchronizeCodecInteractionsWithQueueing; - private final boolean enableImmediateCodecStartAfterFlush; private boolean codecReleased; private @State int state; @@ -147,13 +138,11 @@ import java.nio.ByteBuffer; MediaCodec codec, HandlerThread callbackThread, HandlerThread enqueueingThread, - boolean synchronizeCodecInteractionsWithQueueing, - boolean enableImmediateCodecStartAfterFlush) { + boolean synchronizeCodecInteractionsWithQueueing) { this.codec = codec; this.asynchronousMediaCodecCallback = new AsynchronousMediaCodecCallback(callbackThread); this.bufferEnqueuer = new AsynchronousMediaCodecBufferEnqueuer(codec, enqueueingThread); this.synchronizeCodecInteractionsWithQueueing = synchronizeCodecInteractionsWithQueueing; - this.enableImmediateCodecStartAfterFlush = enableImmediateCodecStartAfterFlush; this.state = STATE_CREATED; } @@ -232,18 +221,13 @@ import java.nio.ByteBuffer; // The order of calls is important: // 1. Flush the bufferEnqueuer to stop queueing input buffers. // 2. Flush the codec to stop producing available input/output buffers. - // 3. Flush the callback after flushing the codec so that in-flight callbacks are discarded. + // 3. Flush the callback so that in-flight callbacks are discarded. + // 4. Start the codec. The asynchronous callback will drop pending callbacks and we can start + // the codec now. bufferEnqueuer.flush(); codec.flush(); - if (enableImmediateCodecStartAfterFlush) { - // The asynchronous callback will drop pending callbacks but we can start the codec now. - asynchronousMediaCodecCallback.flush(/* codec= */ null); - codec.start(); - } else { - // Let the asynchronous callback start the codec in the callback thread after pending - // callbacks are handled. - asynchronousMediaCodecCallback.flush(codec); - } + asynchronousMediaCodecCallback.flush(); + codec.start(); } @Override diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java index a3c5d44d6d..67c6649d27 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java @@ -191,14 +191,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * Initiates a flush asynchronously, which will be completed on the callback thread. When the * flush is complete, it will trigger {@code onFlushCompleted} from the callback thread. - * - * @param codec A {@link MediaCodec} to {@link MediaCodec#start start} after all pending callbacks - * are handled, or {@code null} if starting the {@link MediaCodec} is performed elsewhere. */ - public void flush(@Nullable MediaCodec codec) { + public void flush() { synchronized (lock) { ++pendingFlushCount; - Util.castNonNull(handler).post(() -> this.onFlushCompleted(codec)); + Util.castNonNull(handler).post(this::onFlushCompleted); } } @@ -238,7 +235,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } } - private void onFlushCompleted(@Nullable MediaCodec codec) { + private void onFlushCompleted() { synchronized (lock) { if (shutDown) { return; @@ -254,15 +251,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; return; } flushInternal(); - if (codec != null) { - try { - codec.start(); - } catch (IllegalStateException e) { - setInternalException(e); - } catch (Exception e) { - setInternalException(new IllegalStateException(e)); - } - } } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java index 03e5aafd44..8dea9596c8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java @@ -17,7 +17,6 @@ package androidx.media3.exoplayer.mediacodec; import static java.lang.annotation.ElementType.TYPE_USE; -import android.media.MediaCodec; import androidx.annotation.IntDef; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.Log; @@ -55,11 +54,9 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter. private @Mode int asynchronousMode; private boolean enableSynchronizeCodecInteractionsWithQueueing; - private boolean enableImmediateCodecStartAfterFlush; public DefaultMediaCodecAdapterFactory() { asynchronousMode = MODE_DEFAULT; - enableImmediateCodecStartAfterFlush = true; } /** @@ -96,22 +93,6 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter. enableSynchronizeCodecInteractionsWithQueueing = enabled; } - /** - * Enable calling {@link MediaCodec#start} immediately after {@link MediaCodec#flush} on the - * playback thread, when operating the codec in asynchronous mode. If disabled, {@link - * MediaCodec#start} will be called by the callback thread after pending callbacks are handled. - * - *

By default, this feature is enabled. - * - *

This method is experimental, and will be renamed or removed in a future release. - * - * @param enabled Whether {@link MediaCodec#start()} will be called on the playback thread - * immediately after {@link MediaCodec#flush}. - */ - public void experimentalSetImmediateCodecStartAfterFlushEnabled(boolean enabled) { - enableImmediateCodecStartAfterFlush = enabled; - } - @Override public MediaCodecAdapter createAdapter(MediaCodecAdapter.Configuration configuration) throws IOException { @@ -124,9 +105,7 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter. + Util.getTrackTypeString(trackType)); AsynchronousMediaCodecAdapter.Factory factory = new AsynchronousMediaCodecAdapter.Factory( - trackType, - enableSynchronizeCodecInteractionsWithQueueing, - enableImmediateCodecStartAfterFlush); + trackType, enableSynchronizeCodecInteractionsWithQueueing); return factory.createAdapter(configuration); } return new SynchronousMediaCodecAdapter.Factory().createAdapter(configuration); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapterTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapterTest.java index 0401f95e1d..98707a3226 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapterTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapterTest.java @@ -54,8 +54,7 @@ public class AsynchronousMediaCodecAdapterTest { new AsynchronousMediaCodecAdapter.Factory( /* callbackThreadSupplier= */ () -> callbackThread, /* queueingThreadSupplier= */ () -> queueingThread, - /* synchronizeCodecInteractionsWithQueueing= */ false, - /* enableImmediateCodecStartAfterFlush= */ false) + /* synchronizeCodecInteractionsWithQueueing= */ false) .createAdapter(configuration); bufferInfo = new MediaCodec.BufferInfo(); // After starting the MediaCodec, the ShadowMediaCodec offers input buffer 0. We advance the diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallbackTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallbackTest.java index fea229347e..81df9fd6fb 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallbackTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallbackTest.java @@ -94,7 +94,7 @@ public class AsynchronousMediaCodecCallbackTest { asynchronousMediaCodecCallback.onInputBufferAvailable(codec, 0); asynchronousMediaCodecCallback.onInputBufferAvailable(codec, 1); callbackHandler.post(() -> beforeFlushCompletes.set(true)); - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); callbackHandler.post(() -> flushCompleted.set(true)); while (!beforeFlushCompletes.get()) { shadowCallbackLooper.runOneTask(); @@ -113,7 +113,7 @@ public class AsynchronousMediaCodecCallbackTest { // Send two input buffers to the callback and then flush(). asynchronousMediaCodecCallback.onInputBufferAvailable(codec, 0); asynchronousMediaCodecCallback.onInputBufferAvailable(codec, 1); - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); new Handler(callbackThreadLooper).post(() -> flushCompleted.set(true)); // Progress the callback thread so that flush() completes. shadowOf(callbackThreadLooper).idle(); @@ -132,7 +132,7 @@ public class AsynchronousMediaCodecCallbackTest { // another input buffer. asynchronousMediaCodecCallback.onInputBufferAvailable(codec, 0); asynchronousMediaCodecCallback.onInputBufferAvailable(codec, 1); - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); new Handler(callbackThreadLooper).post(() -> flushCompleted.set(true)); // Progress the callback thread to complete flush. shadowOf(callbackThread.getLooper()).idle(); @@ -207,7 +207,7 @@ public class AsynchronousMediaCodecCallbackTest { asynchronousMediaCodecCallback.onOutputBufferAvailable(codec, 0, bufferInfo); asynchronousMediaCodecCallback.onOutputBufferAvailable(codec, 1, bufferInfo); callbackHandler.post(() -> beforeFlushCompletes.set(true)); - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); callbackHandler.post(() -> flushCompleted.set(true)); while (beforeFlushCompletes.get()) { shadowCallbackLooper.runOneTask(); @@ -227,7 +227,7 @@ public class AsynchronousMediaCodecCallbackTest { MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); asynchronousMediaCodecCallback.onOutputBufferAvailable(codec, 0, bufferInfo); asynchronousMediaCodecCallback.onOutputBufferAvailable(codec, 1, bufferInfo); - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); new Handler(callbackThreadLooper).post(() -> flushCompleted.set(true)); // Progress the callback looper so that flush() completes. shadowOf(callbackThreadLooper).idle(); @@ -248,7 +248,7 @@ public class AsynchronousMediaCodecCallbackTest { asynchronousMediaCodecCallback.onOutputFormatChanged(codec, createMediaFormat("format0")); asynchronousMediaCodecCallback.onOutputBufferAvailable(codec, 0, bufferInfo); asynchronousMediaCodecCallback.onOutputBufferAvailable(codec, 1, bufferInfo); - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); new Handler(callbackThreadLooper).post(() -> flushCompleted.set(true)); // Progress the callback looper so that flush() completes. shadowOf(callbackThreadLooper).idle(); @@ -275,7 +275,7 @@ public class AsynchronousMediaCodecCallbackTest { MediaFormat pendingMediaFormat = new MediaFormat(); asynchronousMediaCodecCallback.onOutputFormatChanged(codec, pendingMediaFormat); // flush() should not discard the last format. - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); new Handler(callbackThreadLooper).post(() -> flushCompleted.set(true)); // Progress the callback looper so that flush() completes. shadowOf(callbackThreadLooper).idle(); @@ -302,7 +302,7 @@ public class AsynchronousMediaCodecCallbackTest { MediaFormat pendingMediaFormat = new MediaFormat(); asynchronousMediaCodecCallback.onOutputFormatChanged(codec, pendingMediaFormat); // flush() should not discard the last format. - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); new Handler(callbackThreadLooper).post(() -> flushCompleted.set(true)); // Progress the callback looper so that flush() completes. shadowOf(callbackThreadLooper).idle(); @@ -367,7 +367,7 @@ public class AsynchronousMediaCodecCallbackTest { asynchronousMediaCodecCallback.onOutputFormatChanged(codec, format); asynchronousMediaCodecCallback.dequeueOutputBufferIndex(new MediaCodec.BufferInfo()); - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); new Handler(callbackThreadLooper).post(() -> flushCompleted.set(true)); // Progress the callback looper so that flush() completes. shadowOf(callbackThreadLooper).idle(); @@ -390,7 +390,7 @@ public class AsynchronousMediaCodecCallbackTest { asynchronousMediaCodecCallback.onOutputFormatChanged(codec, createMediaFormat("format1")); asynchronousMediaCodecCallback.onOutputBufferAvailable( codec, /* index= */ 1, new MediaCodec.BufferInfo()); - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); new Handler(callbackThreadLooper).post(() -> flushCompleted.set(true)); // Progress the looper so that flush is completed shadowCallbackLooper.idle(); @@ -419,11 +419,11 @@ public class AsynchronousMediaCodecCallbackTest { asynchronousMediaCodecCallback.onOutputBufferAvailable( codec, /* index= */ 0, new MediaCodec.BufferInfo()); // Flush and progress the looper so that flush is completed. - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); callbackThreadHandler.post(flushCompleted::incrementAndGet); shadowCallbackLooper.idle(); // Flush again, the pending format from the first flush should remain as pending. - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); callbackThreadHandler.post(flushCompleted::incrementAndGet); shadowCallbackLooper.idle(); asynchronousMediaCodecCallback.onOutputBufferAvailable( @@ -441,7 +441,7 @@ public class AsynchronousMediaCodecCallbackTest { public void flush_withPendingError_resetsError() throws Exception { asynchronousMediaCodecCallback.onError(codec, createCodecException()); // Calling flush should clear any pending error. - asynchronousMediaCodecCallback.flush(/* codec= */ null); + asynchronousMediaCodecCallback.flush(); assertThat(asynchronousMediaCodecCallback.dequeueInputBufferIndex()) .isEqualTo(MediaCodec.INFO_TRY_AGAIN_LATER);