diff --git a/library/core/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java b/library/core/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java index 4cc2d90c90..e8ea2f1621 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java @@ -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); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 550c3c4a43..f955b000db 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -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 getPeriodPosition(Timeline timeline, int windowIndex, - long windowPositionUs) { + private Pair 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; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/LoadControl.java b/library/core/src/main/java/com/google/android/exoplayer2/LoadControl.java index 97f6038965..80be0b9e71 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/LoadControl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/LoadControl.java @@ -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 diff --git a/library/core/src/main/java/com/google/android/exoplayer2/StuckBufferingException.java b/library/core/src/main/java/com/google/android/exoplayer2/StuckBufferingException.java deleted file mode 100644 index b20cf9a48d..0000000000 --- a/library/core/src/main/java/com/google/android/exoplayer2/StuckBufferingException.java +++ /dev/null @@ -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 {}