From 4d518806eade7dbc82add865857e2321b29e4a19 Mon Sep 17 00:00:00 2001 From: christosts Date: Thu, 24 Feb 2022 14:38:33 +0000 Subject: [PATCH] Remove experimental flag for AsynchronousMediaCodecAdapter The AsyncronousMediaCodecAdapter should call MediaCodec.start() on the same thread it calls MediaCodec.flush(), i.e. the playback thread. This change removes the experimental flag that allowed calling MediaCodec.start() from the callback thread. The flag was flipped to true already. PiperOrigin-RevId: 430689665 --- .../exoplayer2/DefaultRenderersFactory.java | 19 ---------- .../AsynchronousMediaCodecAdapter.java | 36 ++++++------------- .../AsynchronousMediaCodecCallback.java | 18 ++-------- .../DefaultMediaCodecAdapterFactory.java | 23 +----------- .../AsynchronousMediaCodecAdapterTest.java | 3 +- .../AsynchronousMediaCodecCallbackTest.java | 26 +++++++------- 6 files changed, 28 insertions(+), 97 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/DefaultRenderersFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/DefaultRenderersFactory.java index 48b374802d5..467db322aa4 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/DefaultRenderersFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/DefaultRenderersFactory.java @@ -169,25 +169,6 @@ public DefaultRenderersFactory experimentalSetSynchronizeCodecInteractionsWithQu 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/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapter.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapter.java index 2154737c754..fe69f3f1247 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapter.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapter.java @@ -54,7 +54,6 @@ public static final class Factory implements MediaCodecAdapter.Factory { 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 @@ public static final class Factory implements MediaCodecAdapter.Factory { * 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 @@ public AsynchronousMediaCodecAdapter createAdapter(Configuration configuration) codec, callbackThreadSupplier.get(), queueingThreadSupplier.get(), - synchronizeCodecInteractionsWithQueueing, - enableImmediateCodecStartAfterFlush); + synchronizeCodecInteractionsWithQueueing); TraceUtil.endSection(); codecAdapter.initialize( configuration.mediaFormat, @@ -139,7 +131,6 @@ public AsynchronousMediaCodecAdapter createAdapter(Configuration configuration) 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 @@ private AsynchronousMediaCodecAdapter( 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 @@ public void flush() { // 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/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecCallback.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecCallback.java index be44abb2903..a4a83c0deef 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecCallback.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecCallback.java @@ -191,14 +191,11 @@ public MediaFormat getOutputFormat() { /** * 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 @@ public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) { } } - private void onFlushCompleted(@Nullable MediaCodec codec) { + private void onFlushCompleted() { synchronized (lock) { if (shutDown) { return; @@ -254,15 +251,6 @@ private void onFlushCompleted(@Nullable MediaCodec codec) { return; } flushInternal(); - if (codec != null) { - try { - codec.start(); - } catch (IllegalStateException e) { - setInternalException(e); - } catch (Exception e) { - setInternalException(new IllegalStateException(e)); - } - } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/DefaultMediaCodecAdapterFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/DefaultMediaCodecAdapterFactory.java index 9807a50c096..0c47db3c64c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/DefaultMediaCodecAdapterFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/DefaultMediaCodecAdapterFactory.java @@ -17,7 +17,6 @@ import static java.lang.annotation.ElementType.TYPE_USE; -import android.media.MediaCodec; import androidx.annotation.IntDef; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.MimeTypes; @@ -53,11 +52,9 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter. private @Mode int asynchronousMode; private boolean enableSynchronizeCodecInteractionsWithQueueing; - private boolean enableImmediateCodecStartAfterFlush; public DefaultMediaCodecAdapterFactory() { asynchronousMode = MODE_DEFAULT; - enableImmediateCodecStartAfterFlush = true; } /** @@ -94,22 +91,6 @@ public void experimentalSetSynchronizeCodecInteractionsWithQueueingEnabled(boole 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 { @@ -122,9 +103,7 @@ public MediaCodecAdapter createAdapter(MediaCodecAdapter.Configuration configura + 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/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapterTest.java b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapterTest.java index 3aacf01791e..9aa2b5f3ba2 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapterTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapterTest.java @@ -54,8 +54,7 @@ public void setUp() throws Exception { 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/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecCallbackTest.java b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecCallbackTest.java index 39d01844d21..99877105617 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecCallbackTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecCallbackTest.java @@ -94,7 +94,7 @@ public void dequeInputBufferIndex_withPendingFlush_returnsTryAgain() { 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 void dequeInputBufferIndex_afterFlush_returnsTryAgain() { // 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 void dequeInputBufferIndex_afterFlushAndNewInputBuffer_returnsEnqueuedBuf // 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 void dequeOutputBufferIndex_withPendingFlush_returnsTryAgain() { 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 void dequeOutputBufferIndex_afterFlush_returnsTryAgain() { 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 void dequeOutputBufferIndex_afterFlushAndNewOutputBuffers_returnsEnqueueB 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 void dequeOutputBufferIndex_withPendingOutputFormat_returnsPendingOutputF 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 void dequeOutputBufferIndex_withPendingOutputFormatAndNewFormat_returnsNe 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 void getOutputFormat_afterFlush_returnsCurrentFormat() { 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 void getOutputFormat_afterFlushWithPendingFormat_returnsPendingFormat() { 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 void getOutputFormat_afterFlushWithPendingFormat_returnsPendingFormat() { 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 void getOutputFormat_afterFlushWithPendingFormat_returnsPendingFormat() { 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);