Mask periodId and loadingPeriodId

This change masks playbackInfo.periodId and playbackInfo.loadingPeriodId for operations which change these periods (set/add/remove sources and seeks).

Because this masking is reflected in the playbackInfo object, player attributes can be retrieved without the maskingXyz variables in EPI. This has the advantage that the playbackInfo object always reflects the public state of the player even when operations are still pending. The maskingXyz variables in EPI are only required for the deprecated use case of an initial seek in an empty timeline.

PiperOrigin-RevId: 321160092
This commit is contained in:
bachinger 2020-07-14 16:11:20 +01:00 committed by Oliver Woodman
parent 683d630ec6
commit d62688cfc0
4 changed files with 1433 additions and 228 deletions

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer2;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import static com.google.android.exoplayer2.util.Util.castNonNull;
import android.annotation.SuppressLint;
import android.os.Handler;
@ -418,16 +419,18 @@ import java.util.concurrent.TimeoutException;
public void addMediaSources(int index, List<MediaSource> mediaSources) {
Assertions.checkArgument(index >= 0);
validateMediaSources(mediaSources, /* mediaSourceReplacement= */ false);
int currentWindowIndex = getCurrentWindowIndex();
long currentPositionMs = getCurrentPosition();
Timeline oldTimeline = getCurrentTimeline();
pendingOperationAcks++;
List<MediaSourceList.MediaSourceHolder> holders = addMediaSourceHolders(index, mediaSources);
PlaybackInfo playbackInfo =
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
Timeline newTimeline = createMaskingTimeline();
PlaybackInfo newPlaybackInfo =
maskTimelineAndPosition(
playbackInfo,
newTimeline,
getPeriodPositionAfterTimelineChanged(oldTimeline, newTimeline));
internalPlayer.addMediaSources(index, holders, shuffleOrder);
updatePlaybackInfo(
playbackInfo,
newPlaybackInfo,
/* positionDiscontinuity= */ false,
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
@ -448,18 +451,20 @@ import java.util.concurrent.TimeoutException;
&& fromIndex <= toIndex
&& toIndex <= mediaSourceHolderSnapshots.size()
&& newFromIndex >= 0);
int currentWindowIndex = getCurrentWindowIndex();
long currentPositionMs = getCurrentPosition();
Timeline oldTimeline = getCurrentTimeline();
pendingOperationAcks++;
newFromIndex =
Math.min(newFromIndex, mediaSourceHolderSnapshots.size() - (toIndex - fromIndex));
Util.moveItems(mediaSourceHolderSnapshots, fromIndex, toIndex, newFromIndex);
PlaybackInfo playbackInfo =
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
Timeline newTimeline = createMaskingTimeline();
PlaybackInfo newPlaybackInfo =
maskTimelineAndPosition(
playbackInfo,
newTimeline,
getPeriodPositionAfterTimelineChanged(oldTimeline, newTimeline));
internalPlayer.moveMediaSources(fromIndex, toIndex, newFromIndex, shuffleOrder);
updatePlaybackInfo(
playbackInfo,
newPlaybackInfo,
/* positionDiscontinuity= */ false,
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
@ -477,13 +482,18 @@ import java.util.concurrent.TimeoutException;
@Override
public void setShuffleOrder(ShuffleOrder shuffleOrder) {
PlaybackInfo playbackInfo = maskTimeline();
maskWithCurrentPosition();
Timeline timeline = createMaskingTimeline();
PlaybackInfo newPlaybackInfo =
maskTimelineAndPosition(
playbackInfo,
timeline,
getPeriodPositionOrMaskWindowPosition(
timeline, getCurrentWindowIndex(), getCurrentPosition()));
pendingOperationAcks++;
this.shuffleOrder = shuffleOrder;
internalPlayer.setShuffleOrder(shuffleOrder);
updatePlaybackInfo(
playbackInfo,
newPlaybackInfo,
/* positionDiscontinuity= */ false,
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
@ -521,7 +531,6 @@ import java.util.concurrent.TimeoutException;
&& playbackInfo.playbackSuppressionReason == playbackSuppressionReason) {
return;
}
maskWithCurrentPosition();
pendingOperationAcks++;
PlaybackInfo playbackInfo =
this.playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason);
@ -589,14 +598,18 @@ import java.util.concurrent.TimeoutException;
new ExoPlayerImplInternal.PlaybackInfoUpdate(playbackInfo));
return;
}
maskWindowIndexAndPositionForSeek(timeline, windowIndex, positionMs);
@Player.State
int newPlaybackState =
getPlaybackState() == Player.STATE_IDLE ? Player.STATE_IDLE : Player.STATE_BUFFERING;
PlaybackInfo playbackInfo = this.playbackInfo.copyWithPlaybackState(newPlaybackState);
PlaybackInfo newPlaybackInfo = this.playbackInfo.copyWithPlaybackState(newPlaybackState);
newPlaybackInfo =
maskTimelineAndPosition(
newPlaybackInfo,
timeline,
getPeriodPositionOrMaskWindowPosition(timeline, windowIndex, positionMs));
internalPlayer.seekTo(timeline, windowIndex, C.msToUs(positionMs));
updatePlaybackInfo(
playbackInfo,
newPlaybackInfo,
/* positionDiscontinuity= */ true,
/* positionDiscontinuityReason= */ DISCONTINUITY_REASON_SEEK,
/* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
@ -732,7 +745,7 @@ import java.util.concurrent.TimeoutException;
@Override
public int getCurrentPeriodIndex() {
if (shouldMaskPosition()) {
if (playbackInfo.timeline.isEmpty()) {
return maskingPeriodIndex;
} else {
return playbackInfo.timeline.getIndexOfPeriod(playbackInfo.periodId.periodUid);
@ -758,7 +771,7 @@ import java.util.concurrent.TimeoutException;
@Override
public long getCurrentPosition() {
if (shouldMaskPosition()) {
if (playbackInfo.timeline.isEmpty()) {
return maskingWindowPositionMs;
} else if (playbackInfo.periodId.isAd()) {
return C.usToMs(playbackInfo.positionUs);
@ -784,7 +797,7 @@ import java.util.concurrent.TimeoutException;
@Override
public boolean isPlayingAd() {
return !shouldMaskPosition() && playbackInfo.periodId.isAd();
return playbackInfo.periodId.isAd();
}
@Override
@ -811,7 +824,7 @@ import java.util.concurrent.TimeoutException;
@Override
public long getContentBufferedPosition() {
if (shouldMaskPosition()) {
if (playbackInfo.timeline.isEmpty()) {
return maskingWindowPositionMs;
}
if (playbackInfo.loadingMediaPeriodId.windowSequenceNumber
@ -864,7 +877,7 @@ import java.util.concurrent.TimeoutException;
}
private int getCurrentWindowIndexInternal() {
if (shouldMaskPosition()) {
if (playbackInfo.timeline.isEmpty()) {
return maskingWindowIndex;
} else {
return playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period)
@ -909,7 +922,8 @@ import java.util.concurrent.TimeoutException;
if (pendingOperationAcks == 0) {
Timeline newTimeline = playbackInfoUpdate.playbackInfo.timeline;
if (!this.playbackInfo.timeline.isEmpty() && newTimeline.isEmpty()) {
// Update the masking variables, which are used when the timeline becomes empty.
// Update the masking variables, which are used when the timeline becomes empty because a
// ConcatenatingMediaSource has been cleared.
resetMaskingPosition();
}
if (!newTimeline.isEmpty()) {
@ -938,8 +952,6 @@ import java.util.concurrent.TimeoutException;
removeMediaSourceHolders(
/* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolderSnapshots.size());
resetMaskingPosition();
} else {
maskWithCurrentPosition();
}
Timeline timeline = playbackInfo.timeline;
MediaPeriodId mediaPeriodId = playbackInfo.periodId;
@ -1006,8 +1018,7 @@ import java.util.concurrent.TimeoutException;
}
List<MediaSourceList.MediaSourceHolder> holders =
addMediaSourceHolders(/* index= */ 0, mediaSources);
PlaybackInfo playbackInfo = maskTimeline();
Timeline timeline = playbackInfo.timeline;
Timeline timeline = createMaskingTimeline();
if (!timeline.isEmpty() && startWindowIndex >= timeline.getWindowCount()) {
throw new IllegalSeekPositionException(timeline, startWindowIndex, startPositionMs);
}
@ -1019,11 +1030,14 @@ import java.util.concurrent.TimeoutException;
startWindowIndex = currentWindowIndex;
startPositionMs = currentPositionMs;
}
maskWindowIndexAndPositionForSeek(
timeline, startWindowIndex == C.INDEX_UNSET ? 0 : startWindowIndex, startPositionMs);
PlaybackInfo newPlaybackInfo =
maskTimelineAndPosition(
playbackInfo,
timeline,
getPeriodPositionOrMaskWindowPosition(timeline, startWindowIndex, startPositionMs));
// Mask the playback state.
int maskingPlaybackState = playbackInfo.playbackState;
if (startWindowIndex != C.INDEX_UNSET && playbackInfo.playbackState != STATE_IDLE) {
int maskingPlaybackState = newPlaybackInfo.playbackState;
if (startWindowIndex != C.INDEX_UNSET && newPlaybackInfo.playbackState != STATE_IDLE) {
// Position reset to startWindowIndex (results in pending initial seek).
if (timeline.isEmpty() || startWindowIndex >= timeline.getWindowCount()) {
// Setting an empty timeline or invalid seek transitions to ended.
@ -1032,11 +1046,11 @@ import java.util.concurrent.TimeoutException;
maskingPlaybackState = STATE_BUFFERING;
}
}
playbackInfo = playbackInfo.copyWithPlaybackState(maskingPlaybackState);
newPlaybackInfo = newPlaybackInfo.copyWithPlaybackState(maskingPlaybackState);
internalPlayer.setMediaSources(
holders, startWindowIndex, C.msToUs(startPositionMs), shuffleOrder);
updatePlaybackInfo(
playbackInfo,
newPlaybackInfo,
/* positionDiscontinuity= */ false,
/* ignored */ Player.DISCONTINUITY_REASON_INTERNAL,
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
@ -1064,26 +1078,29 @@ import java.util.concurrent.TimeoutException;
Assertions.checkArgument(
fromIndex >= 0 && toIndex >= fromIndex && toIndex <= mediaSourceHolderSnapshots.size());
int currentWindowIndex = getCurrentWindowIndex();
long currentPositionMs = getCurrentPosition();
Timeline oldTimeline = getCurrentTimeline();
int currentMediaSourceCount = mediaSourceHolderSnapshots.size();
pendingOperationAcks++;
removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */ toIndex);
PlaybackInfo playbackInfo =
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
Timeline newTimeline = createMaskingTimeline();
PlaybackInfo newPlaybackInfo =
maskTimelineAndPosition(
playbackInfo,
newTimeline,
getPeriodPositionAfterTimelineChanged(oldTimeline, newTimeline));
// Player transitions to STATE_ENDED if the current index is part of the removed tail.
final boolean transitionsToEnded =
playbackInfo.playbackState != STATE_IDLE
&& playbackInfo.playbackState != STATE_ENDED
newPlaybackInfo.playbackState != STATE_IDLE
&& newPlaybackInfo.playbackState != STATE_ENDED
&& fromIndex < toIndex
&& toIndex == currentMediaSourceCount
&& currentWindowIndex >= playbackInfo.timeline.getWindowCount();
&& currentWindowIndex >= newPlaybackInfo.timeline.getWindowCount();
if (transitionsToEnded) {
playbackInfo = playbackInfo.copyWithPlaybackState(STATE_ENDED);
newPlaybackInfo = newPlaybackInfo.copyWithPlaybackState(STATE_ENDED);
}
internalPlayer.removeMediaSources(fromIndex, toIndex, shuffleOrder);
updatePlaybackInfo(
playbackInfo,
newPlaybackInfo,
/* positionDiscontinuity= */ false,
/* ignored */ Player.DISCONTINUITY_REASON_INTERNAL,
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
@ -1131,107 +1148,163 @@ import java.util.concurrent.TimeoutException;
}
}
private PlaybackInfo maskTimeline() {
return playbackInfo.copyWithTimeline(
mediaSourceHolderSnapshots.isEmpty()
? Timeline.EMPTY
: new PlaylistTimeline(mediaSourceHolderSnapshots, shuffleOrder));
private Timeline createMaskingTimeline() {
return new PlaylistTimeline(mediaSourceHolderSnapshots, shuffleOrder);
}
private PlaybackInfo maskTimelineAndWindowIndex(
int currentWindowIndex, long currentPositionMs, Timeline oldTimeline) {
PlaybackInfo playbackInfo = maskTimeline();
Timeline maskingTimeline = playbackInfo.timeline;
if (oldTimeline.isEmpty()) {
// The index is the default index or was set by a seek in the empty old timeline.
maskingWindowIndex = currentWindowIndex;
if (!maskingTimeline.isEmpty() && currentWindowIndex >= maskingTimeline.getWindowCount()) {
// The seek is not valid in the new timeline.
maskWithDefaultPosition(maskingTimeline);
}
private PlaybackInfo maskTimelineAndPosition(
PlaybackInfo playbackInfo, Timeline timeline, @Nullable Pair<Object, Long> periodPosition) {
Assertions.checkArgument(timeline.isEmpty() || periodPosition != null);
Timeline oldTimeline = playbackInfo.timeline;
// Mask the timeline.
playbackInfo = playbackInfo.copyWithTimeline(timeline);
if (timeline.isEmpty()) {
// Reset periodId and loadingPeriodId.
MediaPeriodId dummyMediaPeriodId = PlaybackInfo.getDummyPeriodForEmptyTimeline();
playbackInfo =
playbackInfo.copyWithNewPosition(
dummyMediaPeriodId,
/* positionUs= */ C.msToUs(maskingWindowPositionMs),
/* requestedContentPositionUs= */ C.msToUs(maskingWindowPositionMs),
/* totalBufferedDurationUs= */ 0,
TrackGroupArray.EMPTY,
emptyTrackSelectorResult);
playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(dummyMediaPeriodId);
playbackInfo.bufferedPositionUs = playbackInfo.positionUs;
return playbackInfo;
}
@Nullable
Pair<Object, Long> periodPosition =
oldTimeline.getPeriodPosition(
window,
period,
currentWindowIndex,
C.msToUs(currentPositionMs),
/* defaultPositionProjectionUs= */ 0);
Object periodUid = Util.castNonNull(periodPosition).first;
if (maskingTimeline.getIndexOfPeriod(periodUid) != C.INDEX_UNSET) {
// Get the window index of the current period that exists in the new timeline also.
maskingWindowIndex = maskingTimeline.getPeriodByUid(periodUid, period).windowIndex;
maskingPeriodIndex = maskingTimeline.getIndexOfPeriod(periodUid);
maskingWindowPositionMs = currentPositionMs;
} else {
// Period uid not found in new timeline. Try to get subsequent period.
@Nullable
Object nextPeriodUid =
ExoPlayerImplInternal.resolveSubsequentPeriod(
window,
period,
repeatMode,
shuffleModeEnabled,
periodUid,
oldTimeline,
maskingTimeline);
if (nextPeriodUid != null) {
// Set masking to the default position of the window of the subsequent period.
maskingWindowIndex = maskingTimeline.getPeriodByUid(nextPeriodUid, period).windowIndex;
maskingPeriodIndex = maskingTimeline.getWindow(maskingWindowIndex, window).firstPeriodIndex;
maskingWindowPositionMs = window.getDefaultPositionMs();
} else {
// Reset if no subsequent period is found.
maskWithDefaultPosition(maskingTimeline);
Object oldPeriodUid = playbackInfo.periodId.periodUid;
boolean playingPeriodChanged = !oldPeriodUid.equals(castNonNull(periodPosition).first);
MediaPeriodId newPeriodId =
playingPeriodChanged ? new MediaPeriodId(periodPosition.first) : playbackInfo.periodId;
long newContentPositionUs = periodPosition.second;
long oldContentPositionUs = C.msToUs(getContentPosition());
if (!oldTimeline.isEmpty()) {
oldContentPositionUs -=
oldTimeline.getPeriodByUid(oldPeriodUid, period).getPositionInWindowUs();
}
if (playingPeriodChanged || newContentPositionUs < oldContentPositionUs) {
checkState(!newPeriodId.isAd());
// The playing period changes or a backwards seek within the playing period occurs.
playbackInfo =
playbackInfo.copyWithNewPosition(
newPeriodId,
/* positionUs= */ newContentPositionUs,
/* requestedContentPositionUs= */ newContentPositionUs,
/* totalBufferedDurationUs= */ 0,
playingPeriodChanged ? TrackGroupArray.EMPTY : playbackInfo.trackGroups,
playingPeriodChanged ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult);
playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(newPeriodId);
playbackInfo.bufferedPositionUs = newContentPositionUs;
} else if (newContentPositionUs == oldContentPositionUs) {
// Period position remains unchanged.
int loadingPeriodIndex =
timeline.getIndexOfPeriod(playbackInfo.loadingMediaPeriodId.periodUid);
if (loadingPeriodIndex == C.INDEX_UNSET
|| timeline.getPeriod(loadingPeriodIndex, period).windowIndex
!= timeline.getPeriodByUid(newPeriodId.periodUid, period).windowIndex) {
// Discard periods after the playing period, if the loading period is discarded or the
// playing and loading period are not in the same window.
timeline.getPeriodByUid(newPeriodId.periodUid, period);
long maskedBufferedPositionUs =
newPeriodId.isAd()
? period.getAdDurationUs(newPeriodId.adGroupIndex, newPeriodId.adIndexInAdGroup)
: period.durationUs;
playbackInfo =
playbackInfo.copyWithNewPosition(
newPeriodId,
/* positionUs= */ playbackInfo.positionUs,
/* requestedContentPositionUs= */ playbackInfo.positionUs,
/* totalBufferedDurationUs= */ maskedBufferedPositionUs - playbackInfo.positionUs,
playbackInfo.trackGroups,
playbackInfo.trackSelectorResult);
playbackInfo = playbackInfo.copyWithLoadingMediaPeriodId(newPeriodId);
playbackInfo.bufferedPositionUs = maskedBufferedPositionUs;
}
} else {
checkState(!newPeriodId.isAd());
// A forward seek within the playing period (timeline did not change).
long maskedTotalBufferedDurationUs =
Math.max(
0,
playbackInfo.totalBufferedDurationUs - (newContentPositionUs - oldContentPositionUs));
long maskedBufferedPositionUs = playbackInfo.bufferedPositionUs;
if (playbackInfo.loadingMediaPeriodId.equals(playbackInfo.periodId)) {
maskedBufferedPositionUs = newContentPositionUs + maskedTotalBufferedDurationUs;
}
playbackInfo =
playbackInfo.copyWithNewPosition(
newPeriodId,
/* positionUs= */ newContentPositionUs,
/* requestedContentPositionUs= */ newContentPositionUs,
maskedTotalBufferedDurationUs,
playbackInfo.trackGroups,
playbackInfo.trackSelectorResult);
playbackInfo.bufferedPositionUs = maskedBufferedPositionUs;
}
return playbackInfo;
}
private void maskWindowIndexAndPositionForSeek(
Timeline timeline, int windowIndex, long positionMs) {
maskingWindowIndex = windowIndex;
if (timeline.isEmpty()) {
maskingWindowPositionMs = positionMs == C.TIME_UNSET ? 0 : positionMs;
maskingPeriodIndex = 0;
} else if (windowIndex >= timeline.getWindowCount()) {
// An initial seek now proves to be invalid in the actual timeline.
maskWithDefaultPosition(timeline);
@Nullable
private Pair<Object, Long> getPeriodPositionAfterTimelineChanged(
Timeline oldTimeline, Timeline newTimeline) {
long currentPositionMs = getContentPosition();
if (oldTimeline.isEmpty() || newTimeline.isEmpty()) {
boolean isCleared = !oldTimeline.isEmpty() && newTimeline.isEmpty();
return getPeriodPositionOrMaskWindowPosition(
newTimeline,
isCleared ? C.INDEX_UNSET : getCurrentWindowIndexInternal(),
isCleared ? C.TIME_UNSET : currentPositionMs);
}
int currentWindowIndex = getCurrentWindowIndex();
@Nullable
Pair<Object, Long> oldPeriodPosition =
oldTimeline.getPeriodPosition(
window, period, currentWindowIndex, C.msToUs(currentPositionMs));
Object periodUid = castNonNull(oldPeriodPosition).first;
if (newTimeline.getIndexOfPeriod(periodUid) != C.INDEX_UNSET) {
// The old period position is still available in the new timeline.
return oldPeriodPosition;
}
// Period uid not found in new timeline. Try to get subsequent period.
@Nullable
Object nextPeriodUid =
ExoPlayerImplInternal.resolveSubsequentPeriod(
window, period, repeatMode, shuffleModeEnabled, periodUid, oldTimeline, newTimeline);
if (nextPeriodUid != null) {
// Reset position to the default position of the window of the subsequent period.
newTimeline.getPeriodByUid(nextPeriodUid, period);
return getPeriodPositionOrMaskWindowPosition(
newTimeline,
period.windowIndex,
newTimeline.getWindow(period.windowIndex, window).getDefaultPositionMs());
} else {
long windowPositionUs =
positionMs == C.TIME_UNSET
? timeline.getWindow(windowIndex, window).getDefaultPositionUs()
: C.msToUs(positionMs);
Pair<Object, Long> periodUidAndPosition =
timeline.getPeriodPosition(window, period, windowIndex, windowPositionUs);
maskingWindowPositionMs = C.usToMs(windowPositionUs);
maskingPeriodIndex = timeline.getIndexOfPeriod(periodUidAndPosition.first);
// No subsequent period found and the new timeline is not empty. Use the default position.
return getPeriodPositionOrMaskWindowPosition(
newTimeline, /* windowIndex= */ C.INDEX_UNSET, /* windowPositionMs= */ C.TIME_UNSET);
}
}
private void maskWithCurrentPosition() {
maskingWindowIndex = getCurrentWindowIndexInternal();
maskingPeriodIndex = getCurrentPeriodIndex();
maskingWindowPositionMs = getCurrentPosition();
}
private void maskWithDefaultPosition(Timeline timeline) {
@Nullable
private Pair<Object, Long> getPeriodPositionOrMaskWindowPosition(
Timeline timeline, int windowIndex, long windowPositionMs) {
if (timeline.isEmpty()) {
resetMaskingPosition();
return;
// If empty we store the initial seek in the masking variables.
maskingWindowIndex = windowIndex;
maskingWindowPositionMs = windowPositionMs == C.TIME_UNSET ? 0 : windowPositionMs;
maskingPeriodIndex = 0;
return null;
}
maskingWindowIndex = timeline.getFirstWindowIndex(shuffleModeEnabled);
timeline.getWindow(maskingWindowIndex, window);
maskingWindowPositionMs = window.getDefaultPositionMs();
maskingPeriodIndex = window.firstPeriodIndex;
}
private void resetMaskingPosition() {
maskingWindowIndex = C.INDEX_UNSET;
maskingWindowPositionMs = 0;
maskingPeriodIndex = 0;
if (windowIndex == C.INDEX_UNSET || windowIndex >= timeline.getWindowCount()) {
// Use default position of timeline if window index still unset or if a previous initial seek
// now turns out to be invalid.
windowIndex = timeline.getFirstWindowIndex(shuffleModeEnabled);
windowPositionMs = timeline.getWindow(windowIndex, window).getDefaultPositionMs();
}
return timeline.getPeriodPosition(window, period, windowIndex, C.msToUs(windowPositionMs));
}
private void notifyListeners(ListenerInvocation listenerInvocation) {
@ -1251,6 +1324,12 @@ import java.util.concurrent.TimeoutException;
}
}
private void resetMaskingPosition() {
maskingWindowIndex = C.INDEX_UNSET;
maskingWindowPositionMs = 0;
maskingPeriodIndex = 0;
}
private long periodPositionUsToWindowPositionMs(MediaPeriodId periodId, long positionUs) {
long positionMs = C.usToMs(positionUs);
playbackInfo.timeline.getPeriodByUid(periodId.periodUid, period);
@ -1258,10 +1337,6 @@ import java.util.concurrent.TimeoutException;
return positionMs;
}
private boolean shouldMaskPosition() {
return playbackInfo.timeline.isEmpty() || pendingOperationAcks > 0;
}
private final class PlaybackUpdateListenerImpl
implements ExoPlayerImplInternal.PlaybackUpdateListener, Handler.Callback {
private static final int MSG_PLAYBACK_INFO_CHANGED = 0;

View File

@ -421,7 +421,7 @@ public final class AnalyticsCollectorTest {
assertThat(loadingEvents).hasSize(4);
assertThat(loadingEvents).containsAtLeast(period0, period0).inOrder();
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED))
.containsExactly(period0, period1)
.containsExactly(period0, period1, period1)
.inOrder();
assertThat(listener.getEvents(EVENT_LOAD_STARTED))
.containsExactly(
@ -887,7 +887,7 @@ public final class AnalyticsCollectorTest {
assertThat(listener.getEvents(EVENT_LOADING_CHANGED))
.containsExactly(period0Seq0, period0Seq0, period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED))
.containsExactly(period0Seq0, period0Seq1)
.containsExactly(period0Seq0, period0Seq1, period0Seq1)
.inOrder();
assertThat(listener.getEvents(EVENT_LOAD_STARTED))
.containsExactly(WINDOW_0 /* manifest */, period0Seq0 /* media */, period1Seq1 /* media */)

View File

@ -72,6 +72,7 @@ public class FakeMediaPeriod implements MediaPeriod {
private boolean prepared;
private long seekOffsetUs;
private long discontinuityPositionUs;
private long bufferedPositionUs;
/**
* Constructs a FakeMediaPeriod with a single sample for each track in {@code trackGroupArray}.
@ -149,6 +150,7 @@ public class FakeMediaPeriod implements MediaPeriod {
this.drmEventDispatcher = drmEventDispatcher;
this.deferOnPrepared = deferOnPrepared;
this.trackDataFactory = trackDataFactory;
this.bufferedPositionUs = C.TIME_END_OF_SOURCE;
discontinuityPositionUs = C.TIME_UNSET;
sampleStreams = new ArrayList<>();
fakePreparationLoadTaskId = LoadEventInfo.getNewId();
@ -283,7 +285,11 @@ public class FakeMediaPeriod implements MediaPeriod {
@Override
public long getBufferedPositionUs() {
assertThat(prepared).isTrue();
return C.TIME_END_OF_SOURCE;
return bufferedPositionUs;
}
public void setBufferedPositionUs(long bufferedPositionUs) {
this.bufferedPositionUs = bufferedPositionUs;
}
@Override
@ -293,6 +299,9 @@ public class FakeMediaPeriod implements MediaPeriod {
for (SampleStream sampleStream : sampleStreams) {
seekSampleStream(sampleStream, seekPositionUs);
}
if (bufferedPositionUs != C.TIME_END_OF_SOURCE && seekPositionUs > bufferedPositionUs) {
bufferedPositionUs = seekPositionUs;
}
return seekPositionUs;
}