Reduce flakiness for ServerSideAdInsertion±MediaSourceTest past SDK 30

PiperOrigin-RevId: 737631774
This commit is contained in:
michaelkatz 2025-03-17 08:54:37 -07:00 committed by Copybara-Service
parent ff0a359e93
commit 4932300b9a
5 changed files with 63 additions and 23 deletions

View File

@ -57,6 +57,9 @@ import androidx.media3.exoplayer.FormatHolder;
import androidx.media3.exoplayer.LoadingInfo;
import androidx.media3.exoplayer.analytics.AnalyticsListener;
import androidx.media3.exoplayer.analytics.PlayerId;
import androidx.media3.exoplayer.audio.AudioSink;
import androidx.media3.exoplayer.audio.DefaultAudioSink;
import androidx.media3.exoplayer.audio.TeeAudioProcessor;
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
import androidx.media3.exoplayer.drm.DrmSessionManager;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
@ -71,6 +74,7 @@ import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
import androidx.media3.exoplayer.trackselection.FixedTrackSelection;
import androidx.media3.exoplayer.upstream.Allocator;
import androidx.media3.exoplayer.upstream.DefaultAllocator;
import androidx.media3.test.utils.CapturingAudioSink;
import androidx.media3.test.utils.CapturingRenderersFactory;
import androidx.media3.test.utils.DumpFileAsserts;
import androidx.media3.test.utils.FakeClock;
@ -93,10 +97,8 @@ import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
/** Unit test for {@link ServerSideAdInsertionMediaSource}. */
@Config(sdk = 30) // TODO: b/382017156 - Remove this when the tests pass on API 31+.
@RunWith(AndroidJUnit4.class)
public final class ServerSideAdInsertionMediaSourceTest {
@ -472,7 +474,8 @@ public final class ServerSideAdInsertionMediaSourceTest {
public void playbackWithNewlyInsertedAds_playsSuccessfulWithoutRendererResets() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
AtomicReference<Object> periodUid = new AtomicReference<>();
CapturingRenderersFactory renderersFactory = new CapturingRenderersFactory(context);
CapturingRenderersFactory renderersFactory =
new CapturingRenderersFactory(context, DiscontinuitySkippingCapturingAudioSink.create());
ExoPlayer player =
new ExoPlayer.Builder(context, renderersFactory)
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
@ -552,7 +555,8 @@ public final class ServerSideAdInsertionMediaSourceTest {
throws Exception {
Context context = ApplicationProvider.getApplicationContext();
AtomicReference<Object> periodUid = new AtomicReference<>();
CapturingRenderersFactory renderersFactory = new CapturingRenderersFactory(context);
CapturingRenderersFactory renderersFactory =
new CapturingRenderersFactory(context, DiscontinuitySkippingCapturingAudioSink.create());
ExoPlayer player =
new ExoPlayer.Builder(context, renderersFactory)
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
@ -832,4 +836,29 @@ public final class ServerSideAdInsertionMediaSourceTest {
assertThat(readSamples).containsExactly(0L, 200L, 400L, 600L, 800L).inOrder();
}
private static final class DiscontinuitySkippingCapturingAudioSink extends CapturingAudioSink {
/** Creates the capturing audio sink that skips dumping discontinuity events. */
public static DiscontinuitySkippingCapturingAudioSink create() {
InterceptingBufferSink interceptingBufferSink = new InterceptingBufferSink();
DiscontinuitySkippingCapturingAudioSink capturingAudioSink =
new DiscontinuitySkippingCapturingAudioSink(
new DefaultAudioSink.Builder(ApplicationProvider.getApplicationContext())
.setAudioProcessorChain(
new DefaultAudioSink.DefaultAudioProcessorChain(
new TeeAudioProcessor(interceptingBufferSink)))
.build());
interceptingBufferSink.setCapturingAudioSink(capturingAudioSink);
return capturingAudioSink;
}
private DiscontinuitySkippingCapturingAudioSink(AudioSink sink) {
super(sink);
}
@Override
public void handleDiscontinuity() {
getDelegateAudioSink().handleDiscontinuity();
}
}
}

View File

@ -593,9 +593,6 @@ AudioSink:
buffer #19:
time = 1000000485179
data = empty
discontinuity:
discontinuity:
discontinuity:
buffer #20:
time = 1000000508399
data = empty

View File

@ -620,9 +620,6 @@ AudioSink:
buffer #28:
time = 1000000694158
data = empty
discontinuity:
discontinuity:
discontinuity:
buffer #29:
time = 1000000717378
data = empty
@ -635,7 +632,6 @@ AudioSink:
buffer #32:
time = 1000000787038
data = empty
discontinuity:
buffer #33:
time = 1000000810258
data = empty

View File

@ -41,9 +41,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* discontinuity and buffer events.
*/
@UnstableApi
public final class CapturingAudioSink extends ForwardingAudioSink implements Dumper.Dumpable {
public class CapturingAudioSink extends ForwardingAudioSink implements Dumper.Dumpable {
private final List<Dumper.Dumpable> interceptedData;
private final AudioSink audioSink;
private int bufferCount;
private long lastPresentationTimeUs;
@ -53,19 +54,26 @@ public final class CapturingAudioSink extends ForwardingAudioSink implements Dum
/** Creates the capturing audio sink. */
public static CapturingAudioSink create() {
InterceptingBufferSink interceptingBufferSink = new InterceptingBufferSink();
return new CapturingAudioSink(
new DefaultAudioSink.Builder(ApplicationProvider.getApplicationContext())
.setAudioProcessorChain(
new DefaultAudioSink.DefaultAudioProcessorChain(
new TeeAudioProcessor(interceptingBufferSink)))
.build(),
interceptingBufferSink);
CapturingAudioSink capturingAudioSink =
new CapturingAudioSink(
new DefaultAudioSink.Builder(ApplicationProvider.getApplicationContext())
.setAudioProcessorChain(
new DefaultAudioSink.DefaultAudioProcessorChain(
new TeeAudioProcessor(interceptingBufferSink)))
.build());
interceptingBufferSink.setCapturingAudioSink(capturingAudioSink);
return capturingAudioSink;
}
private CapturingAudioSink(AudioSink sink, InterceptingBufferSink interceptingBufferSink) {
protected CapturingAudioSink(AudioSink sink) {
super(sink);
audioSink = sink;
interceptedData = new ArrayList<>();
interceptingBufferSink.setCapturingAudioSink(this);
}
/** Returns the wrapped {@link AudioSink}. */
protected final AudioSink getDelegateAudioSink() {
return audioSink;
}
@Override
@ -121,7 +129,7 @@ public final class CapturingAudioSink extends ForwardingAudioSink implements Dum
dumper.endBlock();
}
private static final class InterceptingBufferSink implements TeeAudioProcessor.AudioBufferSink {
public static final class InterceptingBufferSink implements TeeAudioProcessor.AudioBufferSink {
private @MonotonicNonNull CapturingAudioSink capturingAudioSink;
private @MonotonicNonNull Format format;

View File

@ -86,9 +86,19 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
* @param context The {@link Context}.
*/
public CapturingRenderersFactory(Context context) {
this(context, CapturingAudioSink.create());
}
/**
* Creates an instance.
*
* @param context The {@link Context}.
* @param capturingAudioSink The audio sink to use for capturing audio output.
*/
public CapturingRenderersFactory(Context context, CapturingAudioSink capturingAudioSink) {
this.context = context;
this.mediaCodecAdapterFactory = new CapturingMediaCodecAdapter.Factory(context);
this.audioSink = CapturingAudioSink.create();
this.audioSink = capturingAudioSink;
this.imageOutput = new CapturingImageOutput();
this.imageDecoderFactory = ImageDecoder.Factory.DEFAULT;
this.textRendererFactory = TextRenderer::new;