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
This commit is contained in:
olly 2016-06-28 02:25:07 -07:00 committed by Oliver Woodman
parent fdd90256f4
commit 76f426e944
18 changed files with 341 additions and 259 deletions

View File

@ -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);

View File

@ -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.
* <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);
}
}

View File

@ -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;

View File

@ -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.
* <p>
* 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.
* <p>
* 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);
}
}

View File

@ -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);
}

View File

@ -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,

View File

@ -1,66 +0,0 @@
/*
* 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;
/**
* Coordinates multiple loaders of time series data.
*/
public interface LoadControl {
/**
* Registers a loader.
*
* @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.
*/
void register(Object loader, int bufferSizeContribution);
/**
* 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 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);
}

View File

@ -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;

View File

@ -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.

View File

@ -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);
}
/**

View File

@ -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;
}

View File

@ -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);
}
/**

View File

@ -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")

View File

@ -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();

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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")

View File

@ -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;
}