diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java index f9bc64d3e4..18708a96e0 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java @@ -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); diff --git a/library/src/main/java/com/google/android/exoplayer/BufferingPolicy.java b/library/src/main/java/com/google/android/exoplayer/BufferingPolicy.java new file mode 100644 index 0000000000..1b122dbd36 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/BufferingPolicy.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer; + +import com.google.android.exoplayer.upstream.Allocator; + +/** + * A media buffering policy. + */ +public interface BufferingPolicy { + + /** + * Invoked by the player to update the playback position. + * + * @param playbackPositionUs The current playback position in microseconds. + */ + void setPlaybackPosition(long playbackPositionUs); + + /** + * Invoked by the player to determine whether sufficient media is buffered for playback to be + * started or resumed. + * + * @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. + */ + boolean haveSufficientBuffer(long bufferedPositionUs, boolean rebuffering); + + /** + * Invoked by the player when a track selection occurs. + * + * @param renderers The renderers. + * @param trackGroups The available {@link TrackGroup}s. + * @param trackSelections The {@link TrackSelection}s that were made. + */ + void onTrackSelections(TrackRenderer[] renderers, TrackGroupArray trackGroups, + TrackSelectionArray trackSelections); + + /** + * 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. + * + * @return The {@link LoadControl}. + */ + LoadControl getLoadControl(); + + /** + * Coordinates multiple loaders of time series data. + */ + 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. + *
+ * 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); + + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer/DefaultLoadControl.java b/library/src/main/java/com/google/android/exoplayer/DefaultBufferingPolicy.java similarity index 61% rename from library/src/main/java/com/google/android/exoplayer/DefaultLoadControl.java rename to library/src/main/java/com/google/android/exoplayer/DefaultBufferingPolicy.java index 0e23c872e4..f760306e58 100644 --- a/library/src/main/java/com/google/android/exoplayer/DefaultLoadControl.java +++ b/library/src/main/java/com/google/android/exoplayer/DefaultBufferingPolicy.java @@ -15,9 +15,11 @@ */ package com.google.android.exoplayer; +import com.google.android.exoplayer.BufferingPolicy.LoadControl; import com.google.android.exoplayer.upstream.Allocator; import com.google.android.exoplayer.upstream.DefaultAllocator; import com.google.android.exoplayer.upstream.NetworkLock; +import com.google.android.exoplayer.util.Util; import android.os.Handler; @@ -40,10 +42,10 @@ import java.util.List; * itself as a task with priority {@link NetworkLock#STREAMING_PRIORITY} during loading periods, * and unregistering itself during draining periods. */ -public final class DefaultLoadControl implements LoadControl { +public final class DefaultBufferingPolicy implements BufferingPolicy, LoadControl { /** - * Interface definition for a callback to be notified of {@link DefaultLoadControl} events. + * Interface definition for a callback to be notified of {@link DefaultBufferingPolicy} events. */ public interface EventListener { @@ -56,8 +58,29 @@ public final class DefaultLoadControl implements LoadControl { } - public static final int DEFAULT_LOW_WATERMARK_MS = 15000; - public static final int DEFAULT_HIGH_WATERMARK_MS = 30000; + /** + * The default minimum duration of media that the player will attempt to ensure is buffered at all + * times, in milliseconds. + */ + public static final int DEFAULT_MIN_BUFFER_MS = 15000; + + /** + * The default maximum duration of media that the player will attempt to buffer, in milliseconds. + */ + public static final int DEFAULT_MAX_BUFFER_MS = 30000; + + /** + * The default duration of media that must be buffered for playback to start or resume following a + * user action such as a seek, in milliseconds. + */ + public static final int DEFAULT_BUFFER_FOR_PLAYBACK_MS = 2500; + + /** + * The default duration of media that must be buffered for playback to resume after a + * player-invoked rebuffer (i.e. a rebuffer that occurs due to buffer depletion rather than a user + * action), in milliseconds. + */ + public static final int DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = 5000; private static final int ABOVE_HIGH_WATERMARK = 0; private static final int BETWEEN_WATERMARKS = 1; @@ -69,22 +92,35 @@ public final class DefaultLoadControl implements LoadControl { private final Handler eventHandler; private final EventListener eventListener; - private final long lowWatermarkUs; - private final long highWatermarkUs; + private final long minBufferUs; + private final long maxBufferUs; + private final long bufferForPlaybackUs; + private final long bufferForPlaybackAfterRebufferUs; - private long maxLoadStartPositionUs; private int targetBufferSize; private boolean targetBufferSizeReached; private boolean fillingBuffers; private boolean streamingPrioritySet; + private long playbackPositionUs; + private long maxLoadStartPositionUs; + + /** + * Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class. + */ + public DefaultBufferingPolicy() { + this(null, null); + } + /** * Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class. * - * @param allocator The {@link DefaultAllocator} used by the loader. + * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be + * null if delivery of events is not required. + * @param eventListener A listener of events. May be null if delivery of events is not required. */ - public DefaultLoadControl(DefaultAllocator allocator) { - this(allocator, null, null); + public DefaultBufferingPolicy(Handler eventHandler, EventListener eventListener) { + this(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE), eventHandler, eventListener); } /** @@ -95,10 +131,10 @@ public final class DefaultLoadControl implements LoadControl { * null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. */ - public DefaultLoadControl(DefaultAllocator allocator, Handler eventHandler, + public DefaultBufferingPolicy(DefaultAllocator allocator, Handler eventHandler, EventListener eventListener) { - this(allocator, eventHandler, eventListener, DEFAULT_LOW_WATERMARK_MS, - DEFAULT_HIGH_WATERMARK_MS); + this(allocator, eventHandler, eventListener, DEFAULT_MIN_BUFFER_MS, DEFAULT_MAX_BUFFER_MS, + DEFAULT_BUFFER_FOR_PLAYBACK_MS, DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS); } /** @@ -108,39 +144,81 @@ public final class DefaultLoadControl implements LoadControl { * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be * null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. - * @param lowWatermarkMs The minimum duration of media that can be buffered for the control to - * be in the draining state. If less media is buffered, then the control will transition to - * the filling state. - * @param highWatermarkMs The minimum duration of media that can be buffered for the control to - * transition from filling to draining. + * @param minBufferMs The minimum duration of media that the player will attempt to ensure is + * buffered at all times, in milliseconds. + * @param maxBufferMs The maximum duration of media that the player will attempt buffer, in + * milliseconds. + * @param bufferForPlaybackMs The duration of media that must be buffered for playback to start or + * resume following a user action such as a seek, in milliseconds. + * @param bufferForPlaybackAfterRebufferMs The default duration of media that must be buffered for + * playback to resume after a player-invoked rebuffer (i.e. a rebuffer that occurs due to + * buffer depletion rather than a user action), in milliseconds. */ - public DefaultLoadControl(DefaultAllocator allocator, Handler eventHandler, - EventListener eventListener, int lowWatermarkMs, int highWatermarkMs) { + public DefaultBufferingPolicy(DefaultAllocator allocator, Handler eventHandler, + EventListener eventListener, int minBufferMs, int maxBufferMs, long bufferForPlaybackMs, + long bufferForPlaybackAfterRebufferMs) { this.allocator = allocator; this.eventHandler = eventHandler; this.eventListener = eventListener; - this.loaders = new ArrayList<>(); - this.loaderStates = new HashMap<>(); - this.lowWatermarkUs = lowWatermarkMs * 1000L; - this.highWatermarkUs = highWatermarkMs * 1000L; + this.minBufferUs = minBufferMs * 1000L; + this.maxBufferUs = maxBufferMs * 1000L; + this.bufferForPlaybackUs = bufferForPlaybackMs * 1000L; + this.bufferForPlaybackAfterRebufferUs = bufferForPlaybackAfterRebufferMs * 1000L; + loaders = new ArrayList<>(); + loaderStates = new HashMap<>(); + } + + // BufferingPolicy implementation. + + @Override + public void setPlaybackPosition(long playbackPositionUs) { + this.playbackPositionUs = playbackPositionUs; } @Override - public void register(Object loader, int bufferSizeContribution) { - loaders.add(loader); - loaderStates.put(loader, new LoaderState(bufferSizeContribution)); - targetBufferSize += bufferSizeContribution; + public boolean haveSufficientBuffer(long bufferedPositionUs, boolean rebuffering) { + long minBufferDurationUs = rebuffering ? bufferForPlaybackAfterRebufferUs : bufferForPlaybackUs; + return minBufferDurationUs <= 0 + || bufferedPositionUs == C.END_OF_SOURCE_US + || bufferedPositionUs >= playbackPositionUs + minBufferDurationUs; + } + + @Override + public void onTrackSelections(TrackRenderer[] renderers, TrackGroupArray trackGroups, + TrackSelectionArray trackSelections) { + targetBufferSize = 0; + for (int i = 0; i < renderers.length; i++) { + if (trackSelections.get(i) != null) { + targetBufferSize += Util.getDefaultBufferSize(renderers[i].getTrackType()); + } + } allocator.setTargetBufferSize(targetBufferSize); + targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize; + updateControlState(); + } + + @Override + public void reset() { + targetBufferSize = 0; + } + + @Override + public LoadControl getLoadControl() { + return this; + } + + // LoadControl implementation. + + @Override + public void register(Object loader) { + loaders.add(loader); + loaderStates.put(loader, new LoaderState()); } @Override public void unregister(Object loader) { loaders.remove(loader); - LoaderState state = loaderStates.remove(loader); - targetBufferSize -= state.bufferSizeContribution; - allocator.setTargetBufferSize(targetBufferSize); - targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize; - updateControlState(); + loaderStates.remove(loader); } @Override @@ -149,8 +227,7 @@ public final class DefaultLoadControl implements LoadControl { } @Override - public boolean update(Object loader, long playbackPositionUs, long nextLoadPositionUs, - boolean loading) { + public boolean update(Object loader, long nextLoadPositionUs, boolean loading) { // Update the loader state. int loaderBufferState = getLoaderBufferState(playbackPositionUs, nextLoadPositionUs); LoaderState loaderState = loaderStates.get(loader); @@ -182,8 +259,8 @@ public final class DefaultLoadControl implements LoadControl { return ABOVE_HIGH_WATERMARK; } else { long timeUntilNextLoadPosition = nextLoadPositionUs - playbackPositionUs; - return timeUntilNextLoadPosition > highWatermarkUs ? ABOVE_HIGH_WATERMARK : - timeUntilNextLoadPosition < lowWatermarkUs ? BELOW_LOW_WATERMARK : + return timeUntilNextLoadPosition > maxBufferUs ? ABOVE_HIGH_WATERMARK : + timeUntilNextLoadPosition < minBufferUs ? BELOW_LOW_WATERMARK : BETWEEN_WATERMARKS; } } @@ -239,14 +316,11 @@ public final class DefaultLoadControl implements LoadControl { private static class LoaderState { - public final int bufferSizeContribution; - public int bufferState; public boolean loading; public long nextLoadPositionUs; - public LoaderState(int bufferSizeContribution) { - this.bufferSizeContribution = bufferSizeContribution; + public LoaderState() { bufferState = ABOVE_HIGH_WATERMARK; loading = false; nextLoadPositionUs = C.UNSET_TIME_US; diff --git a/library/src/main/java/com/google/android/exoplayer/ExoPlayerFactory.java b/library/src/main/java/com/google/android/exoplayer/ExoPlayerFactory.java index 6251622f63..c2d6692170 100644 --- a/library/src/main/java/com/google/android/exoplayer/ExoPlayerFactory.java +++ b/library/src/main/java/com/google/android/exoplayer/ExoPlayerFactory.java @@ -25,19 +25,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,42 +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); - } - - /** - * Obtains an {@link ExoPlayer} instance. - *
- * Must be called from a thread that has an associated {@link Looper}. - * - * @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); + BufferingPolicy bufferingPolicy, DrmSessionManager drmSessionManager, + boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) { + return new SimpleExoPlayer(context, trackSelector, bufferingPolicy, drmSessionManager, + preferExtensionDecoders, allowedVideoJoiningTimeMs); } /** @@ -146,8 +114,21 @@ public final class ExoPlayerFactory { * @param trackSelector The {@link TrackSelector} 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); + return newInstance(renderers, trackSelector, new DefaultBufferingPolicy()); + } + + /** + * Obtains an {@link ExoPlayer} instance. + *
+ * Must be called from a thread that has an associated {@link Looper}.
+ *
+ * @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,
+ BufferingPolicy bufferingPolicy) {
+ return new ExoPlayerImpl(renderers, trackSelector, bufferingPolicy);
}
}
diff --git a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImpl.java b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImpl.java
index f673899cb2..bab2257ebc 100644
--- a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImpl.java
+++ b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImpl.java
@@ -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);
}
diff --git a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java
index dfb71c9240..104d7ca3ea 100644
--- a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java
+++ b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java
@@ -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
- * 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 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.
- */
- boolean update(Object loader, long playbackPositionUs, long nextLoadPositionUs, boolean loading);
-
-}
diff --git a/library/src/main/java/com/google/android/exoplayer/MultiSampleSource.java b/library/src/main/java/com/google/android/exoplayer/MultiSampleSource.java
index 234fb37258..27deef1a00 100644
--- a/library/src/main/java/com/google/android/exoplayer/MultiSampleSource.java
+++ b/library/src/main/java/com/google/android/exoplayer/MultiSampleSource.java
@@ -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;
diff --git a/library/src/main/java/com/google/android/exoplayer/SampleSource.java b/library/src/main/java/com/google/android/exoplayer/SampleSource.java
index c8ed5334ac..824f080220 100644
--- a/library/src/main/java/com/google/android/exoplayer/SampleSource.java
+++ b/library/src/main/java/com/google/android/exoplayer/SampleSource.java
@@ -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.
diff --git a/library/src/main/java/com/google/android/exoplayer/SimpleExoPlayer.java b/library/src/main/java/com/google/android/exoplayer/SimpleExoPlayer.java
index c4bb467970..c6834215ae 100644
--- a/library/src/main/java/com/google/android/exoplayer/SimpleExoPlayer.java
+++ b/library/src/main/java/com/google/android/exoplayer/SimpleExoPlayer.java
@@ -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);
}
/**
diff --git a/library/src/main/java/com/google/android/exoplayer/SingleSampleSource.java b/library/src/main/java/com/google/android/exoplayer/SingleSampleSource.java
index 4f228724bc..e08a2bb3ef 100644
--- a/library/src/main/java/com/google/android/exoplayer/SingleSampleSource.java
+++ b/library/src/main/java/com/google/android/exoplayer/SingleSampleSource.java
@@ -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;
}
diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkTrackStream.java b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkTrackStream.java
index 8b194e2cda..c69c06f8e9 100644
--- a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkTrackStream.java
+++ b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkTrackStream.java
@@ -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