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;
import com.google.android.exoplayer.AspectRatioFrameLayout;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.DefaultBufferingPolicy;
import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
import com.google.android.exoplayer.DefaultTrackSelector;
import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
......@@ -266,8 +267,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
trackSelector.addListener(this);
trackSelector.addListener(eventLogger);
trackSelectionHelper = new TrackSelectionHelper(trackSelector);
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, drmSessionManager,
preferExtensionDecoders);
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, new DefaultBufferingPolicy(),
drmSessionManager, preferExtensionDecoders);
player.addListener(this);
player.addListener(eventLogger);
player.setDebugListener(eventLogger);
......
......@@ -18,49 +18,92 @@ package com.google.android.exoplayer;
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 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.
* @param playbackPositionUs The current playback position in microseconds.
*/
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
* data can be loaded.
* Invoked by the player when a track selection occurs.
*
* @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.
* <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).
* Invoked by the player when a reset occurs, meaning all renderers have been disabled.
*/
void reset();
/**
* Returns a {@link LoadControl} that a {@link SampleSource} can use to control loads according to
* this policy.
*
* @param loader The loader invoking the update.
* @param playbackPositionUs The loader's playback position.
* @param nextLoadPositionUs The loader's next load position. {@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.
* @return The {@link LoadControl}.
*/
LoadControl getLoadControl();
/**
* 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;
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
* ongoing playback.
*/
......@@ -55,7 +42,7 @@ public final class ExoPlayerFactory {
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
*/
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 {
*
* @param context A {@link Context}.
* @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
* will not be used for DRM protected playbacks.
*/
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
DrmSessionManager drmSessionManager) {
return newSimpleInstance(context, trackSelector, drmSessionManager, false);
BufferingPolicy bufferingPolicy, DrmSessionManager drmSessionManager) {
return newSimpleInstance(context, trackSelector, bufferingPolicy, drmSessionManager, false);
}
/**
......@@ -80,6 +68,7 @@ public final class ExoPlayerFactory {
*
* @param context A {@link Context}.
* @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
* will not be used for DRM protected playbacks.
* @param preferExtensionDecoders True to prefer {@link TrackRenderer} instances defined in
......@@ -87,9 +76,10 @@ public final class ExoPlayerFactory {
* included in the application build for setting this flag to have any effect.
*/
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
DrmSessionManager drmSessionManager, boolean preferExtensionDecoders) {
return newSimpleInstance(context, trackSelector, drmSessionManager, preferExtensionDecoders,
DEFAULT_MIN_BUFFER_MS, DEFAULT_MIN_REBUFFER_MS, DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS);
BufferingPolicy bufferingPolicy, DrmSessionManager drmSessionManager,
boolean preferExtensionDecoders) {
return newSimpleInstance(context, trackSelector, bufferingPolicy, drmSessionManager,
preferExtensionDecoders, DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS);
}
/**
......@@ -99,24 +89,20 @@ public final class ExoPlayerFactory {
*
* @param context A {@link Context}.
* @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
* will not be used for DRM protected playbacks.
* @param preferExtensionDecoders True to prefer {@link TrackRenderer} instances defined in
* 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.
* @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
* seamlessly join an ongoing playback.
*/
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
DrmSessionManager drmSessionManager, boolean preferExtensionDecoders, int minBufferMs,
int minRebufferMs, long allowedVideoJoiningTimeMs) {
return new SimpleExoPlayer(context, trackSelector, drmSessionManager, preferExtensionDecoders,
minBufferMs, minRebufferMs, allowedVideoJoiningTimeMs);
BufferingPolicy bufferingPolicy, DrmSessionManager drmSessionManager,
boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) {
return new SimpleExoPlayer(context, trackSelector, bufferingPolicy, drmSessionManager,
preferExtensionDecoders, allowedVideoJoiningTimeMs);
}
/**
......@@ -126,15 +112,9 @@ public final class ExoPlayerFactory {
*
* @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 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,
int minBufferMs, int minRebufferMs) {
return new ExoPlayerImpl(renderers, trackSelector, minBufferMs, minRebufferMs);
public static ExoPlayer newInstance(TrackRenderer[] renderers, TrackSelector trackSelector) {
return newInstance(renderers, trackSelector, new DefaultBufferingPolicy());
}
/**
......@@ -144,10 +124,11 @@ public final class ExoPlayerFactory {
*
* @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 bufferingPolicy The {@link BufferingPolicy} that will be used by the instance.
*/
public static ExoPlayer newInstance(TrackRenderer[] renderers, TrackSelector trackSelector) {
return new ExoPlayerImpl(renderers, trackSelector, DEFAULT_MIN_BUFFER_MS,
DEFAULT_MIN_REBUFFER_MS);
public static ExoPlayer newInstance(TrackRenderer[] renderers, TrackSelector trackSelector,
BufferingPolicy bufferingPolicy) {
return new ExoPlayerImpl(renderers, trackSelector, bufferingPolicy);
}
}
......@@ -55,15 +55,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
*
* @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 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 bufferingPolicy The {@link BufferingPolicy} that will be used by the instance.
*/
@SuppressLint("HandlerLeak")
public ExoPlayerImpl(TrackRenderer[] renderers, TrackSelector trackSelector, int minBufferMs,
int minRebufferMs) {
public ExoPlayerImpl(TrackRenderer[] renderers, TrackSelector trackSelector,
BufferingPolicy bufferingPolicy) {
Log.i(TAG, "Init " + ExoPlayerLibraryInfo.VERSION);
Assertions.checkNotNull(renderers);
Assertions.checkState(renderers.length > 0);
......@@ -76,7 +72,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
ExoPlayerImpl.this.handleEvent(msg);
}
};
internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, minBufferMs, minRebufferMs,
internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, bufferingPolicy,
playWhenReady, eventHandler);
playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0);
}
......
......@@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import com.google.android.exoplayer.ExoPlayer.ExoPlayerMessage;
import com.google.android.exoplayer.TrackSelector.InvalidationListener;
import com.google.android.exoplayer.util.PriorityHandlerThread;
......@@ -90,9 +91,8 @@ import java.util.ArrayList;
private static final int MAXIMUM_BUFFER_AHEAD_SOURCES = 100;
private final TrackSelector trackSelector;
private final BufferingPolicy bufferingPolicy;
private final StandaloneMediaClock standaloneMediaClock;
private final long minBufferUs;
private final long minRebufferUs;
private final Handler handler;
private final HandlerThread internalPlaybackThread;
private final Handler eventHandler;
......@@ -114,10 +114,9 @@ import java.util.ArrayList;
private long internalPositionUs;
public ExoPlayerImplInternal(TrackRenderer[] renderers, TrackSelector trackSelector,
int minBufferMs, int minRebufferMs, boolean playWhenReady, Handler eventHandler) {
BufferingPolicy bufferingPolicy, boolean playWhenReady, Handler eventHandler) {
this.trackSelector = trackSelector;
this.minBufferUs = minBufferMs * 1000L;
this.minRebufferUs = minRebufferMs * 1000L;
this.bufferingPolicy = bufferingPolicy;
this.playWhenReady = playWhenReady;
this.eventHandler = eventHandler;
this.state = ExoPlayer.STATE_IDLE;
......@@ -277,16 +276,6 @@ import java.util.ArrayList;
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) {
try {
resetInternal();
......@@ -359,6 +348,7 @@ import java.util.ArrayList;
}
playbackInfo.positionUs = positionUs;
elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
bufferingPolicy.setPlaybackPosition(positionUs);
// Update the buffered position.
long bufferedPositionUs;
......@@ -412,7 +402,7 @@ import java.util.ArrayList;
stopRenderers();
} else if (state == ExoPlayer.STATE_BUFFERING) {
if ((enabledRenderers.length > 0 ? allRenderersReadyOrEnded : timeline.isReady())
&& haveSufficientBuffer()) {
&& bufferingPolicy.haveSufficientBuffer(playbackInfo.bufferedPositionUs, rebuffering)) {
setState(ExoPlayer.STATE_READY);
if (playWhenReady) {
startRenderers();
......@@ -520,6 +510,7 @@ import java.util.ArrayList;
enabledRenderers = new TrackRenderer[0];
sampleSourceProvider = null;
timeline.reset();
bufferingPolicy.reset();
}
private void sendMessagesInternal(ExoPlayerMessage[] messages) throws ExoPlaybackException {
......@@ -628,10 +619,11 @@ import java.util.ArrayList;
// Continue preparation.
// TODO[playlists]: Add support for setting the start position to play in a source.
long startPositionUs = playingSource == null ? playbackInfo.positionUs : 0;
if (bufferingSource.prepare(startPositionUs)) {
if (bufferingSource.prepare(startPositionUs, bufferingPolicy.getLoadControl())) {
Pair<TrackSelectionArray, Object> result = trackSelector.selectTracks(renderers,
bufferingSource.sampleSource.getTrackGroups());
bufferingSource.selectTracks(result.first, result.second, startPositionUs);
bufferingSource.selectTracks(result.first, result.second, startPositionUs,
bufferingPolicy, renderers);
if (playingSource == null) {
// This is the first prepared source, so start playing it.
readingSource = bufferingSource;
......@@ -937,13 +929,8 @@ import java.util.ArrayList;
|| sampleSource.getBufferedPositionUs() == C.END_OF_SOURCE_US);
}
public void setNextSource(Source nextSource) {
this.nextSource = nextSource;
nextSource.offsetUs = offsetUs + sampleSource.getDurationUs();
}
public boolean prepare(long startPositionUs) throws IOException {
if (sampleSource.prepare(startPositionUs)) {
public boolean prepare(long startPositionUs, LoadControl loadControl) throws IOException {
if (sampleSource.prepare(startPositionUs, loadControl)) {
prepared = true;
return true;
} else {
......@@ -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,
long positionUs) throws ExoPlaybackException {
long positionUs, BufferingPolicy bufferingPolicy, TrackRenderer[] renderers)
throws ExoPlaybackException {
this.trackSelectionData = trackSelectionData;
if (newTrackSelections.equals(trackSelections)) {
return;
......@@ -974,6 +967,9 @@ import java.util.ArrayList;
}
TrackStream[] newStreams = sampleSource.selectTracks(oldStreams, newSelections, positionUs);
updateTrackStreams(newTrackSelections, newSelections, newStreams);
bufferingPolicy.onTrackSelections(renderers, sampleSource.getTrackGroups(),
newTrackSelections);
}
public void updateTrackStreams(TrackSelectionArray newTrackSelections,
......
......@@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import com.google.android.exoplayer.util.Assertions;
import android.util.Pair;
......@@ -46,13 +47,13 @@ public final class MultiSampleSource implements SampleSource {
}
@Override
public boolean prepare(long positionUs) throws IOException {
public boolean prepare(long positionUs, LoadControl loadControl) throws IOException {
if (prepared) {
return true;
}
boolean sourcesPrepared = true;
for (SampleSource source : sources) {
sourcesPrepared &= source.prepare(positionUs);
sourcesPrepared &= source.prepare(positionUs, loadControl);
}
if (!sourcesPrepared) {
return false;
......
......@@ -15,6 +15,8 @@
*/
package com.google.android.exoplayer;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import java.io.IOException;
import java.util.List;
......@@ -31,10 +33,11 @@ public interface SampleSource {
* tracks.
*
* @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.
* @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.
......
......@@ -111,8 +111,8 @@ public final class SimpleExoPlayer implements ExoPlayer {
private CodecCounters audioCodecCounters;
/* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector,
DrmSessionManager drmSessionManager, boolean preferExtensionDecoders, int minBufferMs,
int minRebufferMs, long allowedVideoJoiningTimeMs) {
BufferingPolicy bufferingPolicy, DrmSessionManager drmSessionManager,
boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) {
mainHandler = new Handler();
bandwidthMeter = new DefaultBandwidthMeter();
componentListener = new ComponentListener();
......@@ -145,7 +145,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
this.audioRendererCount = audioRendererCount;
// Build the player and associated objects.
player = new ExoPlayerImpl(renderers, trackSelector, minBufferMs, minRebufferMs);
player = new ExoPlayerImpl(renderers, trackSelector, bufferingPolicy);
}
/**
......
......@@ -15,6 +15,7 @@
*/
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.DataSpec;
import com.google.android.exoplayer.upstream.Loader;
......@@ -108,7 +109,8 @@ public final class SingleSampleSource implements SampleSource, TrackStream,
// SampleSource implementation.
@Override
public boolean prepare(long positionUs) {
public boolean prepare(long positionUs, LoadControl loadControl) {
// TODO: Use the load control.
return true;
}
......
......@@ -16,11 +16,11 @@
package com.google.android.exoplayer.chunk;
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.DecoderInputBuffer;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.FormatHolder;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.extractor.DefaultTrackOutput;
import com.google.android.exoplayer.upstream.Loader;
......@@ -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 chunkSource A {@link ChunkSource} from which chunks to load are obtained.
* @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 minLoadableRetryCount The minimum number of times that the source should retry a load
* before propagating an error.
* @param eventDispatcher A dispatcher to notify of events.
*/
public ChunkTrackStream(int trackType, T chunkSource, LoadControl loadControl,
int bufferSizeContribution, long positionUs, int minLoadableRetryCount,
public ChunkTrackStream(int trackType, T chunkSource, LoadControl loadControl, long positionUs,
int minLoadableRetryCount,
EventDispatcher eventDispatcher) {
this.trackType = trackType;
this.chunkSource = chunkSource;
......@@ -87,7 +86,7 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream,
readingEnabled = true;
downstreamPositionUs = positionUs;
lastSeekPositionUs = positionUs;
loadControl.register(this, bufferSizeContribution);
loadControl.register(this);
restartFrom(positionUs);
}
......@@ -287,7 +286,7 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream,
lastPreferredQueueSizeEvaluationTimeMs = now;
}
boolean isNext = loadControl.update(this, downstreamPositionUs, getNextLoadPositionUs(), false);
boolean isNext = loadControl.update(this, getNextLoadPositionUs(), false);
if (!isNext) {
return;
}
......@@ -301,7 +300,7 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream,
if (endOfStream) {
loadingFinished = true;
loadControl.update(this, downstreamPositionUs, C.UNSET_TIME_US, false);
loadControl.update(this, C.UNSET_TIME_US, false);
return;
}
......@@ -320,7 +319,7 @@ public class ChunkTrackStream<T extends ChunkSource> implements TrackStream,
loadable.formatEvaluatorTrigger, loadable.formatEvaluatorData, loadable.startTimeUs,
loadable.endTimeUs, elapsedRealtimeMs);
// 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;
import com.google.android.exoplayer.AdaptiveSourceEventListener;
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.DefaultLoadControl;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroup;
......@@ -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.DataSource;
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.Callback;
import com.google.android.exoplayer.upstream.ParsingLoadable;
......@@ -76,7 +74,6 @@ public final class DashSampleSource implements SampleSource {
private final DataSourceFactory dataSourceFactory;
private final BandwidthMeter bandwidthMeter;
private final EventDispatcher eventDispatcher;
private final LoadControl loadControl;
private final Loader loader;
private final DataSource dataSource;
private final MediaPresentationDescriptionParser manifestParser;
......@@ -87,6 +84,7 @@ public final class DashSampleSource implements SampleSource {
private long manifestLoadEndTimestamp;
private MediaPresentationDescription manifest;
private LoadControl loadControl;
private boolean prepared;
private long durationUs;
private long elapsedRealtimeOffset;
......@@ -101,7 +99,6 @@ public final class DashSampleSource implements SampleSource {
this.dataSourceFactory = dataSourceFactory;
this.bandwidthMeter = bandwidthMeter;
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
loadControl = new DefaultLoadControl(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
loader = new Loader("Loader:DashSampleSource");
dataSource = dataSourceFactory.createDataSource();
manifestParser = new MediaPresentationDescriptionParser();
......@@ -110,10 +107,11 @@ public final class DashSampleSource implements SampleSource {
}
@Override
public boolean prepare(long positionUs) throws IOException {
public boolean prepare(long positionUs, LoadControl loadControl) throws IOException {
if (prepared) {
return true;
}
this.loadControl = loadControl;
loader.maybeThrowError();
if (!loader.isLoading() && manifest == null) {
startLoadingManifest();
......@@ -361,13 +359,12 @@ public final class DashSampleSource implements SampleSource {
AdaptationSet adaptationSet = manifest.getPeriod(0).adaptationSets.get(
adaptationSetIndex);
int adaptationSetType = adaptationSet.type;
int bufferSize = Util.getDefaultBufferSize(adaptationSetType);
DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
DashChunkSource chunkSource = new DashChunkSource(loader, manifest, adaptationSetIndex,
trackGroups.get(selection.group), selectedTracks, dataSource, adaptiveEvaluator,
elapsedRealtimeOffset);
return new ChunkTrackStream<>(adaptationSetType, chunkSource, loadControl, bufferSize,
positionUs, MIN_LOADABLE_RETRY_COUNT, eventDispatcher);
return new ChunkTrackStream<>(adaptationSetType, chunkSource, loadControl, positionUs,
MIN_LOADABLE_RETRY_COUNT, eventDispatcher);
}
@SuppressWarnings("unchecked")
......
......@@ -15,11 +15,10 @@
*/
package com.google.android.exoplayer.extractor;
import com.google.android.exoplayer.BufferingPolicy.LoadControl;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.DecoderInputBuffer;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.FormatHolder;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroup;
......@@ -30,7 +29,6 @@ import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSourceFactory;
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.Loadable;
import com.google.android.exoplayer.util.Assertions;
......@@ -128,13 +126,13 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
private final EventListener eventListener;
private final DataSource dataSource;
private final ConditionVariable loadCondition;
private final LoadControl loadControl;
private final Loader loader;
private final ExtractorHolder extractorHolder;
private volatile boolean tracksBuilt;
private volatile SeekMap seekMap;
private LoadControl loadControl;
private boolean prepared;
private boolean seenFirstTrackSelection;
private boolean notifyReset;
......@@ -190,7 +188,6 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
this.eventHandler = eventHandler;
dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
loadCondition = new ConditionVariable();
loadControl = new DefaultLoadControl(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
loader = new Loader("Loader:ExtractorSampleSource");
extractorHolder = new ExtractorHolder(extractors, this);
pendingResetPositionUs = C.UNSET_TIME_US;
......@@ -307,10 +304,11 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
// SampleSource implementation.
@Override
public boolean prepare(long positionUs) throws IOException {
public boolean prepare(long positionUs, LoadControl loadControl) throws IOException {
if (prepared) {
return true;
}
this.loadControl = loadControl;
if (seekMap != null && tracksBuilt && haveFormatsForAllTracks()) {
loadCondition.close();
int trackCount = sampleQueues.length;
......@@ -388,7 +386,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
}
} else {
if (!tracksWereEnabled) {
loadControl.register(this, C.DEFAULT_MUXED_BUFFER_SIZE);
loadControl.register(this);
loadCondition.open();
}
if (seenFirstTrackSelection ? newStreams.length > 0 : positionUs != 0) {
......@@ -401,7 +399,7 @@ public final class ExtractorSampleSource implements SampleSource, ExtractorOutpu
@Override
public void continueBuffering(long playbackPositionUs) {
if (loadControl.update(this, playbackPositionUs, getBufferedPositionUs(), loader.isLoading())) {
if (loadControl.update(this, getBufferedPositionUs(), loader.isLoading())) {
loadCondition.open();
} else {
loadCondition.close();
......
......@@ -17,10 +17,9 @@ package com.google.android.exoplayer.hls;
import com.google.android.exoplayer.AdaptiveSourceEventListener;
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.DefaultLoadControl;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroup;
......@@ -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.DataSource;
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.ParsingLoadable;
import com.google.android.exoplayer.util.MimeTypes;
......@@ -66,13 +64,13 @@ public final class HlsSampleSource implements SampleSource,
private final DataSourceFactory dataSourceFactory;
private final BandwidthMeter bandwidthMeter;
private final EventDispatcher eventDispatcher;
private final LoadControl loadControl;
private final IdentityHashMap<TrackStream, HlsTrackStreamWrapper> trackStreamSources;
private final PtsTimestampAdjusterProvider timestampAdjusterProvider;
private final Loader manifestFetcher;
private final DataSource manifestDataSource;
private final HlsPlaylistParser manifestParser;
private LoadControl loadControl;
private boolean seenFirstTrackSelection;
private long durationUs;
private boolean isLive;
......@@ -89,7 +87,6 @@ public final class HlsSampleSource implements SampleSource,
this.bandwidthMeter = bandwidthMeter;
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
loadControl = new DefaultLoadControl(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
trackStreamSources = new IdentityHashMap<>();
......@@ -99,11 +96,12 @@ public final class HlsSampleSource implements SampleSource,
}
@Override
public boolean prepare(long positionUs) throws IOException {
public boolean prepare(long positionUs, LoadControl loadControl) throws IOException {
if (trackGroups != null) {
return true;
}
this.loadControl = loadControl;
if (trackStreamWrappers == null) {
manifestFetcher.maybeThrowError();
if (!manifestFetcher.isLoading()) {
......@@ -268,8 +266,7 @@ public final class HlsSampleSource implements SampleSource,
Format.NO_VALUE);
Variant[] variants = new Variant[] {new Variant(playlist.baseUri, format, null)};
trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_DEFAULT, baseUri, variants,
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter), C.DEFAULT_MUXED_BUFFER_SIZE,
null, null));
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter), null, null));
return trackStreamWrappers;
}
......@@ -302,8 +299,8 @@ public final class HlsSampleSource implements SampleSource,
Variant[] variants = new Variant[selectedVariants.size()];
selectedVariants.toArray(variants);
trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_DEFAULT, baseUri, variants,
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter), C.DEFAULT_MUXED_BUFFER_SIZE,
masterPlaylist.muxedAudioFormat, masterPlaylist.muxedCaptionFormat));
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter), masterPlaylist.muxedAudioFormat,
masterPlaylist.muxedCaptionFormat));
// Build the audio stream wrapper if applicable.
List<Variant> audioVariants = masterPlaylist.audios;
......@@ -311,7 +308,7 @@ public final class HlsSampleSource implements SampleSource,
variants = new Variant[audioVariants.size()];
audioVariants.toArray(variants);
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.
......@@ -320,20 +317,20 @@ public final class HlsSampleSource implements SampleSource,
variants = new Variant[subtitleVariants.size()];
subtitleVariants.toArray(variants);
trackStreamWrappers.add(buildTrackStreamWrapper(C.TRACK_TYPE_TEXT, baseUri, variants, null,
C.DEFAULT_TEXT_BUFFER_SIZE, null, null));
null, null));
}
return trackStreamWrappers;
}
private HlsTrackStreamWrapper buildTrackStreamWrapper(int trackType, String baseUri,
Variant[] variants, FormatEvaluator formatEvaluator, int bufferSize, Format muxedAudioFormat,
Variant[] variants, FormatEvaluator formatEvaluator, Format muxedAudioFormat,
Format muxedCaptionFormat) {
DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource defaultChunkSource = new HlsChunkSource(baseUri, variants, dataSource,
timestampAdjusterProvider, formatEvaluator);
return new HlsTrackStreamWrapper(trackType, defaultChunkSource, loadControl, bufferSize,
muxedAudioFormat, muxedCaptionFormat, MIN_LOADABLE_RETRY_COUNT, eventDispatcher);
return new HlsTrackStreamWrapper(trackType, defaultChunkSource, loadControl, muxedAudioFormat,
muxedCaptionFormat, MIN_LOADABLE_RETRY_COUNT, eventDispatcher);
}
private int selectTracks(HlsTrackStreamWrapper trackStreamWrapper,
......
......@@ -16,11 +16,11 @@
package com.google.android.exoplayer.hls;
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.DecoderInputBuffer;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.FormatHolder;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.TrackGroup;
import com.google.android.exoplayer.TrackGroupArray;
import com.google.android.exoplayer.TrackSelection;
......@@ -54,7 +54,6 @@ import java.util.List;
private final int trackType;
private final HlsChunkSource chunkSource;
private final LoadControl loadControl;
private final int bufferSizeContribution;
private final Format muxedAudioFormat;
private final Format muxedCaptionFormat;
private final int minLoadableRetryCount;
......@@ -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 chunkSource A {@link HlsChunkSource} from which chunks to load are obtained.
* @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,
* this is the audio {@link Format} as defined by the playlist.
* @param muxedCaptionFormat If HLS master playlist indicates that the stream contains muxed
......@@ -98,12 +96,11 @@ import java.util.List;
* @param eventDispatcher A dispatcher to notify of events.
*/
public HlsTrackStreamWrapper(int trackType, HlsChunkSource chunkSource, LoadControl loadControl,
int bufferSizeContribution, Format muxedAudioFormat, Format muxedCaptionFormat,
int minLoadableRetryCount, EventDispatcher eventDispatcher) {
Format muxedAudioFormat, Format muxedCaptionFormat, int minLoadableRetryCount,
EventDispatcher eventDispatcher) {
this.trackType = trackType;
this.chunkSource = chunkSource;
this.loadControl = loadControl;
this.bufferSizeContribution = bufferSizeContribution;
this.muxedAudioFormat = muxedAudioFormat;
this.muxedCaptionFormat = muxedCaptionFormat;
this.minLoadableRetryCount = minLoadableRetryCount;
......@@ -210,7 +207,7 @@ import java.util.List;
loader.cancelLoading();
}
} else if (!tracksWereEnabled) {
loadControl.register(this, bufferSizeContribution);
loadControl.register(this);
}
return newStreams;
}
......@@ -516,7 +513,7 @@ import java.util.List;
private void maybeStartLoading() {
boolean shouldStartLoading = !prepared || (enabledTrackCount > 0
&& loadControl.update(this, downstreamPositionUs, getNextLoadPositionUs(), false));
&& loadControl.update(this, getNextLoadPositionUs(), false));
if (!shouldStartLoading) {
return;
}
......@@ -531,7 +528,7 @@ import java.util.List;
if (endOfStream) {
loadingFinished = true;
if (prepared) {
loadControl.update(this, downstreamPositionUs, C.UNSET_TIME_US, false);
loadControl.update(this, C.UNSET_TIME_US, false);
}
return;
}
......@@ -552,7 +549,7 @@ import java.util.List;
loadable.endTimeUs, elapsedRealtimeMs);
if (prepared) {
// 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;
import com.google.android.exoplayer.AdaptiveSourceEventListener;
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.DefaultLoadControl;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroup;
......@@ -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.DataSource;
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.ParsingLoadable;
import com.google.android.exoplayer.util.Util;
......@@ -68,7 +66,6 @@ public final class SmoothStreamingSampleSource implements SampleSource,
private final DataSourceFactory dataSourceFactory;
private final BandwidthMeter bandwidthMeter;
private final EventDispatcher eventDispatcher;
private final LoadControl loadControl;
private final Loader manifestLoader;
private final DataSource manifestDataSource;
private final SmoothStreamingManifestParser manifestParser;
......@@ -76,6 +73,7 @@ public final class SmoothStreamingSampleSource implements SampleSource,
private long manifestLoadStartTimestamp;
private SmoothStreamingManifest manifest;
private LoadControl loadControl;
private boolean prepared;
private long durationUs;
private TrackEncryptionBox[] trackEncryptionBoxes;
......@@ -92,7 +90,6 @@ public final class SmoothStreamingSampleSource implements SampleSource,
this.dataSourceFactory = dataSourceFactory;
this.bandwidthMeter = bandwidthMeter;
this.eventDispatcher = new EventDispatcher(eventHandler, eventListener);
loadControl = new DefaultLoadControl(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
trackStreams = newTrackStreamArray(0);
manifestDataSource = dataSourceFactory.createDataSource();
manifestParser = new SmoothStreamingManifestParser();
......@@ -100,10 +97,11 @@ public final class SmoothStreamingSampleSource implements SampleSource,
}
@Override
public boolean prepare(long positionUs) throws IOException {
public boolean prepare(long positionUs, LoadControl loadControl) throws IOException {
if (prepared) {
return true;
}
this.loadControl = loadControl;
manifestLoader.maybeThrowError();
if (!manifestLoader.isLoading()) {
startLoadingManifest();
......@@ -282,13 +280,12 @@ public final class SmoothStreamingSampleSource implements SampleSource,
int streamElementIndex = trackGroupElementIndices[selection.group];
StreamElement streamElement = manifest.streamElements[streamElementIndex];
int streamElementType = streamElement.type;
int bufferSize = Util.getDefaultBufferSize(streamElementType);
DataSource dataSource = dataSourceFactory.createDataSource(bandwidthMeter);
SmoothStreamingChunkSource chunkSource = new SmoothStreamingChunkSource(manifestLoader,
manifest, streamElementIndex, trackGroups.get(selection.group), selectedTracks, dataSource,
adaptiveEvaluator, trackEncryptionBoxes);
return new ChunkTrackStream<>(streamElementType, chunkSource, loadControl, bufferSize,
positionUs, MIN_LOADABLE_RETRY_COUNT, eventDispatcher);
return new ChunkTrackStream<>(streamElementType, chunkSource, loadControl, positionUs,
MIN_LOADABLE_RETRY_COUNT, eventDispatcher);
}
@SuppressWarnings("unchecked")
......
......@@ -36,6 +36,7 @@ import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.view.Surface;
import junit.framework.Assert;
/**
......@@ -276,8 +277,7 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
@SuppressWarnings("unused")
protected SimpleExoPlayer buildExoPlayer(HostActivity host, Surface surface,
DefaultTrackSelector trackSelector) {
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(host, trackSelector, null, false,
ExoPlayerFactory.DEFAULT_MIN_BUFFER_MS, ExoPlayerFactory.DEFAULT_MIN_REBUFFER_MS, 0);
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(host, trackSelector);
player.setSurface(surface);
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