mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Report loading on/off changes via ExoPlayer.
Also attempt to clear up naming a little, using "buffering" to mean the user visible buffering state, and "loading" to mean a source being loaded. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=126693592
This commit is contained in:
parent
f4239eb571
commit
19d65a7a9d
14 changed files with 180 additions and 156 deletions
|
|
@ -66,6 +66,11 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb
|
|||
|
||||
// ExoPlayer.EventListener
|
||||
|
||||
@Override
|
||||
public void onLoadingChanged(boolean isLoading) {
|
||||
Log.d(TAG, "loading [" + isLoading + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int state) {
|
||||
Log.d(TAG, "state [" + getSessionTimeString() + ", " + playWhenReady + ", "
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package com.google.android.exoplayer.demo;
|
|||
import com.google.android.exoplayer.AspectRatioFrameLayout;
|
||||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.ConcatenatingSampleSourceProvider;
|
||||
import com.google.android.exoplayer.DefaultBufferingControl;
|
||||
import com.google.android.exoplayer.DefaultLoadControl;
|
||||
import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
|
||||
import com.google.android.exoplayer.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
|
||||
|
|
@ -268,8 +268,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
|||
trackSelector.addListener(this);
|
||||
trackSelector.addListener(eventLogger);
|
||||
trackSelectionHelper = new TrackSelectionHelper(trackSelector);
|
||||
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector,
|
||||
new DefaultBufferingControl(), drmSessionManager, preferExtensionDecoders);
|
||||
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, new DefaultLoadControl(),
|
||||
drmSessionManager, preferExtensionDecoders);
|
||||
player.addListener(this);
|
||||
player.addListener(eventLogger);
|
||||
player.setDebugListener(eventLogger);
|
||||
|
|
@ -388,6 +388,11 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
|||
|
||||
// ExoPlayer.EventListener implementation
|
||||
|
||||
@Override
|
||||
public void onLoadingChanged(boolean isLoading) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
if (playbackState == ExoPlayer.STATE_ENDED) {
|
||||
|
|
|
|||
|
|
@ -88,6 +88,11 @@ public class FlacPlaybackTest extends InstrumentationTestCase {
|
|||
Looper.loop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingChanged(boolean isLoading) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayWhenReadyCommitted () {
|
||||
// Do nothing.
|
||||
|
|
|
|||
|
|
@ -88,6 +88,11 @@ public class OpusPlaybackTest extends InstrumentationTestCase {
|
|||
Looper.loop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingChanged(boolean isLoading) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayWhenReadyCommitted () {
|
||||
// Do nothing.
|
||||
|
|
|
|||
|
|
@ -107,6 +107,11 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
|
|||
Looper.loop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingChanged(boolean isLoading) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayWhenReadyCommitted () {
|
||||
// Do nothing.
|
||||
|
|
|
|||
|
|
@ -19,26 +19,10 @@ import com.google.android.exoplayer.upstream.Allocator;
|
|||
import com.google.android.exoplayer.upstream.DefaultAllocator;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.os.Handler;
|
||||
|
||||
/**
|
||||
* The default {@link BufferingControl} implementation.
|
||||
* The default {@link LoadControl} implementation.
|
||||
*/
|
||||
public final class DefaultBufferingControl implements BufferingControl {
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be notified of {@link DefaultBufferingControl} events.
|
||||
*/
|
||||
public interface EventListener {
|
||||
|
||||
/**
|
||||
* Invoked when the control transitions from a buffering to a draining state or vice versa.
|
||||
*
|
||||
* @param buffering Whether the control is now in the buffering state.
|
||||
*/
|
||||
void onBufferingChanged(boolean buffering);
|
||||
|
||||
}
|
||||
public final class DefaultLoadControl implements LoadControl {
|
||||
|
||||
/**
|
||||
* The default minimum duration of media that the player will attempt to ensure is buffered at all
|
||||
|
|
@ -69,8 +53,6 @@ public final class DefaultBufferingControl implements BufferingControl {
|
|||
private static final int BELOW_LOW_WATERMARK = 2;
|
||||
|
||||
private final DefaultAllocator allocator;
|
||||
private final Handler eventHandler;
|
||||
private final EventListener eventListener;
|
||||
|
||||
private final long minBufferUs;
|
||||
private final long maxBufferUs;
|
||||
|
|
@ -83,7 +65,7 @@ public final class DefaultBufferingControl implements BufferingControl {
|
|||
/**
|
||||
* Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class.
|
||||
*/
|
||||
public DefaultBufferingControl() {
|
||||
public DefaultLoadControl() {
|
||||
this(new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
|
||||
}
|
||||
|
||||
|
|
@ -92,22 +74,9 @@ public final class DefaultBufferingControl implements BufferingControl {
|
|||
*
|
||||
* @param allocator The {@link DefaultAllocator} used by the loader.
|
||||
*/
|
||||
public DefaultBufferingControl(DefaultAllocator allocator) {
|
||||
this(allocator, 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 DefaultBufferingControl(DefaultAllocator allocator, Handler eventHandler,
|
||||
EventListener eventListener) {
|
||||
public DefaultLoadControl(DefaultAllocator allocator) {
|
||||
this(allocator, DEFAULT_MIN_BUFFER_MS, DEFAULT_MAX_BUFFER_MS, DEFAULT_BUFFER_FOR_PLAYBACK_MS,
|
||||
DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, eventHandler, eventListener);
|
||||
DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -123,16 +92,10 @@ public final class DefaultBufferingControl implements BufferingControl {
|
|||
* @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.
|
||||
* @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 DefaultBufferingControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs,
|
||||
long bufferForPlaybackMs, long bufferForPlaybackAfterRebufferMs, Handler eventHandler,
|
||||
EventListener eventListener) {
|
||||
public DefaultLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs,
|
||||
long bufferForPlaybackMs, long bufferForPlaybackAfterRebufferMs) {
|
||||
this.allocator = allocator;
|
||||
this.eventHandler = eventHandler;
|
||||
this.eventListener = eventListener;
|
||||
minBufferUs = minBufferMs * 1000L;
|
||||
maxBufferUs = maxBufferMs * 1000L;
|
||||
bufferForPlaybackUs = bufferForPlaybackMs * 1000L;
|
||||
|
|
@ -154,7 +117,7 @@ public final class DefaultBufferingControl implements BufferingControl {
|
|||
@Override
|
||||
public void reset() {
|
||||
targetBufferSize = 0;
|
||||
setBuffering(false);
|
||||
isBuffering = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -169,20 +132,12 @@ public final class DefaultBufferingControl implements BufferingControl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldContinueBuffering(long bufferedDurationUs) {
|
||||
public boolean shouldContinueLoading(long bufferedDurationUs) {
|
||||
int bufferTimeState = getBufferTimeState(bufferedDurationUs);
|
||||
boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize;
|
||||
boolean shouldBuffer = bufferTimeState == BELOW_LOW_WATERMARK
|
||||
isBuffering = bufferTimeState == BELOW_LOW_WATERMARK
|
||||
|| (bufferTimeState == BETWEEN_WATERMARKS && isBuffering && !targetBufferSizeReached);
|
||||
setBuffering(shouldBuffer);
|
||||
return shouldBuffer;
|
||||
}
|
||||
|
||||
private void setBuffering(boolean isBuffering) {
|
||||
if (this.isBuffering != isBuffering) {
|
||||
this.isBuffering = isBuffering;
|
||||
notifyBufferingChanged(isBuffering);
|
||||
}
|
||||
return isBuffering;
|
||||
}
|
||||
|
||||
private int getBufferTimeState(long bufferedDurationUs) {
|
||||
|
|
@ -190,15 +145,4 @@ public final class DefaultBufferingControl implements BufferingControl {
|
|||
: (bufferedDurationUs < minBufferUs ? BELOW_LOW_WATERMARK : BETWEEN_WATERMARKS);
|
||||
}
|
||||
|
||||
private void notifyBufferingChanged(final boolean buffering) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onBufferingChanged(buffering);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -97,6 +97,13 @@ public interface ExoPlayer {
|
|||
*/
|
||||
interface EventListener {
|
||||
|
||||
/**
|
||||
* Invoked when the player starts or stops loading the source.
|
||||
*
|
||||
* @param isLoading Whether the source is currently being loaded.
|
||||
*/
|
||||
void onLoadingChanged(boolean isLoading);
|
||||
|
||||
/**
|
||||
* Invoked when the value returned from either {@link ExoPlayer#getPlayWhenReady()} or
|
||||
* {@link ExoPlayer#getPlaybackState()} changes.
|
||||
|
|
@ -269,6 +276,13 @@ public interface ExoPlayer {
|
|||
*/
|
||||
boolean isPlayWhenReadyCommitted();
|
||||
|
||||
/**
|
||||
* Whether the player is currently loading the source.
|
||||
*
|
||||
* @return True if the player is currently loading the source. False otherwise.
|
||||
*/
|
||||
boolean isLoading();
|
||||
|
||||
/**
|
||||
* Seeks to a position specified in milliseconds in the current source.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -42,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, new DefaultBufferingControl(), null);
|
||||
return newSimpleInstance(context, trackSelector, new DefaultLoadControl(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -52,13 +52,13 @@ public final class ExoPlayerFactory {
|
|||
*
|
||||
* @param context A {@link Context}.
|
||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||
* @param bufferingControl The {@link BufferingControl} that will be used by the instance.
|
||||
* @param loadControl The {@link LoadControl} 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,
|
||||
BufferingControl bufferingControl, DrmSessionManager drmSessionManager) {
|
||||
return newSimpleInstance(context, trackSelector, bufferingControl, drmSessionManager, false);
|
||||
LoadControl loadControl, DrmSessionManager drmSessionManager) {
|
||||
return newSimpleInstance(context, trackSelector, loadControl, drmSessionManager, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -68,7 +68,7 @@ public final class ExoPlayerFactory {
|
|||
*
|
||||
* @param context A {@link Context}.
|
||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||
* @param bufferingControl The {@link BufferingControl} that will be used by the instance.
|
||||
* @param loadControl The {@link LoadControl} 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
|
||||
|
|
@ -76,9 +76,9 @@ 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,
|
||||
BufferingControl bufferingControl, DrmSessionManager drmSessionManager,
|
||||
LoadControl loadControl, DrmSessionManager drmSessionManager,
|
||||
boolean preferExtensionDecoders) {
|
||||
return newSimpleInstance(context, trackSelector, bufferingControl, drmSessionManager,
|
||||
return newSimpleInstance(context, trackSelector, loadControl, drmSessionManager,
|
||||
preferExtensionDecoders, DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS);
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ public final class ExoPlayerFactory {
|
|||
*
|
||||
* @param context A {@link Context}.
|
||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||
* @param bufferingControl The {@link BufferingControl} that will be used by the instance.
|
||||
* @param loadControl The {@link LoadControl} 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
|
||||
|
|
@ -99,9 +99,9 @@ public final class ExoPlayerFactory {
|
|||
* seamlessly join an ongoing playback.
|
||||
*/
|
||||
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
|
||||
BufferingControl bufferingControl, DrmSessionManager drmSessionManager,
|
||||
LoadControl loadControl, DrmSessionManager drmSessionManager,
|
||||
boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) {
|
||||
return new SimpleExoPlayer(context, trackSelector, bufferingControl, drmSessionManager,
|
||||
return new SimpleExoPlayer(context, trackSelector, loadControl, drmSessionManager,
|
||||
preferExtensionDecoders, allowedVideoJoiningTimeMs);
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ 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 newInstance(renderers, trackSelector, new DefaultBufferingControl());
|
||||
return newInstance(renderers, trackSelector, new DefaultLoadControl());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -124,11 +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 bufferingControl The {@link BufferingControl} that will be used by the instance.
|
||||
* @param loadControl The {@link LoadControl} that will be used by the instance.
|
||||
*/
|
||||
public static ExoPlayer newInstance(TrackRenderer[] renderers, TrackSelector trackSelector,
|
||||
BufferingControl bufferingControl) {
|
||||
return new ExoPlayerImpl(renderers, trackSelector, bufferingControl);
|
||||
LoadControl loadControl) {
|
||||
return new ExoPlayerImpl(renderers, trackSelector, loadControl);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
private int playbackState;
|
||||
private int pendingPlayWhenReadyAcks;
|
||||
private int pendingSetSourceProviderAndSeekAcks;
|
||||
private boolean isLoading;
|
||||
|
||||
// Playback information when there is no pending seek/set source operation.
|
||||
private PlaybackInfo playbackInfo;
|
||||
|
|
@ -55,11 +56,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 bufferingControl The {@link BufferingControl} that will be used by the instance.
|
||||
* @param loadControl The {@link LoadControl} that will be used by the instance.
|
||||
*/
|
||||
@SuppressLint("HandlerLeak")
|
||||
public ExoPlayerImpl(TrackRenderer[] renderers, TrackSelector trackSelector,
|
||||
BufferingControl bufferingControl) {
|
||||
LoadControl loadControl) {
|
||||
Log.i(TAG, "Init " + ExoPlayerLibraryInfo.VERSION);
|
||||
Assertions.checkNotNull(renderers);
|
||||
Assertions.checkState(renderers.length > 0);
|
||||
|
|
@ -72,8 +73,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
ExoPlayerImpl.this.handleEvent(msg);
|
||||
}
|
||||
};
|
||||
internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, bufferingControl,
|
||||
playWhenReady, eventHandler);
|
||||
internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, loadControl, playWhenReady,
|
||||
eventHandler);
|
||||
playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0);
|
||||
}
|
||||
|
||||
|
|
@ -132,6 +133,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
return pendingPlayWhenReadyAcks == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoading() {
|
||||
return isLoading;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekTo(long positionMs) {
|
||||
seekTo(getCurrentSourceIndex(), positionMs);
|
||||
|
|
@ -221,6 +227,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
}
|
||||
break;
|
||||
}
|
||||
case ExoPlayerImplInternal.MSG_LOADING_CHANGED: {
|
||||
isLoading = msg.arg1 != 0;
|
||||
for (EventListener listener : listeners) {
|
||||
listener.onLoadingChanged(isLoading);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExoPlayerImplInternal.MSG_SET_PLAY_WHEN_READY_ACK: {
|
||||
pendingPlayWhenReadyAcks--;
|
||||
if (pendingPlayWhenReadyAcks == 0) {
|
||||
|
|
|
|||
|
|
@ -35,8 +35,6 @@ import java.util.ArrayList;
|
|||
/**
|
||||
* Implements the internal behavior of {@link ExoPlayerImpl}.
|
||||
*/
|
||||
// TODO[REFACTOR]: Make sure renderer errors that will prevent prepare from being called again are
|
||||
// always propagated properly.
|
||||
/* package */ final class ExoPlayerImplInternal implements Handler.Callback, SampleSource.Callback,
|
||||
InvalidationListener {
|
||||
|
||||
|
|
@ -63,11 +61,12 @@ import java.util.ArrayList;
|
|||
|
||||
// External messages
|
||||
public static final int MSG_STATE_CHANGED = 1;
|
||||
public static final int MSG_SET_PLAY_WHEN_READY_ACK = 2;
|
||||
public static final int MSG_SET_SOURCE_PROVIDER_ACK = 3;
|
||||
public static final int MSG_SEEK_ACK = 4;
|
||||
public static final int MSG_SOURCE_CHANGED = 5;
|
||||
public static final int MSG_ERROR = 6;
|
||||
public static final int MSG_LOADING_CHANGED = 2;
|
||||
public static final int MSG_SET_PLAY_WHEN_READY_ACK = 3;
|
||||
public static final int MSG_SET_SOURCE_PROVIDER_ACK = 4;
|
||||
public static final int MSG_SEEK_ACK = 5;
|
||||
public static final int MSG_SOURCE_CHANGED = 6;
|
||||
public static final int MSG_ERROR = 7;
|
||||
|
||||
// Internal messages
|
||||
private static final int MSG_SET_SOURCE_PROVIDER = 0;
|
||||
|
|
@ -93,7 +92,7 @@ import java.util.ArrayList;
|
|||
private static final int MAXIMUM_BUFFER_AHEAD_SOURCES = 100;
|
||||
|
||||
private final TrackSelector trackSelector;
|
||||
private final BufferingControl bufferingControl;
|
||||
private final LoadControl loadControl;
|
||||
private final StandaloneMediaClock standaloneMediaClock;
|
||||
private final Handler handler;
|
||||
private final HandlerThread internalPlaybackThread;
|
||||
|
|
@ -108,6 +107,7 @@ import java.util.ArrayList;
|
|||
private boolean released;
|
||||
private boolean playWhenReady;
|
||||
private boolean rebuffering;
|
||||
private boolean isLoading;
|
||||
private int state;
|
||||
private int customMessagesSent;
|
||||
private int customMessagesProcessed;
|
||||
|
|
@ -116,9 +116,9 @@ import java.util.ArrayList;
|
|||
private long internalPositionUs;
|
||||
|
||||
public ExoPlayerImplInternal(TrackRenderer[] renderers, TrackSelector trackSelector,
|
||||
BufferingControl bufferingControl, boolean playWhenReady, Handler eventHandler) {
|
||||
LoadControl loadControl, boolean playWhenReady, Handler eventHandler) {
|
||||
this.trackSelector = trackSelector;
|
||||
this.bufferingControl = bufferingControl;
|
||||
this.loadControl = loadControl;
|
||||
this.playWhenReady = playWhenReady;
|
||||
this.eventHandler = eventHandler;
|
||||
this.state = ExoPlayer.STATE_IDLE;
|
||||
|
|
@ -294,6 +294,13 @@ import java.util.ArrayList;
|
|||
}
|
||||
}
|
||||
|
||||
private void setIsLoading(boolean isLoading) {
|
||||
if (this.isLoading != isLoading) {
|
||||
this.isLoading = isLoading;
|
||||
eventHandler.obtainMessage(MSG_LOADING_CHANGED, isLoading ? 1 : 0, 0).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void setSourceProviderInternal(SampleSourceProvider sourceProvider) {
|
||||
try {
|
||||
resetInternal();
|
||||
|
|
@ -524,7 +531,8 @@ import java.util.ArrayList;
|
|||
enabledRenderers = new TrackRenderer[0];
|
||||
sampleSourceProvider = null;
|
||||
timeline.reset();
|
||||
bufferingControl.reset();
|
||||
loadControl.reset();
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
private void sendMessagesInternal(ExoPlayerMessage[] messages) throws ExoPlaybackException {
|
||||
|
|
@ -572,7 +580,7 @@ import java.util.ArrayList;
|
|||
|
||||
private Source playingSource;
|
||||
private Source readingSource;
|
||||
private Source bufferingSource;
|
||||
private Source loadingSource;
|
||||
|
||||
private int pendingSourceIndex;
|
||||
private long playingSourceEndPositionUs;
|
||||
|
|
@ -587,62 +595,65 @@ import java.util.ArrayList;
|
|||
}
|
||||
|
||||
public boolean haveSufficientBuffer(boolean rebuffering) {
|
||||
if (bufferingSource == null) {
|
||||
if (loadingSource == null) {
|
||||
return false;
|
||||
}
|
||||
long positionUs = internalPositionUs - bufferingSource.offsetUs;
|
||||
long bufferedPositionUs = !bufferingSource.prepared ? 0
|
||||
: bufferingSource.sampleSource.getBufferedPositionUs();
|
||||
long positionUs = internalPositionUs - loadingSource.offsetUs;
|
||||
long bufferedPositionUs = !loadingSource.prepared ? 0
|
||||
: loadingSource.sampleSource.getBufferedPositionUs();
|
||||
if (bufferedPositionUs == C.END_OF_SOURCE_US) {
|
||||
int sourceCount = sampleSourceProvider.getSourceCount();
|
||||
if (sourceCount != SampleSourceProvider.UNKNOWN_SOURCE_COUNT
|
||||
&& bufferingSource.index == sourceCount - 1) {
|
||||
&& loadingSource.index == sourceCount - 1) {
|
||||
return true;
|
||||
}
|
||||
bufferedPositionUs = bufferingSource.sampleSource.getDurationUs();
|
||||
bufferedPositionUs = loadingSource.sampleSource.getDurationUs();
|
||||
}
|
||||
return bufferingControl.shouldStartPlayback(bufferedPositionUs - positionUs, rebuffering);
|
||||
return loadControl.shouldStartPlayback(bufferedPositionUs - positionUs, rebuffering);
|
||||
}
|
||||
|
||||
public void maybeThrowSourcePrepareError() throws IOException {
|
||||
if (bufferingSource != null && !bufferingSource.prepared
|
||||
&& (readingSource == null || readingSource.nextSource == bufferingSource)) {
|
||||
if (loadingSource != null && !loadingSource.prepared
|
||||
&& (readingSource == null || readingSource.nextSource == loadingSource)) {
|
||||
for (TrackRenderer renderer : enabledRenderers) {
|
||||
if (!renderer.hasReadStreamToEnd()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
bufferingSource.sampleSource.maybeThrowPrepareError();
|
||||
loadingSource.sampleSource.maybeThrowPrepareError();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateSources() throws ExoPlaybackException, IOException {
|
||||
// TODO[playlists]: Let sample source providers invalidate sources that are already buffering.
|
||||
// TODO[playlists]: Let sample source providers invalidate sources that are already loaded.
|
||||
|
||||
// Update the buffering source.
|
||||
// Update the loading source.
|
||||
int sourceCount = sampleSourceProvider.getSourceCount();
|
||||
if (bufferingSource == null
|
||||
|| (bufferingSource.isFullyBuffered() && bufferingSource.index
|
||||
if (loadingSource == null
|
||||
|| (loadingSource.isFullyBuffered() && loadingSource.index
|
||||
- (playingSource != null ? playingSource.index : 0) < MAXIMUM_BUFFER_AHEAD_SOURCES)) {
|
||||
// Try and obtain the next source to start buffering.
|
||||
int sourceIndex = bufferingSource == null ? pendingSourceIndex : bufferingSource.index + 1;
|
||||
// Try and obtain the next source to start loading.
|
||||
int sourceIndex = loadingSource == null ? pendingSourceIndex : loadingSource.index + 1;
|
||||
if (sourceCount == SampleSourceProvider.UNKNOWN_SOURCE_COUNT || sourceIndex < sourceCount) {
|
||||
// Attempt to create the next source.
|
||||
SampleSource sampleSource = sampleSourceProvider.createSource(sourceIndex);
|
||||
if (sampleSource != null) {
|
||||
Source newSource = new Source(renderers, trackSelector, sampleSource, sourceIndex);
|
||||
if (bufferingSource != null) {
|
||||
bufferingSource.setNextSource(newSource);
|
||||
if (loadingSource != null) {
|
||||
loadingSource.setNextSource(newSource);
|
||||
}
|
||||
bufferingSource = newSource;
|
||||
loadingSource = newSource;
|
||||
long startPositionUs = playingSource == null ? playbackInfo.positionUs : 0;
|
||||
bufferingSource.sampleSource.prepare(ExoPlayerImplInternal.this,
|
||||
bufferingControl.getAllocator(), startPositionUs);
|
||||
setIsLoading(true);
|
||||
loadingSource.sampleSource.prepare(ExoPlayerImplInternal.this,
|
||||
loadControl.getAllocator(), startPositionUs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bufferingSource != null && bufferingSource.needsContinueLoading) {
|
||||
if (loadingSource == null || loadingSource.isFullyBuffered()) {
|
||||
setIsLoading(false);
|
||||
} else if (loadingSource != null && loadingSource.needsContinueLoading) {
|
||||
maybeContinueLoading();
|
||||
}
|
||||
|
||||
|
|
@ -713,15 +724,15 @@ import java.util.ArrayList;
|
|||
}
|
||||
|
||||
public void handleSourcePrepared(SampleSource source) throws ExoPlaybackException {
|
||||
if (bufferingSource == null || bufferingSource.sampleSource != source) {
|
||||
if (loadingSource == null || loadingSource.sampleSource != source) {
|
||||
// Stale event.
|
||||
return;
|
||||
}
|
||||
long startPositionUs = playingSource == null ? playbackInfo.positionUs : 0;
|
||||
bufferingSource.handlePrepared(startPositionUs, bufferingControl);
|
||||
loadingSource.handlePrepared(startPositionUs, loadControl);
|
||||
if (playingSource == null) {
|
||||
// This is the first prepared source, so start playing it.
|
||||
readingSource = bufferingSource;
|
||||
readingSource = loadingSource;
|
||||
setPlayingSource(readingSource);
|
||||
updateTimelineState();
|
||||
}
|
||||
|
|
@ -729,24 +740,27 @@ import java.util.ArrayList;
|
|||
}
|
||||
|
||||
public void handleContinueLoadingRequested(SampleSource source) {
|
||||
if (bufferingSource == null || bufferingSource.sampleSource != source) {
|
||||
if (loadingSource == null || loadingSource.sampleSource != source) {
|
||||
return;
|
||||
}
|
||||
maybeContinueLoading();
|
||||
}
|
||||
|
||||
private void maybeContinueLoading() {
|
||||
long nextLoadPositionUs = bufferingSource.sampleSource.getNextLoadPositionUs();
|
||||
long nextLoadPositionUs = loadingSource.sampleSource.getNextLoadPositionUs();
|
||||
if (nextLoadPositionUs != C.END_OF_SOURCE_US) {
|
||||
long positionUs = internalPositionUs - bufferingSource.offsetUs;
|
||||
long positionUs = internalPositionUs - loadingSource.offsetUs;
|
||||
long bufferedDurationUs = nextLoadPositionUs - positionUs;
|
||||
boolean continueBuffering = bufferingControl.shouldContinueBuffering(bufferedDurationUs);
|
||||
if (continueBuffering) {
|
||||
bufferingSource.needsContinueLoading = false;
|
||||
bufferingSource.sampleSource.continueLoading(positionUs);
|
||||
boolean continueLoading = loadControl.shouldContinueLoading(bufferedDurationUs);
|
||||
setIsLoading(continueLoading);
|
||||
if (continueLoading) {
|
||||
loadingSource.needsContinueLoading = false;
|
||||
loadingSource.sampleSource.continueLoading(positionUs);
|
||||
} else {
|
||||
bufferingSource.needsContinueLoading = true;
|
||||
loadingSource.needsContinueLoading = true;
|
||||
}
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -768,7 +782,7 @@ import java.util.ArrayList;
|
|||
setPlayingSource(newPlayingSource);
|
||||
updateTimelineState();
|
||||
readingSource = playingSource;
|
||||
bufferingSource = playingSource;
|
||||
loadingSource = playingSource;
|
||||
if (playingSource.hasEnabledTracks) {
|
||||
seekPositionUs = playingSource.sampleSource.seekToUs(seekPositionUs);
|
||||
}
|
||||
|
|
@ -782,7 +796,7 @@ import java.util.ArrayList;
|
|||
enabledRenderers = new TrackRenderer[0];
|
||||
playingSource = null;
|
||||
readingSource = null;
|
||||
bufferingSource = null;
|
||||
loadingSource = null;
|
||||
pendingSourceIndex = sourceIndex;
|
||||
resetInternalPosition(seekPositionUs);
|
||||
}
|
||||
|
|
@ -818,13 +832,13 @@ import java.util.ArrayList;
|
|||
}
|
||||
playingSource.nextSource = null;
|
||||
readingSource = playingSource;
|
||||
bufferingSource = playingSource;
|
||||
loadingSource = playingSource;
|
||||
playingSourceEndPositionUs = C.UNSET_TIME_US;
|
||||
|
||||
// Update streams for the new selection, recreating all streams if reading ahead.
|
||||
boolean recreateStreams = readingSource != playingSource;
|
||||
TrackSelectionArray playingSourceOldTrackSelections = playingSource.sourceTrackSelections;
|
||||
playingSource.updateSourceTrackSelection(playbackInfo.positionUs, bufferingControl,
|
||||
playingSource.updateSourceTrackSelection(playbackInfo.positionUs, loadControl,
|
||||
recreateStreams);
|
||||
|
||||
int enabledRendererCount = 0;
|
||||
|
|
@ -858,21 +872,21 @@ import java.util.ArrayList;
|
|||
enableRenderers(rendererWasEnabledFlags, enabledRendererCount);
|
||||
} else {
|
||||
// Release and re-prepare/buffer sources after the one whose selection changed.
|
||||
bufferingSource = source;
|
||||
source = bufferingSource.nextSource;
|
||||
loadingSource = source;
|
||||
source = loadingSource.nextSource;
|
||||
while (source != null) {
|
||||
source.release();
|
||||
source = source.nextSource;
|
||||
}
|
||||
bufferingSource.nextSource = null;
|
||||
long positionUs = Math.max(0, internalPositionUs - bufferingSource.offsetUs);
|
||||
bufferingSource.updateSourceTrackSelection(positionUs, bufferingControl, false);
|
||||
loadingSource.nextSource = null;
|
||||
long positionUs = Math.max(0, internalPositionUs - loadingSource.offsetUs);
|
||||
loadingSource.updateSourceTrackSelection(positionUs, loadControl, false);
|
||||
}
|
||||
maybeContinueLoading();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
Source source = playingSource != null ? playingSource : bufferingSource;
|
||||
Source source = playingSource != null ? playingSource : loadingSource;
|
||||
while (source != null) {
|
||||
source.release();
|
||||
source = source.nextSource;
|
||||
|
|
@ -881,7 +895,7 @@ import java.util.ArrayList;
|
|||
isEnded = false;
|
||||
playingSource = null;
|
||||
readingSource = null;
|
||||
bufferingSource = null;
|
||||
loadingSource = null;
|
||||
playingSourceEndPositionUs = C.UNSET_TIME_US;
|
||||
pendingSourceIndex = 0;
|
||||
playbackInfo = new PlaybackInfo(0);
|
||||
|
|
@ -995,7 +1009,6 @@ import java.util.ArrayList;
|
|||
int index) {
|
||||
this.renderers = renderers;
|
||||
this.trackSelector = trackSelector;
|
||||
|
||||
this.sampleSource = sampleSource;
|
||||
this.index = index;
|
||||
trackStreams = new TrackStream[renderers.length];
|
||||
|
|
@ -1011,11 +1024,11 @@ import java.util.ArrayList;
|
|||
|| sampleSource.getBufferedPositionUs() == C.END_OF_SOURCE_US);
|
||||
}
|
||||
|
||||
public void handlePrepared(long positionUs, BufferingControl bufferingControl)
|
||||
public void handlePrepared(long positionUs, LoadControl loadControl)
|
||||
throws ExoPlaybackException {
|
||||
prepared = true;
|
||||
selectTracks();
|
||||
updateSourceTrackSelection(positionUs, bufferingControl, false);
|
||||
updateSourceTrackSelection(positionUs, loadControl, false);
|
||||
}
|
||||
|
||||
public boolean selectTracks() throws ExoPlaybackException {
|
||||
|
|
@ -1030,7 +1043,7 @@ import java.util.ArrayList;
|
|||
return true;
|
||||
}
|
||||
|
||||
public void updateSourceTrackSelection(long positionUs, BufferingControl bufferingControl,
|
||||
public void updateSourceTrackSelection(long positionUs, LoadControl loadControl,
|
||||
boolean forceRecreateStreams) throws ExoPlaybackException {
|
||||
// Populate lists of streams that are being disabled/newly enabled.
|
||||
ArrayList<TrackStream> oldStreams = new ArrayList<>();
|
||||
|
|
@ -1069,7 +1082,7 @@ import java.util.ArrayList;
|
|||
}
|
||||
|
||||
// The track selection has changed.
|
||||
bufferingControl.onTrackSelections(renderers, sampleSource.getTrackGroups(), trackSelections);
|
||||
loadControl.onTrackSelections(renderers, sampleSource.getTrackGroups(), trackSelections);
|
||||
}
|
||||
|
||||
public void release() {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import com.google.android.exoplayer.upstream.Allocator;
|
|||
/**
|
||||
* Controls buffering of media.
|
||||
*/
|
||||
public interface BufferingControl {
|
||||
public interface LoadControl {
|
||||
|
||||
/**
|
||||
* Invoked by the player when a track selection occurs.
|
||||
|
|
@ -55,11 +55,11 @@ public interface BufferingControl {
|
|||
boolean shouldStartPlayback(long bufferedDurationUs, boolean rebuffering);
|
||||
|
||||
/**
|
||||
* Invoked by the player to determine whether buffering should continue.
|
||||
* Invoked by the player to determine whether it should continue to load the source.
|
||||
*
|
||||
* @param bufferedDurationUs The duration of media that's currently buffered.
|
||||
* @return True if the buffering should continue. False otherwise.
|
||||
* @return True if the loading should continue. False otherwise.
|
||||
*/
|
||||
boolean shouldContinueBuffering(long bufferedDurationUs);
|
||||
boolean shouldContinueLoading(long bufferedDurationUs);
|
||||
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
|||
private CodecCounters audioCodecCounters;
|
||||
|
||||
/* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector,
|
||||
BufferingControl bufferingControl, DrmSessionManager drmSessionManager,
|
||||
LoadControl loadControl, DrmSessionManager drmSessionManager,
|
||||
boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) {
|
||||
mainHandler = new Handler();
|
||||
bandwidthMeter = new DefaultBandwidthMeter();
|
||||
|
|
@ -145,7 +145,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
|||
this.audioRendererCount = audioRendererCount;
|
||||
|
||||
// Build the player and associated objects.
|
||||
player = new ExoPlayerImpl(renderers, trackSelector, bufferingControl);
|
||||
player = new ExoPlayerImpl(renderers, trackSelector, loadControl);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -338,6 +338,11 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
|||
return player.isPlayWhenReadyCommitted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoading() {
|
||||
return player.isLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekTo(long positionMs) {
|
||||
player.seekTo(positionMs);
|
||||
|
|
|
|||
|
|
@ -153,6 +153,11 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe
|
|||
|
||||
// ExoPlayer.EventListener implementation
|
||||
|
||||
@Override
|
||||
public void onLoadingChanged(boolean isLoading) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
updateTextView();
|
||||
|
|
|
|||
|
|
@ -174,7 +174,12 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
|
|||
assertPassed(audioCodecCounters, videoCodecCounters);
|
||||
}
|
||||
|
||||
// ExoPlayer.Listener
|
||||
// ExoPlayer.EventListener
|
||||
|
||||
@Override
|
||||
public void onLoadingChanged(boolean isLoading) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue