Fix flaky unit tests
PiperOrigin-RevId: 334580007
This commit is contained in:
parent
c35787a08f
commit
a0d99a6ac8
@ -83,19 +83,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
* labelling the internal thread accordingly.
|
||||
*/
|
||||
/* package */ AsynchronousMediaCodecAdapter(MediaCodec codec, int trackType) {
|
||||
this(codec, trackType, new HandlerThread(createThreadLabel(trackType)));
|
||||
this(
|
||||
codec,
|
||||
trackType,
|
||||
new HandlerThread(createCallbackThreadLabel(trackType)),
|
||||
new HandlerThread(createQueueingThreadLabel(trackType)));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
/* package */ AsynchronousMediaCodecAdapter(
|
||||
MediaCodec codec,
|
||||
int trackType,
|
||||
HandlerThread handlerThread) {
|
||||
HandlerThread callbackThread,
|
||||
HandlerThread enqueueingThread) {
|
||||
this.lock = new Object();
|
||||
this.mediaCodecAsyncCallback = new MediaCodecAsyncCallback();
|
||||
this.codec = codec;
|
||||
this.handlerThread = handlerThread;
|
||||
this.bufferEnqueuer = new AsynchronousMediaCodecBufferEnqueuer(codec, trackType);
|
||||
this.handlerThread = callbackThread;
|
||||
this.bufferEnqueuer = new AsynchronousMediaCodecBufferEnqueuer(codec, enqueueingThread);
|
||||
this.state = STATE_CREATED;
|
||||
}
|
||||
|
||||
@ -276,8 +281,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
}
|
||||
}
|
||||
|
||||
private static String createThreadLabel(int trackType) {
|
||||
StringBuilder labelBuilder = new StringBuilder("ExoPlayer:MediaCodecAsyncAdapter:");
|
||||
private static String createCallbackThreadLabel(int trackType) {
|
||||
return createThreadLabel(trackType, /* prefix= */ "ExoPlayer:MediaCodecAsyncAdapter:");
|
||||
}
|
||||
|
||||
private static String createQueueingThreadLabel(int trackType) {
|
||||
return createThreadLabel(trackType, /* prefix= */ "ExoPlayer:MediaCodecQueueingThread:");
|
||||
}
|
||||
|
||||
private static String createThreadLabel(int trackType, String prefix) {
|
||||
StringBuilder labelBuilder = new StringBuilder(prefix);
|
||||
if (trackType == C.TRACK_TYPE_AUDIO) {
|
||||
labelBuilder.append("Audio");
|
||||
} else if (trackType == C.TRACK_TYPE_VIDEO) {
|
||||
|
@ -26,7 +26,6 @@ import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.decoder.CryptoInfo;
|
||||
import com.google.android.exoplayer2.util.ConditionVariable;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
@ -66,13 +65,10 @@ class AsynchronousMediaCodecBufferEnqueuer {
|
||||
* Creates a new instance that submits input buffers on the specified {@link MediaCodec}.
|
||||
*
|
||||
* @param codec The {@link MediaCodec} to submit input buffers to.
|
||||
* @param trackType The type of stream (used for debug logs).
|
||||
* @param queueingThread The {@link HandlerThread} to use for queueing buffers.
|
||||
*/
|
||||
public AsynchronousMediaCodecBufferEnqueuer(MediaCodec codec, int trackType) {
|
||||
this(
|
||||
codec,
|
||||
new HandlerThread(createThreadLabel(trackType)),
|
||||
/* conditionVariable= */ new ConditionVariable());
|
||||
public AsynchronousMediaCodecBufferEnqueuer(MediaCodec codec, HandlerThread queueingThread) {
|
||||
this(codec, queueingThread, /* conditionVariable= */ new ConditionVariable());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@ -291,18 +287,6 @@ class AsynchronousMediaCodecBufferEnqueuer {
|
||||
return manufacturer.contains("samsung") || manufacturer.contains("motorola");
|
||||
}
|
||||
|
||||
private static String createThreadLabel(int trackType) {
|
||||
StringBuilder labelBuilder = new StringBuilder("ExoPlayer:MediaCodecBufferEnqueuer:");
|
||||
if (trackType == C.TRACK_TYPE_AUDIO) {
|
||||
labelBuilder.append("Audio");
|
||||
} else if (trackType == C.TRACK_TYPE_VIDEO) {
|
||||
labelBuilder.append("Video");
|
||||
} else {
|
||||
labelBuilder.append("Unknown(").append(trackType).append(")");
|
||||
}
|
||||
return labelBuilder.toString();
|
||||
}
|
||||
|
||||
/** Performs a deep copy of {@code cryptoInfo} to {@code frameworkCryptoInfo}. */
|
||||
private static void copy(
|
||||
CryptoInfo cryptoInfo, android.media.MediaCodec.CryptoInfo frameworkCryptoInfo) {
|
||||
|
@ -38,26 +38,27 @@ import org.robolectric.shadows.ShadowLooper;
|
||||
public class AsynchronousMediaCodecAdapterTest {
|
||||
private AsynchronousMediaCodecAdapter adapter;
|
||||
private MediaCodec codec;
|
||||
private TestHandlerThread handlerThread;
|
||||
private TestHandlerThread callbackThread;
|
||||
private HandlerThread queueingThread;
|
||||
private MediaCodec.BufferInfo bufferInfo;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
codec = MediaCodec.createByCodecName("h264");
|
||||
handlerThread = new TestHandlerThread("TestHandlerThread");
|
||||
callbackThread = new TestHandlerThread("TestCallbackThread");
|
||||
queueingThread = new HandlerThread("TestQueueingThread");
|
||||
adapter =
|
||||
new AsynchronousMediaCodecAdapter(
|
||||
codec,
|
||||
/* trackType= */ C.TRACK_TYPE_VIDEO,
|
||||
handlerThread);
|
||||
codec, /* trackType= */ C.TRACK_TYPE_VIDEO, callbackThread, queueingThread);
|
||||
bufferInfo = new MediaCodec.BufferInfo();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
adapter.shutdown();
|
||||
codec.release();
|
||||
|
||||
assertThat(handlerThread.hasQuit()).isTrue();
|
||||
assertThat(callbackThread.hasQuit()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -66,7 +67,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
// After adapter.start(), the ShadowMediaCodec offers one input buffer. We pause the looper so
|
||||
// that the buffer is not propagated to the adapter.
|
||||
shadowOf(handlerThread.getLooper()).pause();
|
||||
shadowOf(callbackThread.getLooper()).pause();
|
||||
adapter.start();
|
||||
|
||||
assertThat(adapter.dequeueInputBufferIndex()).isEqualTo(MediaCodec.INFO_TRY_AGAIN_LATER);
|
||||
@ -79,7 +80,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers input buffer 0. We advance the looper to make sure
|
||||
// and messages have been propagated to the adapter.
|
||||
shadowOf(handlerThread.getLooper()).idle();
|
||||
shadowOf(callbackThread.getLooper()).idle();
|
||||
|
||||
assertThat(adapter.dequeueInputBufferIndex()).isEqualTo(0);
|
||||
}
|
||||
@ -92,7 +93,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
|
||||
// After adapter.start(), the ShadowMediaCodec offers input buffer 0. We run all currently
|
||||
// enqueued messages and pause the looper so that flush is not completed.
|
||||
ShadowLooper shadowLooper = shadowOf(handlerThread.getLooper());
|
||||
ShadowLooper shadowLooper = shadowOf(callbackThread.getLooper());
|
||||
shadowLooper.idle();
|
||||
shadowLooper.pause();
|
||||
adapter.flush();
|
||||
@ -107,7 +108,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
adapter.start();
|
||||
// After adapter.start(), the ShadowMediaCodec offers input buffer 0. We advance the looper to
|
||||
// make sure all messages have been propagated to the adapter.
|
||||
ShadowLooper shadowLooper = shadowOf(handlerThread.getLooper());
|
||||
ShadowLooper shadowLooper = shadowOf(callbackThread.getLooper());
|
||||
shadowLooper.idle();
|
||||
|
||||
adapter.flush();
|
||||
@ -123,7 +124,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
adapter.configure(
|
||||
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
// Pause the looper so that we interact with the adapter from this thread only.
|
||||
shadowOf(handlerThread.getLooper()).pause();
|
||||
shadowOf(callbackThread.getLooper()).pause();
|
||||
adapter.start();
|
||||
|
||||
// Set an error directly on the adapter (not through the looper).
|
||||
@ -140,7 +141,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
// After start(), the ShadowMediaCodec offers input buffer 0, which is available only if we
|
||||
// progress the adapter's looper. We progress the looper so that we call shutdown() on a
|
||||
// non-empty adapter.
|
||||
shadowOf(handlerThread.getLooper()).idle();
|
||||
shadowOf(callbackThread.getLooper()).idle();
|
||||
|
||||
adapter.shutdown();
|
||||
|
||||
@ -155,7 +156,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers an output format change. We progress the looper
|
||||
// so that the format change is propagated to the adapter.
|
||||
shadowOf(handlerThread.getLooper()).idle();
|
||||
shadowOf(callbackThread.getLooper()).idle();
|
||||
|
||||
assertThat(adapter.dequeueOutputBufferIndex(bufferInfo))
|
||||
.isEqualTo(MediaCodec.INFO_OUTPUT_FORMAT_CHANGED);
|
||||
@ -171,13 +172,17 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers input buffer 0, which is available only if we
|
||||
// progress the adapter's looper.
|
||||
ShadowLooper shadowLooper = shadowOf(handlerThread.getLooper());
|
||||
shadowLooper.idle();
|
||||
ShadowLooper callbackShadowLooper = shadowOf(callbackThread.getLooper());
|
||||
callbackShadowLooper.idle();
|
||||
|
||||
int index = adapter.dequeueInputBufferIndex();
|
||||
adapter.queueInputBuffer(index, 0, 0, 0, 0);
|
||||
// Progress the looper so that the ShadowMediaCodec processes the input buffer.
|
||||
shadowLooper.idle();
|
||||
// Progress the queueuing looper first so the asynchronous enqueuer submits the input buffer,
|
||||
// the ShadowMediaCodec processes the input buffer and produces an output buffer. Then, progress
|
||||
// the callback looper so that the available output buffer callback is handled and the output
|
||||
// buffer reaches the adapter.
|
||||
shadowOf(queueingThread.getLooper()).idle();
|
||||
callbackShadowLooper.idle();
|
||||
|
||||
// The ShadowMediaCodec will first offer an output format and then the output buffer.
|
||||
assertThat(adapter.dequeueOutputBufferIndex(bufferInfo))
|
||||
@ -194,7 +199,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers input buffer 0, which is available only if we
|
||||
// progress the adapter's looper.
|
||||
ShadowLooper shadowLooper = shadowOf(handlerThread.getLooper());
|
||||
ShadowLooper shadowLooper = shadowOf(callbackThread.getLooper());
|
||||
shadowLooper.idle();
|
||||
|
||||
// Flush enqueues a task in the looper, but we will pause the looper to leave flush()
|
||||
@ -211,7 +216,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
// Pause the looper so that we interact with the adapter from this thread only.
|
||||
adapter.configure(
|
||||
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
shadowOf(handlerThread.getLooper()).pause();
|
||||
shadowOf(callbackThread.getLooper()).pause();
|
||||
adapter.start();
|
||||
|
||||
// Set an error directly on the adapter.
|
||||
@ -227,7 +232,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers input buffer 0, which is available only if we
|
||||
// progress the adapter's looper.
|
||||
ShadowLooper shadowLooper = shadowOf(handlerThread.getLooper());
|
||||
ShadowLooper shadowLooper = shadowOf(callbackThread.getLooper());
|
||||
shadowLooper.idle();
|
||||
|
||||
int index = adapter.dequeueInputBufferIndex();
|
||||
@ -246,7 +251,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
// After start() the ShadowMediaCodec offers an output format change. Pause the looper so that
|
||||
// the format change is not propagated to the adapter.
|
||||
shadowOf(handlerThread.getLooper()).pause();
|
||||
shadowOf(callbackThread.getLooper()).pause();
|
||||
adapter.start();
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> adapter.getOutputFormat());
|
||||
@ -259,7 +264,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers an output format, which is available only if we
|
||||
// progress the adapter's looper.
|
||||
shadowOf(handlerThread.getLooper()).idle();
|
||||
shadowOf(callbackThread.getLooper()).idle();
|
||||
|
||||
// Add another format directly on the adapter.
|
||||
adapter.onOutputFormatChanged(codec, createMediaFormat("format2"));
|
||||
@ -283,7 +288,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers an output format, which is available only if we
|
||||
// progress the adapter's looper.
|
||||
ShadowLooper shadowLooper = shadowOf(handlerThread.getLooper());
|
||||
ShadowLooper shadowLooper = shadowOf(callbackThread.getLooper());
|
||||
shadowLooper.idle();
|
||||
|
||||
adapter.dequeueOutputBufferIndex(bufferInfo);
|
||||
|
Loading…
x
Reference in New Issue
Block a user