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 ad58917160..4657922d45 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 @@ -92,6 +92,7 @@ public class DefaultRenderersFactory implements RenderersFactory { private boolean enableDecoderFallback; private MediaCodecSelector mediaCodecSelector; private boolean enableAsyncQueueing; + private boolean forceAsyncQueueingSynchronizationWorkaround; private boolean enableSynchronizeCodecInteractionsWithQueueing; private boolean enableFloatOutput; private boolean enableAudioTrackPlaybackParams; @@ -161,6 +162,24 @@ public class DefaultRenderersFactory implements RenderersFactory { return this; } + /** + * Enable the asynchronous queueing synchronization workaround. + * + *
When enabled, the queueing threads for {@link MediaCodec} instances will synchronize on a + * shared lock when submitting buffers to the respective {@link MediaCodec}. + * + *
This method is experimental, and will be renamed or removed in a future release. + * + * @param enabled Whether the asynchronous queueing synchronization workaround is enabled by + * default. + * @return This factory, for convenience. + */ + public DefaultRenderersFactory experimentalSetForceAsyncQueueingSynchronizationWorkaround( + boolean enabled) { + this.forceAsyncQueueingSynchronizationWorkaround = enabled; + return this; + } + /** * Enable synchronizing codec interactions with asynchronous buffer queueing. * @@ -353,6 +372,8 @@ public class DefaultRenderersFactory implements RenderersFactory { eventListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY); videoRenderer.experimentalSetAsynchronousBufferQueueingEnabled(enableAsyncQueueing); + videoRenderer.experimentalSetForceAsyncQueueingSynchronizationWorkaround( + forceAsyncQueueingSynchronizationWorkaround); videoRenderer.experimentalSetSynchronizeCodecInteractionsWithQueueingEnabled( enableSynchronizeCodecInteractionsWithQueueing); out.add(videoRenderer); @@ -480,6 +501,8 @@ public class DefaultRenderersFactory implements RenderersFactory { eventListener, audioSink); audioRenderer.experimentalSetAsynchronousBufferQueueingEnabled(enableAsyncQueueing); + audioRenderer.experimentalSetForceAsyncQueueingSynchronizationWorkaround( + forceAsyncQueueingSynchronizationWorkaround); audioRenderer.experimentalSetSynchronizeCodecInteractionsWithQueueingEnabled( enableSynchronizeCodecInteractionsWithQueueing); out.add(audioRenderer); 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 54ad57cafe..ef20db4614 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 @@ -72,11 +72,15 @@ import java.nio.ByteBuffer; * {@link MediaCodec}. */ /* package */ AsynchronousMediaCodecAdapter( - MediaCodec codec, int trackType, boolean synchronizeCodecInteractionsWithQueueing) { + MediaCodec codec, + int trackType, + boolean forceQueueingSynchronizationWorkaround, + boolean synchronizeCodecInteractionsWithQueueing) { this( codec, new HandlerThread(createCallbackThreadLabel(trackType)), new HandlerThread(createQueueingThreadLabel(trackType)), + forceQueueingSynchronizationWorkaround, synchronizeCodecInteractionsWithQueueing); } @@ -85,10 +89,13 @@ import java.nio.ByteBuffer; MediaCodec codec, HandlerThread callbackThread, HandlerThread enqueueingThread, + boolean forceQueueingSynchronizationWorkaround, boolean synchronizeCodecInteractionsWithQueueing) { this.codec = codec; this.asynchronousMediaCodecCallback = new AsynchronousMediaCodecCallback(callbackThread); - this.bufferEnqueuer = new AsynchronousMediaCodecBufferEnqueuer(codec, enqueueingThread); + this.bufferEnqueuer = + new AsynchronousMediaCodecBufferEnqueuer( + codec, enqueueingThread, forceQueueingSynchronizationWorkaround); this.synchronizeCodecInteractionsWithQueueing = synchronizeCodecInteractionsWithQueueing; this.state = STATE_CREATED; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecBufferEnqueuer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecBufferEnqueuer.java index 79bb981955..21f79a78a2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecBufferEnqueuer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecBufferEnqueuer.java @@ -68,18 +68,29 @@ class AsynchronousMediaCodecBufferEnqueuer { * @param codec The {@link MediaCodec} to submit input buffers to. * @param queueingThread The {@link HandlerThread} to use for queueing buffers. */ - public AsynchronousMediaCodecBufferEnqueuer(MediaCodec codec, HandlerThread queueingThread) { - this(codec, queueingThread, /* conditionVariable= */ new ConditionVariable()); + public AsynchronousMediaCodecBufferEnqueuer( + MediaCodec codec, + HandlerThread queueingThread, + boolean forceQueueingSynchronizationWorkaround) { + this( + codec, + queueingThread, + forceQueueingSynchronizationWorkaround, + /* conditionVariable= */ new ConditionVariable()); } @VisibleForTesting /* package */ AsynchronousMediaCodecBufferEnqueuer( - MediaCodec codec, HandlerThread handlerThread, ConditionVariable conditionVariable) { + MediaCodec codec, + HandlerThread handlerThread, + boolean forceQueueingSynchronizationWorkaround, + ConditionVariable conditionVariable) { this.codec = codec; this.handlerThread = handlerThread; this.conditionVariable = conditionVariable; pendingRuntimeException = new AtomicReference<>(); - needsSynchronizationWorkaround = needsSynchronizationWorkaround(); + needsSynchronizationWorkaround = + forceQueueingSynchronizationWorkaround || needsSynchronizationWorkaround(); } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index 63069b5320..00914fe092 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -348,6 +348,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private boolean waitingForFirstSampleInFormat; private boolean pendingOutputEndOfStream; private boolean enableAsynchronousBufferQueueing; + private boolean forceAsyncQueueingSynchronizationWorkaround; private boolean enableSynchronizeCodecInteractionsWithQueueing; @Nullable private ExoPlaybackException pendingPlaybackException; protected DecoderCounters decoderCounters; @@ -417,6 +418,19 @@ public abstract class MediaCodecRenderer extends BaseRenderer { enableAsynchronousBufferQueueing = enabled; } + /** + * Enable the asynchronous queueing synchronization workaround. + * + *
When enabled, the queueing threads for {@link MediaCodec} instance will synchronize on a + * shared lock when submitting buffers to the respective {@link MediaCodec}. + * + *
This method is experimental, and will be renamed or removed in a future release. It should + * only be called before the renderer is used. + */ + public void experimentalSetForceAsyncQueueingSynchronizationWorkaround(boolean enabled) { + this.forceAsyncQueueingSynchronizationWorkaround = enabled; + } + /** * Enable synchronizing codec interactions with asynchronous buffer queueing. * @@ -1068,7 +1082,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer { if (enableAsynchronousBufferQueueing && Util.SDK_INT >= 23) { codecAdapter = new AsynchronousMediaCodecAdapter( - codec, getTrackType(), enableSynchronizeCodecInteractionsWithQueueing); + codec, + getTrackType(), + forceAsyncQueueingSynchronizationWorkaround, + enableSynchronizeCodecInteractionsWithQueueing); } else { codecAdapter = new SynchronousMediaCodecAdapter(codec); } 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 8874d5ec7c..040e8a576b 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 @@ -51,6 +51,7 @@ public class AsynchronousMediaCodecAdapterTest { codec, callbackThread, queueingThread, + /* forceQueueingSynchronizationWorkaround= */ false, /* synchronizeCodecInteractionsWithQueueing= */ false); bufferInfo = new MediaCodec.BufferInfo(); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecBufferEnqueuerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecBufferEnqueuerTest.java index f3a08df819..e5fdd126f4 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecBufferEnqueuerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecBufferEnqueuerTest.java @@ -56,7 +56,11 @@ public class AsynchronousMediaCodecBufferEnqueuerTest { codec.start(); handlerThread = new TestHandlerThread("TestHandlerThread"); enqueuer = - new AsynchronousMediaCodecBufferEnqueuer(codec, handlerThread, mockConditionVariable); + new AsynchronousMediaCodecBufferEnqueuer( + codec, + handlerThread, + /* forceQueueingSynchronizationWorkaround= */ false, + mockConditionVariable); } @After