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