mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Reduce flakiness for MediaCodecAudioRendererTests past SDK 30
PiperOrigin-RevId: 715770321
This commit is contained in:
parent
62341f31f9
commit
fa4cc7c65c
@ -33,6 +33,7 @@ import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.media.MediaFormat;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -49,6 +50,7 @@ import androidx.media3.exoplayer.RendererConfiguration;
|
||||
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
||||
import androidx.media3.exoplayer.drm.DrmSessionManager;
|
||||
import androidx.media3.exoplayer.mediacodec.DefaultMediaCodecAdapterFactory;
|
||||
import androidx.media3.exoplayer.mediacodec.MediaCodecInfo;
|
||||
import androidx.media3.exoplayer.mediacodec.MediaCodecSelector;
|
||||
import androidx.media3.exoplayer.source.MediaSource;
|
||||
@ -72,7 +74,6 @@ import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
/** Unit tests for {@link MediaCodecAudioRenderer} */
|
||||
@Config(sdk = 30) // TODO: b/382017156 - Remove this when the tests pass on API 31+.
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class MediaCodecAudioRendererTest {
|
||||
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
@ -118,6 +119,8 @@ public class MediaCodecAudioRendererTest {
|
||||
|
||||
private MediaCodecAudioRenderer mediaCodecAudioRenderer;
|
||||
private MediaCodecSelector mediaCodecSelector;
|
||||
@Nullable private HandlerThread callbackThread;
|
||||
@Nullable private HandlerThread queueingThread;
|
||||
|
||||
@Mock private AudioSink audioSink;
|
||||
@Mock private AudioRendererEventListener audioRendererEventListener;
|
||||
@ -154,10 +157,19 @@ public class MediaCodecAudioRendererTest {
|
||||
/* forceSecure= */ false));
|
||||
|
||||
Handler eventHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
mediaCodecAudioRenderer =
|
||||
new MediaCodecAudioRenderer(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
new DefaultMediaCodecAdapterFactory(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
() -> {
|
||||
callbackThread = new HandlerThread("MCARTest:MediaCodecAsyncAdapter");
|
||||
return callbackThread;
|
||||
},
|
||||
() -> {
|
||||
queueingThread = new HandlerThread("MCARTest:MediaCodecQueueingThread");
|
||||
return queueingThread;
|
||||
}),
|
||||
mediaCodecSelector,
|
||||
/* enableDecoderFallback= */ false,
|
||||
eventHandler,
|
||||
@ -169,7 +181,6 @@ public class MediaCodecAudioRendererTest {
|
||||
@Test
|
||||
public void render_configuresAudioSink_afterFormatChange() throws Exception {
|
||||
Format changedFormat = AUDIO_AAC.buildUpon().setSampleRate(48_000).setEncoderDelay(400).build();
|
||||
|
||||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
|
||||
@ -733,11 +744,11 @@ public class MediaCodecAudioRendererTest {
|
||||
/* initialFormat= */ AUDIO_AAC,
|
||||
ImmutableList.of(
|
||||
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 50000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 100000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 150000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 200000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 250000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 50_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 100_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 150_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 200_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 250_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
END_OF_STREAM_ITEM));
|
||||
fakeSampleStream.writeData(/* startPositionUs= */ 0);
|
||||
mediaCodecAudioRenderer.enable(
|
||||
@ -750,14 +761,16 @@ public class MediaCodecAudioRendererTest {
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs= */ 0,
|
||||
new MediaSource.MediaPeriodId(new Object()));
|
||||
// Represents audio sink buffers being full when trying to write 150000 us sample.
|
||||
// Represents audio sink buffers being full when trying to write 150_000 us sample.
|
||||
when(audioSink.handleBuffer(
|
||||
any(), longThat(presentationTimeUs -> presentationTimeUs == 150000), anyInt()))
|
||||
any(), longThat(presentationTimeUs -> presentationTimeUs == 150_000), anyInt()))
|
||||
.thenReturn(false);
|
||||
when(audioSink.getPlaybackParameters()).thenReturn(PlaybackParameters.DEFAULT);
|
||||
mediaCodecAudioRenderer.start();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
||||
maybeIdleAsynchronousMediaCodecAdapterThreads();
|
||||
}
|
||||
|
||||
long durationToProgressUs =
|
||||
mediaCodecAudioRenderer.getDurationToProgressUs(
|
||||
@ -779,11 +792,11 @@ public class MediaCodecAudioRendererTest {
|
||||
/* initialFormat= */ AUDIO_AAC,
|
||||
ImmutableList.of(
|
||||
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 50000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 100000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 150000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 200000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 250000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 50_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 100_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 150_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 200_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 250_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
END_OF_STREAM_ITEM));
|
||||
PlaybackParameters playbackParametersWithDoubleSpeed =
|
||||
new PlaybackParameters(/* speed= */ 2.0f);
|
||||
@ -798,14 +811,16 @@ public class MediaCodecAudioRendererTest {
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs= */ 0,
|
||||
new MediaSource.MediaPeriodId(new Object()));
|
||||
// Represents audio sink buffers being full when trying to write 150000 us sample.
|
||||
// Represents audio sink buffers being full when trying to write 150_000 us sample.
|
||||
when(audioSink.handleBuffer(
|
||||
any(), longThat(presentationTimeUs -> presentationTimeUs == 150000), anyInt()))
|
||||
any(), longThat(presentationTimeUs -> presentationTimeUs == 150_000), anyInt()))
|
||||
.thenReturn(false);
|
||||
when(audioSink.getPlaybackParameters()).thenReturn(playbackParametersWithDoubleSpeed);
|
||||
mediaCodecAudioRenderer.start();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
||||
maybeIdleAsynchronousMediaCodecAdapterThreads();
|
||||
}
|
||||
|
||||
long durationToProgressUs =
|
||||
mediaCodecAudioRenderer.getDurationToProgressUs(
|
||||
@ -822,6 +837,16 @@ public class MediaCodecAudioRendererTest {
|
||||
mediaCodecAudioRenderer =
|
||||
new MediaCodecAudioRenderer(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
new DefaultMediaCodecAdapterFactory(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
() -> {
|
||||
callbackThread = new HandlerThread("MCARTest:MediaCodecAsyncAdapter");
|
||||
return callbackThread;
|
||||
},
|
||||
() -> {
|
||||
queueingThread = new HandlerThread("MCARTest:MediaCodecQueueingThread");
|
||||
return queueingThread;
|
||||
}),
|
||||
mediaCodecSelector,
|
||||
/* enableDecoderFallback= */ false,
|
||||
/* eventHandler= */ new Handler(Looper.getMainLooper()),
|
||||
@ -837,11 +862,11 @@ public class MediaCodecAudioRendererTest {
|
||||
/* initialFormat= */ AUDIO_AAC,
|
||||
ImmutableList.of(
|
||||
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 50000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 100000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 150000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 200000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 250000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 50_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 100_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 150_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 200_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
oneByteSample(/* timeUs= */ 250_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||
END_OF_STREAM_ITEM));
|
||||
fakeSampleStream.writeData(/* startPositionUs= */ 0);
|
||||
mediaCodecAudioRenderer.enable(
|
||||
@ -854,17 +879,19 @@ public class MediaCodecAudioRendererTest {
|
||||
/* startPositionUs= */ 0,
|
||||
/* offsetUs= */ 0,
|
||||
new MediaSource.MediaPeriodId(new Object()));
|
||||
// Represents audio sink buffers being full when trying to write 150000 us sample.
|
||||
// Represents audio sink buffers being full when trying to write 150_000 us sample.
|
||||
when(audioSink.handleBuffer(
|
||||
any(), longThat(presentationTimeUs -> presentationTimeUs == 150000), anyInt()))
|
||||
any(), longThat(presentationTimeUs -> presentationTimeUs == 150_000), anyInt()))
|
||||
.thenReturn(false);
|
||||
when(audioSink.getPlaybackParameters()).thenReturn(PlaybackParameters.DEFAULT);
|
||||
mediaCodecAudioRenderer.start();
|
||||
long rendererPositionElapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, rendererPositionElapsedRealtimeUs);
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, rendererPositionElapsedRealtimeUs);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, fakeClock.elapsedRealtime() * 1000);
|
||||
maybeIdleAsynchronousMediaCodecAdapterThreads();
|
||||
}
|
||||
|
||||
// Simulate playback progressing between render() and getDurationToProgressUs call
|
||||
long rendererPositionElapsedRealtimeUs = fakeClock.elapsedRealtime() * 1000;
|
||||
fakeClock.advanceTime(/* timeDiffMs= */ 10);
|
||||
long durationToProgressUs =
|
||||
mediaCodecAudioRenderer.getDurationToProgressUs(
|
||||
@ -916,8 +943,10 @@ public class MediaCodecAudioRendererTest {
|
||||
.thenReturn(false);
|
||||
when(audioSink.getPlaybackParameters()).thenReturn(PlaybackParameters.DEFAULT);
|
||||
mediaCodecAudioRenderer.start();
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, fakeClock.elapsedRealtime() * 1000);
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, fakeClock.elapsedRealtime() * 1000);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
||||
maybeIdleAsynchronousMediaCodecAdapterThreads();
|
||||
}
|
||||
|
||||
// Simulate a seek through resetPosition which should flush the audio sink.
|
||||
mediaCodecAudioRenderer.stop();
|
||||
@ -972,8 +1001,10 @@ public class MediaCodecAudioRendererTest {
|
||||
.thenReturn(false);
|
||||
when(audioSink.getPlaybackParameters()).thenReturn(PlaybackParameters.DEFAULT);
|
||||
mediaCodecAudioRenderer.start();
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, fakeClock.elapsedRealtime() * 1000);
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, fakeClock.elapsedRealtime() * 1000);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
mediaCodecAudioRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
|
||||
maybeIdleAsynchronousMediaCodecAdapterThreads();
|
||||
}
|
||||
|
||||
// Simulate a track reselection forcing a disable and causing a flush of the audio sink.
|
||||
mediaCodecAudioRenderer.stop();
|
||||
@ -985,6 +1016,15 @@ public class MediaCodecAudioRendererTest {
|
||||
assertThat(durationToProgressUs).isEqualTo(10_000L);
|
||||
}
|
||||
|
||||
private void maybeIdleAsynchronousMediaCodecAdapterThreads() {
|
||||
if (queueingThread != null) {
|
||||
shadowOf(queueingThread.getLooper()).idle();
|
||||
}
|
||||
if (callbackThread != null) {
|
||||
shadowOf(callbackThread.getLooper()).idle();
|
||||
}
|
||||
}
|
||||
|
||||
private static Format getAudioSinkFormat(Format inputFormat) {
|
||||
return new Format.Builder()
|
||||
.setSampleMimeType(MimeTypes.AUDIO_RAW)
|
||||
|
@ -91,7 +91,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@ -148,13 +147,12 @@ public class MediaCodecVideoRendererTest {
|
||||
/* forceDisableAdaptive= */ false,
|
||||
/* forceSecure= */ false);
|
||||
|
||||
private final AtomicReference<HandlerThread> callbackThread = new AtomicReference<>();
|
||||
private final AtomicReference<HandlerThread> queueingThread = new AtomicReference<>();
|
||||
|
||||
private Looper testMainLooper;
|
||||
private Surface surface;
|
||||
private MediaCodecVideoRenderer mediaCodecVideoRenderer;
|
||||
private MediaCodecSelector mediaCodecSelector;
|
||||
@Nullable private HandlerThread callbackThread;
|
||||
@Nullable private HandlerThread queueingThread;
|
||||
@Nullable private Format currentOutputFormat;
|
||||
|
||||
@Mock private VideoRendererEventListener eventListener;
|
||||
@ -181,12 +179,12 @@ public class MediaCodecVideoRendererTest {
|
||||
new DefaultMediaCodecAdapterFactory(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
() -> {
|
||||
callbackThread.set(new HandlerThread("MCVRTest:MediaCodecAsyncAdapter"));
|
||||
return callbackThread.get();
|
||||
callbackThread = new HandlerThread("MCVRTest:MediaCodecAsyncAdapter");
|
||||
return callbackThread;
|
||||
},
|
||||
() -> {
|
||||
queueingThread.set(new HandlerThread("MCVRTest:MediaCodecQueueingThread"));
|
||||
return queueingThread.get();
|
||||
queueingThread = new HandlerThread("MCVRTest:MediaCodecQueueingThread");
|
||||
return queueingThread;
|
||||
}),
|
||||
mediaCodecSelector,
|
||||
/* allowedJoiningTimeMs= */ 0,
|
||||
@ -1612,11 +1610,11 @@ public class MediaCodecVideoRendererTest {
|
||||
}
|
||||
|
||||
private void maybeIdleAsynchronousMediaCodecAdapterThreads() {
|
||||
if (queueingThread.get() != null) {
|
||||
shadowOf(queueingThread.get().getLooper()).idle();
|
||||
if (queueingThread != null) {
|
||||
shadowOf(queueingThread.getLooper()).idle();
|
||||
}
|
||||
if (callbackThread.get() != null) {
|
||||
shadowOf(callbackThread.get().getLooper()).idle();
|
||||
if (callbackThread != null) {
|
||||
shadowOf(callbackThread.getLooper()).idle();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user