mirror of
https://github.com/androidx/media.git
synced 2025-05-09 00:20:45 +08:00
Fix a bunch of position problems.
There are a couple of problems with how positions in PlaybackInfo are set at the moment: 1. PositionUs isn't allowed to be C.TIME_UNSET. This is prevented by always resolving to the current default position if needed. 2. In some places a window position was used as a period position. Use correct position resolution procedure. 3. When creating a placeholder position to restart playback, we used the first period in a window, not the one where the default position is. 4. The start position for ads was in some cases set to 0 without checking the ad resume position. PiperOrigin-RevId: 290749042
This commit is contained in:
parent
665092e4be
commit
5f9a585075
@ -144,7 +144,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
ExoPlayerImpl.this.handleEvent(msg);
|
ExoPlayerImpl.this.handleEvent(msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
playbackInfo = PlaybackInfo.createDummy(/* startPositionUs= */ 0, emptyTrackSelectorResult);
|
playbackInfo = PlaybackInfo.createDummy(emptyTrackSelectorResult);
|
||||||
pendingListenerNotifications = new ArrayDeque<>();
|
pendingListenerNotifications = new ArrayDeque<>();
|
||||||
if (analyticsCollector != null) {
|
if (analyticsCollector != null) {
|
||||||
analyticsCollector.setPlayer(this);
|
analyticsCollector.setPlayer(this);
|
||||||
@ -795,17 +795,6 @@ import java.util.concurrent.TimeoutException;
|
|||||||
@DiscontinuityReason int positionDiscontinuityReason) {
|
@DiscontinuityReason int positionDiscontinuityReason) {
|
||||||
pendingOperationAcks -= operationAcks;
|
pendingOperationAcks -= operationAcks;
|
||||||
if (pendingOperationAcks == 0) {
|
if (pendingOperationAcks == 0) {
|
||||||
if (playbackInfo.startPositionUs == C.TIME_UNSET) {
|
|
||||||
// Replace internal unset start position with externally visible start position of zero.
|
|
||||||
playbackInfo =
|
|
||||||
playbackInfo.copyWithNewPosition(
|
|
||||||
playbackInfo.periodId,
|
|
||||||
/* positionUs= */ 0,
|
|
||||||
playbackInfo.contentPositionUs,
|
|
||||||
playbackInfo.totalBufferedDurationUs,
|
|
||||||
playbackInfo.trackGroups,
|
|
||||||
playbackInfo.trackSelectorResult);
|
|
||||||
}
|
|
||||||
if (!this.playbackInfo.timeline.isEmpty() && playbackInfo.timeline.isEmpty()) {
|
if (!this.playbackInfo.timeline.isEmpty() && playbackInfo.timeline.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.
|
||||||
resetMaskingPosition();
|
resetMaskingPosition();
|
||||||
@ -841,7 +830,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
timeline = Timeline.EMPTY;
|
timeline = Timeline.EMPTY;
|
||||||
mediaPeriodId = PlaybackInfo.getDummyPeriodForEmptyTimeline();
|
mediaPeriodId = PlaybackInfo.getDummyPeriodForEmptyTimeline();
|
||||||
contentPositionUs = C.TIME_UNSET;
|
contentPositionUs = C.TIME_UNSET;
|
||||||
startPositionUs = C.TIME_UNSET;
|
startPositionUs = 0;
|
||||||
}
|
}
|
||||||
return new PlaybackInfo(
|
return new PlaybackInfo(
|
||||||
timeline,
|
timeline,
|
||||||
|
@ -160,8 +160,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
retainBackBufferFromKeyframe = loadControl.retainBackBufferFromKeyframe();
|
retainBackBufferFromKeyframe = loadControl.retainBackBufferFromKeyframe();
|
||||||
|
|
||||||
seekParameters = SeekParameters.DEFAULT;
|
seekParameters = SeekParameters.DEFAULT;
|
||||||
playbackInfo =
|
playbackInfo = PlaybackInfo.createDummy(emptyTrackSelectorResult);
|
||||||
PlaybackInfo.createDummy(/* startPositionUs= */ C.TIME_UNSET, emptyTrackSelectorResult);
|
|
||||||
playbackInfoUpdate = new PlaybackInfoUpdate();
|
playbackInfoUpdate = new PlaybackInfoUpdate();
|
||||||
rendererCapabilities = new RendererCapabilities[renderers.length];
|
rendererCapabilities = new RendererCapabilities[renderers.length];
|
||||||
for (int i = 0; i < renderers.length; i++) {
|
for (int i = 0; i < renderers.length; i++) {
|
||||||
@ -879,9 +878,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
if (resolvedSeekPosition == null) {
|
if (resolvedSeekPosition == null) {
|
||||||
// The seek position was valid for the timeline that it was performed into, but the
|
// The seek position was valid for the timeline that it was performed into, but the
|
||||||
// timeline has changed or is not ready and a suitable seek position could not be resolved.
|
// timeline has changed or is not ready and a suitable seek position could not be resolved.
|
||||||
periodId = getDummyFirstMediaPeriodForAds();
|
Pair<MediaPeriodId, Long> firstPeriodAndPosition = getDummyFirstMediaPeriodPosition();
|
||||||
|
periodId = firstPeriodAndPosition.first;
|
||||||
|
periodPositionUs = firstPeriodAndPosition.second;
|
||||||
contentPositionUs = C.TIME_UNSET;
|
contentPositionUs = C.TIME_UNSET;
|
||||||
periodPositionUs = C.TIME_UNSET;
|
|
||||||
seekPositionAdjusted = true;
|
seekPositionAdjusted = true;
|
||||||
} else {
|
} else {
|
||||||
// Update the resolved seek position to take ads into account.
|
// Update the resolved seek position to take ads into account.
|
||||||
@ -890,7 +890,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
periodId =
|
periodId =
|
||||||
queue.resolveMediaPeriodIdForAds(playbackInfo.timeline, periodUid, contentPositionUs);
|
queue.resolveMediaPeriodIdForAds(playbackInfo.timeline, periodUid, contentPositionUs);
|
||||||
if (periodId.isAd()) {
|
if (periodId.isAd()) {
|
||||||
periodPositionUs = 0;
|
playbackInfo.timeline.getPeriodByUid(periodId.periodUid, period);
|
||||||
|
periodPositionUs =
|
||||||
|
period.getFirstAdIndexToPlay(periodId.adGroupIndex) == periodId.adIndexInAdGroup
|
||||||
|
? period.getAdResumePositionUs()
|
||||||
|
: 0;
|
||||||
seekPositionAdjusted = true;
|
seekPositionAdjusted = true;
|
||||||
} else {
|
} else {
|
||||||
periodPositionUs = resolvedSeekPosition.second;
|
periodPositionUs = resolvedSeekPosition.second;
|
||||||
@ -902,7 +906,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
if (playbackInfo.timeline.isEmpty() || !playlist.isPrepared()) {
|
if (playbackInfo.timeline.isEmpty() || !playlist.isPrepared()) {
|
||||||
// Save seek position for later, as we are still waiting for a prepared source.
|
// Save seek position for later, as we are still waiting for a prepared source.
|
||||||
pendingInitialSeekPosition = seekPosition;
|
pendingInitialSeekPosition = seekPosition;
|
||||||
} else if (periodPositionUs == C.TIME_UNSET) {
|
} else if (resolvedSeekPosition == null) {
|
||||||
// End playback, as we didn't manage to find a valid seek position.
|
// End playback, as we didn't manage to find a valid seek position.
|
||||||
setState(Player.STATE_ENDED);
|
setState(Player.STATE_ENDED);
|
||||||
resetInternal(
|
resetInternal(
|
||||||
@ -1147,20 +1151,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
nextPendingMessageIndex = 0;
|
nextPendingMessageIndex = 0;
|
||||||
}
|
}
|
||||||
MediaPeriodId mediaPeriodId = playbackInfo.periodId;
|
MediaPeriodId mediaPeriodId = playbackInfo.periodId;
|
||||||
|
long startPositionUs = playbackInfo.positionUs;
|
||||||
long contentPositionUs = playbackInfo.contentPositionUs;
|
long contentPositionUs = playbackInfo.contentPositionUs;
|
||||||
boolean resetTrackInfo = clearPlaylist;
|
boolean resetTrackInfo = clearPlaylist;
|
||||||
if (resetPosition) {
|
if (resetPosition) {
|
||||||
mediaPeriodId =
|
Pair<MediaPeriodId, Long> firstPeriodAndPosition = getDummyFirstMediaPeriodPosition();
|
||||||
timeline.isEmpty()
|
mediaPeriodId = firstPeriodAndPosition.first;
|
||||||
? PlaybackInfo.getDummyPeriodForEmptyTimeline()
|
startPositionUs = firstPeriodAndPosition.second;
|
||||||
: getDummyFirstMediaPeriodForAds();
|
|
||||||
contentPositionUs = C.TIME_UNSET;
|
contentPositionUs = C.TIME_UNSET;
|
||||||
if (!mediaPeriodId.equals(playbackInfo.periodId)) {
|
if (!mediaPeriodId.equals(playbackInfo.periodId)) {
|
||||||
resetTrackInfo = true;
|
resetTrackInfo = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set the start position to TIME_UNSET so that a subsequent seek to 0 isn't ignored.
|
|
||||||
long startPositionUs = resetPosition ? C.TIME_UNSET : playbackInfo.positionUs;
|
|
||||||
playbackInfo =
|
playbackInfo =
|
||||||
new PlaybackInfo(
|
new PlaybackInfo(
|
||||||
timeline,
|
timeline,
|
||||||
@ -1181,17 +1183,27 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaPeriodId getDummyFirstMediaPeriodForAds() {
|
private Pair<MediaPeriodId, Long> getDummyFirstMediaPeriodPosition() {
|
||||||
MediaPeriodId dummyFirstMediaPeriodId =
|
if (playbackInfo.timeline.isEmpty()) {
|
||||||
getDummyFirstMediaPeriodId(
|
return Pair.create(PlaybackInfo.getDummyPeriodForEmptyTimeline(), 0L);
|
||||||
playbackInfo.timeline, playbackInfo.periodId, shuffleModeEnabled, window, period);
|
|
||||||
if (!playbackInfo.timeline.isEmpty()) {
|
|
||||||
// add ad metadata if any and propagate the window sequence number to new period id.
|
|
||||||
dummyFirstMediaPeriodId =
|
|
||||||
queue.resolveMediaPeriodIdForAds(
|
|
||||||
playbackInfo.timeline, dummyFirstMediaPeriodId.periodUid, /* positionUs= */ 0);
|
|
||||||
}
|
}
|
||||||
return dummyFirstMediaPeriodId;
|
int firstWindowIndex = playbackInfo.timeline.getFirstWindowIndex(shuffleModeEnabled);
|
||||||
|
Pair<Object, Long> firstPeriodAndPosition =
|
||||||
|
playbackInfo.timeline.getPeriodPosition(
|
||||||
|
window, period, firstWindowIndex, /* windowPositionUs= */ C.TIME_UNSET);
|
||||||
|
// Add ad metadata if any and propagate the window sequence number to new period id.
|
||||||
|
MediaPeriodId firstPeriodId =
|
||||||
|
queue.resolveMediaPeriodIdForAds(
|
||||||
|
playbackInfo.timeline, firstPeriodAndPosition.first, /* positionUs= */ 0);
|
||||||
|
long positionUs = firstPeriodAndPosition.second;
|
||||||
|
if (firstPeriodId.isAd()) {
|
||||||
|
playbackInfo.timeline.getPeriodByUid(firstPeriodId.periodUid, period);
|
||||||
|
positionUs =
|
||||||
|
firstPeriodId.adIndexInAdGroup == period.getFirstAdIndexToPlay(firstPeriodId.adGroupIndex)
|
||||||
|
? period.getAdResumePositionUs()
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
return Pair.create(firstPeriodId, positionUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessageInternal(PlayerMessage message) throws ExoPlaybackException {
|
private void sendMessageInternal(PlayerMessage message) throws ExoPlaybackException {
|
||||||
@ -1538,11 +1550,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
MediaPeriodId newPeriodId = positionUpdate.periodId;
|
MediaPeriodId newPeriodId = positionUpdate.periodId;
|
||||||
long newContentPositionUs = positionUpdate.contentPositionUs;
|
long newContentPositionUs = positionUpdate.contentPositionUs;
|
||||||
boolean forceBufferingState = positionUpdate.forceBufferingState;
|
boolean forceBufferingState = positionUpdate.forceBufferingState;
|
||||||
long newPositionUs = newPeriodId.isAd() ? 0 : newContentPositionUs;
|
long newPositionUs = positionUpdate.periodPositionUs;
|
||||||
long oldContentPositionUs =
|
|
||||||
playbackInfo.periodId.isAd() ? playbackInfo.contentPositionUs : playbackInfo.positionUs;
|
|
||||||
boolean isPlaybackPositionUnchanged =
|
boolean isPlaybackPositionUnchanged =
|
||||||
playbackInfo.periodId.equals(newPeriodId) && oldContentPositionUs == newContentPositionUs;
|
playbackInfo.periodId.equals(newPeriodId) && newPositionUs == playbackInfo.positionUs;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (positionUpdate.endPlayback) {
|
if (positionUpdate.endPlayback) {
|
||||||
@ -2080,6 +2090,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
if (timeline.isEmpty()) {
|
if (timeline.isEmpty()) {
|
||||||
return new PositionUpdateForPlaylistChange(
|
return new PositionUpdateForPlaylistChange(
|
||||||
PlaybackInfo.getDummyPeriodForEmptyTimeline(),
|
PlaybackInfo.getDummyPeriodForEmptyTimeline(),
|
||||||
|
/* periodPositionUs= */ 0,
|
||||||
/* contentPositionUs= */ C.TIME_UNSET,
|
/* contentPositionUs= */ C.TIME_UNSET,
|
||||||
/* forceBufferingState= */ false,
|
/* forceBufferingState= */ false,
|
||||||
/* endPlayback= */ true);
|
/* endPlayback= */ true);
|
||||||
@ -2089,6 +2100,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
long oldContentPositionUs =
|
long oldContentPositionUs =
|
||||||
oldPeriodId.isAd() ? playbackInfo.contentPositionUs : playbackInfo.positionUs;
|
oldPeriodId.isAd() ? playbackInfo.contentPositionUs : playbackInfo.positionUs;
|
||||||
long newContentPositionUs = oldContentPositionUs;
|
long newContentPositionUs = oldContentPositionUs;
|
||||||
|
int startAtDefaultPositionWindowIndex = C.INDEX_UNSET;
|
||||||
boolean forceBufferingState = false;
|
boolean forceBufferingState = false;
|
||||||
boolean endPlayback = false;
|
boolean endPlayback = false;
|
||||||
if (pendingInitialSeekPosition != null) {
|
if (pendingInitialSeekPosition != null) {
|
||||||
@ -2106,30 +2118,21 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
if (periodPosition == null) {
|
if (periodPosition == null) {
|
||||||
// The initial seek in the empty old timeline is invalid in the new timeline.
|
// The initial seek in the empty old timeline is invalid in the new timeline.
|
||||||
endPlayback = true;
|
endPlayback = true;
|
||||||
newPeriodUid =
|
startAtDefaultPositionWindowIndex = timeline.getFirstWindowIndex(shuffleModeEnabled);
|
||||||
getDummyFirstMediaPeriodId(
|
|
||||||
timeline, playbackInfo.periodId, shuffleModeEnabled, window, period)
|
|
||||||
.periodUid;
|
|
||||||
newContentPositionUs = C.TIME_UNSET;
|
|
||||||
} else {
|
} else {
|
||||||
// The pending seek has been resolved successfully in the new timeline.
|
// The pending seek has been resolved successfully in the new timeline.
|
||||||
newPeriodUid = periodPosition.first;
|
if (pendingInitialSeekPosition.windowPositionUs == C.TIME_UNSET) {
|
||||||
newContentPositionUs =
|
startAtDefaultPositionWindowIndex =
|
||||||
pendingInitialSeekPosition.windowPositionUs == C.TIME_UNSET
|
timeline.getPeriodByUid(periodPosition.first, period).windowIndex;
|
||||||
? C.TIME_UNSET
|
} else {
|
||||||
: periodPosition.second;
|
newPeriodUid = periodPosition.first;
|
||||||
|
newContentPositionUs = periodPosition.second;
|
||||||
|
}
|
||||||
forceBufferingState = playbackInfo.playbackState == Player.STATE_ENDED;
|
forceBufferingState = playbackInfo.playbackState == Player.STATE_ENDED;
|
||||||
}
|
}
|
||||||
} else if (playbackInfo.timeline.isEmpty()) {
|
} else if (playbackInfo.timeline.isEmpty()) {
|
||||||
// Resolve to default position if the old timeline is empty and no seek is requested above.
|
// Resolve to default position if the old timeline is empty and no seek is requested above.
|
||||||
Pair<Object, Long> defaultPosition =
|
startAtDefaultPositionWindowIndex = timeline.getFirstWindowIndex(shuffleModeEnabled);
|
||||||
timeline.getPeriodPosition(
|
|
||||||
window,
|
|
||||||
period,
|
|
||||||
timeline.getFirstWindowIndex(shuffleModeEnabled),
|
|
||||||
/* windowPositionUs= */ C.TIME_UNSET);
|
|
||||||
newPeriodUid = defaultPosition.first;
|
|
||||||
newContentPositionUs = C.TIME_UNSET;
|
|
||||||
} else if (timeline.getIndexOfPeriod(newPeriodUid) == C.INDEX_UNSET) {
|
} else if (timeline.getIndexOfPeriod(newPeriodUid) == C.INDEX_UNSET) {
|
||||||
// The current period isn't in the new timeline. Attempt to resolve a subsequent period whose
|
// The current period isn't in the new timeline. Attempt to resolve a subsequent period whose
|
||||||
// window we can restart from.
|
// window we can restart from.
|
||||||
@ -2146,38 +2149,38 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
if (subsequentPeriodUid == null) {
|
if (subsequentPeriodUid == null) {
|
||||||
// We failed to resolve a suitable restart position but the timeline is not empty.
|
// We failed to resolve a suitable restart position but the timeline is not empty.
|
||||||
endPlayback = true;
|
endPlayback = true;
|
||||||
newPeriodUid =
|
startAtDefaultPositionWindowIndex = timeline.getFirstWindowIndex(shuffleModeEnabled);
|
||||||
getDummyFirstMediaPeriodId(
|
|
||||||
timeline, playbackInfo.periodId, shuffleModeEnabled, window, period)
|
|
||||||
.periodUid;
|
|
||||||
newContentPositionUs = C.TIME_UNSET;
|
|
||||||
} else {
|
} else {
|
||||||
// We resolved a subsequent period. Start at the default position in the corresponding
|
// We resolved a subsequent period. Start at the default position in the corresponding
|
||||||
// window.
|
// window.
|
||||||
Pair<Object, Long> defaultPosition =
|
startAtDefaultPositionWindowIndex =
|
||||||
timeline.getPeriodPosition(
|
timeline.getPeriodByUid(subsequentPeriodUid, period).windowIndex;
|
||||||
window,
|
|
||||||
period,
|
|
||||||
timeline.getPeriodByUid(subsequentPeriodUid, period).windowIndex,
|
|
||||||
/* windowPositionUs= */ C.TIME_UNSET);
|
|
||||||
newPeriodUid = defaultPosition.first;
|
|
||||||
newContentPositionUs = C.TIME_UNSET;
|
|
||||||
}
|
}
|
||||||
|
} else if (oldContentPositionUs == C.TIME_UNSET) {
|
||||||
|
// We previously set the content position to be the default position of the current window.
|
||||||
|
// Re-resolve the period uid and position in case they changed since last time.
|
||||||
|
startAtDefaultPositionWindowIndex = timeline.getPeriodByUid(newPeriodUid, period).windowIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set period uid for default positions and resolve position for ad resolution.
|
||||||
|
long contentPositionForAdResolutionUs = newContentPositionUs;
|
||||||
|
if (startAtDefaultPositionWindowIndex != C.INDEX_UNSET) {
|
||||||
|
Pair<Object, Long> defaultPosition =
|
||||||
|
timeline.getPeriodPosition(
|
||||||
|
window,
|
||||||
|
period,
|
||||||
|
startAtDefaultPositionWindowIndex,
|
||||||
|
/* windowPositionUs= */ C.TIME_UNSET);
|
||||||
|
newPeriodUid = defaultPosition.first;
|
||||||
|
contentPositionForAdResolutionUs = defaultPosition.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure ad insertion metadata is up to date.
|
// Ensure ad insertion metadata is up to date.
|
||||||
long contentPositionForAdResolution = newContentPositionUs;
|
|
||||||
if (contentPositionForAdResolution == C.TIME_UNSET) {
|
|
||||||
// TODO: Fix me. Using a window position as period position is wrong.
|
|
||||||
contentPositionForAdResolution =
|
|
||||||
timeline.getWindow(timeline.getPeriodByUid(newPeriodUid, period).windowIndex, window)
|
|
||||||
.defaultPositionUs;
|
|
||||||
}
|
|
||||||
MediaPeriodId periodIdWithAds =
|
MediaPeriodId periodIdWithAds =
|
||||||
queue.resolveMediaPeriodIdForAds(timeline, newPeriodUid, contentPositionForAdResolution);
|
queue.resolveMediaPeriodIdForAds(timeline, newPeriodUid, contentPositionForAdResolutionUs);
|
||||||
if (!periodIdWithAds.isAd() && newContentPositionUs == C.TIME_UNSET) {
|
if (!periodIdWithAds.isAd() && newContentPositionUs == C.TIME_UNSET) {
|
||||||
// We are not going to play an ad, so use resolved content position.
|
// We are not going to play an ad, so use resolved content position.
|
||||||
newContentPositionUs = contentPositionForAdResolution;
|
newContentPositionUs = contentPositionForAdResolutionUs;
|
||||||
}
|
}
|
||||||
boolean oldAndNewPeriodIdAreSame =
|
boolean oldAndNewPeriodIdAreSame =
|
||||||
oldPeriodId.periodUid.equals(newPeriodUid)
|
oldPeriodId.periodUid.equals(newPeriodUid)
|
||||||
@ -2188,8 +2191,21 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
// discontinuity until we reach the former next ad group position.
|
// discontinuity until we reach the former next ad group position.
|
||||||
MediaPeriodId newPeriodId = oldAndNewPeriodIdAreSame ? oldPeriodId : periodIdWithAds;
|
MediaPeriodId newPeriodId = oldAndNewPeriodIdAreSame ? oldPeriodId : periodIdWithAds;
|
||||||
|
|
||||||
|
long periodPositionUs = contentPositionForAdResolutionUs;
|
||||||
|
if (newPeriodId.isAd()) {
|
||||||
|
if (newPeriodId.equals(oldPeriodId)) {
|
||||||
|
periodPositionUs = playbackInfo.positionUs;
|
||||||
|
} else {
|
||||||
|
timeline.getPeriodByUid(newPeriodId.periodUid, period);
|
||||||
|
periodPositionUs =
|
||||||
|
newPeriodId.adIndexInAdGroup == period.getFirstAdIndexToPlay(newPeriodId.adGroupIndex)
|
||||||
|
? period.getAdResumePositionUs()
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new PositionUpdateForPlaylistChange(
|
return new PositionUpdateForPlaylistChange(
|
||||||
newPeriodId, newContentPositionUs, forceBufferingState, endPlayback);
|
newPeriodId, periodPositionUs, newContentPositionUs, forceBufferingState, endPlayback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2267,40 +2283,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns dummy media period id for the first-to-be-played period of the current timeline.
|
|
||||||
*
|
|
||||||
* @param timeline The timeline whose first-to-be-played period needs to be found.
|
|
||||||
* @param currentMediaPeriodId The current media period id, not guaranteed to be part of {@code
|
|
||||||
* timeline}.
|
|
||||||
* @param shuffleModeEnabled Whether shuffle mode is enabled.
|
|
||||||
* @param window A writable {@link Timeline.Window}.
|
|
||||||
* @param period A writable {@link Timeline.Period}.
|
|
||||||
* @return A dummy media period id for the first-to-be-played period of the current timeline.
|
|
||||||
*/
|
|
||||||
private static MediaPeriodId getDummyFirstMediaPeriodId(
|
|
||||||
Timeline timeline,
|
|
||||||
MediaPeriodId currentMediaPeriodId,
|
|
||||||
boolean shuffleModeEnabled,
|
|
||||||
Timeline.Window window,
|
|
||||||
Timeline.Period period) {
|
|
||||||
if (timeline.isEmpty()) {
|
|
||||||
return PlaybackInfo.getDummyPeriodForEmptyTimeline();
|
|
||||||
}
|
|
||||||
int firstWindowIndex = timeline.getFirstWindowIndex(shuffleModeEnabled);
|
|
||||||
int firstPeriodIndex = timeline.getWindow(firstWindowIndex, window).firstPeriodIndex;
|
|
||||||
int currentPeriodIndex = timeline.getIndexOfPeriod(currentMediaPeriodId.periodUid);
|
|
||||||
long windowSequenceNumber = C.INDEX_UNSET;
|
|
||||||
if (currentPeriodIndex != C.INDEX_UNSET) {
|
|
||||||
int currentWindowIndex = timeline.getPeriod(currentPeriodIndex, period).windowIndex;
|
|
||||||
if (firstWindowIndex == currentWindowIndex) {
|
|
||||||
// Keep window sequence number if the new position is still in the same window.
|
|
||||||
windowSequenceNumber = currentMediaPeriodId.windowSequenceNumber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new MediaPeriodId(timeline.getUidOfPeriod(firstPeriodIndex), windowSequenceNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a period index into an old timeline, finds the first subsequent period that also exists
|
* Given a period index into an old timeline, finds the first subsequent period that also exists
|
||||||
* in a new timeline. The uid of this period in the new timeline is returned.
|
* in a new timeline. The uid of this period in the new timeline is returned.
|
||||||
@ -2364,16 +2346,19 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
|
|
||||||
private static final class PositionUpdateForPlaylistChange {
|
private static final class PositionUpdateForPlaylistChange {
|
||||||
public final MediaPeriodId periodId;
|
public final MediaPeriodId periodId;
|
||||||
|
public final long periodPositionUs;
|
||||||
public final long contentPositionUs;
|
public final long contentPositionUs;
|
||||||
public final boolean forceBufferingState;
|
public final boolean forceBufferingState;
|
||||||
public final boolean endPlayback;
|
public final boolean endPlayback;
|
||||||
|
|
||||||
public PositionUpdateForPlaylistChange(
|
public PositionUpdateForPlaylistChange(
|
||||||
MediaPeriodId periodId,
|
MediaPeriodId periodId,
|
||||||
|
long periodPositionUs,
|
||||||
long contentPositionUs,
|
long contentPositionUs,
|
||||||
boolean forceBufferingState,
|
boolean forceBufferingState,
|
||||||
boolean endPlayback) {
|
boolean endPlayback) {
|
||||||
this.periodId = periodId;
|
this.periodId = periodId;
|
||||||
|
this.periodPositionUs = periodPositionUs;
|
||||||
this.contentPositionUs = contentPositionUs;
|
this.contentPositionUs = contentPositionUs;
|
||||||
this.forceBufferingState = forceBufferingState;
|
this.forceBufferingState = forceBufferingState;
|
||||||
this.endPlayback = endPlayback;
|
this.endPlayback = endPlayback;
|
||||||
|
@ -28,7 +28,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Dummy media period id used while the timeline is empty and no period id is specified. This id
|
* Dummy media period id used while the timeline is empty and no period id is specified. This id
|
||||||
* is used when playback infos are created with {@link #createDummy(long, TrackSelectorResult)}.
|
* is used when playback infos are created with {@link #createDummy(TrackSelectorResult)}.
|
||||||
*/
|
*/
|
||||||
private static final MediaPeriodId DUMMY_MEDIA_PERIOD_ID =
|
private static final MediaPeriodId DUMMY_MEDIA_PERIOD_ID =
|
||||||
new MediaPeriodId(/* periodUid= */ new Object());
|
new MediaPeriodId(/* periodUid= */ new Object());
|
||||||
@ -83,17 +83,15 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||||||
* Creates empty dummy playback info which can be used for masking as long as no real playback
|
* Creates empty dummy playback info which can be used for masking as long as no real playback
|
||||||
* info is available.
|
* info is available.
|
||||||
*
|
*
|
||||||
* @param startPositionUs The start position at which playback should start, in microseconds.
|
|
||||||
* @param emptyTrackSelectorResult An empty track selector result with null entries for each
|
* @param emptyTrackSelectorResult An empty track selector result with null entries for each
|
||||||
* renderer.
|
* renderer.
|
||||||
* @return A dummy playback info.
|
* @return A dummy playback info.
|
||||||
*/
|
*/
|
||||||
public static PlaybackInfo createDummy(
|
public static PlaybackInfo createDummy(TrackSelectorResult emptyTrackSelectorResult) {
|
||||||
long startPositionUs, TrackSelectorResult emptyTrackSelectorResult) {
|
|
||||||
return new PlaybackInfo(
|
return new PlaybackInfo(
|
||||||
Timeline.EMPTY,
|
Timeline.EMPTY,
|
||||||
DUMMY_MEDIA_PERIOD_ID,
|
DUMMY_MEDIA_PERIOD_ID,
|
||||||
startPositionUs,
|
/* startPositionUs= */ 0,
|
||||||
/* contentPositionUs= */ C.TIME_UNSET,
|
/* contentPositionUs= */ C.TIME_UNSET,
|
||||||
Player.STATE_IDLE,
|
Player.STATE_IDLE,
|
||||||
/* playbackError= */ null,
|
/* playbackError= */ null,
|
||||||
@ -101,9 +99,9 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
|||||||
TrackGroupArray.EMPTY,
|
TrackGroupArray.EMPTY,
|
||||||
emptyTrackSelectorResult,
|
emptyTrackSelectorResult,
|
||||||
DUMMY_MEDIA_PERIOD_ID,
|
DUMMY_MEDIA_PERIOD_ID,
|
||||||
startPositionUs,
|
/* bufferedPositionUs= */ 0,
|
||||||
/* totalBufferedDurationUs= */ 0,
|
/* totalBufferedDurationUs= */ 0,
|
||||||
startPositionUs);
|
/* positionUs= */ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user