Skip to content

Commit

Permalink
Remove experimental flag for AsynchronousMediaCodecAdapter
Browse files Browse the repository at this point in the history
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
  • Loading branch information
christosts authored and icbaker committed Mar 1, 2022
1 parent 8416f46 commit 4d51880
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
* <p>By default, this feature is disabled.
*
* <p>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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ public static final class Factory implements MediaCodecAdapter.Factory {
private final Supplier<HandlerThread> callbackThreadSupplier;
private final Supplier<HandlerThread> queueingThreadSupplier;
private final boolean synchronizeCodecInteractionsWithQueueing;
private final boolean enableImmediateCodecStartAfterFlush;

/**
* Creates an factory for {@link AsynchronousMediaCodecAdapter} instances.
Expand All @@ -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<HandlerThread> callbackThreadSupplier,
Supplier<HandlerThread> queueingThreadSupplier,
boolean synchronizeCodecInteractionsWithQueueing,
boolean enableImmediateCodecStartAfterFlush) {
boolean synchronizeCodecInteractionsWithQueueing) {
this.callbackThreadSupplier = callbackThreadSupplier;
this.queueingThreadSupplier = queueingThreadSupplier;
this.synchronizeCodecInteractionsWithQueueing = synchronizeCodecInteractionsWithQueueing;
this.enableImmediateCodecStartAfterFlush = enableImmediateCodecStartAfterFlush;
}

@Override
Expand All @@ -105,8 +98,7 @@ public AsynchronousMediaCodecAdapter createAdapter(Configuration configuration)
codec,
callbackThreadSupplier.get(),
queueingThreadSupplier.get(),
synchronizeCodecInteractionsWithQueueing,
enableImmediateCodecStartAfterFlush);
synchronizeCodecInteractionsWithQueueing);
TraceUtil.endSection();
codecAdapter.initialize(
configuration.mediaFormat,
Expand Down Expand Up @@ -139,21 +131,18 @@ 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;

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;
}

Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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;
Expand All @@ -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));
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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.
*
* <p>By default, this feature is enabled.
*
* <p>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 {
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand Down Expand Up @@ -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(
Expand All @@ -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);
Expand Down

0 comments on commit 4d51880

Please sign in to comment.