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:
tonihei 2020-01-21 16:33:21 +00:00 committed by Ian Baker
parent 665092e4be
commit 5f9a585075
3 changed files with 93 additions and 121 deletions

View File

@ -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,

View File

@ -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.
if (pendingInitialSeekPosition.windowPositionUs == C.TIME_UNSET) {
startAtDefaultPositionWindowIndex =
timeline.getPeriodByUid(periodPosition.first, period).windowIndex;
} else {
newPeriodUid = periodPosition.first; newPeriodUid = periodPosition.first;
newContentPositionUs = newContentPositionUs = periodPosition.second;
pendingInitialSeekPosition.windowPositionUs == C.TIME_UNSET }
? C.TIME_UNSET
: 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.
startAtDefaultPositionWindowIndex =
timeline.getPeriodByUid(subsequentPeriodUid, period).windowIndex;
}
} 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 = Pair<Object, Long> defaultPosition =
timeline.getPeriodPosition( timeline.getPeriodPosition(
window, window,
period, period,
timeline.getPeriodByUid(subsequentPeriodUid, period).windowIndex, startAtDefaultPositionWindowIndex,
/* windowPositionUs= */ C.TIME_UNSET); /* windowPositionUs= */ C.TIME_UNSET);
newPeriodUid = defaultPosition.first; newPeriodUid = defaultPosition.first;
newContentPositionUs = C.TIME_UNSET; 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;

View File

@ -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);
} }
/** /**