Partially revert "Tell LoadControl whether playback can start"

- Renderers becoming ready is asynchronous, so the change wasn't
well thought through :(.
- This will bring back the possibility of getting stuck in the
buffering-but-not-loading anything state. This will need to be
addressed in a future CL.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=183646837
This commit is contained in:
olly 2018-01-29 04:31:49 -08:00 committed by Oliver Woodman
parent d32181e4e9
commit e26dc3990d
4 changed files with 43 additions and 75 deletions

View File

@ -214,8 +214,7 @@ public class DefaultLoadControl implements LoadControl {
} }
@Override @Override
public boolean shouldContinueLoading( public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) {
boolean canStartPlayback, long bufferedDurationUs, float playbackSpeed) {
boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize; boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize;
boolean wasBuffering = isBuffering; boolean wasBuffering = isBuffering;
if (prioritizeTimeOverSizeThresholds) { if (prioritizeTimeOverSizeThresholds) {
@ -230,9 +229,6 @@ public class DefaultLoadControl implements LoadControl {
&& (bufferedDurationUs < minBufferUs // below low watermark && (bufferedDurationUs < minBufferUs // below low watermark
|| (bufferedDurationUs <= maxBufferUs && isBuffering)); // between watermarks || (bufferedDurationUs <= maxBufferUs && isBuffering)); // between watermarks
} }
if (!isBuffering && !canStartPlayback && !targetBufferSizeReached) {
isBuffering = true;
}
if (priorityTaskManager != null && isBuffering != wasBuffering) { if (priorityTaskManager != null && isBuffering != wasBuffering) {
if (isBuffering) { if (isBuffering) {
priorityTaskManager.add(C.PRIORITY_PLAYBACK); priorityTaskManager.add(C.PRIORITY_PLAYBACK);

View File

@ -117,8 +117,7 @@ import java.util.Collections;
private boolean released; private boolean released;
private boolean playWhenReady; private boolean playWhenReady;
private boolean rebuffering; private boolean rebuffering;
private boolean renderersReadyOrEnded; @Player.RepeatMode private int repeatMode;
private @Player.RepeatMode int repeatMode;
private boolean shuffleModeEnabled; private boolean shuffleModeEnabled;
private int pendingPrepareCount; private int pendingPrepareCount;
@ -554,8 +553,6 @@ import java.util.Collections;
} }
renderersReadyOrEnded = renderersReadyOrEnded && rendererReadyOrEnded; renderersReadyOrEnded = renderersReadyOrEnded && rendererReadyOrEnded;
} }
this.renderersReadyOrEnded = renderersReadyOrEnded;
if (!renderersReadyOrEnded) { if (!renderersReadyOrEnded) {
maybeThrowPeriodPrepareError(); maybeThrowPeriodPrepareError();
} }
@ -567,25 +564,14 @@ import java.util.Collections;
&& playingPeriodHolder.info.isFinal) { && playingPeriodHolder.info.isFinal) {
setState(Player.STATE_ENDED); setState(Player.STATE_ENDED);
stopRenderers(); stopRenderers();
} else if (playbackInfo.playbackState == Player.STATE_BUFFERING) { } else if (playbackInfo.playbackState == Player.STATE_BUFFERING
boolean shouldStartPlayback = isReady(); && shouldTransitionToReadyState(renderersReadyOrEnded)) {
if (shouldStartPlayback && playbackInfo.isLoading && enabledRenderers.length != 0) { setState(Player.STATE_READY);
MediaPeriodHolder loadingHolder = queue.getLoadingPeriod(); if (playWhenReady) {
long bufferedPositionUs = loadingHolder.getBufferedPositionUs(!loadingHolder.info.isFinal); startRenderers();
shouldStartPlayback =
bufferedPositionUs == C.TIME_END_OF_SOURCE
|| loadControl.shouldStartPlayback(
bufferedPositionUs - loadingHolder.toPeriodTime(rendererPositionUs),
mediaClock.getPlaybackParameters().speed,
rebuffering);
} }
if (shouldStartPlayback) { } else if (playbackInfo.playbackState == Player.STATE_READY
setState(Player.STATE_READY); && !(enabledRenderers.length == 0 ? isTimelineReady() : renderersReadyOrEnded)) {
if (playWhenReady) {
startRenderers();
}
}
} else if (playbackInfo.playbackState == Player.STATE_READY && !isReady()) {
rebuffering = playWhenReady; rebuffering = playWhenReady;
setState(Player.STATE_BUFFERING); setState(Player.STATE_BUFFERING);
stopRenderers(); stopRenderers();
@ -694,7 +680,6 @@ import java.util.Collections;
throws ExoPlaybackException { throws ExoPlaybackException {
stopRenderers(); stopRenderers();
rebuffering = false; rebuffering = false;
renderersReadyOrEnded = false;
setState(Player.STATE_BUFFERING); setState(Player.STATE_BUFFERING);
// Clear the timeline, but keep the requested period if it is already prepared. // Clear the timeline, but keep the requested period if it is already prepared.
@ -736,8 +721,8 @@ import java.util.Collections;
return periodPositionUs; return periodPositionUs;
} }
private boolean shouldKeepPeriodHolder(MediaPeriodId seekPeriodId, long positionUs, private boolean shouldKeepPeriodHolder(
MediaPeriodHolder holder) { MediaPeriodId seekPeriodId, long positionUs, MediaPeriodHolder holder) {
if (seekPeriodId.equals(holder.info.id) && holder.prepared) { if (seekPeriodId.equals(holder.info.id) && holder.prepared) {
playbackInfo.timeline.getPeriod(holder.info.id.periodIndex, period); playbackInfo.timeline.getPeriod(holder.info.id.periodIndex, period);
int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(positionUs); int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(positionUs);
@ -802,7 +787,6 @@ import java.util.Collections;
boolean releaseMediaSource, boolean resetPosition, boolean resetState) { boolean releaseMediaSource, boolean resetPosition, boolean resetState) {
handler.removeMessages(MSG_DO_SOME_WORK); handler.removeMessages(MSG_DO_SOME_WORK);
rebuffering = false; rebuffering = false;
renderersReadyOrEnded = false;
mediaClock.stop(); mediaClock.stop();
rendererPositionUs = RENDERER_TIMESTAMP_OFFSET_US; rendererPositionUs = RENDERER_TIMESTAMP_OFFSET_US;
for (Renderer renderer : enabledRenderers) { for (Renderer renderer : enabledRenderers) {
@ -1115,11 +1099,30 @@ import java.util.Collections;
} }
} }
private boolean isReady() { private boolean shouldTransitionToReadyState(boolean renderersReadyOrEnded) {
if (enabledRenderers.length != 0) { if (enabledRenderers.length == 0) {
return renderersReadyOrEnded; // If there are no enabled renderers, determine whether we're ready based on the timeline.
return isTimelineReady();
} }
// Determine whether we're ready based on the timeline. if (!renderersReadyOrEnded) {
return false;
}
if (!playbackInfo.isLoading) {
// Renderers are ready and we're not loading. Transition to ready, since the alternative is
// getting stuck waiting for additional media that's not being loaded.
return true;
}
// Renderers are ready and we're loading. Ask the LoadControl whether to transition.
MediaPeriodHolder loadingHolder = queue.getLoadingPeriod();
long bufferedPositionUs = loadingHolder.getBufferedPositionUs(!loadingHolder.info.isFinal);
return bufferedPositionUs == C.TIME_END_OF_SOURCE
|| loadControl.shouldStartPlayback(
bufferedPositionUs - loadingHolder.toPeriodTime(rendererPositionUs),
mediaClock.getPlaybackParameters().speed,
rebuffering);
}
private boolean isTimelineReady() {
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod(); MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
long playingPeriodDurationUs = playingPeriodHolder.info.durationUs; long playingPeriodDurationUs = playingPeriodHolder.info.durationUs;
return playingPeriodDurationUs == C.TIME_UNSET return playingPeriodDurationUs == C.TIME_UNSET
@ -1312,8 +1315,8 @@ import java.util.Collections;
* @return The index in the new timeline of the first subsequent period, or {@link C#INDEX_UNSET} * @return The index in the new timeline of the first subsequent period, or {@link C#INDEX_UNSET}
* if no such period was found. * if no such period was found.
*/ */
private int resolveSubsequentPeriod(int oldPeriodIndex, Timeline oldTimeline, private int resolveSubsequentPeriod(
Timeline newTimeline) { int oldPeriodIndex, Timeline oldTimeline, Timeline newTimeline) {
int newPeriodIndex = C.INDEX_UNSET; int newPeriodIndex = C.INDEX_UNSET;
int maxIterations = oldTimeline.getPeriodCount(); int maxIterations = oldTimeline.getPeriodCount();
for (int i = 0; i < maxIterations && newPeriodIndex == C.INDEX_UNSET; i++) { for (int i = 0; i < maxIterations && newPeriodIndex == C.INDEX_UNSET; i++) {
@ -1391,8 +1394,8 @@ import java.util.Collections;
* Calls {@link Timeline#getPeriodPosition(Timeline.Window, Timeline.Period, int, long)} using the * Calls {@link Timeline#getPeriodPosition(Timeline.Window, Timeline.Period, int, long)} using the
* current timeline. * current timeline.
*/ */
private Pair<Integer, Long> getPeriodPosition(Timeline timeline, int windowIndex, private Pair<Integer, Long> getPeriodPosition(
long windowPositionUs) { Timeline timeline, int windowIndex, long windowPositionUs) {
return timeline.getPeriodPosition(window, period, windowIndex, windowPositionUs); return timeline.getPeriodPosition(window, period, windowIndex, windowPositionUs);
} }
@ -1576,15 +1579,11 @@ import java.util.Collections;
setIsLoading(false); setIsLoading(false);
return; return;
} }
boolean canStartPlayback = playbackInfo.playbackState == Player.STATE_READY || isReady();
long bufferedDurationUs = long bufferedDurationUs =
nextLoadPositionUs - loadingPeriodHolder.toPeriodTime(rendererPositionUs); nextLoadPositionUs - loadingPeriodHolder.toPeriodTime(rendererPositionUs);
boolean continueLoading = boolean continueLoading =
loadControl.shouldContinueLoading( loadControl.shouldContinueLoading(
canStartPlayback, bufferedDurationUs, mediaClock.getPlaybackParameters().speed); bufferedDurationUs, mediaClock.getPlaybackParameters().speed);
if (!canStartPlayback && !continueLoading) {
throw new StuckBufferingException();
}
setIsLoading(continueLoading); setIsLoading(continueLoading);
if (continueLoading) { if (continueLoading) {
loadingPeriodHolder.continueLoading(rendererPositionUs); loadingPeriodHolder.continueLoading(rendererPositionUs);
@ -1632,8 +1631,9 @@ import java.util.Collections;
} }
} }
private void enableRenderer(int rendererIndex, boolean wasRendererEnabled, private void enableRenderer(
int enabledRendererIndex) throws ExoPlaybackException { int rendererIndex, boolean wasRendererEnabled, int enabledRendererIndex)
throws ExoPlaybackException {
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod(); MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
Renderer renderer = renderers[rendererIndex]; Renderer renderer = renderers[rendererIndex];
enabledRenderers[enabledRendererIndex] = renderer; enabledRenderers[enabledRendererIndex] = renderer;

View File

@ -90,17 +90,11 @@ public interface LoadControl {
/** /**
* Called by the player to determine whether it should continue to load the source. * Called by the player to determine whether it should continue to load the source.
* *
* @param canStartPlayback Whether the player has the minimum amount of data necessary to start
* playback. If {@code false}, this method must return {@code true} or playback will fail.
* Hence {@code true} should be returned in this case, unless some hard upper limit (e.g. on
* the amount of memory that the control will permit to be allocated) has been exceeded.
* Always true if playback is currently started.
* @param bufferedDurationUs The duration of media that's currently buffered. * @param bufferedDurationUs The duration of media that's currently buffered.
* @param playbackSpeed The current playback speed. * @param playbackSpeed The current playback speed.
* @return Whether the loading should continue. * @return Whether the loading should continue.
*/ */
boolean shouldContinueLoading( boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed);
boolean canStartPlayback, long bufferedDurationUs, float playbackSpeed);
/** /**
* Called repeatedly by the player when it's loading the source, has yet to start playback, and * Called repeatedly by the player when it's loading the source, has yet to start playback, and

View File

@ -1,22 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2;
/**
* Thrown when the player is stuck in a state where it has insufficient media to start playback, but
* its {@link LoadControl} is indicating that no further media should be loaded.
*/
public final class StuckBufferingException extends IllegalStateException {}