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.LoadingInfo;
import androidx.media3.exoplayer.analytics.AnalyticsListener; import androidx.media3.exoplayer.analytics.AnalyticsListener;
import androidx.media3.exoplayer.analytics.PlayerId; 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.DrmSessionEventListener;
import androidx.media3.exoplayer.drm.DrmSessionManager; import androidx.media3.exoplayer.drm.DrmSessionManager;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; 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.trackselection.FixedTrackSelection;
import androidx.media3.exoplayer.upstream.Allocator; import androidx.media3.exoplayer.upstream.Allocator;
import androidx.media3.exoplayer.upstream.DefaultAllocator; import androidx.media3.exoplayer.upstream.DefaultAllocator;
import androidx.media3.test.utils.CapturingAudioSink;
import androidx.media3.test.utils.CapturingRenderersFactory; import androidx.media3.test.utils.CapturingRenderersFactory;
import androidx.media3.test.utils.DumpFileAsserts; import androidx.media3.test.utils.DumpFileAsserts;
import androidx.media3.test.utils.FakeClock; import androidx.media3.test.utils.FakeClock;
@ -93,10 +97,8 @@ import org.junit.Assert;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
/** Unit test for {@link ServerSideAdInsertionMediaSource}. */ /** Unit test for {@link ServerSideAdInsertionMediaSource}. */
@Config(sdk = 30) // TODO: b/382017156 - Remove this when the tests pass on API 31+.
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public final class ServerSideAdInsertionMediaSourceTest { public final class ServerSideAdInsertionMediaSourceTest {
@ -472,7 +474,8 @@ public final class ServerSideAdInsertionMediaSourceTest {
public void playbackWithNewlyInsertedAds_playsSuccessfulWithoutRendererResets() throws Exception { public void playbackWithNewlyInsertedAds_playsSuccessfulWithoutRendererResets() throws Exception {
Context context = ApplicationProvider.getApplicationContext(); Context context = ApplicationProvider.getApplicationContext();
AtomicReference<Object> periodUid = new AtomicReference<>(); AtomicReference<Object> periodUid = new AtomicReference<>();
CapturingRenderersFactory renderersFactory = new CapturingRenderersFactory(context); CapturingRenderersFactory renderersFactory =
new CapturingRenderersFactory(context, DiscontinuitySkippingCapturingAudioSink.create());
ExoPlayer player = ExoPlayer player =
new ExoPlayer.Builder(context, renderersFactory) new ExoPlayer.Builder(context, renderersFactory)
.setClock(new FakeClock(/* isAutoAdvancing= */ true)) .setClock(new FakeClock(/* isAutoAdvancing= */ true))
@ -552,7 +555,8 @@ public final class ServerSideAdInsertionMediaSourceTest {
throws Exception { throws Exception {
Context context = ApplicationProvider.getApplicationContext(); Context context = ApplicationProvider.getApplicationContext();
AtomicReference<Object> periodUid = new AtomicReference<>(); AtomicReference<Object> periodUid = new AtomicReference<>();
CapturingRenderersFactory renderersFactory = new CapturingRenderersFactory(context); CapturingRenderersFactory renderersFactory =
new CapturingRenderersFactory(context, DiscontinuitySkippingCapturingAudioSink.create());
ExoPlayer player = ExoPlayer player =
new ExoPlayer.Builder(context, renderersFactory) new ExoPlayer.Builder(context, renderersFactory)
.setClock(new FakeClock(/* isAutoAdvancing= */ true)) .setClock(new FakeClock(/* isAutoAdvancing= */ true))
@ -832,4 +836,29 @@ public final class ServerSideAdInsertionMediaSourceTest {
assertThat(readSamples).containsExactly(0L, 200L, 400L, 600L, 800L).inOrder(); 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: buffer #19:
time = 1000000485179 time = 1000000485179
data = empty data = empty
discontinuity:
discontinuity:
discontinuity:
buffer #20: buffer #20:
time = 1000000508399 time = 1000000508399
data = empty data = empty

View File

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

View File

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

View File

@ -86,9 +86,19 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
* @param context The {@link Context}. * @param context The {@link Context}.
*/ */
public CapturingRenderersFactory(Context 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.context = context;
this.mediaCodecAdapterFactory = new CapturingMediaCodecAdapter.Factory(context); this.mediaCodecAdapterFactory = new CapturingMediaCodecAdapter.Factory(context);
this.audioSink = CapturingAudioSink.create(); this.audioSink = capturingAudioSink;
this.imageOutput = new CapturingImageOutput(); this.imageOutput = new CapturingImageOutput();
this.imageDecoderFactory = ImageDecoder.Factory.DEFAULT; this.imageDecoderFactory = ImageDecoder.Factory.DEFAULT;
this.textRendererFactory = TextRenderer::new; this.textRendererFactory = TextRenderer::new;