Commit 029c9583 by tonihei Committed by Oliver Woodman

Add queue abstraction to ExoPlayerImplInternal.

This gets rid of the manual tracking of this queue with reading, playing,
and loading period holders. Still keeping these names for queue access methods.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=182378944
parent c577d9d3
...@@ -118,6 +118,7 @@ import java.util.Collections; ...@@ -118,6 +118,7 @@ import java.util.Collections;
private final PlaybackInfoUpdate playbackInfoUpdate; private final PlaybackInfoUpdate playbackInfoUpdate;
private final ArrayList<CustomMessageInfo> customMessageInfos; private final ArrayList<CustomMessageInfo> customMessageInfos;
private final Clock clock; private final Clock clock;
private final MediaPeriodHolderQueue queue;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private SeekParameters seekParameters; private SeekParameters seekParameters;
...@@ -136,10 +137,6 @@ import java.util.Collections; ...@@ -136,10 +137,6 @@ import java.util.Collections;
private long rendererPositionUs; private long rendererPositionUs;
private int nextCustomMessageInfoIndex; private int nextCustomMessageInfoIndex;
private MediaPeriodHolder loadingPeriodHolder;
private MediaPeriodHolder readingPeriodHolder;
private MediaPeriodHolder playingPeriodHolder;
public ExoPlayerImplInternal( public ExoPlayerImplInternal(
Renderer[] renderers, Renderer[] renderers,
TrackSelector trackSelector, TrackSelector trackSelector,
...@@ -161,6 +158,7 @@ import java.util.Collections; ...@@ -161,6 +158,7 @@ import java.util.Collections;
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.player = player; this.player = player;
this.clock = clock; this.clock = clock;
this.queue = new MediaPeriodHolderQueue();
backBufferDurationUs = loadControl.getBackBufferDurationUs(); backBufferDurationUs = loadControl.getBackBufferDurationUs();
retainBackBufferFromKeyframe = loadControl.retainBackBufferFromKeyframe(); retainBackBufferFromKeyframe = loadControl.retainBackBufferFromKeyframe();
...@@ -444,8 +442,7 @@ import java.util.Collections; ...@@ -444,8 +442,7 @@ import java.util.Collections;
private void validateExistingPeriodHolders() throws ExoPlaybackException { private void validateExistingPeriodHolders() throws ExoPlaybackException {
// Find the last existing period holder that matches the new period order. // Find the last existing period holder that matches the new period order.
MediaPeriodHolder lastValidPeriodHolder = playingPeriodHolder != null MediaPeriodHolder lastValidPeriodHolder = queue.getFrontPeriod();
? playingPeriodHolder : loadingPeriodHolder;
if (lastValidPeriodHolder == null) { if (lastValidPeriodHolder == null) {
return; return;
} }
...@@ -465,30 +462,19 @@ import java.util.Collections; ...@@ -465,30 +462,19 @@ import java.util.Collections;
} }
// Release any period holders that don't match the new period order. // Release any period holders that don't match the new period order.
int loadingPeriodHolderIndex = loadingPeriodHolder.index; boolean readingPeriodRemoved = queue.removeAfter(lastValidPeriodHolder);
int readingPeriodHolderIndex =
readingPeriodHolder != null ? readingPeriodHolder.index : C.INDEX_UNSET;
if (lastValidPeriodHolder.next != null) {
releasePeriodHoldersFrom(lastValidPeriodHolder.next);
lastValidPeriodHolder.next = null;
}
// Update the period info for the last holder, as it may now be the last period in the timeline. // Update the period info for the last holder, as it may now be the last period in the timeline.
lastValidPeriodHolder.info = lastValidPeriodHolder.info =
mediaPeriodInfoSequence.getUpdatedMediaPeriodInfo(lastValidPeriodHolder.info); mediaPeriodInfoSequence.getUpdatedMediaPeriodInfo(lastValidPeriodHolder.info);
// Handle cases where loadingPeriodHolder or readingPeriodHolder have been removed. if (readingPeriodRemoved && queue.hasPlayingPeriod()) {
boolean seenLoadingPeriodHolder = loadingPeriodHolderIndex <= lastValidPeriodHolder.index;
if (!seenLoadingPeriodHolder) {
loadingPeriodHolder = lastValidPeriodHolder;
}
boolean seenReadingPeriodHolder = readingPeriodHolderIndex != C.INDEX_UNSET
&& readingPeriodHolderIndex <= lastValidPeriodHolder.index;
if (!seenReadingPeriodHolder && playingPeriodHolder != null) {
// Renderers may have read from a period that's been removed. Seek back to the current // Renderers may have read from a period that's been removed. Seek back to the current
// position of the playing period to make sure none of the removed period is played. // position of the playing period to make sure none of the removed period is played.
MediaPeriodId periodId = playingPeriodHolder.info.id; MediaPeriodId periodId = queue.getPlayingPeriod().info.id;
long newPositionUs = seekToPeriodPosition(periodId, playbackInfo.positionUs); long newPositionUs =
seekToPeriodPosition(
periodId, playbackInfo.positionUs, /* forceDisableRenderers= */ true);
if (newPositionUs != playbackInfo.positionUs) { if (newPositionUs != playbackInfo.positionUs) {
playbackInfo = playbackInfo =
playbackInfo.fromNewPosition(periodId, newPositionUs, playbackInfo.contentPositionUs); playbackInfo.fromNewPosition(periodId, newPositionUs, playbackInfo.contentPositionUs);
...@@ -513,11 +499,12 @@ import java.util.Collections; ...@@ -513,11 +499,12 @@ import java.util.Collections;
} }
private void updatePlaybackPositions() throws ExoPlaybackException { private void updatePlaybackPositions() throws ExoPlaybackException {
if (playingPeriodHolder == null) { if (!queue.hasPlayingPeriod()) {
return; return;
} }
// Update the playback position. // Update the playback position.
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
long periodPositionUs = playingPeriodHolder.mediaPeriod.readDiscontinuity(); long periodPositionUs = playingPeriodHolder.mediaPeriod.readDiscontinuity();
if (periodPositionUs != C.TIME_UNSET) { if (periodPositionUs != C.TIME_UNSET) {
resetRendererPosition(periodPositionUs); resetRendererPosition(periodPositionUs);
...@@ -545,12 +532,13 @@ import java.util.Collections; ...@@ -545,12 +532,13 @@ import java.util.Collections;
private void doSomeWork() throws ExoPlaybackException, IOException { private void doSomeWork() throws ExoPlaybackException, IOException {
long operationStartTimeMs = clock.uptimeMillis(); long operationStartTimeMs = clock.uptimeMillis();
updatePeriods(); updatePeriods();
if (playingPeriodHolder == null) { if (!queue.hasPlayingPeriod()) {
// We're still waiting for the first period to be prepared. // We're still waiting for the first period to be prepared.
maybeThrowPeriodPrepareError(); maybeThrowPeriodPrepareError();
scheduleNextWork(operationStartTimeMs, PREPARING_SOURCE_INTERVAL_MS); scheduleNextWork(operationStartTimeMs, PREPARING_SOURCE_INTERVAL_MS);
return; return;
} }
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
TraceUtil.beginSection("doSomeWork"); TraceUtil.beginSection("doSomeWork");
...@@ -594,10 +582,13 @@ import java.util.Collections; ...@@ -594,10 +582,13 @@ import java.util.Collections;
stopRenderers(); stopRenderers();
} else if (playbackInfo.playbackState == Player.STATE_BUFFERING) { } else if (playbackInfo.playbackState == Player.STATE_BUFFERING) {
float playbackSpeed = mediaClock.getPlaybackParameters().speed; float playbackSpeed = mediaClock.getPlaybackParameters().speed;
boolean isNewlyReady = enabledRenderers.length > 0 boolean isNewlyReady =
? (allRenderersReadyOrEnded && loadingPeriodHolder.haveSufficientBuffer( enabledRenderers.length > 0
rendererPositionUs, playbackSpeed, rebuffering)) ? (allRenderersReadyOrEnded
: isTimelineReady(playingPeriodDurationUs); && queue
.getLoadingPeriod()
.haveSufficientBuffer(rendererPositionUs, playbackSpeed, rebuffering))
: isTimelineReady(playingPeriodDurationUs);
if (isNewlyReady) { if (isNewlyReady) {
setState(Player.STATE_READY); setState(Player.STATE_READY);
if (playWhenReady) { if (playWhenReady) {
...@@ -672,6 +663,7 @@ import java.util.Collections; ...@@ -672,6 +663,7 @@ import java.util.Collections;
try { try {
long newPeriodPositionUs = periodPositionUs; long newPeriodPositionUs = periodPositionUs;
if (periodId.equals(playbackInfo.periodId)) { if (periodId.equals(playbackInfo.periodId)) {
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
if (playingPeriodHolder != null && newPeriodPositionUs != 0) { if (playingPeriodHolder != null && newPeriodPositionUs != 0) {
newPeriodPositionUs = newPeriodPositionUs =
playingPeriodHolder.mediaPeriod.getAdjustedSeekPositionUs( playingPeriodHolder.mediaPeriod.getAdjustedSeekPositionUs(
...@@ -698,58 +690,50 @@ import java.util.Collections; ...@@ -698,58 +690,50 @@ import java.util.Collections;
private long seekToPeriodPosition(MediaPeriodId periodId, long periodPositionUs) private long seekToPeriodPosition(MediaPeriodId periodId, long periodPositionUs)
throws ExoPlaybackException { throws ExoPlaybackException {
// Force disable renderers if they are reading from a period other than the one being played.
return seekToPeriodPosition(
periodId, periodPositionUs, queue.getPlayingPeriod() != queue.getReadingPeriod());
}
private long seekToPeriodPosition(
MediaPeriodId periodId, long periodPositionUs, boolean forceDisableRenderers)
throws ExoPlaybackException {
stopRenderers(); stopRenderers();
rebuffering = false; rebuffering = false;
setState(Player.STATE_BUFFERING); setState(Player.STATE_BUFFERING);
MediaPeriodHolder newPlayingPeriodHolder = null; // Clear the timeline, but keep the requested period if it is already prepared.
if (playingPeriodHolder == null) { MediaPeriodHolder oldPlayingPeriodHolder = queue.getPlayingPeriod();
// We're still waiting for the first period to be prepared. MediaPeriodHolder newPlayingPeriodHolder = oldPlayingPeriodHolder;
if (loadingPeriodHolder != null) { while (newPlayingPeriodHolder != null) {
loadingPeriodHolder.release(); if (shouldKeepPeriodHolder(periodId, periodPositionUs, newPlayingPeriodHolder)) {
} queue.removeAfter(newPlayingPeriodHolder);
} else { break;
// Clear the timeline, but keep the requested period if it is already prepared.
MediaPeriodHolder periodHolder = playingPeriodHolder;
while (periodHolder != null) {
if (newPlayingPeriodHolder == null
&& shouldKeepPeriodHolder(periodId, periodPositionUs, periodHolder)) {
newPlayingPeriodHolder = periodHolder;
} else {
periodHolder.release();
}
periodHolder = periodHolder.next;
} }
newPlayingPeriodHolder = queue.advancePlayingPeriod();
} }
// Disable all the renderers if the period being played is changing, or if the renderers are // Disable all the renderers if the period being played is changing, or if forced.
// reading from a period other than the one being played. if (oldPlayingPeriodHolder != newPlayingPeriodHolder || forceDisableRenderers) {
if (playingPeriodHolder != newPlayingPeriodHolder
|| playingPeriodHolder != readingPeriodHolder) {
for (Renderer renderer : enabledRenderers) { for (Renderer renderer : enabledRenderers) {
disableRenderer(renderer); disableRenderer(renderer);
} }
enabledRenderers = new Renderer[0]; enabledRenderers = new Renderer[0];
playingPeriodHolder = null; oldPlayingPeriodHolder = null;
} }
// Update the holders. // Update the holders.
if (newPlayingPeriodHolder != null) { if (newPlayingPeriodHolder != null) {
newPlayingPeriodHolder.next = null; updatePlayingPeriodRenderers(oldPlayingPeriodHolder);
loadingPeriodHolder = newPlayingPeriodHolder; if (newPlayingPeriodHolder.hasEnabledTracks) {
readingPeriodHolder = newPlayingPeriodHolder; periodPositionUs = newPlayingPeriodHolder.mediaPeriod.seekToUs(periodPositionUs);
setPlayingPeriodHolder(newPlayingPeriodHolder); newPlayingPeriodHolder.mediaPeriod.discardBuffer(
if (playingPeriodHolder.hasEnabledTracks) { periodPositionUs - backBufferDurationUs, retainBackBufferFromKeyframe);
periodPositionUs = playingPeriodHolder.mediaPeriod.seekToUs(periodPositionUs);
playingPeriodHolder.mediaPeriod.discardBuffer(periodPositionUs - backBufferDurationUs,
retainBackBufferFromKeyframe);
} }
resetRendererPosition(periodPositionUs); resetRendererPosition(periodPositionUs);
maybeContinueLoading(); maybeContinueLoading();
} else { } else {
loadingPeriodHolder = null; queue.clear();
readingPeriodHolder = null;
playingPeriodHolder = null;
resetRendererPosition(periodPositionUs); resetRendererPosition(periodPositionUs);
} }
...@@ -771,9 +755,10 @@ import java.util.Collections; ...@@ -771,9 +755,10 @@ import java.util.Collections;
} }
private void resetRendererPosition(long periodPositionUs) throws ExoPlaybackException { private void resetRendererPosition(long periodPositionUs) throws ExoPlaybackException {
rendererPositionUs = playingPeriodHolder == null rendererPositionUs =
? periodPositionUs + RENDERER_TIMESTAMP_OFFSET_US !queue.hasPlayingPeriod()
: playingPeriodHolder.toRendererTime(periodPositionUs); ? periodPositionUs + RENDERER_TIMESTAMP_OFFSET_US
: queue.getPlayingPeriod().toRendererTime(periodPositionUs);
mediaClock.resetPosition(rendererPositionUs); mediaClock.resetPosition(rendererPositionUs);
for (Renderer renderer : enabledRenderers) { for (Renderer renderer : enabledRenderers) {
renderer.resetPosition(rendererPositionUs); renderer.resetPosition(rendererPositionUs);
...@@ -825,11 +810,7 @@ import java.util.Collections; ...@@ -825,11 +810,7 @@ import java.util.Collections;
} }
} }
enabledRenderers = new Renderer[0]; enabledRenderers = new Renderer[0];
releasePeriodHoldersFrom(playingPeriodHolder != null ? playingPeriodHolder queue.clear();
: loadingPeriodHolder);
loadingPeriodHolder = null;
readingPeriodHolder = null;
playingPeriodHolder = null;
setIsLoading(false); setIsLoading(false);
Timeline timeline = playbackInfo.timeline; Timeline timeline = playbackInfo.timeline;
int firstPeriodIndex = int firstPeriodIndex =
...@@ -1030,13 +1011,14 @@ import java.util.Collections; ...@@ -1030,13 +1011,14 @@ import java.util.Collections;
} }
private void reselectTracksInternal() throws ExoPlaybackException { private void reselectTracksInternal() throws ExoPlaybackException {
if (playingPeriodHolder == null) { if (!queue.hasPlayingPeriod()) {
// We don't have tracks yet, so we don't care. // We don't have tracks yet, so we don't care.
return; return;
} }
float playbackSpeed = mediaClock.getPlaybackParameters().speed; float playbackSpeed = mediaClock.getPlaybackParameters().speed;
// Reselect tracks on each period in turn, until the selection changes. // Reselect tracks on each period in turn, until the selection changes.
MediaPeriodHolder periodHolder = playingPeriodHolder; MediaPeriodHolder periodHolder = queue.getPlayingPeriod();
MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
boolean selectionsChangedForReadPeriod = true; boolean selectionsChangedForReadPeriod = true;
while (true) { while (true) {
if (periodHolder == null || !periodHolder.prepared) { if (periodHolder == null || !periodHolder.prepared) {
...@@ -1056,11 +1038,8 @@ import java.util.Collections; ...@@ -1056,11 +1038,8 @@ import java.util.Collections;
if (selectionsChangedForReadPeriod) { if (selectionsChangedForReadPeriod) {
// Update streams and rebuffer for the new selection, recreating all streams if reading ahead. // Update streams and rebuffer for the new selection, recreating all streams if reading ahead.
boolean recreateStreams = readingPeriodHolder != playingPeriodHolder; MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
releasePeriodHoldersFrom(playingPeriodHolder.next); boolean recreateStreams = queue.removeAfter(playingPeriodHolder);
playingPeriodHolder.next = null;
loadingPeriodHolder = playingPeriodHolder;
readingPeriodHolder = playingPeriodHolder;
boolean[] streamResetFlags = new boolean[renderers.length]; boolean[] streamResetFlags = new boolean[renderers.length];
long periodPositionUs = playingPeriodHolder.updatePeriodTrackSelection( long periodPositionUs = playingPeriodHolder.updatePeriodTrackSelection(
...@@ -1092,21 +1071,17 @@ import java.util.Collections; ...@@ -1092,21 +1071,17 @@ import java.util.Collections;
} }
} }
} }
playbackInfo = playbackInfo.copyWithTrackSelectorResult(periodHolder.trackSelectorResult); playbackInfo =
playbackInfo.copyWithTrackSelectorResult(playingPeriodHolder.trackSelectorResult);
enableRenderers(rendererWasEnabledFlags, enabledRendererCount); enableRenderers(rendererWasEnabledFlags, enabledRendererCount);
} else { } else {
// Release and re-prepare/buffer periods after the one whose selection changed. // Release and re-prepare/buffer periods after the one whose selection changed.
loadingPeriodHolder = periodHolder; queue.removeAfter(periodHolder);
periodHolder = loadingPeriodHolder.next; if (periodHolder.prepared) {
while (periodHolder != null) { long loadingPeriodPositionUs =
periodHolder.release(); Math.max(
periodHolder = periodHolder.next; periodHolder.info.startPositionUs, periodHolder.toPeriodTime(rendererPositionUs));
} periodHolder.updatePeriodTrackSelection(loadingPeriodPositionUs, false);
loadingPeriodHolder.next = null;
if (loadingPeriodHolder.prepared) {
long loadingPeriodPositionUs = Math.max(loadingPeriodHolder.info.startPositionUs,
loadingPeriodHolder.toPeriodTime(rendererPositionUs));
loadingPeriodHolder.updatePeriodTrackSelection(loadingPeriodPositionUs, false);
} }
} }
if (playbackInfo.playbackState != Player.STATE_ENDED) { if (playbackInfo.playbackState != Player.STATE_ENDED) {
...@@ -1117,8 +1092,7 @@ import java.util.Collections; ...@@ -1117,8 +1092,7 @@ import java.util.Collections;
} }
private void updateTrackSelectionPlaybackSpeed(float playbackSpeed) { private void updateTrackSelectionPlaybackSpeed(float playbackSpeed) {
MediaPeriodHolder periodHolder = MediaPeriodHolder periodHolder = queue.getFrontPeriod();
playingPeriodHolder != null ? playingPeriodHolder : loadingPeriodHolder;
while (periodHolder != null) { while (periodHolder != null) {
if (periodHolder.trackSelectorResult != null) { if (periodHolder.trackSelectorResult != null) {
TrackSelection[] trackSelections = periodHolder.trackSelectorResult.selections.getAll(); TrackSelection[] trackSelections = periodHolder.trackSelectorResult.selections.getAll();
...@@ -1133,6 +1107,7 @@ import java.util.Collections; ...@@ -1133,6 +1107,7 @@ import java.util.Collections;
} }
private boolean isTimelineReady(long playingPeriodDurationUs) { private boolean isTimelineReady(long playingPeriodDurationUs) {
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
return playingPeriodDurationUs == C.TIME_UNSET return playingPeriodDurationUs == C.TIME_UNSET
|| playbackInfo.positionUs < playingPeriodDurationUs || playbackInfo.positionUs < playingPeriodDurationUs
|| (playingPeriodHolder.next != null || (playingPeriodHolder.next != null
...@@ -1140,6 +1115,8 @@ import java.util.Collections; ...@@ -1140,6 +1115,8 @@ import java.util.Collections;
} }
private void maybeThrowPeriodPrepareError() throws IOException { private void maybeThrowPeriodPrepareError() throws IOException {
MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod();
MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
if (loadingPeriodHolder != null && !loadingPeriodHolder.prepared if (loadingPeriodHolder != null && !loadingPeriodHolder.prepared
&& (readingPeriodHolder == null || readingPeriodHolder.next == loadingPeriodHolder)) { && (readingPeriodHolder == null || readingPeriodHolder.next == loadingPeriodHolder)) {
for (Renderer renderer : enabledRenderers) { for (Renderer renderer : enabledRenderers) {
...@@ -1202,8 +1179,7 @@ import java.util.Collections; ...@@ -1202,8 +1179,7 @@ import java.util.Collections;
} }
int playingPeriodIndex = playbackInfo.periodId.periodIndex; int playingPeriodIndex = playbackInfo.periodId.periodIndex;
MediaPeriodHolder periodHolder = playingPeriodHolder != null ? playingPeriodHolder MediaPeriodHolder periodHolder = queue.getFrontPeriod();
: loadingPeriodHolder;
if (periodHolder == null && playingPeriodIndex >= oldTimeline.getPeriodCount()) { if (periodHolder == null && playingPeriodIndex >= oldTimeline.getPeriodCount()) {
return; return;
} }
...@@ -1283,22 +1259,15 @@ import java.util.Collections; ...@@ -1283,22 +1259,15 @@ import java.util.Collections;
periodHolder = updatePeriodInfo(periodHolder, periodIndex); periodHolder = updatePeriodInfo(periodHolder, periodIndex);
} else { } else {
// The holder is inconsistent with the new timeline. // The holder is inconsistent with the new timeline.
boolean seenReadingPeriodHolder = boolean readingPeriodRemoved = queue.removeAfter(previousPeriodHolder);
readingPeriodHolder != null && readingPeriodHolder.index < periodHolder.index; if (readingPeriodRemoved) {
if (!seenReadingPeriodHolder) {
// Renderers may have read from a period that's been removed. Seek back to the current // Renderers may have read from a period that's been removed. Seek back to the current
// position of the playing period to make sure none of the removed period is played. // position of the playing period to make sure none of the removed period is played.
MediaPeriodId id = queue.getPlayingPeriod().info.id;
long newPositionUs = long newPositionUs =
seekToPeriodPosition(playingPeriodHolder.info.id, playbackInfo.positionUs); seekToPeriodPosition(id, playbackInfo.positionUs, /* forceDisableRenderers= */ true);
playbackInfo = playbackInfo.fromNewPosition(playingPeriodHolder.info.id, newPositionUs, playbackInfo =
playbackInfo.contentPositionUs); playbackInfo.fromNewPosition(id, newPositionUs, playbackInfo.contentPositionUs);
} else {
// Update the loading period to be the last period that's still valid, and release all
// subsequent periods.
loadingPeriodHolder = previousPeriodHolder;
loadingPeriodHolder.next = null;
// Release the rest of the timeline.
releasePeriodHoldersFrom(periodHolder);
} }
break; break;
} }
...@@ -1426,19 +1395,21 @@ import java.util.Collections; ...@@ -1426,19 +1395,21 @@ import java.util.Collections;
// Update the loading period if required. // Update the loading period if required.
maybeUpdateLoadingPeriod(); maybeUpdateLoadingPeriod();
MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod();
if (loadingPeriodHolder == null || loadingPeriodHolder.isFullyBuffered()) { if (loadingPeriodHolder == null || loadingPeriodHolder.isFullyBuffered()) {
setIsLoading(false); setIsLoading(false);
} else if (loadingPeriodHolder != null && !playbackInfo.isLoading) { } else if (loadingPeriodHolder != null && !playbackInfo.isLoading) {
maybeContinueLoading(); maybeContinueLoading();
} }
if (playingPeriodHolder == null) { if (!queue.hasPlayingPeriod()) {
// We're waiting for the first period to be prepared. // We're waiting for the first period to be prepared.
return; return;
} }
// Advance the playing period if necessary. // Advance the playing period if necessary.
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
boolean advancedPlayingPeriod = false; boolean advancedPlayingPeriod = false;
while (playWhenReady && playingPeriodHolder != readingPeriodHolder while (playWhenReady && playingPeriodHolder != readingPeriodHolder
&& rendererPositionUs >= playingPeriodHolder.next.rendererPositionOffsetUs) { && rendererPositionUs >= playingPeriodHolder.next.rendererPositionOffsetUs) {
...@@ -1452,8 +1423,9 @@ import java.util.Collections; ...@@ -1452,8 +1423,9 @@ import java.util.Collections;
playingPeriodHolder.info.isLastInTimelinePeriod playingPeriodHolder.info.isLastInTimelinePeriod
? Player.DISCONTINUITY_REASON_PERIOD_TRANSITION ? Player.DISCONTINUITY_REASON_PERIOD_TRANSITION
: Player.DISCONTINUITY_REASON_AD_INSERTION; : Player.DISCONTINUITY_REASON_AD_INSERTION;
playingPeriodHolder.release(); MediaPeriodHolder oldPlayingPeriodHolder = playingPeriodHolder;
setPlayingPeriodHolder(playingPeriodHolder.next); playingPeriodHolder = queue.advancePlayingPeriod();
updatePlayingPeriodRenderers(oldPlayingPeriodHolder);
playbackInfo = playbackInfo.fromNewPosition(playingPeriodHolder.info.id, playbackInfo = playbackInfo.fromNewPosition(playingPeriodHolder.info.id,
playingPeriodHolder.info.startPositionUs, playingPeriodHolder.info.contentPositionUs); playingPeriodHolder.info.startPositionUs, playingPeriodHolder.info.contentPositionUs);
playbackInfoUpdate.setPositionDiscontinuity(discontinuityReason); playbackInfoUpdate.setPositionDiscontinuity(discontinuityReason);
...@@ -1492,7 +1464,7 @@ import java.util.Collections; ...@@ -1492,7 +1464,7 @@ import java.util.Collections;
} }
TrackSelectorResult oldTrackSelectorResult = readingPeriodHolder.trackSelectorResult; TrackSelectorResult oldTrackSelectorResult = readingPeriodHolder.trackSelectorResult;
readingPeriodHolder = readingPeriodHolder.next; readingPeriodHolder = queue.advanceReadingPeriod();
TrackSelectorResult newTrackSelectorResult = readingPeriodHolder.trackSelectorResult; TrackSelectorResult newTrackSelectorResult = readingPeriodHolder.trackSelectorResult;
boolean initialDiscontinuity = boolean initialDiscontinuity =
...@@ -1536,6 +1508,7 @@ import java.util.Collections; ...@@ -1536,6 +1508,7 @@ import java.util.Collections;
private void maybeUpdateLoadingPeriod() throws IOException { private void maybeUpdateLoadingPeriod() throws IOException {
MediaPeriodInfo info; MediaPeriodInfo info;
MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod();
if (loadingPeriodHolder == null) { if (loadingPeriodHolder == null) {
info = mediaPeriodInfoSequence.getFirstMediaPeriodInfo(playbackInfo); info = mediaPeriodInfoSequence.getFirstMediaPeriodInfo(playbackInfo);
} else { } else {
...@@ -1544,12 +1517,9 @@ import java.util.Collections; ...@@ -1544,12 +1517,9 @@ import java.util.Collections;
|| loadingPeriodHolder.info.durationUs == C.TIME_UNSET) { || loadingPeriodHolder.info.durationUs == C.TIME_UNSET) {
return; return;
} }
if (playingPeriodHolder != null) { if (queue.getLength() == MAXIMUM_BUFFER_AHEAD_PERIODS) {
int bufferAheadPeriodCount = loadingPeriodHolder.index - playingPeriodHolder.index; // We are already buffering the maximum number of periods ahead.
if (bufferAheadPeriodCount == MAXIMUM_BUFFER_AHEAD_PERIODS) { return;
// We are already buffering the maximum number of periods ahead.
return;
}
} }
info = mediaPeriodInfoSequence.getNextMediaPeriodInfo(loadingPeriodHolder.info, info = mediaPeriodInfoSequence.getNextMediaPeriodInfo(loadingPeriodHolder.info,
loadingPeriodHolder.getRendererOffset(), rendererPositionUs); loadingPeriodHolder.getRendererOffset(), rendererPositionUs);
...@@ -1563,34 +1533,40 @@ import java.util.Collections; ...@@ -1563,34 +1533,40 @@ import java.util.Collections;
loadingPeriodHolder == null loadingPeriodHolder == null
? (info.startPositionUs + RENDERER_TIMESTAMP_OFFSET_US) ? (info.startPositionUs + RENDERER_TIMESTAMP_OFFSET_US)
: (loadingPeriodHolder.getRendererOffset() + loadingPeriodHolder.info.durationUs); : (loadingPeriodHolder.getRendererOffset() + loadingPeriodHolder.info.durationUs);
int holderIndex = loadingPeriodHolder == null ? 0 : loadingPeriodHolder.index + 1;
Object uid = playbackInfo.timeline.getPeriod(info.id.periodIndex, period, true).uid; Object uid = playbackInfo.timeline.getPeriod(info.id.periodIndex, period, true).uid;
MediaPeriodHolder newPeriodHolder = new MediaPeriodHolder(renderers, rendererCapabilities, MediaPeriodHolder newPeriodHolder =
rendererPositionOffsetUs, trackSelector, loadControl, mediaSource, uid, holderIndex, info); new MediaPeriodHolder(
if (loadingPeriodHolder != null) { renderers,
loadingPeriodHolder.next = newPeriodHolder; rendererCapabilities,
} rendererPositionOffsetUs,
loadingPeriodHolder = newPeriodHolder; trackSelector,
loadingPeriodHolder.mediaPeriod.prepare(this, info.startPositionUs); loadControl,
mediaSource,
uid,
info);
queue.enqueueLoadingPeriod(newPeriodHolder);
newPeriodHolder.mediaPeriod.prepare(this, info.startPositionUs);
setIsLoading(true); setIsLoading(true);
} }
private void handlePeriodPrepared(MediaPeriod period) throws ExoPlaybackException { private void handlePeriodPrepared(MediaPeriod period) throws ExoPlaybackException {
MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod();
if (loadingPeriodHolder == null || loadingPeriodHolder.mediaPeriod != period) { if (loadingPeriodHolder == null || loadingPeriodHolder.mediaPeriod != period) {
// Stale event. // Stale event.
return; return;
} }
loadingPeriodHolder.handlePrepared(mediaClock.getPlaybackParameters().speed); loadingPeriodHolder.handlePrepared(mediaClock.getPlaybackParameters().speed);
if (playingPeriodHolder == null) { if (!queue.hasPlayingPeriod()) {
// This is the first prepared period, so start playing it. // This is the first prepared period, so start playing it.
readingPeriodHolder = loadingPeriodHolder; MediaPeriodHolder playingPeriodHolder = queue.advancePlayingPeriod();
resetRendererPosition(readingPeriodHolder.info.startPositionUs); resetRendererPosition(playingPeriodHolder.info.startPositionUs);
setPlayingPeriodHolder(readingPeriodHolder); updatePlayingPeriodRenderers(/* oldPlayingPeriodHolder= */ null);
} }
maybeContinueLoading(); maybeContinueLoading();
} }
private void handleContinueLoadingRequested(MediaPeriod period) { private void handleContinueLoadingRequested(MediaPeriod period) {
MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod();
if (loadingPeriodHolder == null || loadingPeriodHolder.mediaPeriod != period) { if (loadingPeriodHolder == null || loadingPeriodHolder.mediaPeriod != period) {
// Stale event. // Stale event.
return; return;
...@@ -1600,6 +1576,7 @@ import java.util.Collections; ...@@ -1600,6 +1576,7 @@ import java.util.Collections;
} }
private void maybeContinueLoading() { private void maybeContinueLoading() {
MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod();
boolean continueLoading = loadingPeriodHolder.shouldContinueLoading( boolean continueLoading = loadingPeriodHolder.shouldContinueLoading(
rendererPositionUs, mediaClock.getPlaybackParameters().speed); rendererPositionUs, mediaClock.getPlaybackParameters().speed);
setIsLoading(continueLoading); setIsLoading(continueLoading);
...@@ -1608,38 +1585,32 @@ import java.util.Collections; ...@@ -1608,38 +1585,32 @@ import java.util.Collections;
} }
} }
private void releasePeriodHoldersFrom(MediaPeriodHolder periodHolder) { private void updatePlayingPeriodRenderers(@Nullable MediaPeriodHolder oldPlayingPeriodHolder)
while (periodHolder != null) { throws ExoPlaybackException {
periodHolder.release(); MediaPeriodHolder newPlayingPeriodHolder = queue.getPlayingPeriod();
periodHolder = periodHolder.next; if (newPlayingPeriodHolder == null || oldPlayingPeriodHolder == newPlayingPeriodHolder) {
}
}
private void setPlayingPeriodHolder(MediaPeriodHolder periodHolder) throws ExoPlaybackException {
if (playingPeriodHolder == periodHolder) {
return; return;
} }
int enabledRendererCount = 0; int enabledRendererCount = 0;
boolean[] rendererWasEnabledFlags = new boolean[renderers.length]; boolean[] rendererWasEnabledFlags = new boolean[renderers.length];
for (int i = 0; i < renderers.length; i++) { for (int i = 0; i < renderers.length; i++) {
Renderer renderer = renderers[i]; Renderer renderer = renderers[i];
rendererWasEnabledFlags[i] = renderer.getState() != Renderer.STATE_DISABLED; rendererWasEnabledFlags[i] = renderer.getState() != Renderer.STATE_DISABLED;
if (periodHolder.trackSelectorResult.renderersEnabled[i]) { if (newPlayingPeriodHolder.trackSelectorResult.renderersEnabled[i]) {
enabledRendererCount++; enabledRendererCount++;
} }
if (rendererWasEnabledFlags[i] && (!periodHolder.trackSelectorResult.renderersEnabled[i] if (rendererWasEnabledFlags[i]
|| (renderer.isCurrentStreamFinal() && (!newPlayingPeriodHolder.trackSelectorResult.renderersEnabled[i]
&& renderer.getStream() == playingPeriodHolder.sampleStreams[i]))) { || (renderer.isCurrentStreamFinal()
&& renderer.getStream() == oldPlayingPeriodHolder.sampleStreams[i]))) {
// The renderer should be disabled before playing the next period, either because it's not // The renderer should be disabled before playing the next period, either because it's not
// needed to play the next period, or because we need to re-enable it as its current stream // needed to play the next period, or because we need to re-enable it as its current stream
// is final and it's not reading ahead. // is final and it's not reading ahead.
disableRenderer(renderer); disableRenderer(renderer);
} }
} }
playbackInfo =
playingPeriodHolder = periodHolder; playbackInfo.copyWithTrackSelectorResult(newPlayingPeriodHolder.trackSelectorResult);
playbackInfo = playbackInfo.copyWithTrackSelectorResult(periodHolder.trackSelectorResult);
enableRenderers(rendererWasEnabledFlags, enabledRendererCount); enableRenderers(rendererWasEnabledFlags, enabledRendererCount);
} }
...@@ -1647,6 +1618,7 @@ import java.util.Collections; ...@@ -1647,6 +1618,7 @@ import java.util.Collections;
throws ExoPlaybackException { throws ExoPlaybackException {
enabledRenderers = new Renderer[totalEnabledRendererCount]; enabledRenderers = new Renderer[totalEnabledRendererCount];
int enabledRendererCount = 0; int enabledRendererCount = 0;
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
for (int i = 0; i < renderers.length; i++) { for (int i = 0; i < renderers.length; i++) {
if (playingPeriodHolder.trackSelectorResult.renderersEnabled[i]) { if (playingPeriodHolder.trackSelectorResult.renderersEnabled[i]) {
enableRenderer(i, rendererWasEnabledFlags[i], enabledRendererCount++); enableRenderer(i, rendererWasEnabledFlags[i], enabledRendererCount++);
...@@ -1656,6 +1628,7 @@ import java.util.Collections; ...@@ -1656,6 +1628,7 @@ import java.util.Collections;
private void enableRenderer(int rendererIndex, boolean wasRendererEnabled, private void enableRenderer(int rendererIndex, boolean wasRendererEnabled,
int enabledRendererIndex) throws ExoPlaybackException { int enabledRendererIndex) throws ExoPlaybackException {
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
Renderer renderer = renderers[rendererIndex]; Renderer renderer = renderers[rendererIndex];
enabledRenderers[enabledRendererIndex] = renderer; enabledRenderers[enabledRendererIndex] = renderer;
if (renderer.getState() == Renderer.STATE_DISABLED) { if (renderer.getState() == Renderer.STATE_DISABLED) {
...@@ -1681,6 +1654,7 @@ import java.util.Collections; ...@@ -1681,6 +1654,7 @@ import java.util.Collections;
} }
private boolean rendererWaitingForNextStream(Renderer renderer) { private boolean rendererWaitingForNextStream(Renderer renderer) {
MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
return readingPeriodHolder.next != null && readingPeriodHolder.next.prepared return readingPeriodHolder.next != null && readingPeriodHolder.next.prepared
&& renderer.hasReadStreamToEnd(); && renderer.hasReadStreamToEnd();
} }
...@@ -1697,13 +1671,152 @@ import java.util.Collections; ...@@ -1697,13 +1671,152 @@ import java.util.Collections;
} }
/** /**
* Holds a queue of {@link MediaPeriodHolder}s from the currently playing period holder at the
* front to the loading period holder at the end of the queue. Also has a reference to the reading
* period holder.
*/
private static final class MediaPeriodHolderQueue {
private MediaPeriodHolder playing;
private MediaPeriodHolder reading;
private MediaPeriodHolder loading;
private int length;
/**
* Returns the loading period holder which is at the end of the queue, or null if the queue is
* empty.
*/
public MediaPeriodHolder getLoadingPeriod() {
return loading;
}
/**
* Returns the playing period holder which is at the front of the queue, or null if the queue is
* empty or hasn't started playing.
*/
public MediaPeriodHolder getPlayingPeriod() {
return playing;
}
/**
* Returns the reading period holder, or null if the queue is empty or the player hasn't started
* reading.
*/
public MediaPeriodHolder getReadingPeriod() {
return reading;
}
/**
* Returns the period holder in the front of the queue which is the playing period holder when
* playing, or null if the queue is empty.
*/
public MediaPeriodHolder getFrontPeriod() {
return hasPlayingPeriod() ? playing : loading;
}
/** Returns the current length of the queue. */
public int getLength() {
return length;
}
/** Returns whether the reading and playing period holders are set. */
public boolean hasPlayingPeriod() {
return playing != null;
}
/**
* Continues reading from the next period holder in the queue.
*
* @return The updated reading period holder.
*/
public MediaPeriodHolder advanceReadingPeriod() {
Assertions.checkState(reading != null && reading.next != null);
reading = reading.next;
return reading;
}
/** Enqueues a new period holder at the end, which becomes the new loading period holder. */
public void enqueueLoadingPeriod(MediaPeriodHolder mediaPeriodHolder) {
Assertions.checkState(mediaPeriodHolder != null);
if (loading != null) {
Assertions.checkState(hasPlayingPeriod());
loading.next = mediaPeriodHolder;
}
loading = mediaPeriodHolder;
length++;
}
/**
* Dequeues the playing period holder from the front of the queue and advances the playing
* period holder to be the next item in the queue. If the playing period holder is unset, set it
* to the item in the front of the queue.
*
* @return The updated playing period holder, or null if the queue is or becomes empty.
*/
public MediaPeriodHolder advancePlayingPeriod() {
if (playing != null) {
if (playing == reading) {
reading = playing.next;
}
playing.release();
playing = playing.next;
length--;
if (length == 0) {
loading = null;
}
} else {
playing = loading;
reading = loading;
}
return playing;
}
/**
* Removes all period holders after the given period holder. This process may also remove the
* currently reading period holder. If that is the case, the reading period holder is set to be
* the same as the playing period holder at the front of the queue.
*
* @param mediaPeriodHolder The media period holder that shall be the new end of the queue.
* @return Whether the reading period has been removed.
*/
public boolean removeAfter(MediaPeriodHolder mediaPeriodHolder) {
Assertions.checkState(mediaPeriodHolder != null);
boolean removedReading = false;
loading = mediaPeriodHolder;
while (mediaPeriodHolder.next != null) {
mediaPeriodHolder = mediaPeriodHolder.next;
if (mediaPeriodHolder == reading) {
reading = playing;
removedReading = true;
}
mediaPeriodHolder.release();
length--;
}
loading.next = null;
return removedReading;
}
/** Clears the queue. */
public void clear() {
MediaPeriodHolder front = getFrontPeriod();
if (front != null) {
front.release();
removeAfter(front);
}
playing = null;
loading = null;
reading = null;
length = 0;
}
}
/**
* Holds a {@link MediaPeriod} with information required to play it as part of a timeline. * Holds a {@link MediaPeriod} with information required to play it as part of a timeline.
*/ */
private static final class MediaPeriodHolder { private static final class MediaPeriodHolder {
public final MediaPeriod mediaPeriod; public final MediaPeriod mediaPeriod;
public final Object uid; public final Object uid;
public final int index;
public final SampleStream[] sampleStreams; public final SampleStream[] sampleStreams;
public final boolean[] mayRetainStreamFlags; public final boolean[] mayRetainStreamFlags;
...@@ -1722,9 +1835,15 @@ import java.util.Collections; ...@@ -1722,9 +1835,15 @@ import java.util.Collections;
private TrackSelectorResult periodTrackSelectorResult; private TrackSelectorResult periodTrackSelectorResult;
public MediaPeriodHolder(Renderer[] renderers, RendererCapabilities[] rendererCapabilities, public MediaPeriodHolder(
long rendererPositionOffsetUs, TrackSelector trackSelector, LoadControl loadControl, Renderer[] renderers,
MediaSource mediaSource, Object periodUid, int index, MediaPeriodInfo info) { RendererCapabilities[] rendererCapabilities,
long rendererPositionOffsetUs,
TrackSelector trackSelector,
LoadControl loadControl,
MediaSource mediaSource,
Object periodUid,
MediaPeriodInfo info) {
this.renderers = renderers; this.renderers = renderers;
this.rendererCapabilities = rendererCapabilities; this.rendererCapabilities = rendererCapabilities;
this.rendererPositionOffsetUs = rendererPositionOffsetUs - info.startPositionUs; this.rendererPositionOffsetUs = rendererPositionOffsetUs - info.startPositionUs;
...@@ -1732,7 +1851,6 @@ import java.util.Collections; ...@@ -1732,7 +1851,6 @@ import java.util.Collections;
this.loadControl = loadControl; this.loadControl = loadControl;
this.mediaSource = mediaSource; this.mediaSource = mediaSource;
this.uid = Assertions.checkNotNull(periodUid); this.uid = Assertions.checkNotNull(periodUid);
this.index = index;
this.info = info; this.info = info;
sampleStreams = new SampleStream[renderers.length]; sampleStreams = new SampleStream[renderers.length];
mayRetainStreamFlags = new boolean[renderers.length]; mayRetainStreamFlags = new boolean[renderers.length];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment