From daa8750382e6514b5b89e6aded232e883cafc37c Mon Sep 17 00:00:00 2001 From: tonihei Date: Fri, 10 May 2024 02:49:36 -0700 Subject: [PATCH] Ensure silence skipping doesn't interfere with other discontinuities The same doSomeWork iteration that triggers the silence skipping discontinuity may already have another discontinuities (like AUTO_TRANSITION), which should take precedence over the silence skipping. PiperOrigin-RevId: 632432851 --- RELEASENOTES.md | 2 + .../exoplayer/ExoPlayerImplInternal.java | 4 +- .../media3/exoplayer/ExoPlayerTest.java | 56 +++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0acf25d3ef..5d7c84494c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -24,6 +24,8 @@ `DefaultPreloadManager` and the custom `PreloadMediaSource.PreloadControl` implementations to preload the next source or take other actions. + * Fix bug where silence skipping at the end of items can trigger a + playback exception. * Transformer: * Work around a decoder bug where the number of audio channels was capped at stereo when handling PCM input. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index a2842d452f..7deff633ba 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -1038,13 +1038,15 @@ import java.util.concurrent.atomic.AtomicBoolean; long periodPositionUs = playingPeriodHolder.toPeriodTime(rendererPositionUs); maybeTriggerPendingMessages(playbackInfo.positionUs, periodPositionUs); if (mediaClock.hasSkippedSilenceSinceLastCall()) { + // Only report silence skipping if there isn't already another discontinuity. + boolean reportSilenceSkip = !playbackInfoUpdate.positionDiscontinuity; playbackInfo = handlePositionDiscontinuity( playbackInfo.periodId, /* positionUs= */ periodPositionUs, playbackInfo.requestedContentPositionUs, /* discontinuityStartPositionUs= */ periodPositionUs, - /* reportDiscontinuity= */ true, + /* reportDiscontinuity= */ reportSilenceSkip, Player.DISCONTINUITY_REASON_SILENCE_SKIP); } else { playbackInfo.updatePositionUs(periodPositionUs); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index 0e67717fa9..cdb0515cec 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -14748,6 +14748,62 @@ public class ExoPlayerTest { player.release(); } + @Test + public void silenceSkipped_atItemTransition_doesNotHideAutoTransitionDiscontinuity() + throws Exception { + FakeMediaClockRenderer audioRenderer = + new FakeMediaClockRenderer(C.TRACK_TYPE_AUDIO) { + private long positionUs; + + @Override + protected void onStreamChanged( + Format[] formats, long startPositionUs, long offsetUs, MediaPeriodId mediaPeriodId) { + positionUs = offsetUs; + } + + @Override + public long getPositionUs() { + // Advance position to make playback progress. + positionUs += 10_000; + return positionUs; + } + + @Override + public boolean hasSkippedSilenceSinceLastCall() { + // Contuniuosly report skipped silences to ensure they will overlap with other + // discontinuities like AUTO_TRANSITION. + return true; + } + + @Override + public void setPlaybackParameters(PlaybackParameters playbackParameters) {} + + @Override + public PlaybackParameters getPlaybackParameters() { + return PlaybackParameters.DEFAULT; + } + }; + ExoPlayer player = + parameterizeTestExoPlayerBuilder( + new TestExoPlayerBuilder(context).setRenderers(audioRenderer)) + .build(); + Player.Listener mockPlayerListener = mock(Player.Listener.class); + player.addListener(mockPlayerListener); + + player.setMediaSources( + ImmutableList.of( + new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT), + new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT), + new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT))); + player.prepare(); + player.play(); + runUntilPlaybackState(player, Player.STATE_ENDED); + player.release(); + + verify(mockPlayerListener, times(2)) + .onPositionDiscontinuity(any(), any(), eq(Player.DISCONTINUITY_REASON_AUTO_TRANSITION)); + } + @Test public void seekToZeroAndTrackSelection_withNonZeroDefaultPosition_startsPlaybackAtZero() throws Exception {