diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e3b7ce1b0b..4daafc1236 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -13,6 +13,8 @@ playback thread for a new ExoPlayer instance. * Allow download manager helpers to be cleared ([#10776](https://github.com/google/ExoPlayer/issues/10776)). + * Add parameter to `BasePlayer.seekTo` to also indicate the command used + for seeking. * Audio: * Use the compressed audio format bitrate to calculate the min buffer size for `AudioTrack` in direct playbacks (passthrough). diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index 57b81b3fa5..b7c1443816 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -15,6 +15,7 @@ */ package androidx.media3.cast; +import static androidx.annotation.VisibleForTesting.PROTECTED; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Util.castNonNull; import static java.lang.Math.min; @@ -399,7 +400,12 @@ public final class CastPlayer extends BasePlayer { // don't implement onPositionDiscontinuity(). @SuppressWarnings("deprecation") @Override - public void seekTo(int mediaItemIndex, long positionMs) { + @VisibleForTesting(otherwise = PROTECTED) + public void seekTo( + int mediaItemIndex, + long positionMs, + @Player.Command int seekCommand, + boolean isRepeatingCurrentItem) { MediaStatus mediaStatus = getMediaStatus(); // We assume the default position is 0. There is no support for seeking to the default position // in RemoteMediaClient. diff --git a/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java b/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java index 74b144baa3..b0f31e5d21 100644 --- a/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/BasePlayer.java @@ -15,14 +15,15 @@ */ package androidx.media3.common; +import static androidx.annotation.VisibleForTesting.PROTECTED; import static java.lang.Math.max; import static java.lang.Math.min; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import com.google.common.collect.ImmutableList; -import com.google.errorprone.annotations.ForOverride; import java.util.List; /** Abstract base {@link Player} which implements common implementation independent methods. */ @@ -121,27 +122,23 @@ public abstract class BasePlayer implements Player { @Override public final void seekToDefaultPosition() { - seekToDefaultPosition(getCurrentMediaItemIndex()); + seekToDefaultPositionInternal( + getCurrentMediaItemIndex(), Player.COMMAND_SEEK_TO_DEFAULT_POSITION); } @Override public final void seekToDefaultPosition(int mediaItemIndex) { - seekTo(mediaItemIndex, /* positionMs= */ C.TIME_UNSET); - } - - @Override - public final void seekTo(long positionMs) { - seekTo(getCurrentMediaItemIndex(), positionMs); + seekToDefaultPositionInternal(mediaItemIndex, Player.COMMAND_SEEK_TO_MEDIA_ITEM); } @Override public final void seekBack() { - seekToOffset(-getSeekBackIncrement()); + seekToOffset(-getSeekBackIncrement(), Player.COMMAND_SEEK_BACK); } @Override public final void seekForward() { - seekToOffset(getSeekForwardIncrement()); + seekToOffset(getSeekForwardIncrement(), Player.COMMAND_SEEK_FORWARD); } /** @@ -187,15 +184,7 @@ public abstract class BasePlayer implements Player { @Override public final void seekToPreviousMediaItem() { - int previousMediaItemIndex = getPreviousMediaItemIndex(); - if (previousMediaItemIndex == C.INDEX_UNSET) { - return; - } - if (previousMediaItemIndex == getCurrentMediaItemIndex()) { - repeatCurrentMediaItem(); - } else { - seekToDefaultPosition(previousMediaItemIndex); - } + seekToPreviousMediaItemInternal(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); } @Override @@ -207,12 +196,12 @@ public abstract class BasePlayer implements Player { boolean hasPreviousMediaItem = hasPreviousMediaItem(); if (isCurrentMediaItemLive() && !isCurrentMediaItemSeekable()) { if (hasPreviousMediaItem) { - seekToPreviousMediaItem(); + seekToPreviousMediaItemInternal(Player.COMMAND_SEEK_TO_PREVIOUS); } } else if (hasPreviousMediaItem && getCurrentPosition() <= getMaxSeekToPreviousPosition()) { - seekToPreviousMediaItem(); + seekToPreviousMediaItemInternal(Player.COMMAND_SEEK_TO_PREVIOUS); } else { - seekTo(/* positionMs= */ 0); + seekToCurrentItem(/* positionMs= */ 0, Player.COMMAND_SEEK_TO_PREVIOUS); } } @@ -259,15 +248,7 @@ public abstract class BasePlayer implements Player { @Override public final void seekToNextMediaItem() { - int nextMediaItemIndex = getNextMediaItemIndex(); - if (nextMediaItemIndex == C.INDEX_UNSET) { - return; - } - if (nextMediaItemIndex == getCurrentMediaItemIndex()) { - repeatCurrentMediaItem(); - } else { - seekToDefaultPosition(nextMediaItemIndex); - } + seekToNextMediaItemInternal(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); } @Override @@ -277,12 +258,42 @@ public abstract class BasePlayer implements Player { return; } if (hasNextMediaItem()) { - seekToNextMediaItem(); + seekToNextMediaItemInternal(Player.COMMAND_SEEK_TO_NEXT); } else if (isCurrentMediaItemLive() && isCurrentMediaItemDynamic()) { - seekToDefaultPosition(); + seekToDefaultPositionInternal(getCurrentMediaItemIndex(), Player.COMMAND_SEEK_TO_NEXT); } } + @Override + public final void seekTo(long positionMs) { + seekToCurrentItem(positionMs, Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + } + + @Override + public final void seekTo(int mediaItemIndex, long positionMs) { + seekTo( + mediaItemIndex, + positionMs, + Player.COMMAND_SEEK_TO_MEDIA_ITEM, + /* isRepeatingCurrentItem= */ false); + } + + /** + * Seeks to a position in the specified {@link MediaItem}. + * + * @param mediaItemIndex The index of the {@link MediaItem}. + * @param positionMs The seek position in the specified {@link MediaItem} in milliseconds, or + * {@link C#TIME_UNSET} to seek to the media item's default position. + * @param seekCommand The {@link Player.Command} used to trigger the seek. + * @param isRepeatingCurrentItem Whether this seeks repeats the current item. + */ + @VisibleForTesting(otherwise = PROTECTED) + public abstract void seekTo( + int mediaItemIndex, + long positionMs, + @Player.Command int seekCommand, + boolean isRepeatingCurrentItem); + @Override public final void setPlaybackSpeed(float speed) { setPlaybackParameters(getPlaybackParameters().withSpeed(speed)); @@ -437,29 +448,63 @@ public abstract class BasePlayer implements Player { : timeline.getWindow(getCurrentMediaItemIndex(), window).getDurationMs(); } - /** - * Repeat the current media item. - * - *
The default implementation seeks to the default position in the current item, which can be - * overridden for additional handling. - */ - @ForOverride - protected void repeatCurrentMediaItem() { - seekToDefaultPosition(); - } - private @RepeatMode int getRepeatModeForNavigation() { @RepeatMode int repeatMode = getRepeatMode(); return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode; } - private void seekToOffset(long offsetMs) { + private void seekToCurrentItem(long positionMs, @Player.Command int seekCommand) { + seekTo( + getCurrentMediaItemIndex(), positionMs, seekCommand, /* isRepeatingCurrentItem= */ false); + } + + private void seekToOffset(long offsetMs, @Player.Command int seekCommand) { long positionMs = getCurrentPosition() + offsetMs; long durationMs = getDuration(); if (durationMs != C.TIME_UNSET) { positionMs = min(positionMs, durationMs); } positionMs = max(positionMs, 0); - seekTo(positionMs); + seekToCurrentItem(positionMs, seekCommand); + } + + private void seekToDefaultPositionInternal(int mediaItemIndex, @Player.Command int seekCommand) { + seekTo( + mediaItemIndex, + /* positionMs= */ C.TIME_UNSET, + seekCommand, + /* isRepeatingCurrentItem= */ false); + } + + private void seekToNextMediaItemInternal(@Player.Command int seekCommand) { + int nextMediaItemIndex = getNextMediaItemIndex(); + if (nextMediaItemIndex == C.INDEX_UNSET) { + return; + } + if (nextMediaItemIndex == getCurrentMediaItemIndex()) { + repeatCurrentMediaItem(seekCommand); + } else { + seekToDefaultPositionInternal(nextMediaItemIndex, seekCommand); + } + } + + private void seekToPreviousMediaItemInternal(@Player.Command int seekCommand) { + int previousMediaItemIndex = getPreviousMediaItemIndex(); + if (previousMediaItemIndex == C.INDEX_UNSET) { + return; + } + if (previousMediaItemIndex == getCurrentMediaItemIndex()) { + repeatCurrentMediaItem(seekCommand); + } else { + seekToDefaultPositionInternal(previousMediaItemIndex, seekCommand); + } + } + + private void repeatCurrentMediaItem(@Player.Command int seekCommand) { + seekTo( + getCurrentMediaItemIndex(), + /* positionMs= */ C.TIME_UNSET, + seekCommand, + /* isRepeatingCurrentItem= */ true); } } diff --git a/libraries/common/src/main/java/androidx/media3/common/SimpleBasePlayer.java b/libraries/common/src/main/java/androidx/media3/common/SimpleBasePlayer.java index 2a2a3ffc45..512b9d9311 100644 --- a/libraries/common/src/main/java/androidx/media3/common/SimpleBasePlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/SimpleBasePlayer.java @@ -15,6 +15,7 @@ */ package androidx.media3.common; +import static androidx.annotation.VisibleForTesting.PROTECTED; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Util.castNonNull; @@ -32,6 +33,7 @@ import android.view.TextureView; import androidx.annotation.FloatRange; import androidx.annotation.IntRange; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.media3.common.text.CueGroup; import androidx.media3.common.util.Clock; import androidx.media3.common.util.HandlerWrapper; @@ -2133,13 +2135,12 @@ public abstract class SimpleBasePlayer extends BasePlayer { } @Override - public final void seekTo(int mediaItemIndex, long positionMs) { - // TODO: implement. - throw new IllegalStateException(); - } - - @Override - protected final void repeatCurrentMediaItem() { + @VisibleForTesting(otherwise = PROTECTED) + public final void seekTo( + int mediaItemIndex, + long positionMs, + @Player.Command int seekCommand, + boolean isRepeatingCurrentItem) { // TODO: implement. throw new IllegalStateException(); } diff --git a/libraries/common/src/test/java/androidx/media3/common/BasePlayerTest.java b/libraries/common/src/test/java/androidx/media3/common/BasePlayerTest.java new file mode 100644 index 0000000000..4f3c677f66 --- /dev/null +++ b/libraries/common/src/test/java/androidx/media3/common/BasePlayerTest.java @@ -0,0 +1,318 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.common; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import androidx.media3.test.utils.FakeTimeline; +import androidx.media3.test.utils.StubPlayer; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for {@link BasePlayer}. */ +@RunWith(AndroidJUnit4.class) +public class BasePlayerTest { + + @Test + public void seekTo_withIndexAndPosition_usesCommandSeekToMediaItem() { + BasePlayer player = spy(new TestBasePlayer()); + + player.seekTo(/* mediaItemIndex= */ 2, /* positionMs= */ 4000); + + verify(player) + .seekTo( + /* mediaItemIndex= */ 2, + /* positionMs= */ 4000, + Player.COMMAND_SEEK_TO_MEDIA_ITEM, + /* isRepeatingCurrentItem= */ false); + } + + @Test + public void seekTo_withPosition_usesCommandSeekInCurrentMediaItem() { + BasePlayer player = + spy( + new TestBasePlayer() { + @Override + public int getCurrentMediaItemIndex() { + return 1; + } + }); + + player.seekTo(/* positionMs= */ 4000); + + verify(player) + .seekTo( + /* mediaItemIndex= */ 1, + /* positionMs= */ 4000, + Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, + /* isRepeatingCurrentItem= */ false); + } + + @Test + public void seekToDefaultPosition_withIndex_usesCommandSeekToMediaItem() { + BasePlayer player = spy(new TestBasePlayer()); + + player.seekToDefaultPosition(/* mediaItemIndex= */ 2); + + verify(player) + .seekTo( + /* mediaItemIndex= */ 2, + /* positionMs= */ C.TIME_UNSET, + Player.COMMAND_SEEK_TO_MEDIA_ITEM, + /* isRepeatingCurrentItem= */ false); + } + + @Test + public void seekToDefaultPosition_withoutIndex_usesCommandSeekToDefaultPosition() { + BasePlayer player = + spy( + new TestBasePlayer() { + @Override + public int getCurrentMediaItemIndex() { + return 1; + } + }); + + player.seekToDefaultPosition(); + + verify(player) + .seekTo( + /* mediaItemIndex= */ 1, + /* positionMs= */ C.TIME_UNSET, + Player.COMMAND_SEEK_TO_DEFAULT_POSITION, + /* isRepeatingCurrentItem= */ false); + } + + @Test + public void seekToNext_usesCommandSeekToNext() { + BasePlayer player = + spy( + new TestBasePlayer() { + @Override + public int getCurrentMediaItemIndex() { + return 1; + } + }); + + player.seekToNext(); + + verify(player) + .seekTo( + /* mediaItemIndex= */ 2, + /* positionMs= */ C.TIME_UNSET, + Player.COMMAND_SEEK_TO_NEXT, + /* isRepeatingCurrentItem= */ false); + } + + @Test + public void seekToNextMediaItem_usesCommandSeekToNextMediaItem() { + BasePlayer player = + spy( + new TestBasePlayer() { + @Override + public int getCurrentMediaItemIndex() { + return 1; + } + }); + + player.seekToNextMediaItem(); + + verify(player) + .seekTo( + /* mediaItemIndex= */ 2, + /* positionMs= */ C.TIME_UNSET, + Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, + /* isRepeatingCurrentItem= */ false); + } + + @Test + public void seekForward_usesCommandSeekForward() { + BasePlayer player = + spy( + new TestBasePlayer() { + @Override + public long getSeekForwardIncrement() { + return 2000; + } + + @Override + public int getCurrentMediaItemIndex() { + return 1; + } + + @Override + public long getCurrentPosition() { + return 5000; + } + }); + + player.seekForward(); + + verify(player) + .seekTo( + /* mediaItemIndex= */ 1, + /* positionMs= */ 7000, + Player.COMMAND_SEEK_FORWARD, + /* isRepeatingCurrentItem= */ false); + } + + @Test + public void seekToPrevious_usesCommandSeekToPrevious() { + BasePlayer player = + spy( + new TestBasePlayer() { + @Override + public int getCurrentMediaItemIndex() { + return 1; + } + + @Override + public long getMaxSeekToPreviousPosition() { + return 4000; + } + + @Override + public long getCurrentPosition() { + return 2000; + } + }); + + player.seekToPrevious(); + + verify(player) + .seekTo( + /* mediaItemIndex= */ 0, + /* positionMs= */ C.TIME_UNSET, + Player.COMMAND_SEEK_TO_PREVIOUS, + /* isRepeatingCurrentItem= */ false); + } + + @Test + public void seekToPreviousMediaItem_usesCommandSeekToPreviousMediaItem() { + BasePlayer player = + spy( + new TestBasePlayer() { + @Override + public int getCurrentMediaItemIndex() { + return 1; + } + }); + + player.seekToPreviousMediaItem(); + + verify(player) + .seekTo( + /* mediaItemIndex= */ 0, + /* positionMs= */ C.TIME_UNSET, + Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, + /* isRepeatingCurrentItem= */ false); + } + + @Test + public void seekBack_usesCommandSeekBack() { + BasePlayer player = + spy( + new TestBasePlayer() { + @Override + public long getSeekBackIncrement() { + return 2000; + } + + @Override + public int getCurrentMediaItemIndex() { + return 1; + } + + @Override + public long getCurrentPosition() { + return 5000; + } + }); + + player.seekBack(); + + verify(player) + .seekTo( + /* mediaItemIndex= */ 1, + /* positionMs= */ 3000, + Player.COMMAND_SEEK_BACK, + /* isRepeatingCurrentItem= */ false); + } + + private static class TestBasePlayer extends StubPlayer { + + @Override + public void seekTo( + int mediaItemIndex, + long positionMs, + @Player.Command int seekCommand, + boolean isRepeatingCurrentItem) { + // Do nothing. + } + + @Override + public long getSeekBackIncrement() { + return 2000; + } + + @Override + public long getSeekForwardIncrement() { + return 2000; + } + + @Override + public long getMaxSeekToPreviousPosition() { + return 2000; + } + + @Override + public Timeline getCurrentTimeline() { + return new FakeTimeline(/* windowCount= */ 3); + } + + @Override + public int getCurrentMediaItemIndex() { + return 1; + } + + @Override + public long getCurrentPosition() { + return 5000; + } + + @Override + public long getDuration() { + return 20000; + } + + @Override + public boolean isPlayingAd() { + return false; + } + + @Override + public int getRepeatMode() { + return Player.REPEAT_MODE_OFF; + } + + @Override + public boolean getShuffleModeEnabled() { + return false; + } + } +} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 5f27dc546a..4e6ebf0c32 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -823,16 +823,51 @@ import java.util.concurrent.TimeoutException; } @Override - protected void repeatCurrentMediaItem() { + public void seekTo( + int mediaItemIndex, + long positionMs, + @Player.Command int seekCommand, + boolean isRepeatingCurrentItem) { verifyApplicationThread(); - seekToInternal( - getCurrentMediaItemIndex(), /* positionMs= */ C.TIME_UNSET, /* repeatMediaItem= */ true); - } - - @Override - public void seekTo(int mediaItemIndex, long positionMs) { - verifyApplicationThread(); - seekToInternal(mediaItemIndex, positionMs, /* repeatMediaItem= */ false); + analyticsCollector.notifySeekStarted(); + Timeline timeline = playbackInfo.timeline; + if (mediaItemIndex < 0 + || (!timeline.isEmpty() && mediaItemIndex >= timeline.getWindowCount())) { + throw new IllegalSeekPositionException(timeline, mediaItemIndex, positionMs); + } + pendingOperationAcks++; + if (isPlayingAd()) { + // TODO: Investigate adding support for seeking during ads. This is complicated to do in + // general because the midroll ad preceding the seek destination must be played before the + // content position can be played, if a different ad is playing at the moment. + Log.w(TAG, "seekTo ignored because an ad is playing"); + ExoPlayerImplInternal.PlaybackInfoUpdate playbackInfoUpdate = + new ExoPlayerImplInternal.PlaybackInfoUpdate(this.playbackInfo); + playbackInfoUpdate.incrementPendingOperationAcks(1); + playbackInfoUpdateListener.onPlaybackInfoUpdate(playbackInfoUpdate); + return; + } + @Player.State + int newPlaybackState = + getPlaybackState() == Player.STATE_IDLE ? Player.STATE_IDLE : STATE_BUFFERING; + int oldMaskingMediaItemIndex = getCurrentMediaItemIndex(); + PlaybackInfo newPlaybackInfo = playbackInfo.copyWithPlaybackState(newPlaybackState); + newPlaybackInfo = + maskTimelineAndPosition( + newPlaybackInfo, + timeline, + maskWindowPositionMsOrGetPeriodPositionUs(timeline, mediaItemIndex, positionMs)); + internalPlayer.seekTo(timeline, mediaItemIndex, Util.msToUs(positionMs)); + updatePlaybackInfo( + newPlaybackInfo, + /* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, + /* seekProcessed= */ true, + /* positionDiscontinuity= */ true, + /* positionDiscontinuityReason= */ DISCONTINUITY_REASON_SEEK, + /* discontinuityWindowStartPositionUs= */ getCurrentPositionUsInternal(newPlaybackInfo), + oldMaskingMediaItemIndex, + isRepeatingCurrentItem); } @Override @@ -2696,48 +2731,6 @@ import java.util.concurrent.TimeoutException; } } - private void seekToInternal(int mediaItemIndex, long positionMs, boolean repeatMediaItem) { - analyticsCollector.notifySeekStarted(); - Timeline timeline = playbackInfo.timeline; - if (mediaItemIndex < 0 - || (!timeline.isEmpty() && mediaItemIndex >= timeline.getWindowCount())) { - throw new IllegalSeekPositionException(timeline, mediaItemIndex, positionMs); - } - pendingOperationAcks++; - if (isPlayingAd()) { - // TODO: Investigate adding support for seeking during ads. This is complicated to do in - // general because the midroll ad preceding the seek destination must be played before the - // content position can be played, if a different ad is playing at the moment. - Log.w(TAG, "seekTo ignored because an ad is playing"); - ExoPlayerImplInternal.PlaybackInfoUpdate playbackInfoUpdate = - new ExoPlayerImplInternal.PlaybackInfoUpdate(this.playbackInfo); - playbackInfoUpdate.incrementPendingOperationAcks(1); - playbackInfoUpdateListener.onPlaybackInfoUpdate(playbackInfoUpdate); - return; - } - @Player.State - int newPlaybackState = - getPlaybackState() == Player.STATE_IDLE ? Player.STATE_IDLE : STATE_BUFFERING; - int oldMaskingMediaItemIndex = getCurrentMediaItemIndex(); - PlaybackInfo newPlaybackInfo = playbackInfo.copyWithPlaybackState(newPlaybackState); - newPlaybackInfo = - maskTimelineAndPosition( - newPlaybackInfo, - timeline, - maskWindowPositionMsOrGetPeriodPositionUs(timeline, mediaItemIndex, positionMs)); - internalPlayer.seekTo(timeline, mediaItemIndex, Util.msToUs(positionMs)); - updatePlaybackInfo( - newPlaybackInfo, - /* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - /* seekProcessed= */ true, - /* positionDiscontinuity= */ true, - /* positionDiscontinuityReason= */ DISCONTINUITY_REASON_SEEK, - /* discontinuityWindowStartPositionUs= */ getCurrentPositionUsInternal(newPlaybackInfo), - oldMaskingMediaItemIndex, - repeatMediaItem); - } - private static DeviceInfo createDeviceInfo(StreamVolumeManager streamVolumeManager) { return new DeviceInfo( DeviceInfo.PLAYBACK_TYPE_LOCAL, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index c5150e5c7f..5676ce7554 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -15,6 +15,8 @@ */ package androidx.media3.exoplayer; +import static androidx.annotation.VisibleForTesting.PROTECTED; + import android.content.Context; import android.media.AudioDeviceInfo; import android.os.Looper; @@ -1004,10 +1006,16 @@ public class SimpleExoPlayer extends BasePlayer return player.isLoading(); } + @SuppressWarnings("ForOverride") // Forwarding to ForOverride method in ExoPlayerImpl. @Override - public void seekTo(int mediaItemIndex, long positionMs) { + @VisibleForTesting(otherwise = PROTECTED) + public void seekTo( + int mediaItemIndex, + long positionMs, + @Player.Command int seekCommand, + boolean isRepeatingCurrentItem) { blockUntilConstructorFinished(); - player.seekTo(mediaItemIndex, positionMs); + player.seekTo(mediaItemIndex, positionMs, seekCommand, isRepeatingCurrentItem); } @Override diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java index 8638745a56..5845cdaceb 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubPlayer.java @@ -147,7 +147,11 @@ public class StubPlayer extends BasePlayer { } @Override - public void seekTo(int mediaItemIndex, long positionMs) { + public void seekTo( + int mediaItemIndex, + long positionMs, + @Player.Command int seekCommand, + boolean isRepeatingCurrentItem) { throw new UnsupportedOperationException(); }