mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Fix flakiness in SimpleDecoderVideoRendererTest
The test had two problems: 1. It posts messages using a Handler and we need to idle the main looper to actually deliver this message. 2. SimpleDecoder uses a background thread that is not within our control from the test. Ensure the decoding happens after we queue input buffers by using a lock. PiperOrigin-RevId: 298300175
This commit is contained in:
parent
0b946ac317
commit
a12d72c72c
@ -24,6 +24,7 @@ import android.graphics.SurfaceTexture;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
import androidx.annotation.GuardedBy;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
@ -43,8 +44,11 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnit;
|
import org.mockito.junit.MockitoJUnit;
|
||||||
import org.mockito.junit.MockitoRule;
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.annotation.LooperMode;
|
||||||
|
import org.robolectric.shadows.ShadowLooper;
|
||||||
|
|
||||||
/** Unit test for {@link SimpleDecoderVideoRenderer}. */
|
/** Unit test for {@link SimpleDecoderVideoRenderer}. */
|
||||||
|
@LooperMode(LooperMode.Mode.PAUSED)
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public final class SimpleDecoderVideoRendererTest {
|
public final class SimpleDecoderVideoRendererTest {
|
||||||
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
|
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
|
||||||
@ -67,6 +71,12 @@ public final class SimpleDecoderVideoRendererTest {
|
|||||||
new Handler(),
|
new Handler(),
|
||||||
eventListener,
|
eventListener,
|
||||||
/* maxDroppedFramesToNotify= */ -1) {
|
/* maxDroppedFramesToNotify= */ -1) {
|
||||||
|
|
||||||
|
private final Object pendingDecodeCallLock = new Object();
|
||||||
|
|
||||||
|
@GuardedBy("pendingDecodeCallLock")
|
||||||
|
private int pendingDecodeCalls;
|
||||||
|
|
||||||
@C.VideoOutputMode private int outputMode;
|
@C.VideoOutputMode private int outputMode;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -86,6 +96,34 @@ public final class SimpleDecoderVideoRendererTest {
|
|||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onQueueInputBuffer(VideoDecoderInputBuffer buffer) {
|
||||||
|
// SimpleDecoder.decode() is called on a background thread we have no control about from
|
||||||
|
// the test. Ensure the background calls are predictably serialized by waiting for them
|
||||||
|
// to finish:
|
||||||
|
// 1. Mark decode calls as "pending" here.
|
||||||
|
// 2. Send a message on the test thread to wait for all pending decode calls.
|
||||||
|
// 3. Decrement the pending counter in decode calls and wake up the waiting test.
|
||||||
|
// 4. The tests need to call ShadowLooper.idleMainThread() to wait for pending calls.
|
||||||
|
synchronized (pendingDecodeCallLock) {
|
||||||
|
pendingDecodeCalls++;
|
||||||
|
}
|
||||||
|
new Handler()
|
||||||
|
.post(
|
||||||
|
() -> {
|
||||||
|
synchronized (pendingDecodeCallLock) {
|
||||||
|
while (pendingDecodeCalls > 0) {
|
||||||
|
try {
|
||||||
|
pendingDecodeCallLock.wait();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Ignore.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
super.onQueueInputBuffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SimpleDecoder<
|
protected SimpleDecoder<
|
||||||
VideoDecoderInputBuffer,
|
VideoDecoderInputBuffer,
|
||||||
@ -117,6 +155,10 @@ public final class SimpleDecoderVideoRendererTest {
|
|||||||
VideoDecoderOutputBuffer outputBuffer,
|
VideoDecoderOutputBuffer outputBuffer,
|
||||||
boolean reset) {
|
boolean reset) {
|
||||||
outputBuffer.init(inputBuffer.timeUs, outputMode, /* supplementalData= */ null);
|
outputBuffer.init(inputBuffer.timeUs, outputMode, /* supplementalData= */ null);
|
||||||
|
synchronized (pendingDecodeCallLock) {
|
||||||
|
pendingDecodeCalls--;
|
||||||
|
pendingDecodeCallLock.notify();
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,6 +192,8 @@ public final class SimpleDecoderVideoRendererTest {
|
|||||||
/* offsetUs */ 0);
|
/* offsetUs */ 0);
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
renderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
renderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
||||||
|
// Ensure pending messages are delivered.
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(eventListener).onRenderedFirstFrame(any());
|
verify(eventListener).onRenderedFirstFrame(any());
|
||||||
@ -176,6 +220,8 @@ public final class SimpleDecoderVideoRendererTest {
|
|||||||
/* offsetUs */ 0);
|
/* offsetUs */ 0);
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
renderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
renderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
||||||
|
// Ensure pending messages are delivered.
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(eventListener, never()).onRenderedFirstFrame(any());
|
verify(eventListener, never()).onRenderedFirstFrame(any());
|
||||||
@ -202,6 +248,8 @@ public final class SimpleDecoderVideoRendererTest {
|
|||||||
renderer.start();
|
renderer.start();
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
renderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
renderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
||||||
|
// Ensure pending messages are delivered.
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(eventListener).onRenderedFirstFrame(any());
|
verify(eventListener).onRenderedFirstFrame(any());
|
||||||
@ -244,6 +292,8 @@ public final class SimpleDecoderVideoRendererTest {
|
|||||||
renderer.replaceStream(new Format[] {H264_FORMAT}, fakeSampleStream2, /* offsetUs= */ 100);
|
renderer.replaceStream(new Format[] {H264_FORMAT}, fakeSampleStream2, /* offsetUs= */ 100);
|
||||||
replacedStream = true;
|
replacedStream = true;
|
||||||
}
|
}
|
||||||
|
// Ensure pending messages are delivered.
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(eventListener, times(2)).onRenderedFirstFrame(any());
|
verify(eventListener, times(2)).onRenderedFirstFrame(any());
|
||||||
@ -283,6 +333,8 @@ public final class SimpleDecoderVideoRendererTest {
|
|||||||
renderer.replaceStream(new Format[] {H264_FORMAT}, fakeSampleStream2, /* offsetUs= */ 100);
|
renderer.replaceStream(new Format[] {H264_FORMAT}, fakeSampleStream2, /* offsetUs= */ 100);
|
||||||
replacedStream = true;
|
replacedStream = true;
|
||||||
}
|
}
|
||||||
|
// Ensure pending messages are delivered.
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(eventListener).onRenderedFirstFrame(any());
|
verify(eventListener).onRenderedFirstFrame(any());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user