mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Allow unset index and position values + remove period index
This simplifies some position tracking needs for an app implementing
SimpleBasePlayer.
- The period index can always be derived from the media item index
and the position. So there is no need to set it separately.
- The media item index can be left unset in the State in case the app
doesn't care about the value or wants to set it the default start
index (e.g. while the playlist is still empty where UNSET is
different from zero).
- Similarly, we should allow to set the content position (and buffered
position) to C.TIME_UNSET to let the app ignore it or indicate the
default position explictly.
PiperOrigin-RevId: 495352633
(cherry picked from commit 545fa59462)
This commit is contained in:
parent
8e8abdaead
commit
b1e4ac446f
2 changed files with 309 additions and 127 deletions
|
|
@ -19,6 +19,7 @@ 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;
|
||||
import static androidx.media3.common.util.Util.msToUs;
|
||||
import static androidx.media3.common.util.Util.usToMs;
|
||||
import static java.lang.Math.max;
|
||||
|
||||
|
|
@ -127,12 +128,11 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
private Timeline timeline;
|
||||
private MediaMetadata playlistMetadata;
|
||||
private int currentMediaItemIndex;
|
||||
private int currentPeriodIndex;
|
||||
private int currentAdGroupIndex;
|
||||
private int currentAdIndexInAdGroup;
|
||||
private long contentPositionMs;
|
||||
@Nullable private Long contentPositionMs;
|
||||
private PositionSupplier contentPositionMsSupplier;
|
||||
private long adPositionMs;
|
||||
@Nullable private Long adPositionMs;
|
||||
private PositionSupplier adPositionMsSupplier;
|
||||
private PositionSupplier contentBufferedPositionMsSupplier;
|
||||
private PositionSupplier adBufferedPositionMsSupplier;
|
||||
|
|
@ -170,15 +170,14 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
playlist = ImmutableList.of();
|
||||
timeline = Timeline.EMPTY;
|
||||
playlistMetadata = MediaMetadata.EMPTY;
|
||||
currentMediaItemIndex = 0;
|
||||
currentPeriodIndex = C.INDEX_UNSET;
|
||||
currentMediaItemIndex = C.INDEX_UNSET;
|
||||
currentAdGroupIndex = C.INDEX_UNSET;
|
||||
currentAdIndexInAdGroup = C.INDEX_UNSET;
|
||||
contentPositionMs = C.TIME_UNSET;
|
||||
contentPositionMsSupplier = PositionSupplier.ZERO;
|
||||
adPositionMs = C.TIME_UNSET;
|
||||
contentPositionMs = null;
|
||||
contentPositionMsSupplier = PositionSupplier.getConstant(C.TIME_UNSET);
|
||||
adPositionMs = null;
|
||||
adPositionMsSupplier = PositionSupplier.ZERO;
|
||||
contentBufferedPositionMsSupplier = PositionSupplier.ZERO;
|
||||
contentBufferedPositionMsSupplier = PositionSupplier.getConstant(C.TIME_UNSET);
|
||||
adBufferedPositionMsSupplier = PositionSupplier.ZERO;
|
||||
totalBufferedDurationMsSupplier = PositionSupplier.ZERO;
|
||||
hasPositionDiscontinuity = false;
|
||||
|
|
@ -215,12 +214,11 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
this.timeline = state.timeline;
|
||||
this.playlistMetadata = state.playlistMetadata;
|
||||
this.currentMediaItemIndex = state.currentMediaItemIndex;
|
||||
this.currentPeriodIndex = state.currentPeriodIndex;
|
||||
this.currentAdGroupIndex = state.currentAdGroupIndex;
|
||||
this.currentAdIndexInAdGroup = state.currentAdIndexInAdGroup;
|
||||
this.contentPositionMs = C.TIME_UNSET;
|
||||
this.contentPositionMs = null;
|
||||
this.contentPositionMsSupplier = state.contentPositionMsSupplier;
|
||||
this.adPositionMs = C.TIME_UNSET;
|
||||
this.adPositionMs = null;
|
||||
this.adPositionMsSupplier = state.adPositionMsSupplier;
|
||||
this.contentBufferedPositionMsSupplier = state.contentBufferedPositionMsSupplier;
|
||||
this.adBufferedPositionMsSupplier = state.adBufferedPositionMsSupplier;
|
||||
|
|
@ -574,7 +572,8 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
* <p>The media item index must be less than the number of {@linkplain #setPlaylist media
|
||||
* items in the playlist}, if set.
|
||||
*
|
||||
* @param currentMediaItemIndex The current media item index.
|
||||
* @param currentMediaItemIndex The current media item index, or {@link C#INDEX_UNSET} to
|
||||
* assume the default first item in the playlist.
|
||||
* @return This builder.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
|
|
@ -583,26 +582,6 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current period index, or {@link C#INDEX_UNSET} to assume the first period of the
|
||||
* current media item is played.
|
||||
*
|
||||
* <p>The period index must be less than the total number of {@linkplain
|
||||
* MediaItemData.Builder#setPeriods periods} in the media item, if set, and the period at the
|
||||
* specified index must be part of the {@linkplain #setCurrentMediaItemIndex current media
|
||||
* item}.
|
||||
*
|
||||
* @param currentPeriodIndex The current period index, or {@link C#INDEX_UNSET} to assume the
|
||||
* first period of the current media item is played.
|
||||
* @return This builder.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setCurrentPeriodIndex(int currentPeriodIndex) {
|
||||
checkArgument(currentPeriodIndex == C.INDEX_UNSET || currentPeriodIndex >= 0);
|
||||
this.currentPeriodIndex = currentPeriodIndex;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current ad indices, or {@link C#INDEX_UNSET} if no ad is playing.
|
||||
*
|
||||
|
|
@ -632,7 +611,11 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
* <p>This position will be converted to an advancing {@link PositionSupplier} if the overall
|
||||
* state indicates an advancing playback position.
|
||||
*
|
||||
* @param positionMs The current content playback position in milliseconds.
|
||||
* <p>This method overrides any other {@link PositionSupplier} set via {@link
|
||||
* #setContentPositionMs(PositionSupplier)}.
|
||||
*
|
||||
* @param positionMs The current content playback position in milliseconds, or {@link
|
||||
* C#TIME_UNSET} to indicate the default start position.
|
||||
* @return This builder.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
|
|
@ -648,24 +631,30 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
* <p>The supplier is expected to return the updated position on every call if the playback is
|
||||
* advancing, for example by using {@link PositionSupplier#getExtrapolating}.
|
||||
*
|
||||
* <p>This method overrides any other position set via {@link #setContentPositionMs(long)}.
|
||||
*
|
||||
* @param contentPositionMsSupplier The {@link PositionSupplier} for the current content
|
||||
* playback position in milliseconds.
|
||||
* playback position in milliseconds, or {@link C#TIME_UNSET} to indicate the default
|
||||
* start position.
|
||||
* @return This builder.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setContentPositionMs(PositionSupplier contentPositionMsSupplier) {
|
||||
this.contentPositionMs = C.TIME_UNSET;
|
||||
this.contentPositionMs = null;
|
||||
this.contentPositionMsSupplier = contentPositionMsSupplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current ad playback position in milliseconds. The * value is unused if no ad is
|
||||
* Sets the current ad playback position in milliseconds. The value is unused if no ad is
|
||||
* playing.
|
||||
*
|
||||
* <p>This position will be converted to an advancing {@link PositionSupplier} if the overall
|
||||
* state indicates an advancing ad playback position.
|
||||
*
|
||||
* <p>This method overrides any other {@link PositionSupplier} set via {@link
|
||||
* #setAdPositionMs(PositionSupplier)}.
|
||||
*
|
||||
* @param positionMs The current ad playback position in milliseconds.
|
||||
* @return This builder.
|
||||
*/
|
||||
|
|
@ -682,13 +671,15 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
* <p>The supplier is expected to return the updated position on every call if the playback is
|
||||
* advancing, for example by using {@link PositionSupplier#getExtrapolating}.
|
||||
*
|
||||
* <p>This method overrides any other position set via {@link #setAdPositionMs(long)}.
|
||||
*
|
||||
* @param adPositionMsSupplier The {@link PositionSupplier} for the current ad playback
|
||||
* position in milliseconds. The value is unused if no ad is playing.
|
||||
* @return This builder.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setAdPositionMs(PositionSupplier adPositionMsSupplier) {
|
||||
this.adPositionMs = C.TIME_UNSET;
|
||||
this.adPositionMs = null;
|
||||
this.adPositionMsSupplier = adPositionMsSupplier;
|
||||
return this;
|
||||
}
|
||||
|
|
@ -698,7 +689,8 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
* playing content is buffered, in milliseconds.
|
||||
*
|
||||
* @param contentBufferedPositionMsSupplier The {@link PositionSupplier} for the estimated
|
||||
* position up to which the currently playing content is buffered, in milliseconds.
|
||||
* position up to which the currently playing content is buffered, in milliseconds, or
|
||||
* {@link C#TIME_UNSET} to indicate the default start position.
|
||||
* @return This builder.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
|
|
@ -838,18 +830,19 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
public final Timeline timeline;
|
||||
/** The playlist {@link MediaMetadata}. */
|
||||
public final MediaMetadata playlistMetadata;
|
||||
/** The current media item index. */
|
||||
public final int currentMediaItemIndex;
|
||||
/**
|
||||
* The current period index, or {@link C#INDEX_UNSET} to assume the first period of the current
|
||||
* media item is played.
|
||||
* The current media item index, or {@link C#INDEX_UNSET} to assume the default first item of
|
||||
* the playlist is played.
|
||||
*/
|
||||
public final int currentPeriodIndex;
|
||||
public final int currentMediaItemIndex;
|
||||
/** The current ad group index, or {@link C#INDEX_UNSET} if no ad is playing. */
|
||||
public final int currentAdGroupIndex;
|
||||
/** The current ad index in the ad group, or {@link C#INDEX_UNSET} if no ad is playing. */
|
||||
public final int currentAdIndexInAdGroup;
|
||||
/** The {@link PositionSupplier} for the current content playback position in milliseconds. */
|
||||
/**
|
||||
* The {@link PositionSupplier} for the current content playback position in milliseconds, or
|
||||
* {@link C#TIME_UNSET} to indicate the default start position.
|
||||
*/
|
||||
public final PositionSupplier contentPositionMsSupplier;
|
||||
/**
|
||||
* The {@link PositionSupplier} for the current ad playback position in milliseconds. The value
|
||||
|
|
@ -858,7 +851,8 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
public final PositionSupplier adPositionMsSupplier;
|
||||
/**
|
||||
* The {@link PositionSupplier} for the estimated position up to which the currently playing
|
||||
* content is buffered, in milliseconds.
|
||||
* content is buffered, in milliseconds, or {@link C#TIME_UNSET} to indicate the default start
|
||||
* position.
|
||||
*/
|
||||
public final PositionSupplier contentBufferedPositionMsSupplier;
|
||||
/**
|
||||
|
|
@ -887,22 +881,27 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
checkArgument(
|
||||
builder.playbackState == Player.STATE_IDLE
|
||||
|| builder.playbackState == Player.STATE_ENDED);
|
||||
checkArgument(
|
||||
builder.currentAdGroupIndex == C.INDEX_UNSET
|
||||
&& builder.currentAdIndexInAdGroup == C.INDEX_UNSET);
|
||||
} else {
|
||||
checkArgument(builder.currentMediaItemIndex < builder.timeline.getWindowCount());
|
||||
if (builder.currentPeriodIndex != C.INDEX_UNSET) {
|
||||
checkArgument(builder.currentPeriodIndex < builder.timeline.getPeriodCount());
|
||||
checkArgument(
|
||||
builder.timeline.getPeriod(builder.currentPeriodIndex, new Timeline.Period())
|
||||
.windowIndex
|
||||
== builder.currentMediaItemIndex);
|
||||
int mediaItemIndex = builder.currentMediaItemIndex;
|
||||
if (mediaItemIndex == C.INDEX_UNSET) {
|
||||
mediaItemIndex = 0; // TODO: Use shuffle order to find first index.
|
||||
} else {
|
||||
checkArgument(builder.currentMediaItemIndex < builder.timeline.getWindowCount());
|
||||
}
|
||||
if (builder.currentAdGroupIndex != C.INDEX_UNSET) {
|
||||
Timeline.Period period = new Timeline.Period();
|
||||
Timeline.Window window = new Timeline.Window();
|
||||
long contentPositionMs =
|
||||
builder.contentPositionMs != null
|
||||
? builder.contentPositionMs
|
||||
: builder.contentPositionMsSupplier.get();
|
||||
int periodIndex =
|
||||
builder.currentPeriodIndex != C.INDEX_UNSET
|
||||
? builder.currentPeriodIndex
|
||||
: builder.timeline.getWindow(builder.currentMediaItemIndex, new Timeline.Window())
|
||||
.firstPeriodIndex;
|
||||
Timeline.Period period = builder.timeline.getPeriod(periodIndex, new Timeline.Period());
|
||||
getPeriodIndexFromWindowPosition(
|
||||
builder.timeline, mediaItemIndex, contentPositionMs, window, period);
|
||||
builder.timeline.getPeriod(periodIndex, period);
|
||||
checkArgument(builder.currentAdGroupIndex < period.getAdGroupCount());
|
||||
int adCountInGroup = period.getAdCountInAdGroup(builder.currentAdGroupIndex);
|
||||
if (adCountInGroup != C.LENGTH_UNSET) {
|
||||
|
|
@ -918,11 +917,12 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
checkArgument(!builder.isLoading);
|
||||
}
|
||||
PositionSupplier contentPositionMsSupplier = builder.contentPositionMsSupplier;
|
||||
if (builder.contentPositionMs != C.TIME_UNSET) {
|
||||
if (builder.contentPositionMs != null) {
|
||||
if (builder.currentAdGroupIndex == C.INDEX_UNSET
|
||||
&& builder.playWhenReady
|
||||
&& builder.playbackState == Player.STATE_READY
|
||||
&& builder.playbackSuppressionReason == Player.PLAYBACK_SUPPRESSION_REASON_NONE) {
|
||||
&& builder.playbackSuppressionReason == Player.PLAYBACK_SUPPRESSION_REASON_NONE
|
||||
&& builder.contentPositionMs != C.TIME_UNSET) {
|
||||
contentPositionMsSupplier =
|
||||
PositionSupplier.getExtrapolating(
|
||||
builder.contentPositionMs, builder.playbackParameters.speed);
|
||||
|
|
@ -931,7 +931,7 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
}
|
||||
}
|
||||
PositionSupplier adPositionMsSupplier = builder.adPositionMsSupplier;
|
||||
if (builder.adPositionMs != C.TIME_UNSET) {
|
||||
if (builder.adPositionMs != null) {
|
||||
if (builder.currentAdGroupIndex != C.INDEX_UNSET
|
||||
&& builder.playWhenReady
|
||||
&& builder.playbackState == Player.STATE_READY
|
||||
|
|
@ -970,7 +970,6 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
this.timeline = builder.timeline;
|
||||
this.playlistMetadata = builder.playlistMetadata;
|
||||
this.currentMediaItemIndex = builder.currentMediaItemIndex;
|
||||
this.currentPeriodIndex = builder.currentPeriodIndex;
|
||||
this.currentAdGroupIndex = builder.currentAdGroupIndex;
|
||||
this.currentAdIndexInAdGroup = builder.currentAdIndexInAdGroup;
|
||||
this.contentPositionMsSupplier = contentPositionMsSupplier;
|
||||
|
|
@ -1024,7 +1023,6 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
&& playlist.equals(state.playlist)
|
||||
&& playlistMetadata.equals(state.playlistMetadata)
|
||||
&& currentMediaItemIndex == state.currentMediaItemIndex
|
||||
&& currentPeriodIndex == state.currentPeriodIndex
|
||||
&& currentAdGroupIndex == state.currentAdGroupIndex
|
||||
&& currentAdIndexInAdGroup == state.currentAdIndexInAdGroup
|
||||
&& contentPositionMsSupplier.equals(state.contentPositionMsSupplier)
|
||||
|
|
@ -1068,7 +1066,6 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
result = 31 * result + playlist.hashCode();
|
||||
result = 31 * result + playlistMetadata.hashCode();
|
||||
result = 31 * result + currentMediaItemIndex;
|
||||
result = 31 * result + currentPeriodIndex;
|
||||
result = 31 * result + currentAdGroupIndex;
|
||||
result = 31 * result + currentAdIndexInAdGroup;
|
||||
result = 31 * result + contentPositionMsSupplier.hashCode();
|
||||
|
|
@ -2198,7 +2195,8 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
.buildUpon()
|
||||
.setPlaybackState(Player.STATE_IDLE)
|
||||
.setTotalBufferedDurationMs(PositionSupplier.ZERO)
|
||||
.setContentBufferedPositionMs(state.contentPositionMsSupplier)
|
||||
.setContentBufferedPositionMs(
|
||||
PositionSupplier.getConstant(getContentPositionMsInternal(state)))
|
||||
.setAdBufferedPositionMs(state.adPositionMsSupplier)
|
||||
.setIsLoading(false)
|
||||
.build());
|
||||
|
|
@ -2230,7 +2228,8 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
.buildUpon()
|
||||
.setPlaybackState(Player.STATE_IDLE)
|
||||
.setTotalBufferedDurationMs(PositionSupplier.ZERO)
|
||||
.setContentBufferedPositionMs(state.contentPositionMsSupplier)
|
||||
.setContentBufferedPositionMs(
|
||||
PositionSupplier.getConstant(getContentPositionMsInternal(state)))
|
||||
.setAdBufferedPositionMs(state.adPositionMsSupplier)
|
||||
.setIsLoading(false)
|
||||
.build();
|
||||
|
|
@ -2297,13 +2296,13 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
@Override
|
||||
public final int getCurrentPeriodIndex() {
|
||||
verifyApplicationThreadAndInitState();
|
||||
return getCurrentPeriodIndexInternal(state, window);
|
||||
return getCurrentPeriodIndexInternal(state, window, period);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getCurrentMediaItemIndex() {
|
||||
verifyApplicationThreadAndInitState();
|
||||
return state.currentMediaItemIndex;
|
||||
return getCurrentMediaItemIndexInternal(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -2359,14 +2358,13 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
@Override
|
||||
public final long getContentPosition() {
|
||||
verifyApplicationThreadAndInitState();
|
||||
return state.contentPositionMsSupplier.get();
|
||||
return getContentPositionMsInternal(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long getContentBufferedPosition() {
|
||||
verifyApplicationThreadAndInitState();
|
||||
return max(
|
||||
state.contentBufferedPositionMsSupplier.get(), state.contentPositionMsSupplier.get());
|
||||
return max(getContentBufferedPositionMsInternal(state), getContentPositionMsInternal(state));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -2939,7 +2937,7 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
MediaItem mediaItem =
|
||||
newState.timeline.isEmpty()
|
||||
? null
|
||||
: newState.playlist.get(state.currentMediaItemIndex).mediaItem;
|
||||
: newState.playlist.get(getCurrentMediaItemIndexInternal(newState)).mediaItem;
|
||||
listeners.queueEvent(
|
||||
Player.EVENT_MEDIA_ITEM_TRANSITION,
|
||||
listener -> listener.onMediaItemTransition(mediaItem, mediaItemTransitionReason));
|
||||
|
|
@ -3159,23 +3157,59 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
private static Tracks getCurrentTracksInternal(State state) {
|
||||
return state.playlist.isEmpty()
|
||||
? Tracks.EMPTY
|
||||
: state.playlist.get(state.currentMediaItemIndex).tracks;
|
||||
: state.playlist.get(getCurrentMediaItemIndexInternal(state)).tracks;
|
||||
}
|
||||
|
||||
private static MediaMetadata getMediaMetadataInternal(State state) {
|
||||
return state.playlist.isEmpty()
|
||||
? MediaMetadata.EMPTY
|
||||
: state.playlist.get(state.currentMediaItemIndex).combinedMediaMetadata;
|
||||
: state.playlist.get(getCurrentMediaItemIndexInternal(state)).combinedMediaMetadata;
|
||||
}
|
||||
|
||||
private static int getCurrentPeriodIndexInternal(State state, Timeline.Window window) {
|
||||
if (state.currentPeriodIndex != C.INDEX_UNSET) {
|
||||
return state.currentPeriodIndex;
|
||||
}
|
||||
if (state.timeline.isEmpty()) {
|
||||
private static int getCurrentMediaItemIndexInternal(State state) {
|
||||
if (state.currentMediaItemIndex != C.INDEX_UNSET) {
|
||||
return state.currentMediaItemIndex;
|
||||
}
|
||||
return state.timeline.getWindow(state.currentMediaItemIndex, window).firstPeriodIndex;
|
||||
return 0; // TODO: Use shuffle order to get first item if playlist is not empty.
|
||||
}
|
||||
|
||||
private static long getContentPositionMsInternal(State state) {
|
||||
return getPositionOrDefaultInMediaItem(state.contentPositionMsSupplier.get(), state);
|
||||
}
|
||||
|
||||
private static long getContentBufferedPositionMsInternal(State state) {
|
||||
return getPositionOrDefaultInMediaItem(state.contentBufferedPositionMsSupplier.get(), state);
|
||||
}
|
||||
|
||||
private static long getPositionOrDefaultInMediaItem(long positionMs, State state) {
|
||||
if (positionMs != C.TIME_UNSET) {
|
||||
return positionMs;
|
||||
}
|
||||
if (state.playlist.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
return usToMs(state.playlist.get(getCurrentMediaItemIndexInternal(state)).defaultPositionUs);
|
||||
}
|
||||
|
||||
private static int getCurrentPeriodIndexInternal(
|
||||
State state, Timeline.Window window, Timeline.Period period) {
|
||||
int currentMediaItemIndex = getCurrentMediaItemIndexInternal(state);
|
||||
if (state.timeline.isEmpty()) {
|
||||
return currentMediaItemIndex;
|
||||
}
|
||||
return getPeriodIndexFromWindowPosition(
|
||||
state.timeline, currentMediaItemIndex, getContentPositionMsInternal(state), window, period);
|
||||
}
|
||||
|
||||
private static int getPeriodIndexFromWindowPosition(
|
||||
Timeline timeline,
|
||||
int windowIndex,
|
||||
long windowPositionMs,
|
||||
Timeline.Window window,
|
||||
Timeline.Period period) {
|
||||
Object periodUid =
|
||||
timeline.getPeriodPositionUs(window, period, windowIndex, msToUs(windowPositionMs)).first;
|
||||
return timeline.getIndexOfPeriod(periodUid);
|
||||
}
|
||||
|
||||
private static @Player.TimelineChangeReason int getTimelineChangeReason(
|
||||
|
|
@ -3206,9 +3240,10 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
return Player.DISCONTINUITY_REASON_REMOVE;
|
||||
}
|
||||
Object previousPeriodUid =
|
||||
previousState.timeline.getUidOfPeriod(getCurrentPeriodIndexInternal(previousState, window));
|
||||
previousState.timeline.getUidOfPeriod(
|
||||
getCurrentPeriodIndexInternal(previousState, window, period));
|
||||
Object newPeriodUid =
|
||||
newState.timeline.getUidOfPeriod(getCurrentPeriodIndexInternal(newState, window));
|
||||
newState.timeline.getUidOfPeriod(getCurrentPeriodIndexInternal(newState, window, period));
|
||||
if (!newPeriodUid.equals(previousPeriodUid)
|
||||
|| previousState.currentAdGroupIndex != newState.currentAdGroupIndex
|
||||
|| previousState.currentAdIndexInAdGroup != newState.currentAdIndexInAdGroup) {
|
||||
|
|
@ -3244,7 +3279,7 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
State state, Object currentPeriodUid, Timeline.Period period) {
|
||||
return state.currentAdGroupIndex != C.INDEX_UNSET
|
||||
? state.adPositionMsSupplier.get()
|
||||
: state.contentPositionMsSupplier.get()
|
||||
: getContentPositionMsInternal(state)
|
||||
- state.timeline.getPeriodByUid(currentPeriodUid, period).getPositionInWindowMs();
|
||||
}
|
||||
|
||||
|
|
@ -3265,11 +3300,11 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
Timeline.Period period) {
|
||||
@Nullable Object windowUid = null;
|
||||
@Nullable Object periodUid = null;
|
||||
int mediaItemIndex = state.currentMediaItemIndex;
|
||||
int mediaItemIndex = getCurrentMediaItemIndexInternal(state);
|
||||
int periodIndex = C.INDEX_UNSET;
|
||||
@Nullable MediaItem mediaItem = null;
|
||||
if (!state.timeline.isEmpty()) {
|
||||
periodIndex = getCurrentPeriodIndexInternal(state, window);
|
||||
periodIndex = getCurrentPeriodIndexInternal(state, window, period);
|
||||
periodUid = state.timeline.getPeriod(periodIndex, period, /* setIds= */ true).uid;
|
||||
windowUid = state.timeline.getWindow(mediaItemIndex, window).uid;
|
||||
mediaItem = window.mediaItem;
|
||||
|
|
@ -3281,9 +3316,9 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
contentPositionMs =
|
||||
state.currentAdGroupIndex == C.INDEX_UNSET
|
||||
? positionMs
|
||||
: state.contentPositionMsSupplier.get();
|
||||
: getContentPositionMsInternal(state);
|
||||
} else {
|
||||
contentPositionMs = state.contentPositionMsSupplier.get();
|
||||
contentPositionMs = getContentPositionMsInternal(state);
|
||||
positionMs =
|
||||
state.currentAdGroupIndex != C.INDEX_UNSET
|
||||
? state.adPositionMsSupplier.get()
|
||||
|
|
@ -3314,8 +3349,10 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
return MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED;
|
||||
}
|
||||
Object previousWindowUid =
|
||||
previousState.timeline.getWindow(previousState.currentMediaItemIndex, window).uid;
|
||||
Object newWindowUid = newState.timeline.getWindow(newState.currentMediaItemIndex, window).uid;
|
||||
previousState.timeline.getWindow(getCurrentMediaItemIndexInternal(previousState), window)
|
||||
.uid;
|
||||
Object newWindowUid =
|
||||
newState.timeline.getWindow(getCurrentMediaItemIndexInternal(newState), window).uid;
|
||||
if (!previousWindowUid.equals(newWindowUid)) {
|
||||
if (positionDiscontinuityReason == DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
||||
return MEDIA_ITEM_TRANSITION_REASON_AUTO;
|
||||
|
|
@ -3328,8 +3365,7 @@ public abstract class SimpleBasePlayer extends BasePlayer {
|
|||
// Only mark changes within the current item as a transition if we are repeating automatically
|
||||
// or via a seek to next/previous.
|
||||
if (positionDiscontinuityReason == DISCONTINUITY_REASON_AUTO_TRANSITION
|
||||
&& previousState.contentPositionMsSupplier.get()
|
||||
> newState.contentPositionMsSupplier.get()) {
|
||||
&& getContentPositionMsInternal(previousState) > getContentPositionMsInternal(newState)) {
|
||||
return MEDIA_ITEM_TRANSITION_REASON_REPEAT;
|
||||
}
|
||||
if (positionDiscontinuityReason == DISCONTINUITY_REASON_SEEK
|
||||
|
|
|
|||
|
|
@ -128,7 +128,6 @@ public class SimpleBasePlayerTest {
|
|||
.build()))
|
||||
.setPlaylistMetadata(new MediaMetadata.Builder().setArtist("artist").build())
|
||||
.setCurrentMediaItemIndex(1)
|
||||
.setCurrentPeriodIndex(1)
|
||||
.setCurrentAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2)
|
||||
.setContentPositionMs(() -> 456)
|
||||
.setAdPositionMs(() -> 6678)
|
||||
|
|
@ -275,7 +274,6 @@ public class SimpleBasePlayerTest {
|
|||
.setPlaylist(playlist)
|
||||
.setPlaylistMetadata(playlistMetadata)
|
||||
.setCurrentMediaItemIndex(1)
|
||||
.setCurrentPeriodIndex(1)
|
||||
.setCurrentAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 2)
|
||||
.setContentPositionMs(contentPositionSupplier)
|
||||
.setAdPositionMs(adPositionSupplier)
|
||||
|
|
@ -315,7 +313,6 @@ public class SimpleBasePlayerTest {
|
|||
assertThat(state.playlist).isEqualTo(playlist);
|
||||
assertThat(state.playlistMetadata).isEqualTo(playlistMetadata);
|
||||
assertThat(state.currentMediaItemIndex).isEqualTo(1);
|
||||
assertThat(state.currentPeriodIndex).isEqualTo(1);
|
||||
assertThat(state.currentAdGroupIndex).isEqualTo(1);
|
||||
assertThat(state.currentAdIndexInAdGroup).isEqualTo(2);
|
||||
assertThat(state.contentPositionMsSupplier).isEqualTo(contentPositionSupplier);
|
||||
|
|
@ -362,7 +359,32 @@ public class SimpleBasePlayerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void stateBuilderBuild_currentWindowIndexExceedsPlaylistLength_throwsException() {
|
||||
public void stateBuilderBuild_currentMediaItemIndexUnset_doesNotThrow() {
|
||||
SimpleBasePlayer.State state =
|
||||
new SimpleBasePlayer.State.Builder()
|
||||
.setPlaylist(
|
||||
ImmutableList.of(
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ new Object()).build(),
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ new Object()).build()))
|
||||
.setCurrentMediaItemIndex(C.INDEX_UNSET)
|
||||
.build();
|
||||
|
||||
assertThat(state.currentMediaItemIndex).isEqualTo(C.INDEX_UNSET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stateBuilderBuild_currentMediaItemIndexSetForEmptyPlaylist_doesNotThrow() {
|
||||
SimpleBasePlayer.State state =
|
||||
new SimpleBasePlayer.State.Builder()
|
||||
.setPlaylist(ImmutableList.of())
|
||||
.setCurrentMediaItemIndex(20)
|
||||
.build();
|
||||
|
||||
assertThat(state.currentMediaItemIndex).isEqualTo(20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stateBuilderBuild_currentMediaItemIndexExceedsPlaylistLength_throwsException() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
|
|
@ -376,37 +398,6 @@ public class SimpleBasePlayerTest {
|
|||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stateBuilderBuild_currentPeriodIndexExceedsPlaylistLength_throwsException() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
new SimpleBasePlayer.State.Builder()
|
||||
.setPlaylist(
|
||||
ImmutableList.of(
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ new Object()).build(),
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ new Object())
|
||||
.build()))
|
||||
.setCurrentPeriodIndex(2)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stateBuilderBuild_currentPeriodIndexInOtherMediaItem_throwsException() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
new SimpleBasePlayer.State.Builder()
|
||||
.setPlaylist(
|
||||
ImmutableList.of(
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ new Object()).build(),
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ new Object())
|
||||
.build()))
|
||||
.setCurrentMediaItemIndex(0)
|
||||
.setCurrentPeriodIndex(1)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stateBuilderBuild_currentAdGroupIndexExceedsAdGroupCount_throwsException() {
|
||||
assertThrows(
|
||||
|
|
@ -453,6 +444,16 @@ public class SimpleBasePlayerTest {
|
|||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stateBuilderBuild_setAdAndEmptyPlaylist_throwsException() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
new SimpleBasePlayer.State.Builder()
|
||||
.setCurrentAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 3)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stateBuilderBuild_playerErrorInNonIdleState_throwsException() {
|
||||
assertThrows(
|
||||
|
|
@ -534,6 +535,27 @@ public class SimpleBasePlayerTest {
|
|||
assertThat(position2).isEqualTo(8000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stateBuilderBuild_withUnsetPositionAndPlaying_returnsConstantContentPosition() {
|
||||
SystemClock.setCurrentTimeMillis(10000);
|
||||
|
||||
SimpleBasePlayer.State state =
|
||||
new SimpleBasePlayer.State.Builder()
|
||||
.setPlaylist(
|
||||
ImmutableList.of(
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ new Object()).build()))
|
||||
.setContentPositionMs(C.TIME_UNSET)
|
||||
.setPlayWhenReady(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
|
||||
.setPlaybackState(Player.STATE_READY)
|
||||
.build();
|
||||
long position1 = state.contentPositionMsSupplier.get();
|
||||
SystemClock.setCurrentTimeMillis(12000);
|
||||
long position2 = state.contentPositionMsSupplier.get();
|
||||
|
||||
assertThat(position1).isEqualTo(C.TIME_UNSET);
|
||||
assertThat(position2).isEqualTo(C.TIME_UNSET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stateBuilderBuild_returnsConstantContentPositionWhenNotPlaying() {
|
||||
SystemClock.setCurrentTimeMillis(10000);
|
||||
|
|
@ -865,7 +887,6 @@ public class SimpleBasePlayerTest {
|
|||
.setPlaylist(playlist)
|
||||
.setPlaylistMetadata(playlistMetadata)
|
||||
.setCurrentMediaItemIndex(1)
|
||||
.setCurrentPeriodIndex(1)
|
||||
.setContentPositionMs(contentPositionSupplier)
|
||||
.setContentBufferedPositionMs(contentBufferedPositionSupplier)
|
||||
.setTotalBufferedDurationMs(totalBufferedPositionSupplier)
|
||||
|
|
@ -1049,6 +1070,131 @@ public class SimpleBasePlayerTest {
|
|||
assertThat(player.getCurrentMediaItemIndex()).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentMediaItemIndex_withUnsetIndexInState_returnsDefaultIndex() {
|
||||
State state = new State.Builder().setCurrentMediaItemIndex(C.INDEX_UNSET).build();
|
||||
|
||||
SimpleBasePlayer player =
|
||||
new SimpleBasePlayer(Looper.myLooper()) {
|
||||
@Override
|
||||
protected State getState() {
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
assertThat(player.getCurrentMediaItemIndex()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentPeriodIndex_withUnsetIndexInState_returnsPeriodForCurrentPosition() {
|
||||
State state =
|
||||
new State.Builder()
|
||||
.setPlaylist(
|
||||
ImmutableList.of(
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ 0).build(),
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ 1)
|
||||
.setPeriods(
|
||||
ImmutableList.of(
|
||||
new SimpleBasePlayer.PeriodData.Builder(/* uid= */ "period0")
|
||||
.setDurationUs(60_000_000)
|
||||
.build(),
|
||||
new SimpleBasePlayer.PeriodData.Builder(/* uid= */ "period1")
|
||||
.setDurationUs(5_000_000)
|
||||
.build(),
|
||||
new SimpleBasePlayer.PeriodData.Builder(/* uid= */ "period2")
|
||||
.setDurationUs(5_000_000)
|
||||
.build()))
|
||||
.setPositionInFirstPeriodUs(50_000_000)
|
||||
.build(),
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ 2).build()))
|
||||
.setCurrentMediaItemIndex(1)
|
||||
.setContentPositionMs(12_000)
|
||||
.build();
|
||||
|
||||
SimpleBasePlayer player =
|
||||
new SimpleBasePlayer(Looper.myLooper()) {
|
||||
@Override
|
||||
protected State getState() {
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
assertThat(player.getCurrentPeriodIndex()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCurrentPosition_withUnsetPositionInState_returnsDefaultPosition() {
|
||||
State state =
|
||||
new State.Builder()
|
||||
.setPlaylist(
|
||||
ImmutableList.of(
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ 0)
|
||||
.setDefaultPositionUs(5_000_000)
|
||||
.build()))
|
||||
.setContentPositionMs(C.TIME_UNSET)
|
||||
.build();
|
||||
|
||||
SimpleBasePlayer player =
|
||||
new SimpleBasePlayer(Looper.myLooper()) {
|
||||
@Override
|
||||
protected State getState() {
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
assertThat(player.getCurrentPosition()).isEqualTo(5000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBufferedPosition_withUnsetBufferedPositionInState_returnsDefaultPosition() {
|
||||
State state =
|
||||
new State.Builder()
|
||||
.setPlaylist(
|
||||
ImmutableList.of(
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ 0)
|
||||
.setDefaultPositionUs(5_000_000)
|
||||
.build()))
|
||||
.setContentBufferedPositionMs(
|
||||
SimpleBasePlayer.PositionSupplier.getConstant(C.TIME_UNSET))
|
||||
.build();
|
||||
|
||||
SimpleBasePlayer player =
|
||||
new SimpleBasePlayer(Looper.myLooper()) {
|
||||
@Override
|
||||
protected State getState() {
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
assertThat(player.getBufferedPosition()).isEqualTo(5000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
getBufferedPosition_withUnsetBufferedPositionAndPositionInState_returnsDefaultPosition() {
|
||||
State state =
|
||||
new State.Builder()
|
||||
.setPlaylist(
|
||||
ImmutableList.of(
|
||||
new SimpleBasePlayer.MediaItemData.Builder(/* uid= */ 0)
|
||||
.setDefaultPositionUs(5_000_000)
|
||||
.build()))
|
||||
.setContentPositionMs(C.TIME_UNSET)
|
||||
.setContentBufferedPositionMs(
|
||||
SimpleBasePlayer.PositionSupplier.getConstant(C.TIME_UNSET))
|
||||
.build();
|
||||
|
||||
SimpleBasePlayer player =
|
||||
new SimpleBasePlayer(Looper.myLooper()) {
|
||||
@Override
|
||||
protected State getState() {
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
assertThat(player.getBufferedPosition()).isEqualTo(5000);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // Verifying deprecated listener call.
|
||||
@Test
|
||||
public void invalidateState_updatesStateAndInformsListeners() throws Exception {
|
||||
|
|
|
|||
Loading…
Reference in a new issue