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

View File

@ -117,8 +117,7 @@ import java.util.Collections;
private boolean released;
private boolean playWhenReady;
private boolean rebuffering;
private boolean renderersReadyOrEnded;
private @Player.RepeatMode int repeatMode;
@Player.RepeatMode private int repeatMode;
private boolean shuffleModeEnabled;
private int pendingPrepareCount;
@ -554,8 +553,6 @@ import java.util.Collections;
}
renderersReadyOrEnded = renderersReadyOrEnded && rendererReadyOrEnded;
}
this.renderersReadyOrEnded = renderersReadyOrEnded;
if (!renderersReadyOrEnded) {
maybeThrowPeriodPrepareError();
}
@ -567,25 +564,14 @@ import java.util.Collections;
&& playingPeriodHolder.info.isFinal) {
setState(Player.STATE_ENDED);
stopRenderers();
} else if (playbackInfo.playbackState == Player.STATE_BUFFERING) {
boolean shouldStartPlayback = isReady();
if (shouldStartPlayback && playbackInfo.isLoading && enabledRenderers.length != 0) {
MediaPeriodHolder loadingHolder = queue.getLoadingPeriod();
long bufferedPositionUs = loadingHolder.getBufferedPositionUs(!loadingHolder.info.isFinal);
shouldStartPlayback =
bufferedPositionUs == C.TIME_END_OF_SOURCE
|| loadControl.shouldStartPlayback(
bufferedPositionUs - loadingHolder.toPeriodTime(rendererPositionUs),
mediaClock.getPlaybackParameters().speed,
rebuffering);
} else if (playbackInfo.playbackState == Player.STATE_BUFFERING
&& shouldTransitionToReadyState(renderersReadyOrEnded)) {
setState(Player.STATE_READY);
if (playWhenReady) {
startRenderers();
}
if (shouldStartPlayback) {
setState(Player.STATE_READY);
if (playWhenReady) {
startRenderers();
}
}
} else if (playbackInfo.playbackState == Player.STATE_READY && !isReady()) {
} else if (playbackInfo.playbackState == Player.STATE_READY
&& !(enabledRenderers.length == 0 ? isTimelineReady() : renderersReadyOrEnded)) {
rebuffering = playWhenReady;
setState(Player.STATE_BUFFERING);
stopRenderers();
@ -694,7 +680,6 @@ import java.util.Collections;
throws ExoPlaybackException {
stopRenderers();
rebuffering = false;
renderersReadyOrEnded = false;
setState(Player.STATE_BUFFERING);
// Clear the timeline, but keep the requested period if it is already prepared.
@ -736,8 +721,8 @@ import java.util.Collections;
return periodPositionUs;
}
private boolean shouldKeepPeriodHolder(MediaPeriodId seekPeriodId, long positionUs,
MediaPeriodHolder holder) {
private boolean shouldKeepPeriodHolder(
MediaPeriodId seekPeriodId, long positionUs, MediaPeriodHolder holder) {
if (seekPeriodId.equals(holder.info.id) && holder.prepared) {
playbackInfo.timeline.getPeriod(holder.info.id.periodIndex, period);
int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(positionUs);
@ -802,7 +787,6 @@ import java.util.Collections;
boolean releaseMediaSource, boolean resetPosition, boolean resetState) {
handler.removeMessages(MSG_DO_SOME_WORK);
rebuffering = false;
renderersReadyOrEnded = false;
mediaClock.stop();
rendererPositionUs = RENDERER_TIMESTAMP_OFFSET_US;
for (Renderer renderer : enabledRenderers) {
@ -1115,11 +1099,30 @@ import java.util.Collections;
}
}
private boolean isReady() {
if (enabledRenderers.length != 0) {
return renderersReadyOrEnded;
private boolean shouldTransitionToReadyState(boolean renderersReadyOrEnded) {
if (enabledRenderers.length == 0) {
// 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();
long playingPeriodDurationUs = playingPeriodHolder.info.durationUs;
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}
* if no such period was found.
*/
private int resolveSubsequentPeriod(int oldPeriodIndex, Timeline oldTimeline,
Timeline newTimeline) {
private int resolveSubsequentPeriod(
int oldPeriodIndex, Timeline oldTimeline, Timeline newTimeline) {
int newPeriodIndex = C.INDEX_UNSET;
int maxIterations = oldTimeline.getPeriodCount();
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
* current timeline.
*/
private Pair<Integer, Long> getPeriodPosition(Timeline timeline, int windowIndex,
long windowPositionUs) {
private Pair<Integer, Long> getPeriodPosition(
Timeline timeline, int windowIndex, long windowPositionUs) {
return timeline.getPeriodPosition(window, period, windowIndex, windowPositionUs);
}
@ -1576,15 +1579,11 @@ import java.util.Collections;
setIsLoading(false);
return;
}
boolean canStartPlayback = playbackInfo.playbackState == Player.STATE_READY || isReady();
long bufferedDurationUs =
nextLoadPositionUs - loadingPeriodHolder.toPeriodTime(rendererPositionUs);
boolean continueLoading =
loadControl.shouldContinueLoading(
canStartPlayback, bufferedDurationUs, mediaClock.getPlaybackParameters().speed);
if (!canStartPlayback && !continueLoading) {
throw new StuckBufferingException();
}
bufferedDurationUs, mediaClock.getPlaybackParameters().speed);
setIsLoading(continueLoading);
if (continueLoading) {
loadingPeriodHolder.continueLoading(rendererPositionUs);
@ -1632,8 +1631,9 @@ import java.util.Collections;
}
}
private void enableRenderer(int rendererIndex, boolean wasRendererEnabled,
int enabledRendererIndex) throws ExoPlaybackException {
private void enableRenderer(
int rendererIndex, boolean wasRendererEnabled, int enabledRendererIndex)
throws ExoPlaybackException {
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
Renderer renderer = renderers[rendererIndex];
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.
*
* @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 playbackSpeed The current playback speed.
* @return Whether the loading should continue.
*/
boolean shouldContinueLoading(
boolean canStartPlayback, long bufferedDurationUs, float playbackSpeed);
boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed);
/**
* 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 {}