Commit 76f426e9 by olly Committed by Oliver Woodman

Introduce a global buffering policy.

This is required for buffering to work properly across
playlist transitions. It's also much simpler, since
specifying the buffering policy becomes independent of
the type of media being played (i.e. the source).
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=126051755
parent fdd90256
Showing with 293 additions and 211 deletions
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer.demo; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer.demo;
import com.google.android.exoplayer.AspectRatioFrameLayout; import com.google.android.exoplayer.AspectRatioFrameLayout;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.DefaultBufferingPolicy;
import com.google.android.exoplayer.DefaultTrackSelectionPolicy; import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
import com.google.android.exoplayer.DefaultTrackSelector; import com.google.android.exoplayer.DefaultTrackSelector;
import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo; import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
...@@ -266,8 +267,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -266,8 +267,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
trackSelector.addListener(this); trackSelector.addListener(this);
trackSelector.addListener(eventLogger); trackSelector.addListener(eventLogger);
trackSelectionHelper = new TrackSelectionHelper(trackSelector); trackSelectionHelper = new TrackSelectionHelper(trackSelector);
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, drmSessionManager, player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, new DefaultBufferingPolicy(),
preferExtensionDecoders); drmSessionManager, preferExtensionDecoders);
player.addListener(this); player.addListener(this);
player.addListener(eventLogger); player.addListener(eventLogger);
player.setDebugListener(eventLogger); player.setDebugListener(eventLogger);
......
...@@ -18,49 +18,92 @@ package com.google.android.exoplayer; ...@@ -18,49 +18,92 @@ package com.google.android.exoplayer;
import com.google.android.exoplayer.upstream.Allocator; import com.google.android.exoplayer.upstream.Allocator;
/** /**
* Coordinates multiple loaders of time series data. * A media buffering policy.
*/ */
public interface LoadControl { public interface BufferingPolicy {
/** /**
* Registers a loader. * Invoked by the player to update the playback position.
* *
* @param loader The loader being registered. * @param playbackPositionUs The current playback position in microseconds.
* @param bufferSizeContribution For instances whose {@link Allocator} maintains a pool of memory
* for the purpose of satisfying allocation requests, this is a hint indicating the loader's
* desired contribution to the size of the pool, in bytes.
*/ */
void register(Object loader, int bufferSizeContribution); void setPlaybackPosition(long playbackPositionUs);
/** /**
* Unregisters a loader. * Invoked by the player to determine whether sufficient media is buffered for playback to be
* started or resumed.
* *
* @param loader The loader being unregistered. * @param bufferedPositionUs The position up to which media is buffered.
* @param rebuffering Whether the player is re-buffering.
* @return True if playback should be allowed to start or resume. False otherwise.
*/ */
void unregister(Object loader); boolean haveSufficientBuffer(long bufferedPositionUs, boolean rebuffering);
/** /**
* Gets the {@link Allocator} that loaders should use to obtain memory allocations into which * Invoked by the player when a track selection occurs.
* data can be loaded.
* *
* @return The {@link Allocator} to use. * @param renderers The renderers.
* @param trackGroups The available {@link TrackGroup}s.
* @param trackSelections The {@link TrackSelection}s that were made.
*/ */
Allocator getAllocator(); void onTrackSelections(TrackRenderer[] renderers, TrackGroupArray trackGroups,
TrackSelectionArray trackSelections);
/** /**
* Invoked by a loader to update the control with its current state. * Invoked by the player when a reset occurs, meaning all renderers have been disabled.
* <p> */
* This method must be called by a registered loader whenever its state changes. This is true void reset();
* even if the registered loader does not itself wish to start its next load (since the state of
* the loader will still affect whether other registered loaders are allowed to proceed). /**
* Returns a {@link LoadControl} that a {@link SampleSource} can use to control loads according to
* this policy.
* *
* @param loader The loader invoking the update. * @return The {@link LoadControl}.
* @param playbackPositionUs The loader's playback position. */
* @param nextLoadPositionUs The loader's next load position. {@link C#UNSET_TIME_US} if finished, LoadControl getLoadControl();
* failed, or if the next load position is not yet known.
* @param loading Whether the loader is currently loading data. /**
* @return True if the loader is allowed to start its next load. False otherwise. * Coordinates multiple loaders of time series data.
*/ */
boolean update(Object loader, long playbackPositionUs, long nextLoadPositionUs, boolean loading); interface LoadControl {
/**
* Registers a loader.
*
* @param loader The loader being registered.
*/
void register(Object loader);
/**
* Unregisters a loader.
*
* @param loader The loader being unregistered.
*/
void unregister(Object loader);
/**
* Gets the {@link Allocator} that loaders should use to obtain memory allocations into which
* data can be loaded.
*
* @return The {@link Allocator} to use.
*/
Allocator getAllocator();
/**
* Invoked by a loader to update the control with its current state.
* <p>
* This method must be called by a registered loader whenever its state changes. This is true
* even if the registered loader does not itself wish to start its next load (since the state of
* the loader will still affect whether other registered loaders are allowed to proceed).
*
* @param loader The loader invoking the update.
* @param nextLoadPositionUs The loader's next load position, or {@link C#UNSET_TIME_US} if
* finished, failed, or if the next load position is not yet known.
* @param loading Whether the loader is currently loading data.
* @return True if the loader is allowed to start its next load. False otherwise.
*/
boolean update(Object loader, long nextLoadPositionUs, boolean loading);
}
} }
...@@ -26,19 +26,6 @@ import android.os.Looper; ...@@ -26,19 +26,6 @@ import android.os.Looper;
public final class ExoPlayerFactory { public final class ExoPlayerFactory {
/** /**
* The default minimum duration of data that must be buffered for playback to start or resume
* following a user action such as a seek.
*/
public static final int DEFAULT_MIN_BUFFER_MS = 2500;
/**
* The default minimum duration of data that must be buffered for playback to resume
* after a player-invoked rebuffer (i.e. a rebuffer that occurs due to buffer depletion, and
* not due to a user action such as starting playback or seeking).
*/
public static final int DEFAULT_MIN_REBUFFER_MS = 5000;
/**
* The default maximum duration for which a video renderer can attempt to seamlessly join an * The default maximum duration for which a video renderer can attempt to seamlessly join an
* ongoing playback. * ongoing playback.
*/ */
...@@ -55,7 +42,7 @@ public final class ExoPlayerFactory { ...@@ -55,7 +42,7 @@ public final class ExoPlayerFactory {
* @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance.
*/ */
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector) { public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector) {
return newSimpleInstance(context, trackSelector, null); return newSimpleInstance(context, trackSelector, new DefaultBufferingPolicy(), null);
} }
/** /**
...@@ -65,12 +52,13 @@ public final class ExoPlayerFactory { ...@@ -65,12 +52,13 @@ public final class ExoPlayerFactory {
* *
* @param context A {@link Context}. * @param context A {@link Context}.
* @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param bufferingPolicy The {@link BufferingPolicy} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance * @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks. * will not be used for DRM protected playbacks.
*/ */
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
DrmSessionManager drmSessionManager) { BufferingPolicy bufferingPolicy, DrmSessionManager drmSessionManager) {
return newSimpleInstance(context, trackSelector, drmSessionManager, false); return newSimpleInstance(context, trackSelector, bufferingPolicy, drmSessionManager, false);
} }
/** /**
...@@ -80,6 +68,7 @@ public final class ExoPlayerFactory { ...@@ -80,6 +68,7 @@ public final class ExoPlayerFactory {
* *
* @param context A {@link Context}. * @param context A {@link Context}.
* @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param bufferingPolicy The {@link BufferingPolicy} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance * @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks. * will not be used for DRM protected playbacks.
* @param preferExtensionDecoders True to prefer {@link TrackRenderer} instances defined in * @param preferExtensionDecoders True to prefer {@link TrackRenderer} instances defined in
...@@ -87,9 +76,10 @@ public final class ExoPlayerFactory { ...@@ -87,9 +76,10 @@ public final class ExoPlayerFactory {
* included in the application build for setting this flag to have any effect. * included in the application build for setting this flag to have any effect.
*/ */
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
DrmSessionManager drmSessionManager, boolean preferExtensionDecoders) { BufferingPolicy bufferingPolicy, DrmSessionManager drmSessionManager,
return newSimpleInstance(context, trackSelector, drmSessionManager, preferExtensionDecoders, boolean preferExtensionDecoders) {
DEFAULT_MIN_BUFFER_MS, DEFAULT_MIN_REBUFFER_MS, DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS); return newSimpleInstance(context, trackSelector, bufferingPolicy, drmSessionManager,
preferExtensionDecoders, DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS);
} }
/** /**
...@@ -99,24 +89,20 @@ public final class ExoPlayerFactory { ...@@ -99,24 +89,20 @@ public final class ExoPlayerFactory {
* *
* @param context A {@link Context}. * @param context A {@link Context}.
* @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param bufferingPolicy The {@link BufferingPolicy} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance * @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks. * will not be used for DRM protected playbacks.
* @param preferExtensionDecoders True to prefer {@link TrackRenderer} instances defined in * @param preferExtensionDecoders True to prefer {@link TrackRenderer} instances defined in
* available extensions over those defined in the core library. Note that extensions must be * available extensions over those defined in the core library. Note that extensions must be
* included in the application build for setting this flag to have any effect. * included in the application build for setting this flag to have any effect.
* @param minBufferMs A minimum duration of data that must be buffered for playback to start
* or resume following a user action such as a seek.
* @param minRebufferMs A minimum duration of data that must be buffered for playback to resume
* after a player-invoked rebuffer (i.e. a rebuffer that occurs due to buffer depletion, and
* not due to a user action such as starting playback or seeking).
* @param allowedVideoJoiningTimeMs The maximum duration for which a video renderer can attempt to * @param allowedVideoJoiningTimeMs The maximum duration for which a video renderer can attempt to
* seamlessly join an ongoing playback. * seamlessly join an ongoing playback.
*/ */
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector, public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
DrmSessionManager drmSessionManager, boolean preferExtensionDecoders, int minBufferMs, BufferingPolicy bufferingPolicy, DrmSessionManager drmSessionManager,
int minRebufferMs, long allowedVideoJoiningTimeMs) { boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) {
return new SimpleExoPlayer(context, trackSelector, drmSessionManager, preferExtensionDecoders, return new SimpleExoPlayer(context, trackSelector, bufferingPolicy, drmSessionManager,
minBufferMs, minRebufferMs, allowedVideoJoiningTimeMs); preferExtensionDecoders, allowedVideoJoiningTimeMs);
} }
/** /**
...@@ -126,15 +112,9 @@ public final class ExoPlayerFactory { ...@@ -126,15 +112,9 @@ public final class ExoPlayerFactory {
* *
* @param renderers The {@link TrackRenderer}s that will be used by the instance. * @param renderers The {@link TrackRenderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param minBufferMs A minimum duration of data that must be buffered for playback to start
* or resume following a user action such as a seek.
* @param minRebufferMs A minimum duration of data that must be buffered for playback to resume
* after a player-invoked rebuffer (i.e. a rebuffer that occurs due to buffer depletion, and
* not due to a user action such as starting playback or seeking).
*/ */
public static ExoPlayer newInstance(TrackRenderer[] renderers, TrackSelector trackSelector, public static ExoPlayer newInstance(TrackRenderer[] renderers, TrackSelector trackSelector) {
int minBufferMs, int minRebufferMs) { return newInstance(renderers, trackSelector, new DefaultBufferingPolicy());
return new ExoPlayerImpl(renderers, trackSelector, minBufferMs, minRebufferMs);
} }
/** /**
...@@ -144,10 +124,11 @@ public final class ExoPlayerFactory { ...@@ -144,10 +124,11 @@ public final class ExoPlayerFactory {
* *
* @param renderers The {@link TrackRenderer}s that will be used by the instance. * @param renderers The {@link TrackRenderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param bufferingPolicy The {@link BufferingPolicy} that will be used by the instance.
*/ */
public static ExoPlayer newInstance(TrackRenderer[] renderers, TrackSelector trackSelector) { public static ExoPlayer newInstance(TrackRenderer[] renderers, TrackSelector trackSelector,
return new ExoPlayerImpl(renderers, trackSelector, DEFAULT_MIN_BUFFER_MS, BufferingPolicy bufferingPolicy) {
DEFAULT_MIN_REBUFFER_MS); return new ExoPlayerImpl(renderers, trackSelector, bufferingPolicy);
} }
} }
...@@ -55,15 +55,11 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -55,15 +55,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
* *
* @param renderers The {@link TrackRenderer}s that will be used by the instance. * @param renderers The {@link TrackRenderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance. * @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param minBufferMs A minimum duration of data that must be buffered for playback to start * @param bufferingPolicy The {@link BufferingPolicy} that will be used by the instance.
* or resume following a user action such as a seek.
* @param minRebufferMs A minimum duration of data that must be buffered for playback to resume
* after a player invoked rebuffer (i.e. a rebuffer that occurs due to buffer depletion, and
* not due to a user action such as starting playback or seeking).
*/ */
@SuppressLint("HandlerLeak") @SuppressLint("HandlerLeak")
public ExoPlayerImpl(TrackRenderer[] renderers, TrackSelector trackSelector, int minBufferMs, public ExoPlayerImpl(TrackRenderer[] renderers, TrackSelector trackSelector,
int minRebufferMs) { BufferingPolicy bufferingPolicy) {
Log.i(TAG, "Init " + ExoPlayerLibraryInfo.VERSION); Log.i(TAG, "Init " + ExoPlayerLibraryInfo.VERSION);
Assertions.checkNotNull(renderers); Assertions.checkNotNull(renderers);
Assertions.checkState(renderers.length > 0); Assertions.checkState(renderers.length > 0);
...@@ -76,7 +72,7 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -76,7 +72,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
ExoPlayerImpl.this.handleEvent(msg); ExoPlayerImpl.this.handleEvent(msg);
} }
}; };
internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, minBufferMs, minRebufferMs, internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, bufferingPolicy,
playWhenReady, eventHandler); playWhenReady, eventHandler);
playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0); playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0);
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import com.google.android.exoplayer.ExoPlayer.ExoPlayerMessage; import com.google.android.exoplayer.ExoPlayer.ExoPlayerMessage;
import com.google.android.exoplayer.TrackSelector.InvalidationListener; import com.google.android.exoplayer.TrackSelector.InvalidationListener;
import com.google.android.exoplayer.util.PriorityHandlerThread; import com.google.android.exoplayer.util.PriorityHandlerThread;
...@@ -90,9 +91,8 @@ import java.util.ArrayList; ...@@ -90,9 +91,8 @@ import java.util.ArrayList;
private static final int MAXIMUM_BUFFER_AHEAD_SOURCES = 100; private static final int MAXIMUM_BUFFER_AHEAD_SOURCES = 100;
private final TrackSelector trackSelector; private final TrackSelector trackSelector;
private final BufferingPolicy bufferingPolicy;
private final StandaloneMediaClock standaloneMediaClock; private final StandaloneMediaClock standaloneMediaClock;
private final long minBufferUs;
private final long minRebufferUs;
private final Handler handler; private final Handler handler;
private final HandlerThread internalPlaybackThread; private final HandlerThread internalPlaybackThread;
private final Handler eventHandler; private final Handler eventHandler;
...@@ -114,10 +114,9 @@ import java.util.ArrayList; ...@@ -114,10 +114,9 @@ import java.util.ArrayList;
private long internalPositionUs; private long internalPositionUs;
public ExoPlayerImplInternal(TrackRenderer[] renderers, TrackSelector trackSelector, public ExoPlayerImplInternal(TrackRenderer[] renderers, TrackSelector trackSelector,
int minBufferMs, int minRebufferMs, boolean playWhenReady, Handler eventHandler) { BufferingPolicy bufferingPolicy, boolean playWhenReady, Handler eventHandler) {
this.trackSelector = trackSelector; this.trackSelector = trackSelector;
this.minBufferUs = minBufferMs * 1000L; this.bufferingPolicy = bufferingPolicy;
this.minRebufferUs = minRebufferMs * 1000L;
this.playWhenReady = playWhenReady; this.playWhenReady = playWhenReady;
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.state = ExoPlayer.STATE_IDLE; this.state = ExoPlayer.STATE_IDLE;
...@@ -277,16 +276,6 @@ import java.util.ArrayList; ...@@ -277,16 +276,6 @@ import java.util.ArrayList;
return renderer.isReady() || renderer.isEnded(); return renderer.isReady() || renderer.isEnded();
} }
private boolean haveSufficientBuffer() {
// TODO[playlists]: Take into account the buffered position in the timeline.
long minBufferDurationUs = rebuffering ? minRebufferUs : minBufferUs;
return minBufferDurationUs <= 0
|| playbackInfo.bufferedPositionUs == C.END_OF_SOURCE_US
|| playbackInfo.bufferedPositionUs >= playbackInfo.positionUs + minBufferDurationUs
|| (playbackInfo.durationUs != C.UNSET_TIME_US
&& playbackInfo.bufferedPositionUs >= playbackInfo.durationUs);
}
private void setSourceProviderInternal(SampleSourceProvider sourceProvider) { private void setSourceProviderInternal(SampleSourceProvider sourceProvider) {
try { try {
resetInternal(); resetInternal();
...@@ -359,6 +348,7 @@ import java.util.ArrayList; ...@@ -359,6 +348,7 @@ import java.util.ArrayList;
} }
playbackInfo.positionUs = positionUs; playbackInfo.positionUs = positionUs;
elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000; elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
bufferingPolicy.setPlaybackPosition(positionUs);
// Update the buffered position. // Update the buffered position.
long bufferedPositionUs; long bufferedPositionUs;
...@@ -412,7 +402,7 @@ import java.util.ArrayList; ...@@ -412,7 +402,7 @@ import java.util.ArrayList;
stopRenderers(); stopRenderers();
} else if (state == ExoPlayer.STATE_BUFFERING) { } else if (state == ExoPlayer.STATE_BUFFERING) {
if ((enabledRenderers.length > 0 ? allRenderersReadyOrEnded : timeline.isReady()) if ((enabledRenderers.length > 0 ? allRenderersReadyOrEnded : timeline.isReady())
&& haveSufficientBuffer()) { && bufferingPolicy.haveSufficientBuffer(playbackInfo.bufferedPositionUs, rebuffering)) {
setState(ExoPlayer.STATE_READY); setState(ExoPlayer.STATE_READY);
if (playWhenReady) { if (playWhenReady) {
startRenderers(); startRenderers();
...@@ -520,6 +510,7 @@ import java.util.ArrayList; ...@@ -520,6 +510,7 @@ import java.util.ArrayList;
enabledRenderers = new TrackRenderer[0]; enabledRenderers = new TrackRenderer[0];
sampleSourceProvider = null; sampleSourceProvider = null;
timeline.reset(); timeline.reset();
bufferingPolicy.reset();
} }
private void sendMessagesInternal(ExoPlayerMessage[] messages) throws ExoPlaybackException { private void sendMessagesInternal(ExoPlayerMessage[] messages) throws ExoPlaybackException {
...@@ -628,10 +619,11 @@ import java.util.ArrayList; ...@@ -628,10 +619,11 @@ import java.util.ArrayList;
// Continue preparation. // Continue preparation.
// TODO[playlists]: Add support for setting the start position to play in a source. // TODO[playlists]: Add support for setting the start position to play in a source.
long startPositionUs = playingSource == null ? playbackInfo.positionUs : 0; long startPositionUs = playingSource == null ? playbackInfo.positionUs : 0;
if (bufferingSource.prepare(startPositionUs)) { if (bufferingSource.prepare(startPositionUs, bufferingPolicy.getLoadControl())) {
Pair<TrackSelectionArray, Object> result = trackSelector.selectTracks(renderers, Pair<TrackSelectionArray, Object> result = trackSelector.selectTracks(renderers,
bufferingSource.sampleSource.getTrackGroups()); bufferingSource.sampleSource.getTrackGroups());
bufferingSource.selectTracks(result.first, result.second, startPositionUs); bufferingSource.selectTracks(result.first, result.second, startPositionUs,
bufferingPolicy, renderers);
if (playingSource == null) { if (playingSource == null) {
// This is the first prepared source, so start playing it. // This is the first prepared source, so start playing it.
readingSource = bufferingSource; readingSource = bufferingSource;
...@@ -937,13 +929,8 @@ import java.util.ArrayList; ...@@ -937,13 +929,8 @@ import java.util.ArrayList;
|| sampleSource.getBufferedPositionUs() == C.END_OF_SOURCE_US); || sampleSource.getBufferedPositionUs() == C.END_OF_SOURCE_US);
} }
public void setNextSource(Source nextSource) { public boolean prepare(long startPositionUs, LoadControl loadControl) throws IOException {
this.nextSource = nextSource; if (sampleSource.prepare(startPositionUs, loadControl)) {
nextSource.offsetUs = offsetUs + sampleSource.getDurationUs();
}
public boolean prepare(long startPositionUs) throws IOException {
if (sampleSource.prepare(startPositionUs)) {
prepared = true; prepared = true;
return true; return true;
} else { } else {
...@@ -951,8 +938,14 @@ import java.util.ArrayList; ...@@ -951,8 +938,14 @@ import java.util.ArrayList;
} }
} }
public void setNextSource(Source nextSource) {
this.nextSource = nextSource;
nextSource.offsetUs = offsetUs + sampleSource.getDurationUs();
}
public void selectTracks(TrackSelectionArray newTrackSelections, Object trackSelectionData, public void selectTracks(TrackSelectionArray newTrackSelections, Object trackSelectionData,
long positionUs) throws ExoPlaybackException { long positionUs, BufferingPolicy bufferingPolicy, TrackRenderer[] renderers)
throws ExoPlaybackException {
this.trackSelectionData = trackSelectionData; this.trackSelectionData = trackSelectionData;
if (newTrackSelections.equals(trackSelections)) { if (newTrackSelections.equals(trackSelections)) {
return; return;
...@@ -974,6 +967,9 @@ import java.util.ArrayList; ...@@ -974,6 +967,9 @@ import java.util.ArrayList;
} }
TrackStream[] newStreams = sampleSource.selectTracks(oldStreams, newSelections, positionUs); TrackStream[] newStreams = sampleSource.selectTracks(oldStreams, newSelections, positionUs);
updateTrackStreams(newTrackSelections, newSelections, newStreams); updateTrackStreams(newTrackSelections, newSelections, newStreams);
bufferingPolicy.onTrackSelections(renderers, sampleSource.getTrackGroups(),
newTrackSelections);
} }
public void updateTrackStreams(TrackSelectionArray newTrackSelections, public void updateTrackStreams(TrackSelectionArray newTrackSelections,
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import android.util.Pair; import android.util.Pair;
...@@ -46,13 +47,13 @@ public final class MultiSampleSource implements SampleSource { ...@@ -46,13 +47,13 @@ public final class MultiSampleSource implements SampleSource {
} }
@Override @Override
public boolean prepare(long positionUs) throws IOException { public boolean prepare(long positionUs, LoadControl loadControl) throws IOException {
if (prepared) { if (prepared) {
return true; return true;
} }
boolean sourcesPrepared = true; boolean sourcesPrepared = true;
for (SampleSource source : sources) { for (SampleSource source : sources) {
sourcesPrepared &= source.prepare(positionUs); sourcesPrepared &= source.prepare(positionUs, loadControl);
} }
if (!sourcesPrepared) { if (!sourcesPrepared) {
return false; return false;
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
...@@ -31,10 +33,11 @@ public interface SampleSource { ...@@ -31,10 +33,11 @@ public interface SampleSource {
* tracks. * tracks.
* *
* @param positionUs The player's current playback position. * @param positionUs The player's current playback position.
* @param loadControl A {@link LoadControl} to determine when to load data.
* @return True if the source is prepared, false otherwise. * @return True if the source is prepared, false otherwise.
* @throws IOException If there's an error preparing the source. * @throws IOException If there's an error preparing the source.
*/ */
boolean prepare(long positionUs) throws IOException; boolean prepare(long positionUs, LoadControl loadControl) throws IOException;
/** /**
* Returns the duration of the source in microseconds, or {@link C#UNSET_TIME_US} if not known. * Returns the duration of the source in microseconds, or {@link C#UNSET_TIME_US} if not known.
......
...@@ -111,8 +111,8 @@ public final class SimpleExoPlayer implements ExoPlayer { ...@@ -111,8 +111,8 @@ public final class SimpleExoPlayer implements ExoPlayer {
private CodecCounters audioCodecCounters; private CodecCounters audioCodecCounters;
/* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector, /* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector,
DrmSessionManager drmSessionManager, boolean preferExtensionDecoders, int minBufferMs, BufferingPolicy bufferingPolicy, DrmSessionManager drmSessionManager,
int minRebufferMs, long allowedVideoJoiningTimeMs) { boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) {
mainHandler = new Handler(); mainHandler = new Handler();
bandwidthMeter = new DefaultBandwidthMeter(); bandwidthMeter = new DefaultBandwidthMeter();
componentListener = new ComponentListener(); componentListener = new ComponentListener();
...@@ -145,7 +145,7 @@ public final class SimpleExoPlayer implements ExoPlayer { ...@@ -145,7 +145,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
this.audioRendererCount = audioRendererCount; this.audioRendererCount = audioRendererCount;
// Build the player and associated objects. // Build the player and associated objects.
player = new ExoPlayerImpl(renderers, trackSelector, minBufferMs, minRebufferMs); player = new ExoPlayerImpl(renderers, trackSelector, bufferingPolicy);
} }
/** /**
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader;
...@@ -108,7 +109,8 @@ public final class SingleSampleSource implements SampleSource, TrackStream, ...@@ -108,7 +109,8 @@ public final class SingleSampleSource implements SampleSource, TrackStream,
// SampleSource implementation. // SampleSource implementation.
@Override @Override
public boolean prepare(long positionUs) { public boolean prepare(long positionUs, LoadControl loadControl) {
// TODO: Use the load control.
return true; return true;
} }
......
...@@ -16,11 +16,11 @@ ...@@ -16,11 +16,11 @@
package com.google.android.exoplayer.chunk; package com.google.android.exoplayer.chunk;
import com.google.android.exoplayer.AdaptiveSourceEventListener.EventDispatcher; import com.google.android.exoplayer.AdaptiveSourceEventListener.EventDispatcher;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.DecoderInputBuffer; import com.google.android.exoplayer.DecoderInputBuffer;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.FormatHolder; import com.google.android.exoplayer.FormatHolder;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.TrackStream; import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.extractor.DefaultTrackOutput; import com.google.android.exoplayer.extractor.DefaultTrackOutput;
import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader;
...@@ -64,14 +64,13 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream, ...@@ -64,14 +64,13 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream,
* @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants. * @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants.
* @param chunkSource A {@link ChunkSource} from which chunks to load are obtained. * @param chunkSource A {@link ChunkSource} from which chunks to load are obtained.
* @param loadControl Controls when the source is permitted to load data. * @param loadControl Controls when the source is permitted to load data.
* @param bufferSizeContribution The contribution of this source to the media buffer, in bytes.
* @param positionUs The position from which to start loading media. * @param positionUs The position from which to start loading media.
* @param minLoadableRetryCount The minimum number of times that the source should retry a load * @param minLoadableRetryCount The minimum number of times that the source should retry a load
* before propagating an error. * before propagating an error.
* @param eventDispatcher A dispatcher to notify of events. * @param eventDispatcher A dispatcher to notify of events.
*/ */
public ChunkTrackStream(int trackType, T chunkSource, LoadControl loadControl, public ChunkTrackStream(int trackType, T chunkSource, LoadControl loadControl, long positionUs,
int bufferSizeContribution, long positionUs, int minLoadableRetryCount, int minLoadableRetryCount,
EventDispatcher eventDispatcher) { EventDispatcher eventDispatcher) {
this.trackType = trackType; this.trackType = trackType;
this.chunkSource = chunkSource; this.chunkSource = chunkSource;
...@@ -87,7 +86,7 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream, ...@@ -87,7 +86,7 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream,
readingEnabled = true; readingEnabled = true;
downstreamPositionUs = positionUs; downstreamPositionUs = positionUs;
lastSeekPositionUs = positionUs; lastSeekPositionUs = positionUs;
loadControl.register(this, bufferSizeContribution); loadControl.register(this);
restartFrom(positionUs); restartFrom(positionUs);
} }
...@@ -287,7 +286,7 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream, ...@@ -287,7 +286,7 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream,
lastPreferredQueueSizeEvaluationTimeMs = now; lastPreferredQueueSizeEvaluationTimeMs = now;
} }
boolean isNext = loadControl.update(this, downstreamPositionUs, getNextLoadPositionUs(), false); boolean isNext = loadControl.update(this, getNextLoadPositionUs(), false);
if (!isNext) { if (!isNext) {
return; return;
} }
...@@ -301,7 +300,7 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream, ...@@ -301,7 +300,7 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream,
if (endOfStream) { if (endOfStream) {
loadingFinished = true; loadingFinished = true;
loadControl.update(this, downstreamPositionUs, C.UNSET_TIME_US, false); loadControl.update(this, C.UNSET_TIME_US, false);
return; return;
} }
...@@ -320,7 +319,7 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream, ...@@ -320,7 +319,7 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream,
loadable.formatEvaluatorTrigger, loadable.formatEvaluatorData, loadable.startTimeUs, loadable.formatEvaluatorTrigger, loadable.formatEvaluatorData, loadable.startTimeUs,
loadable.endTimeUs, elapsedRealtimeMs); loadable.endTimeUs, elapsedRealtimeMs);
// Update the load control again to indicate that we're now loading. // Update the load control again to indicate that we're now loading.
loadControl.update(this, downstreamPositionUs, getNextLoadPositionUs(), true); loadControl.update(this, getNextLoadPositionUs(), true);
} }
/** /**
......
...@@ -17,10 +17,9 @@ package com.google.android.exoplayer.dash; ...@@ -17,10 +17,9 @@ package com.google.android.exoplayer.dash;
import com.google.android.exoplayer.AdaptiveSourceEventListener; import com.google.android.exoplayer.AdaptiveSourceEventListener;
import com.google.android.exoplayer.AdaptiveSourceEventListener.EventDispatcher; import com.google.android.exoplayer.AdaptiveSourceEventListener.EventDispatcher;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroup;
...@@ -39,7 +38,6 @@ import com.google.android.exoplayer.dash.mpd.UtcTimingElement; ...@@ -39,7 +38,6 @@ import com.google.android.exoplayer.dash.mpd.UtcTimingElement;
import com.google.android.exoplayer.upstream.BandwidthMeter; import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSourceFactory; import com.google.android.exoplayer.upstream.DataSourceFactory;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader;
import com.google.android.exoplayer.upstream.Loader.Callback; import com.google.android.exoplayer.upstream.Loader.Callback;
import com.google.android.exoplayer.upstream.ParsingLoadable; import com.google.android.exoplayer.upstream.ParsingLoadable;
...@@ -76,7 +74,6 @@ public final class DashSampleSource implements SampleSource { ...@@ -76,7 +74,6 @@ public final class DashSampleSource implements SampleSource {
private final DataSourceFactory dataSourceFactory; private final DataSourceFactory dataSourceFactory;
private final BandwidthMeter bandwidthMeter; private final BandwidthMeter bandwidthMeter;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final LoadControl loadControl;
private final Loader loader; private final Loader loader;
private final DataSource dataSource; private final DataSource dataSource;
private final MediaPresentationDescriptionParser manifestParser; private final MediaPresentationDescriptionParser manifestParser;
...@@ -87,6 +84,7 @@ public final class DashSampleSource implements SampleSource { ...@@ -87,6 +84,7 @@ public final class DashSampleSource implements SampleSource {
private long manifestLoadEndTimestamp; private long manifestLoadEndTimestamp;
private MediaPresentationDescription manifest; private MediaPresentationDescription manifest;
private LoadControl loadControl;
private boolean prepared; private boolean prepared;
private long durationUs; private long durationUs;
private long elapsedRealtimeOffset; private long elapsedRealtimeOffset;
...@@ -101,7 +99,6 @@ public final class DashSampleSource implements SampleSource { ...@@ -101,7 +99,6 @@ public final class DashSampleSource implements SampleSource {
this.dataSourceFactory = dataSourceFactory; this.dataSourceFactory = dataSourceFactory;
this.bandwidthMeter = bandwidthMeter; this.bandwidthMeter = bandwidthMeter;
eventDispatcher = new EventDispatcher(eventHandler, eventListener); eventDispatcher = new EventDispatcher(eventHandler, eventListener);
loadControl = new DefaultLoadControl(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
loader = new Loader("Loader:DashSampleSource"); loader = new Loader("Loader:DashSampleSource");
dataSource = dataSourceFactory.createDataSource(); dataSource = dataSourceFactory.createDataSource();
manifestParser = new MediaPresentationDescriptionParser(); manifestParser = new MediaPresentationDescriptionParser();
...@@ -110,10 +107,11 @@ public final class DashSampleSource implements SampleSource { ...@@ -110,10 +107,11 @@ public final class DashSampleSource implements SampleSource {
} }
@Override @Override
public boolean prepare(long positionUs) throws IOException { public boolean prepare(long positionUs, LoadControl loadControl) throws IOException {
if (prepared) { if (prepared) {
return true; return true;
} }
this.loadControl = loadControl;
loader.maybeThrowError(); loader.maybeThrowError();
if (!loader.isLoading() && manifest == null) { if (!loader.isLoading() && manifest == null) {
startLoadingManifest(); startLoadingManifest();
...@@ -361,13 +359,12 @@ public final class DashSampleSource implements SampleSource { ...@@ -361,13 +359,12 @@ public final class DashSampleSource implements SampleSource {
AdaptationSet adaptationSet = manifest.getPeriod(0).adaptationSets.get( AdaptationSet adaptationSet = manifest.getPeriod(0).adaptationSets.get(
adaptationSetIndex); adaptationSetIndex);
int adaptationSetType = adaptationSet.type; int adaptationSetType = adaptationSet.type;
int bufferSize = Util.getDefaultBufferSize(adaptationSetType);
DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter); DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
DashChunkSource chunkSource = new DashChunkSource(loader, manifest, adaptationSetIndex, DashChunkSource chunkSource = new DashChunkSource(loader, manifest, adaptationSetIndex,
trackGroups.get(selection.group), selectedTracks, dataSource, adaptiveEvaluator, trackGroups.get(selection.group), selectedTracks, dataSource, adaptiveEvaluator,
elapsedRealtimeOffset); elapsedRealtimeOffset);
return new ChunkTrackStream<>(adaptationSetType, chunkSource, loadControl, bufferSize, return new ChunkTrackStream<>(adaptationSetType, chunkSource, loadControl, positionUs,
positionUs, MIN_LOADABLE_RETRY_COUNT, eventDispatcher); MIN_LOADABLE_RETRY_COUNT, eventDispatcher);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
......
...@@ -15,11 +15,10 @@ ...@@ -15,11 +15,10 @@
*/ */
package com.google.android.exoplayer.extractor; package com.google.android.exoplayer.extractor;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.DecoderInputBuffer; import com.google.android.exoplayer.DecoderInputBuffer;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.FormatHolder; import com.google.android.exoplayer.FormatHolder;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroup;
...@@ -30,7 +29,6 @@ import com.google.android.exoplayer.upstream.BandwidthMeter; ...@@ -30,7 +29,6 @@ import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSourceFactory; import com.google.android.exoplayer.upstream.DataSourceFactory;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader;
import com.google.android.exoplayer.upstream.Loader.Loadable; import com.google.android.exoplayer.upstream.Loader.Loadable;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
...@@ -128,13 +126,13 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -128,13 +126,13 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
private final EventListener eventListener; private final EventListener eventListener;
private final DataSource dataSource; private final DataSource dataSource;
private final ConditionVariable loadCondition; private final ConditionVariable loadCondition;
private final LoadControl loadControl;
private final Loader loader; private final Loader loader;
private final ExtractorHolder extractorHolder; private final ExtractorHolder extractorHolder;
private volatile boolean tracksBuilt; private volatile boolean tracksBuilt;
private volatile SeekMap seekMap; private volatile SeekMap seekMap;
private LoadControl loadControl;
private boolean prepared; private boolean prepared;
private boolean seenFirstTrackSelection; private boolean seenFirstTrackSelection;
private boolean notifyReset; private boolean notifyReset;
...@@ -190,7 +188,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -190,7 +188,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
dataSource = dataSourceFactory.createDataSource(bandwidthMeter); dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
loadCondition = new ConditionVariable(); loadCondition = new ConditionVariable();
loadControl = new DefaultLoadControl(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
loader = new Loader("Loader:ExtractorSampleSource"); loader = new Loader("Loader:ExtractorSampleSource");
extractorHolder = new ExtractorHolder(extractors, this); extractorHolder = new ExtractorHolder(extractors, this);
pendingResetPositionUs = C.UNSET_TIME_US; pendingResetPositionUs = C.UNSET_TIME_US;
...@@ -307,10 +304,11 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -307,10 +304,11 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
// SampleSource implementation. // SampleSource implementation.
@Override @Override
public boolean prepare(long positionUs) throws IOException { public boolean prepare(long positionUs, LoadControl loadControl) throws IOException {
if (prepared) { if (prepared) {
return true; return true;
} }
this.loadControl = loadControl;
if (seekMap != null && tracksBuilt && haveFormatsForAllTracks()) { if (seekMap != null && tracksBuilt && haveFormatsForAllTracks()) {
loadCondition.close(); loadCondition.close();
int trackCount = sampleQueues.length; int trackCount = sampleQueues.length;
...@@ -388,7 +386,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -388,7 +386,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
} }
} else { } else {
if (!tracksWereEnabled) { if (!tracksWereEnabled) {
loadControl.register(this, C.DEFAULT_MUXED_BUFFER_SIZE); loadControl.register(this);
loadCondition.open(); loadCondition.open();
} }
if (seenFirstTrackSelection ? newStreams.length > 0 : positionUs != 0) { if (seenFirstTrackSelection ? newStreams.length > 0 : positionUs != 0) {
...@@ -401,7 +399,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu ...@@ -401,7 +399,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
@Override @Override
public void continueBuffering(long playbackPositionUs) { public void continueBuffering(long playbackPositionUs) {
if (loadControl.update(this, playbackPositionUs, getBufferedPositionUs(), loader.isLoading())) { if (loadControl.update(this, getBufferedPositionUs(), loader.isLoading())) {
loadCondition.open(); loadCondition.open();
} else { } else {
loadCondition.close(); loadCondition.close();
......
...@@ -17,10 +17,9 @@ package com.google.android.exoplayer.hls; ...@@ -17,10 +17,9 @@ package com.google.android.exoplayer.hls;
import com.google.android.exoplayer.AdaptiveSourceEventListener; import com.google.android.exoplayer.AdaptiveSourceEventListener;
import com.google.android.exoplayer.AdaptiveSourceEventListener.EventDispatcher; import com.google.android.exoplayer.AdaptiveSourceEventListener.EventDispatcher;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroup;
...@@ -36,7 +35,6 @@ import com.google.android.exoplayer.hls.playlist.Variant; ...@@ -36,7 +35,6 @@ import com.google.android.exoplayer.hls.playlist.Variant;
import com.google.android.exoplayer.upstream.BandwidthMeter; import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSourceFactory; import com.google.android.exoplayer.upstream.DataSourceFactory;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader;
import com.google.android.exoplayer.upstream.ParsingLoadable; import com.google.android.exoplayer.upstream.ParsingLoadable;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
...@@ -66,13 +64,13 @@ public final class HlsSampleSource implements SampleSource, ...@@ -66,13 +64,13 @@ public final class HlsSampleSource implements SampleSource,
private final DataSourceFactory dataSourceFactory; private final DataSourceFactory dataSourceFactory;
private final BandwidthMeter bandwidthMeter; private final BandwidthMeter bandwidthMeter;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final LoadControl loadControl;
private final IdentityHashMap<TrackStream, HlsTrackStreamWrapper> trackStreamSources; private final IdentityHashMap<TrackStream, HlsTrackStreamWrapper> trackStreamSources;
private final PtsTimestampAdjusterProvider timestampAdjusterProvider; private final PtsTimestampAdjusterProvider timestampAdjusterProvider;
private final Loader manifestFetcher; private final Loader manifestFetcher;
private final DataSource manifestDataSource; private final DataSource manifestDataSource;
private final HlsPlaylistParser manifestParser; private final HlsPlaylistParser manifestParser;
private LoadControl loadControl;
private boolean seenFirstTrackSelection; private boolean seenFirstTrackSelection;
private long durationUs; private long durationUs;
private boolean isLive; private boolean isLive;
...@@ -89,7 +87,6 @@ public final class HlsSampleSource implements SampleSource, ...@@ -89,7 +87,6 @@ public final class HlsSampleSource implements SampleSource,
this.bandwidthMeter = bandwidthMeter; this.bandwidthMeter = bandwidthMeter;
eventDispatcher = new EventDispatcher(eventHandler, eventListener); eventDispatcher = new EventDispatcher(eventHandler, eventListener);
loadControl = new DefaultLoadControl(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
timestampAdjusterProvider = new PtsTimestampAdjusterProvider(); timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
trackStreamSources = new IdentityHashMap<>(); trackStreamSources = new IdentityHashMap<>();
...@@ -99,11 +96,12 @@ public final class HlsSampleSource implements SampleSource, ...@@ -99,11 +96,12 @@ public final class HlsSampleSource implements SampleSource,
} }
@Override @Override
public boolean prepare(long positionUs) throws IOException { public boolean prepare(long positionUs, LoadControl loadControl) throws IOException {
if (trackGroups != null) { if (trackGroups != null) {
return true; return true;
} }
this.loadControl = loadControl;
if (trackStreamWrappers == null) { if (trackStreamWrappers == null) {
manifestFetcher.maybeThrowError(); manifestFetcher.maybeThrowError();
if (!manifestFetcher.isLoading()) { if (!manifestFetcher.isLoading()) {
...@@ -268,8 +266,7 @@ public final class HlsSampleSource implements SampleSource, ...@@ -268,8 +266,7 @@ public final class HlsSampleSource implements SampleSource,
Format.NO_VALUE); Format.NO_VALUE);
Variant[] variants = new Variant[] {new Variant(playlist.baseUri, format, null)}; Variant[] variants = new Variant[] {new Variant(playlist.baseUri, format, null)};
trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_DEFAULT, baseUri, variants, trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_DEFAULT, baseUri, variants,
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter), C.DEFAULT_MUXED_BUFFER_SIZE, new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter), null, null));
null, null));
return trackStreamWrappers; return trackStreamWrappers;
} }
...@@ -302,8 +299,8 @@ public final class HlsSampleSource implements SampleSource, ...@@ -302,8 +299,8 @@ public final class HlsSampleSource implements SampleSource,
Variant[] variants = new Variant[selectedVariants.size()]; Variant[] variants = new Variant[selectedVariants.size()];
selectedVariants.toArray(variants); selectedVariants.toArray(variants);
trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_DEFAULT, baseUri, variants, trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_DEFAULT, baseUri, variants,
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter), C.DEFAULT_MUXED_BUFFER_SIZE, new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter), masterPlaylist.muxedAudioFormat,
masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormat)); masterPlaylist.muxedCaptionFormat));
// Build the audio stream wrapper if applicable. // Build the audio stream wrapper if applicable.
List<Variant> audioVariants = masterPlaylist.audios; List<Variant> audioVariants = masterPlaylist.audios;
...@@ -311,7 +308,7 @@ public final class HlsSampleSource implements SampleSource, ...@@ -311,7 +308,7 @@ public final class HlsSampleSource implements SampleSource,
variants = new Variant[audioVariants.size()]; variants = new Variant[audioVariants.size()];
audioVariants.toArray(variants); audioVariants.toArray(variants);
trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_AUDIO, baseUri, variants, null, trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_AUDIO, baseUri, variants, null,
C.DEFAULT_AUDIO_BUFFER_SIZE, null, null)); null, null));
} }
// Build the text stream wrapper if applicable. // Build the text stream wrapper if applicable.
...@@ -320,20 +317,20 @@ public final class HlsSampleSource implements SampleSource, ...@@ -320,20 +317,20 @@ public final class HlsSampleSource implements SampleSource,
variants = new Variant[subtitleVariants.size()]; variants = new Variant[subtitleVariants.size()];
subtitleVariants.toArray(variants); subtitleVariants.toArray(variants);
trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_TEXT, baseUri, variants, null, trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_TEXT, baseUri, variants, null,
C.DEFAULT_TEXT_BUFFER_SIZE, null, null)); null, null));
} }
return trackStreamWrappers; return trackStreamWrappers;
} }
private HlsTrackStreamWrapper buildTrackStreamWrapper(int trackType, String baseUri, private HlsTrackStreamWrapper buildTrackStreamWrapper(int trackType, String baseUri,
Variant[] variants, FormatEvaluator formatEvaluator, int bufferSize, Format muxedAudioFormat, Variant[] variants, FormatEvaluator formatEvaluator, Format muxedAudioFormat,
Format muxedCaptionFormat) { Format muxedCaptionFormat) {
DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter); DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource defaultChunkSource = new HlsChunkSource(baseUri, variants, dataSource, HlsChunkSource defaultChunkSource = new HlsChunkSource(baseUri, variants, dataSource,
timestampAdjusterProvider, formatEvaluator); timestampAdjusterProvider, formatEvaluator);
return new HlsTrackStreamWrapper(trackType, defaultChunkSource, loadControl, bufferSize, return new HlsTrackStreamWrapper(trackType, defaultChunkSource, loadControl, muxedAudioFormat,
muxedAudioFormat, muxedCaptionFormat, MIN_LOADABLE_RETRY_COUNT, eventDispatcher); muxedCaptionFormat, MIN_LOADABLE_RETRY_COUNT, eventDispatcher);
} }
private int selectTracks(HlsTrackStreamWrapper trackStreamWrapper, private int selectTracks(HlsTrackStreamWrapper trackStreamWrapper,
......
...@@ -16,11 +16,11 @@ ...@@ -16,11 +16,11 @@
package com.google.android.exoplayer.hls; package com.google.android.exoplayer.hls;
import com.google.android.exoplayer.AdaptiveSourceEventListener.EventDispatcher; import com.google.android.exoplayer.AdaptiveSourceEventListener.EventDispatcher;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.DecoderInputBuffer; import com.google.android.exoplayer.DecoderInputBuffer;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.FormatHolder; import com.google.android.exoplayer.FormatHolder;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroup;
import com.google.android.exoplayer.TrackGroupArray; import com.google.android.exoplayer.TrackGroupArray;
import com.google.android.exoplayer.TrackSelection; import com.google.android.exoplayer.TrackSelection;
...@@ -54,7 +54,6 @@ import java.util.List; ...@@ -54,7 +54,6 @@ import java.util.List;
private final int trackType; private final int trackType;
private final HlsChunkSource chunkSource; private final HlsChunkSource chunkSource;
private final LoadControl loadControl; private final LoadControl loadControl;
private final int bufferSizeContribution;
private final Format muxedAudioFormat; private final Format muxedAudioFormat;
private final Format muxedCaptionFormat; private final Format muxedCaptionFormat;
private final int minLoadableRetryCount; private final int minLoadableRetryCount;
...@@ -88,7 +87,6 @@ import java.util.List; ...@@ -88,7 +87,6 @@ import java.util.List;
* @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants. * @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants.
* @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained. * @param chunkSource A {@link HlsChunkSource} from which chunks to load are obtained.
* @param loadControl Controls when the source is permitted to load data. * @param loadControl Controls when the source is permitted to load data.
* @param bufferSizeContribution The contribution of this source to the media buffer, in bytes.
* @param muxedAudioFormat If HLS master playlist indicates that the stream contains muxed audio, * @param muxedAudioFormat If HLS master playlist indicates that the stream contains muxed audio,
* this is the audio {@link Format} as defined by the playlist. * this is the audio {@link Format} as defined by the playlist.
* @param muxedCaptionFormat If HLS master playlist indicates that the stream contains muxed * @param muxedCaptionFormat If HLS master playlist indicates that the stream contains muxed
...@@ -98,12 +96,11 @@ import java.util.List; ...@@ -98,12 +96,11 @@ import java.util.List;
* @param eventDispatcher A dispatcher to notify of events. * @param eventDispatcher A dispatcher to notify of events.
*/ */
public HlsTrackStreamWrapper(int trackType, HlsChunkSource chunkSource, LoadControl loadControl, public HlsTrackStreamWrapper(int trackType, HlsChunkSource chunkSource, LoadControl loadControl,
int bufferSizeContribution, Format muxedAudioFormat, Format muxedCaptionFormat, Format muxedAudioFormat, Format muxedCaptionFormat, int minLoadableRetryCount,
int minLoadableRetryCount, EventDispatcher eventDispatcher) { EventDispatcher eventDispatcher) {
this.trackType = trackType; this.trackType = trackType;
this.chunkSource = chunkSource; this.chunkSource = chunkSource;
this.loadControl = loadControl; this.loadControl = loadControl;
this.bufferSizeContribution = bufferSizeContribution;
this.muxedAudioFormat = muxedAudioFormat; this.muxedAudioFormat = muxedAudioFormat;
this.muxedCaptionFormat = muxedCaptionFormat; this.muxedCaptionFormat = muxedCaptionFormat;
this.minLoadableRetryCount = minLoadableRetryCount; this.minLoadableRetryCount = minLoadableRetryCount;
...@@ -210,7 +207,7 @@ import java.util.List; ...@@ -210,7 +207,7 @@ import java.util.List;
loader.cancelLoading(); loader.cancelLoading();
} }
} else if (!tracksWereEnabled) { } else if (!tracksWereEnabled) {
loadControl.register(this, bufferSizeContribution); loadControl.register(this);
} }
return newStreams; return newStreams;
} }
...@@ -516,7 +513,7 @@ import java.util.List; ...@@ -516,7 +513,7 @@ import java.util.List;
private void maybeStartLoading() { private void maybeStartLoading() {
boolean shouldStartLoading = !prepared || (enabledTrackCount > 0 boolean shouldStartLoading = !prepared || (enabledTrackCount > 0
&& loadControl.update(this, downstreamPositionUs, getNextLoadPositionUs(), false)); && loadControl.update(this, getNextLoadPositionUs(), false));
if (!shouldStartLoading) { if (!shouldStartLoading) {
return; return;
} }
...@@ -531,7 +528,7 @@ import java.util.List; ...@@ -531,7 +528,7 @@ import java.util.List;
if (endOfStream) { if (endOfStream) {
loadingFinished = true; loadingFinished = true;
if (prepared) { if (prepared) {
loadControl.update(this, downstreamPositionUs, C.UNSET_TIME_US, false); loadControl.update(this, C.UNSET_TIME_US, false);
} }
return; return;
} }
...@@ -552,7 +549,7 @@ import java.util.List; ...@@ -552,7 +549,7 @@ import java.util.List;
loadable.endTimeUs, elapsedRealtimeMs); loadable.endTimeUs, elapsedRealtimeMs);
if (prepared) { if (prepared) {
// Update the load control again to indicate that we're now loading. // Update the load control again to indicate that we're now loading.
loadControl.update(this, downstreamPositionUs, getNextLoadPositionUs(), true); loadControl.update(this, getNextLoadPositionUs(), true);
} }
} }
......
...@@ -17,10 +17,9 @@ package com.google.android.exoplayer.smoothstreaming; ...@@ -17,10 +17,9 @@ package com.google.android.exoplayer.smoothstreaming;
import com.google.android.exoplayer.AdaptiveSourceEventListener; import com.google.android.exoplayer.AdaptiveSourceEventListener;
import com.google.android.exoplayer.AdaptiveSourceEventListener.EventDispatcher; import com.google.android.exoplayer.AdaptiveSourceEventListener.EventDispatcher;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroup;
...@@ -36,7 +35,6 @@ import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.Stre ...@@ -36,7 +35,6 @@ import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.Stre
import com.google.android.exoplayer.upstream.BandwidthMeter; import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSourceFactory; import com.google.android.exoplayer.upstream.DataSourceFactory;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.Loader; import com.google.android.exoplayer.upstream.Loader;
import com.google.android.exoplayer.upstream.ParsingLoadable; import com.google.android.exoplayer.upstream.ParsingLoadable;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
...@@ -68,7 +66,6 @@ public final class SmoothStreamingSampleSource implements SampleSource, ...@@ -68,7 +66,6 @@ public final class SmoothStreamingSampleSource implements SampleSource,
private final DataSourceFactory dataSourceFactory; private final DataSourceFactory dataSourceFactory;
private final BandwidthMeter bandwidthMeter; private final BandwidthMeter bandwidthMeter;
private final EventDispatcher eventDispatcher; private final EventDispatcher eventDispatcher;
private final LoadControl loadControl;
private final Loader manifestLoader; private final Loader manifestLoader;
private final DataSource manifestDataSource; private final DataSource manifestDataSource;
private final SmoothStreamingManifestParser manifestParser; private final SmoothStreamingManifestParser manifestParser;
...@@ -76,6 +73,7 @@ public final class SmoothStreamingSampleSource implements SampleSource, ...@@ -76,6 +73,7 @@ public final class SmoothStreamingSampleSource implements SampleSource,
private long manifestLoadStartTimestamp; private long manifestLoadStartTimestamp;
private SmoothStreamingManifest manifest; private SmoothStreamingManifest manifest;
private LoadControl loadControl;
private boolean prepared; private boolean prepared;
private long durationUs; private long durationUs;
private TrackEncryptionBox[] trackEncryptionBoxes; private TrackEncryptionBox[] trackEncryptionBoxes;
...@@ -92,7 +90,6 @@ public final class SmoothStreamingSampleSource implements SampleSource, ...@@ -92,7 +90,6 @@ public final class SmoothStreamingSampleSource implements SampleSource,
this.dataSourceFactory = dataSourceFactory; this.dataSourceFactory = dataSourceFactory;
this.bandwidthMeter = bandwidthMeter; this.bandwidthMeter = bandwidthMeter;
this.eventDispatcher = new EventDispatcher(eventHandler, eventListener); this.eventDispatcher = new EventDispatcher(eventHandler, eventListener);
loadControl = new DefaultLoadControl(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
trackStreams = newTrackStreamArray(0); trackStreams = newTrackStreamArray(0);
manifestDataSource = dataSourceFactory.createDataSource(); manifestDataSource = dataSourceFactory.createDataSource();
manifestParser = new SmoothStreamingManifestParser(); manifestParser = new SmoothStreamingManifestParser();
...@@ -100,10 +97,11 @@ public final class SmoothStreamingSampleSource implements SampleSource, ...@@ -100,10 +97,11 @@ public final class SmoothStreamingSampleSource implements SampleSource,
} }
@Override @Override
public boolean prepare(long positionUs) throws IOException { public boolean prepare(long positionUs, LoadControl loadControl) throws IOException {
if (prepared) { if (prepared) {
return true; return true;
} }
this.loadControl = loadControl;
manifestLoader.maybeThrowError(); manifestLoader.maybeThrowError();
if (!manifestLoader.isLoading()) { if (!manifestLoader.isLoading()) {
startLoadingManifest(); startLoadingManifest();
...@@ -282,13 +280,12 @@ public final class SmoothStreamingSampleSource implements SampleSource, ...@@ -282,13 +280,12 @@ public final class SmoothStreamingSampleSource implements SampleSource,
int streamElementIndex = trackGroupElementIndices[selection.group]; int streamElementIndex = trackGroupElementIndices[selection.group];
StreamElement streamElement = manifest.streamElements[streamElementIndex]; StreamElement streamElement = manifest.streamElements[streamElementIndex];
int streamElementType = streamElement.type; int streamElementType = streamElement.type;
int bufferSize = Util.getDefaultBufferSize(streamElementType);
DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter); DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
SmoothStreamingChunkSource chunkSource = new SmoothStreamingChunkSource(manifestLoader, SmoothStreamingChunkSource chunkSource = new SmoothStreamingChunkSource(manifestLoader,
manifest, streamElementIndex, trackGroups.get(selection.group), selectedTracks, dataSource, manifest, streamElementIndex, trackGroups.get(selection.group), selectedTracks, dataSource,
adaptiveEvaluator, trackEncryptionBoxes); adaptiveEvaluator, trackEncryptionBoxes);
return new ChunkTrackStream<>(streamElementType, chunkSource, loadControl, bufferSize, return new ChunkTrackStream<>(streamElementType, chunkSource, loadControl, positionUs,
positionUs, MIN_LOADABLE_RETRY_COUNT, eventDispatcher); MIN_LOADABLE_RETRY_COUNT, eventDispatcher);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
......
...@@ -36,6 +36,7 @@ import android.os.Handler; ...@@ -36,6 +36,7 @@ import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import android.view.Surface; import android.view.Surface;
import junit.framework.Assert; import junit.framework.Assert;
/** /**
...@@ -276,8 +277,7 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen ...@@ -276,8 +277,7 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
@SuppressWarnings("unused") @SuppressWarnings("unused")
protected SimpleExoPlayer buildExoPlayer(HostActivity host, Surface surface, protected SimpleExoPlayer buildExoPlayer(HostActivity host, Surface surface,
DefaultTrackSelector trackSelector) { DefaultTrackSelector trackSelector) {
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(host, trackSelector, null, false, SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(host, trackSelector);
ExoPlayerFactory.DEFAULT_MIN_BUFFER_MS, ExoPlayerFactory.DEFAULT_MIN_REBUFFER_MS, 0);
player.setSurface(surface); player.setSurface(surface);
return player; return player;
} }
......
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