mirror of
https://github.com/samsonjs/media.git
synced 2026-03-27 09:45:47 +00:00
Tell LoadControl whether playback can start
- This gives LoadControl enough information in shouldContinueLoading to know whether returning false will result in a terminal non-playback state. - DefaultLoadControl will always return true when returning false will result in a terminal non-playback state, unless the target buffer size is exceeded. This can help to avoid getting stuck in the case that a MediaPeriod is providing samples from an unexpected starting time. - Make the terminal state actually terminal. Previously the player would end up in an indefinite buffering state. We now fail with an error. - Also remove the opportunity for LoadControl implementations to livelock playback. No sane LoadControl should simultaneously tell the player that it doesn't want to load anything and that it doesn't want to start playback. So this change removes the opportunity and starts playback in EPII instead in this case. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=183114797
This commit is contained in:
parent
35dad90bea
commit
23ff4efda1
5 changed files with 158 additions and 104 deletions
|
|
@ -214,22 +214,8 @@ public class DefaultLoadControl implements LoadControl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldStartPlayback(long bufferedDurationUs, float playbackSpeed,
|
||||
boolean rebuffering) {
|
||||
if (bufferedDurationUs >= minBufferUs) {
|
||||
// It's possible that we're not loading, so allow playback to start unconditionally.
|
||||
return true;
|
||||
}
|
||||
bufferedDurationUs = Util.getPlayoutDurationForMediaDuration(bufferedDurationUs, playbackSpeed);
|
||||
long minBufferDurationUs = rebuffering ? bufferForPlaybackAfterRebufferUs : bufferForPlaybackUs;
|
||||
return minBufferDurationUs <= 0
|
||||
|| bufferedDurationUs >= minBufferDurationUs
|
||||
|| (!prioritizeTimeOverSizeThresholds
|
||||
&& allocator.getTotalBytesAllocated() >= targetBufferSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) {
|
||||
public boolean shouldContinueLoading(
|
||||
boolean canStartPlayback, long bufferedDurationUs, float playbackSpeed) {
|
||||
boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize;
|
||||
boolean wasBuffering = isBuffering;
|
||||
if (prioritizeTimeOverSizeThresholds) {
|
||||
|
|
@ -244,6 +230,9 @@ 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);
|
||||
|
|
@ -254,6 +243,17 @@ public class DefaultLoadControl implements LoadControl {
|
|||
return isBuffering;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldStartPlayback(
|
||||
long bufferedDurationUs, float playbackSpeed, boolean rebuffering) {
|
||||
bufferedDurationUs = Util.getPlayoutDurationForMediaDuration(bufferedDurationUs, playbackSpeed);
|
||||
long minBufferDurationUs = rebuffering ? bufferForPlaybackAfterRebufferUs : bufferForPlaybackUs;
|
||||
return minBufferDurationUs <= 0
|
||||
|| bufferedDurationUs >= minBufferDurationUs
|
||||
|| (!prioritizeTimeOverSizeThresholds
|
||||
&& allocator.getTotalBytesAllocated() >= targetBufferSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate target buffer size in bytes based on the selected tracks. The player will try not to
|
||||
* exceed this target buffer. Only used when {@code targetBufferBytes} is {@link C#LENGTH_UNSET}.
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ import java.util.Collections;
|
|||
private boolean released;
|
||||
private boolean playWhenReady;
|
||||
private boolean rebuffering;
|
||||
private boolean renderersReadyOrEnded;
|
||||
private @Player.RepeatMode int repeatMode;
|
||||
private boolean shuffleModeEnabled;
|
||||
|
||||
|
|
@ -520,10 +521,10 @@ import java.util.Collections;
|
|||
}
|
||||
|
||||
// Update the buffered position.
|
||||
long bufferedPositionUs = enabledRenderers.length == 0 ? C.TIME_END_OF_SOURCE
|
||||
: playingPeriodHolder.mediaPeriod.getBufferedPositionUs();
|
||||
playbackInfo.bufferedPositionUs = bufferedPositionUs == C.TIME_END_OF_SOURCE
|
||||
? playingPeriodHolder.info.durationUs : bufferedPositionUs;
|
||||
playbackInfo.bufferedPositionUs =
|
||||
enabledRenderers.length == 0
|
||||
? playingPeriodHolder.info.durationUs
|
||||
: playingPeriodHolder.getBufferedPositionUs(/* convertEosToDuration= */ true);
|
||||
}
|
||||
|
||||
private void doSomeWork() throws ExoPlaybackException, IOException {
|
||||
|
|
@ -545,15 +546,14 @@ import java.util.Collections;
|
|||
playingPeriodHolder.mediaPeriod.discardBuffer(playbackInfo.positionUs - backBufferDurationUs,
|
||||
retainBackBufferFromKeyframe);
|
||||
|
||||
boolean allRenderersEnded = true;
|
||||
boolean allRenderersReadyOrEnded = true;
|
||||
|
||||
boolean renderersEnded = true;
|
||||
boolean renderersReadyOrEnded = true;
|
||||
for (Renderer renderer : enabledRenderers) {
|
||||
// TODO: Each renderer should return the maximum delay before which it wishes to be called
|
||||
// again. The minimum of these values should then be used as the delay before the next
|
||||
// invocation of this method.
|
||||
renderer.render(rendererPositionUs, rendererPositionElapsedRealtimeUs);
|
||||
allRenderersEnded = allRenderersEnded && renderer.isEnded();
|
||||
renderersEnded = renderersEnded && renderer.isEnded();
|
||||
// Determine whether the renderer is ready (or ended). We override to assume the renderer is
|
||||
// ready if it needs the next sample stream. This is necessary to avoid getting stuck if
|
||||
// tracks in the current period have uneven durations. See:
|
||||
|
|
@ -563,43 +563,43 @@ import java.util.Collections;
|
|||
if (!rendererReadyOrEnded) {
|
||||
renderer.maybeThrowStreamError();
|
||||
}
|
||||
allRenderersReadyOrEnded = allRenderersReadyOrEnded && rendererReadyOrEnded;
|
||||
renderersReadyOrEnded = renderersReadyOrEnded && rendererReadyOrEnded;
|
||||
}
|
||||
|
||||
if (!allRenderersReadyOrEnded) {
|
||||
this.renderersReadyOrEnded = renderersReadyOrEnded;
|
||||
if (!renderersReadyOrEnded) {
|
||||
maybeThrowPeriodPrepareError();
|
||||
}
|
||||
|
||||
long playingPeriodDurationUs = playingPeriodHolder.info.durationUs;
|
||||
if (allRenderersEnded
|
||||
if (renderersEnded
|
||||
&& (playingPeriodDurationUs == C.TIME_UNSET
|
||||
|| playingPeriodDurationUs <= playbackInfo.positionUs)
|
||||
|| playingPeriodDurationUs <= playbackInfo.positionUs)
|
||||
&& playingPeriodHolder.info.isFinal) {
|
||||
setState(Player.STATE_ENDED);
|
||||
stopRenderers();
|
||||
} else if (playbackInfo.playbackState == Player.STATE_BUFFERING) {
|
||||
float playbackSpeed = mediaClock.getPlaybackParameters().speed;
|
||||
boolean isNewlyReady =
|
||||
enabledRenderers.length > 0
|
||||
? (allRenderersReadyOrEnded
|
||||
&& queue
|
||||
.getLoadingPeriod()
|
||||
.haveSufficientBuffer(rendererPositionUs, playbackSpeed, rebuffering))
|
||||
: isTimelineReady(playingPeriodDurationUs);
|
||||
if (isNewlyReady) {
|
||||
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);
|
||||
}
|
||||
if (shouldStartPlayback) {
|
||||
setState(Player.STATE_READY);
|
||||
if (playWhenReady) {
|
||||
startRenderers();
|
||||
}
|
||||
}
|
||||
} else if (playbackInfo.playbackState == Player.STATE_READY) {
|
||||
boolean isStillReady = enabledRenderers.length > 0 ? allRenderersReadyOrEnded
|
||||
: isTimelineReady(playingPeriodDurationUs);
|
||||
if (!isStillReady) {
|
||||
rebuffering = playWhenReady;
|
||||
setState(Player.STATE_BUFFERING);
|
||||
stopRenderers();
|
||||
}
|
||||
} else if (playbackInfo.playbackState == Player.STATE_READY && !isReady()) {
|
||||
rebuffering = playWhenReady;
|
||||
setState(Player.STATE_BUFFERING);
|
||||
stopRenderers();
|
||||
}
|
||||
|
||||
if (playbackInfo.playbackState == Player.STATE_BUFFERING) {
|
||||
|
|
@ -706,6 +706,7 @@ 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.
|
||||
|
|
@ -813,6 +814,7 @@ 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) {
|
||||
|
|
@ -1053,8 +1055,10 @@ import java.util.Collections;
|
|||
boolean recreateStreams = queue.removeAfter(playingPeriodHolder);
|
||||
|
||||
boolean[] streamResetFlags = new boolean[renderers.length];
|
||||
long periodPositionUs = playingPeriodHolder.updatePeriodTrackSelection(
|
||||
playbackInfo.positionUs, recreateStreams, streamResetFlags);
|
||||
long periodPositionUs =
|
||||
playingPeriodHolder.applyTrackSelection(
|
||||
playbackInfo.positionUs, recreateStreams, streamResetFlags);
|
||||
updateLoadControlTrackSelection(playingPeriodHolder);
|
||||
if (playbackInfo.playbackState != Player.STATE_ENDED
|
||||
&& periodPositionUs != playbackInfo.positionUs) {
|
||||
playbackInfo = playbackInfo.fromNewPosition(playbackInfo.periodId, periodPositionUs,
|
||||
|
|
@ -1092,7 +1096,8 @@ import java.util.Collections;
|
|||
long loadingPeriodPositionUs =
|
||||
Math.max(
|
||||
periodHolder.info.startPositionUs, periodHolder.toPeriodTime(rendererPositionUs));
|
||||
periodHolder.updatePeriodTrackSelection(loadingPeriodPositionUs, false);
|
||||
periodHolder.applyTrackSelection(loadingPeriodPositionUs, false);
|
||||
updateLoadControlTrackSelection(periodHolder);
|
||||
}
|
||||
}
|
||||
if (playbackInfo.playbackState != Player.STATE_ENDED) {
|
||||
|
|
@ -1102,6 +1107,12 @@ import java.util.Collections;
|
|||
}
|
||||
}
|
||||
|
||||
private void updateLoadControlTrackSelection(MediaPeriodHolder periodHolder) {
|
||||
TrackSelectorResult trackSelectorResult = periodHolder.trackSelectorResult;
|
||||
loadControl.onTracksSelected(
|
||||
renderers, trackSelectorResult.groups, trackSelectorResult.selections);
|
||||
}
|
||||
|
||||
private void updateTrackSelectionPlaybackSpeed(float playbackSpeed) {
|
||||
MediaPeriodHolder periodHolder = queue.getFrontPeriod();
|
||||
while (periodHolder != null) {
|
||||
|
|
@ -1117,12 +1128,17 @@ import java.util.Collections;
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isTimelineReady(long playingPeriodDurationUs) {
|
||||
private boolean isReady() {
|
||||
if (enabledRenderers.length != 0) {
|
||||
return renderersReadyOrEnded;
|
||||
}
|
||||
// Determine whether we're ready based on the timeline.
|
||||
MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
|
||||
long playingPeriodDurationUs = playingPeriodHolder.info.durationUs;
|
||||
return playingPeriodDurationUs == C.TIME_UNSET
|
||||
|| playbackInfo.positionUs < playingPeriodDurationUs
|
||||
|| (playingPeriodHolder.next != null
|
||||
&& (playingPeriodHolder.next.prepared || playingPeriodHolder.next.info.id.isAd()));
|
||||
&& (playingPeriodHolder.next.prepared || playingPeriodHolder.next.info.id.isAd()));
|
||||
}
|
||||
|
||||
private void maybeThrowPeriodPrepareError() throws IOException {
|
||||
|
|
@ -1551,11 +1567,10 @@ import java.util.Collections;
|
|||
Object uid = playbackInfo.timeline.getPeriod(info.id.periodIndex, period, true).uid;
|
||||
MediaPeriodHolder newPeriodHolder =
|
||||
new MediaPeriodHolder(
|
||||
renderers,
|
||||
rendererCapabilities,
|
||||
rendererPositionOffsetUs,
|
||||
trackSelector,
|
||||
loadControl,
|
||||
loadControl.getAllocator(),
|
||||
mediaSource,
|
||||
uid,
|
||||
info);
|
||||
|
|
@ -1571,6 +1586,7 @@ import java.util.Collections;
|
|||
return;
|
||||
}
|
||||
loadingPeriodHolder.handlePrepared(mediaClock.getPlaybackParameters().speed);
|
||||
updateLoadControlTrackSelection(loadingPeriodHolder);
|
||||
if (!queue.hasPlayingPeriod()) {
|
||||
// This is the first prepared period, so start playing it.
|
||||
MediaPeriodHolder playingPeriodHolder = queue.advancePlayingPeriod();
|
||||
|
|
@ -1592,8 +1608,20 @@ import java.util.Collections;
|
|||
|
||||
private void maybeContinueLoading() {
|
||||
MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod();
|
||||
boolean continueLoading = loadingPeriodHolder.shouldContinueLoading(
|
||||
rendererPositionUs, mediaClock.getPlaybackParameters().speed);
|
||||
long nextLoadPositionUs = loadingPeriodHolder.getNextLoadPositionUs();
|
||||
if (nextLoadPositionUs == C.TIME_END_OF_SOURCE) {
|
||||
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();
|
||||
}
|
||||
setIsLoading(continueLoading);
|
||||
if (continueLoading) {
|
||||
loadingPeriodHolder.continueLoading(rendererPositionUs);
|
||||
|
|
|
|||
|
|
@ -88,8 +88,25 @@ public interface LoadControl {
|
|||
boolean retainBackBufferFromKeyframe();
|
||||
|
||||
/**
|
||||
* Called by the player to determine whether sufficient media is buffered for playback to be
|
||||
* started or resumed.
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Called repeatedly by the player when it's loading the source, has yet to start playback, and
|
||||
* has the minimum amount of data necessary for playback to be started. The value returned
|
||||
* determines whether playback is actually started. The load control may opt to return {@code
|
||||
* false} until some condition has been met (e.g. a certain amount of media is buffered).
|
||||
*
|
||||
* @param bufferedDurationUs The duration of media that's currently buffered.
|
||||
* @param playbackSpeed The current playback speed.
|
||||
|
|
@ -99,14 +116,4 @@ public interface LoadControl {
|
|||
* @return Whether playback should be allowed to start or resume.
|
||||
*/
|
||||
boolean shouldStartPlayback(long bufferedDurationUs, float playbackSpeed, boolean rebuffering);
|
||||
|
||||
/**
|
||||
* Called by the player to determine whether it should continue to load the source.
|
||||
*
|
||||
* @param bufferedDurationUs The duration of media that's currently buffered.
|
||||
* @param playbackSpeed The current playback speed.
|
||||
* @return Whether the loading should continue.
|
||||
*/
|
||||
boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
|
|||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
|
||||
/** Holds a {@link MediaPeriod} with information required to play it as part of a timeline. */
|
||||
|
|
@ -39,40 +40,35 @@ import com.google.android.exoplayer2.util.Assertions;
|
|||
public final boolean[] mayRetainStreamFlags;
|
||||
|
||||
public long rendererPositionOffsetUs;
|
||||
public MediaPeriodInfo info;
|
||||
public boolean prepared;
|
||||
public boolean hasEnabledTracks;
|
||||
public MediaPeriodInfo info;
|
||||
public MediaPeriodHolder next;
|
||||
public TrackSelectorResult trackSelectorResult;
|
||||
|
||||
private final Renderer[] renderers;
|
||||
private final RendererCapabilities[] rendererCapabilities;
|
||||
private final TrackSelector trackSelector;
|
||||
private final LoadControl loadControl;
|
||||
private final MediaSource mediaSource;
|
||||
|
||||
private TrackSelectorResult periodTrackSelectorResult;
|
||||
|
||||
public MediaPeriodHolder(
|
||||
Renderer[] renderers,
|
||||
RendererCapabilities[] rendererCapabilities,
|
||||
long rendererPositionOffsetUs,
|
||||
TrackSelector trackSelector,
|
||||
LoadControl loadControl,
|
||||
Allocator allocator,
|
||||
MediaSource mediaSource,
|
||||
Object periodUid,
|
||||
MediaPeriodInfo info) {
|
||||
this.renderers = renderers;
|
||||
this.rendererCapabilities = rendererCapabilities;
|
||||
this.rendererPositionOffsetUs = rendererPositionOffsetUs - info.startPositionUs;
|
||||
this.trackSelector = trackSelector;
|
||||
this.loadControl = loadControl;
|
||||
this.mediaSource = mediaSource;
|
||||
this.uid = Assertions.checkNotNull(periodUid);
|
||||
this.info = info;
|
||||
sampleStreams = new SampleStream[renderers.length];
|
||||
mayRetainStreamFlags = new boolean[renderers.length];
|
||||
MediaPeriod mediaPeriod = mediaSource.createPeriod(info.id, loadControl.getAllocator());
|
||||
sampleStreams = new SampleStream[rendererCapabilities.length];
|
||||
mayRetainStreamFlags = new boolean[rendererCapabilities.length];
|
||||
MediaPeriod mediaPeriod = mediaSource.createPeriod(info.id, allocator);
|
||||
if (info.endPositionUs != C.TIME_END_OF_SOURCE) {
|
||||
ClippingMediaPeriod clippingMediaPeriod = new ClippingMediaPeriod(mediaPeriod, true);
|
||||
clippingMediaPeriod.setClipping(0, info.endPositionUs);
|
||||
|
|
@ -98,24 +94,37 @@ import com.google.android.exoplayer2.util.Assertions;
|
|||
&& (!hasEnabledTracks || mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE);
|
||||
}
|
||||
|
||||
public boolean haveSufficientBuffer(
|
||||
long rendererPositionUs, float playbackSpeed, boolean rebuffering) {
|
||||
long bufferedPositionUs =
|
||||
!prepared ? info.startPositionUs : mediaPeriod.getBufferedPositionUs();
|
||||
if (bufferedPositionUs == C.TIME_END_OF_SOURCE) {
|
||||
if (info.isFinal) {
|
||||
return true;
|
||||
}
|
||||
bufferedPositionUs = info.durationUs;
|
||||
public long getDurationUs() {
|
||||
return info.durationUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the buffered position in microseconds. If the period is buffered to the end then
|
||||
* {@link C#TIME_END_OF_SOURCE} is returned unless {@code convertEosToDuration} is true, in which
|
||||
* case the period duration is returned.
|
||||
*
|
||||
* @param convertEosToDuration Whether to return the period duration rather than
|
||||
* {@link C#TIME_END_OF_SOURCE} if the period is fully buffered.
|
||||
* @return The buffered position in microseconds.
|
||||
*/
|
||||
public long getBufferedPositionUs(boolean convertEosToDuration) {
|
||||
if (!prepared) {
|
||||
return info.startPositionUs;
|
||||
}
|
||||
return loadControl.shouldStartPlayback(
|
||||
bufferedPositionUs - toPeriodTime(rendererPositionUs), playbackSpeed, rebuffering);
|
||||
long bufferedPositionUs = mediaPeriod.getBufferedPositionUs();
|
||||
return bufferedPositionUs == C.TIME_END_OF_SOURCE && convertEosToDuration
|
||||
? info.durationUs
|
||||
: bufferedPositionUs;
|
||||
}
|
||||
|
||||
public long getNextLoadPositionUs() {
|
||||
return !prepared ? 0 : mediaPeriod.getNextLoadPositionUs();
|
||||
}
|
||||
|
||||
public void handlePrepared(float playbackSpeed) throws ExoPlaybackException {
|
||||
prepared = true;
|
||||
selectTracks(playbackSpeed);
|
||||
long newStartPositionUs = updatePeriodTrackSelection(info.startPositionUs, false);
|
||||
long newStartPositionUs = applyTrackSelection(info.startPositionUs, false);
|
||||
rendererPositionOffsetUs += info.startPositionUs - newStartPositionUs;
|
||||
info = info.copyWithStartPositionUs(newStartPositionUs);
|
||||
}
|
||||
|
|
@ -126,16 +135,6 @@ import com.google.android.exoplayer2.util.Assertions;
|
|||
}
|
||||
}
|
||||
|
||||
public boolean shouldContinueLoading(long rendererPositionUs, float playbackSpeed) {
|
||||
long nextLoadPositionUs = !prepared ? 0 : mediaPeriod.getNextLoadPositionUs();
|
||||
if (nextLoadPositionUs == C.TIME_END_OF_SOURCE) {
|
||||
return false;
|
||||
} else {
|
||||
long bufferedDurationUs = nextLoadPositionUs - toPeriodTime(rendererPositionUs);
|
||||
return loadControl.shouldContinueLoading(bufferedDurationUs, playbackSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
public void continueLoading(long rendererPositionUs) {
|
||||
long loadingPeriodPositionUs = toPeriodTime(rendererPositionUs);
|
||||
mediaPeriod.continueLoading(loadingPeriodPositionUs);
|
||||
|
|
@ -156,12 +155,12 @@ import com.google.android.exoplayer2.util.Assertions;
|
|||
return true;
|
||||
}
|
||||
|
||||
public long updatePeriodTrackSelection(long positionUs, boolean forceRecreateStreams) {
|
||||
return updatePeriodTrackSelection(
|
||||
positionUs, forceRecreateStreams, new boolean[renderers.length]);
|
||||
public long applyTrackSelection(long positionUs, boolean forceRecreateStreams) {
|
||||
return applyTrackSelection(
|
||||
positionUs, forceRecreateStreams, new boolean[rendererCapabilities.length]);
|
||||
}
|
||||
|
||||
public long updatePeriodTrackSelection(
|
||||
public long applyTrackSelection(
|
||||
long positionUs, boolean forceRecreateStreams, boolean[] streamResetFlags) {
|
||||
TrackSelectionArray trackSelections = trackSelectorResult.selections;
|
||||
for (int i = 0; i < trackSelections.length; i++) {
|
||||
|
|
@ -196,8 +195,6 @@ import com.google.android.exoplayer2.util.Assertions;
|
|||
Assertions.checkState(trackSelections.get(i) == null);
|
||||
}
|
||||
}
|
||||
// The track selection has changed.
|
||||
loadControl.onTracksSelected(renderers, trackSelectorResult.groups, trackSelections);
|
||||
return positionUs;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 {}
|
||||
Loading…
Reference in a new issue