mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +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
|
// ExoPlayer.EventListener
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadingChanged(boolean isLoading) {
|
||||||
|
Log.d(TAG, "loading [" + isLoading + "]");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlayerStateChanged(boolean playWhenReady, int state) {
|
public void onPlayerStateChanged(boolean playWhenReady, int state) {
|
||||||
Log.d(TAG, "state [" + getSessionTimeString() + ", " + playWhenReady + ", "
|
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.AspectRatioFrameLayout;
|
||||||
import com.google.android.exoplayer.C;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.ConcatenatingSampleSourceProvider;
|
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.DefaultTrackSelectionPolicy;
|
||||||
import com.google.android.exoplayer.DefaultTrackSelector;
|
import com.google.android.exoplayer.DefaultTrackSelector;
|
||||||
import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
|
import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
|
||||||
|
|
@ -268,8 +268,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||||
trackSelector.addListener(this);
|
trackSelector.addListener(this);
|
||||||
trackSelector.addListener(eventLogger);
|
trackSelector.addListener(eventLogger);
|
||||||
trackSelectionHelper = new TrackSelectionHelper(trackSelector);
|
trackSelectionHelper = new TrackSelectionHelper(trackSelector);
|
||||||
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector,
|
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, new DefaultLoadControl(),
|
||||||
new DefaultBufferingControl(), drmSessionManager, preferExtensionDecoders);
|
drmSessionManager, preferExtensionDecoders);
|
||||||
player.addListener(this);
|
player.addListener(this);
|
||||||
player.addListener(eventLogger);
|
player.addListener(eventLogger);
|
||||||
player.setDebugListener(eventLogger);
|
player.setDebugListener(eventLogger);
|
||||||
|
|
@ -388,6 +388,11 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||||
|
|
||||||
// ExoPlayer.EventListener implementation
|
// ExoPlayer.EventListener implementation
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadingChanged(boolean isLoading) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||||
if (playbackState == ExoPlayer.STATE_ENDED) {
|
if (playbackState == ExoPlayer.STATE_ENDED) {
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,11 @@ public class FlacPlaybackTest extends InstrumentationTestCase {
|
||||||
Looper.loop();
|
Looper.loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadingChanged(boolean isLoading) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlayWhenReadyCommitted () {
|
public void onPlayWhenReadyCommitted () {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,11 @@ public class OpusPlaybackTest extends InstrumentationTestCase {
|
||||||
Looper.loop();
|
Looper.loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadingChanged(boolean isLoading) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlayWhenReadyCommitted () {
|
public void onPlayWhenReadyCommitted () {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,11 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
|
||||||
Looper.loop();
|
Looper.loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadingChanged(boolean isLoading) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlayWhenReadyCommitted () {
|
public void onPlayWhenReadyCommitted () {
|
||||||
// Do nothing.
|
// 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.upstream.DefaultAllocator;
|
||||||
import com.google.android.exoplayer.util.Util;
|
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 {
|
public final class DefaultLoadControl implements LoadControl {
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default minimum duration of media that the player will attempt to ensure is buffered at all
|
* 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 static final int BELOW_LOW_WATERMARK = 2;
|
||||||
|
|
||||||
private final DefaultAllocator allocator;
|
private final DefaultAllocator allocator;
|
||||||
private final Handler eventHandler;
|
|
||||||
private final EventListener eventListener;
|
|
||||||
|
|
||||||
private final long minBufferUs;
|
private final long minBufferUs;
|
||||||
private final long maxBufferUs;
|
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.
|
* 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));
|
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.
|
* @param allocator The {@link DefaultAllocator} used by the loader.
|
||||||
*/
|
*/
|
||||||
public DefaultBufferingControl(DefaultAllocator allocator) {
|
public DefaultLoadControl(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) {
|
|
||||||
this(allocator, DEFAULT_MIN_BUFFER_MS, DEFAULT_MAX_BUFFER_MS, DEFAULT_BUFFER_FOR_PLAYBACK_MS,
|
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
|
* @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
|
* 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.
|
* 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,
|
public DefaultLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs,
|
||||||
long bufferForPlaybackMs, long bufferForPlaybackAfterRebufferMs, Handler eventHandler,
|
long bufferForPlaybackMs, long bufferForPlaybackAfterRebufferMs) {
|
||||||
EventListener eventListener) {
|
|
||||||
this.allocator = allocator;
|
this.allocator = allocator;
|
||||||
this.eventHandler = eventHandler;
|
|
||||||
this.eventListener = eventListener;
|
|
||||||
minBufferUs = minBufferMs * 1000L;
|
minBufferUs = minBufferMs * 1000L;
|
||||||
maxBufferUs = maxBufferMs * 1000L;
|
maxBufferUs = maxBufferMs * 1000L;
|
||||||
bufferForPlaybackUs = bufferForPlaybackMs * 1000L;
|
bufferForPlaybackUs = bufferForPlaybackMs * 1000L;
|
||||||
|
|
@ -154,7 +117,7 @@ public final class DefaultBufferingControl implements BufferingControl {
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
targetBufferSize = 0;
|
targetBufferSize = 0;
|
||||||
setBuffering(false);
|
isBuffering = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -169,20 +132,12 @@ public final class DefaultBufferingControl implements BufferingControl {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldContinueBuffering(long bufferedDurationUs) {
|
public boolean shouldContinueLoading(long bufferedDurationUs) {
|
||||||
int bufferTimeState = getBufferTimeState(bufferedDurationUs);
|
int bufferTimeState = getBufferTimeState(bufferedDurationUs);
|
||||||
boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize;
|
boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize;
|
||||||
boolean shouldBuffer = bufferTimeState == BELOW_LOW_WATERMARK
|
isBuffering = bufferTimeState == BELOW_LOW_WATERMARK
|
||||||
|| (bufferTimeState == BETWEEN_WATERMARKS && isBuffering && !targetBufferSizeReached);
|
|| (bufferTimeState == BETWEEN_WATERMARKS && isBuffering && !targetBufferSizeReached);
|
||||||
setBuffering(shouldBuffer);
|
return isBuffering;
|
||||||
return shouldBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setBuffering(boolean isBuffering) {
|
|
||||||
if (this.isBuffering != isBuffering) {
|
|
||||||
this.isBuffering = isBuffering;
|
|
||||||
notifyBufferingChanged(isBuffering);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getBufferTimeState(long bufferedDurationUs) {
|
private int getBufferTimeState(long bufferedDurationUs) {
|
||||||
|
|
@ -190,15 +145,4 @@ public final class DefaultBufferingControl implements BufferingControl {
|
||||||
: (bufferedDurationUs < minBufferUs ? BELOW_LOW_WATERMARK : BETWEEN_WATERMARKS);
|
: (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 {
|
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
|
* Invoked when the value returned from either {@link ExoPlayer#getPlayWhenReady()} or
|
||||||
* {@link ExoPlayer#getPlaybackState()} changes.
|
* {@link ExoPlayer#getPlaybackState()} changes.
|
||||||
|
|
@ -269,6 +276,13 @@ public interface ExoPlayer {
|
||||||
*/
|
*/
|
||||||
boolean isPlayWhenReadyCommitted();
|
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.
|
* 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.
|
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||||
*/
|
*/
|
||||||
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector) {
|
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector) {
|
||||||
return newSimpleInstance(context, trackSelector, new DefaultBufferingControl(), null);
|
return newSimpleInstance(context, trackSelector, new DefaultLoadControl(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -52,13 +52,13 @@ public final class ExoPlayerFactory {
|
||||||
*
|
*
|
||||||
* @param context A {@link Context}.
|
* @param context A {@link Context}.
|
||||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||||
* @param 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
|
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
|
||||||
* will not be used for DRM protected playbacks.
|
* will not be used for DRM protected playbacks.
|
||||||
*/
|
*/
|
||||||
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
|
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
|
||||||
BufferingControl bufferingControl, DrmSessionManager drmSessionManager) {
|
LoadControl loadControl, DrmSessionManager drmSessionManager) {
|
||||||
return newSimpleInstance(context, trackSelector, bufferingControl, drmSessionManager, false);
|
return newSimpleInstance(context, trackSelector, loadControl, drmSessionManager, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -68,7 +68,7 @@ public final class ExoPlayerFactory {
|
||||||
*
|
*
|
||||||
* @param context A {@link Context}.
|
* @param context A {@link Context}.
|
||||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||||
* @param 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
|
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
|
||||||
* will not be used for DRM protected playbacks.
|
* will not be used for DRM protected playbacks.
|
||||||
* @param preferExtensionDecoders True to prefer {@link TrackRenderer} instances defined in
|
* @param preferExtensionDecoders True to prefer {@link TrackRenderer} instances defined in
|
||||||
|
|
@ -76,9 +76,9 @@ public final class ExoPlayerFactory {
|
||||||
* included in the application build for setting this flag to have any effect.
|
* included in the application build for setting this flag to have any effect.
|
||||||
*/
|
*/
|
||||||
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
|
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
|
||||||
BufferingControl bufferingControl, DrmSessionManager drmSessionManager,
|
LoadControl loadControl, DrmSessionManager drmSessionManager,
|
||||||
boolean preferExtensionDecoders) {
|
boolean preferExtensionDecoders) {
|
||||||
return newSimpleInstance(context, trackSelector, bufferingControl, drmSessionManager,
|
return newSimpleInstance(context, trackSelector, loadControl, drmSessionManager,
|
||||||
preferExtensionDecoders, DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS);
|
preferExtensionDecoders, DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ public final class ExoPlayerFactory {
|
||||||
*
|
*
|
||||||
* @param context A {@link Context}.
|
* @param context A {@link Context}.
|
||||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||||
* @param 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
|
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
|
||||||
* will not be used for DRM protected playbacks.
|
* will not be used for DRM protected playbacks.
|
||||||
* @param preferExtensionDecoders True to prefer {@link TrackRenderer} instances defined in
|
* @param preferExtensionDecoders True to prefer {@link TrackRenderer} instances defined in
|
||||||
|
|
@ -99,9 +99,9 @@ public final class ExoPlayerFactory {
|
||||||
* seamlessly join an ongoing playback.
|
* seamlessly join an ongoing playback.
|
||||||
*/
|
*/
|
||||||
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
|
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
|
||||||
BufferingControl bufferingControl, DrmSessionManager drmSessionManager,
|
LoadControl loadControl, DrmSessionManager drmSessionManager,
|
||||||
boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) {
|
boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) {
|
||||||
return new SimpleExoPlayer(context, trackSelector, bufferingControl, drmSessionManager,
|
return new SimpleExoPlayer(context, trackSelector, loadControl, drmSessionManager,
|
||||||
preferExtensionDecoders, allowedVideoJoiningTimeMs);
|
preferExtensionDecoders, allowedVideoJoiningTimeMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,7 +114,7 @@ public final class ExoPlayerFactory {
|
||||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||||
*/
|
*/
|
||||||
public static ExoPlayer newInstance(TrackRenderer[] renderers, TrackSelector trackSelector) {
|
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 renderers The {@link TrackRenderer}s that will be used by the instance.
|
||||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||||
* @param 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,
|
public static ExoPlayer newInstance(TrackRenderer[] renderers, TrackSelector trackSelector,
|
||||||
BufferingControl bufferingControl) {
|
LoadControl loadControl) {
|
||||||
return new ExoPlayerImpl(renderers, trackSelector, bufferingControl);
|
return new ExoPlayerImpl(renderers, trackSelector, loadControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
private int playbackState;
|
private int playbackState;
|
||||||
private int pendingPlayWhenReadyAcks;
|
private int pendingPlayWhenReadyAcks;
|
||||||
private int pendingSetSourceProviderAndSeekAcks;
|
private int pendingSetSourceProviderAndSeekAcks;
|
||||||
|
private boolean isLoading;
|
||||||
|
|
||||||
// Playback information when there is no pending seek/set source operation.
|
// Playback information when there is no pending seek/set source operation.
|
||||||
private PlaybackInfo playbackInfo;
|
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 renderers The {@link TrackRenderer}s that will be used by the instance.
|
||||||
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
|
||||||
* @param 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")
|
@SuppressLint("HandlerLeak")
|
||||||
public ExoPlayerImpl(TrackRenderer[] renderers, TrackSelector trackSelector,
|
public ExoPlayerImpl(TrackRenderer[] renderers, TrackSelector trackSelector,
|
||||||
BufferingControl bufferingControl) {
|
LoadControl loadControl) {
|
||||||
Log.i(TAG, "Init " + ExoPlayerLibraryInfo.VERSION);
|
Log.i(TAG, "Init " + ExoPlayerLibraryInfo.VERSION);
|
||||||
Assertions.checkNotNull(renderers);
|
Assertions.checkNotNull(renderers);
|
||||||
Assertions.checkState(renderers.length > 0);
|
Assertions.checkState(renderers.length > 0);
|
||||||
|
|
@ -72,8 +73,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
ExoPlayerImpl.this.handleEvent(msg);
|
ExoPlayerImpl.this.handleEvent(msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, bufferingControl,
|
internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, loadControl, playWhenReady,
|
||||||
playWhenReady, eventHandler);
|
eventHandler);
|
||||||
playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0);
|
playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,6 +133,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
return pendingPlayWhenReadyAcks == 0;
|
return pendingPlayWhenReadyAcks == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLoading() {
|
||||||
|
return isLoading;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seekTo(long positionMs) {
|
public void seekTo(long positionMs) {
|
||||||
seekTo(getCurrentSourceIndex(), positionMs);
|
seekTo(getCurrentSourceIndex(), positionMs);
|
||||||
|
|
@ -221,6 +227,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
}
|
}
|
||||||
break;
|
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: {
|
case ExoPlayerImplInternal.MSG_SET_PLAY_WHEN_READY_ACK: {
|
||||||
pendingPlayWhenReadyAcks--;
|
pendingPlayWhenReadyAcks--;
|
||||||
if (pendingPlayWhenReadyAcks == 0) {
|
if (pendingPlayWhenReadyAcks == 0) {
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,6 @@ import java.util.ArrayList;
|
||||||
/**
|
/**
|
||||||
* Implements the internal behavior of {@link ExoPlayerImpl}.
|
* 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,
|
/* package */ final class ExoPlayerImplInternal implements Handler.Callback, SampleSource.Callback,
|
||||||
InvalidationListener {
|
InvalidationListener {
|
||||||
|
|
||||||
|
|
@ -63,11 +61,12 @@ import java.util.ArrayList;
|
||||||
|
|
||||||
// External messages
|
// External messages
|
||||||
public static final int MSG_STATE_CHANGED = 1;
|
public static final int MSG_STATE_CHANGED = 1;
|
||||||
public static final int MSG_SET_PLAY_WHEN_READY_ACK = 2;
|
public static final int MSG_LOADING_CHANGED = 2;
|
||||||
public static final int MSG_SET_SOURCE_PROVIDER_ACK = 3;
|
public static final int MSG_SET_PLAY_WHEN_READY_ACK = 3;
|
||||||
public static final int MSG_SEEK_ACK = 4;
|
public static final int MSG_SET_SOURCE_PROVIDER_ACK = 4;
|
||||||
public static final int MSG_SOURCE_CHANGED = 5;
|
public static final int MSG_SEEK_ACK = 5;
|
||||||
public static final int MSG_ERROR = 6;
|
public static final int MSG_SOURCE_CHANGED = 6;
|
||||||
|
public static final int MSG_ERROR = 7;
|
||||||
|
|
||||||
// Internal messages
|
// Internal messages
|
||||||
private static final int MSG_SET_SOURCE_PROVIDER = 0;
|
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 static final int MAXIMUM_BUFFER_AHEAD_SOURCES = 100;
|
||||||
|
|
||||||
private final TrackSelector trackSelector;
|
private final TrackSelector trackSelector;
|
||||||
private final BufferingControl bufferingControl;
|
private final LoadControl loadControl;
|
||||||
private final StandaloneMediaClock standaloneMediaClock;
|
private final StandaloneMediaClock standaloneMediaClock;
|
||||||
private final Handler handler;
|
private final Handler handler;
|
||||||
private final HandlerThread internalPlaybackThread;
|
private final HandlerThread internalPlaybackThread;
|
||||||
|
|
@ -108,6 +107,7 @@ import java.util.ArrayList;
|
||||||
private boolean released;
|
private boolean released;
|
||||||
private boolean playWhenReady;
|
private boolean playWhenReady;
|
||||||
private boolean rebuffering;
|
private boolean rebuffering;
|
||||||
|
private boolean isLoading;
|
||||||
private int state;
|
private int state;
|
||||||
private int customMessagesSent;
|
private int customMessagesSent;
|
||||||
private int customMessagesProcessed;
|
private int customMessagesProcessed;
|
||||||
|
|
@ -116,9 +116,9 @@ import java.util.ArrayList;
|
||||||
private long internalPositionUs;
|
private long internalPositionUs;
|
||||||
|
|
||||||
public ExoPlayerImplInternal(TrackRenderer[] renderers, TrackSelector trackSelector,
|
public ExoPlayerImplInternal(TrackRenderer[] renderers, TrackSelector trackSelector,
|
||||||
BufferingControl bufferingControl, boolean playWhenReady, Handler eventHandler) {
|
LoadControl loadControl, boolean playWhenReady, Handler eventHandler) {
|
||||||
this.trackSelector = trackSelector;
|
this.trackSelector = trackSelector;
|
||||||
this.bufferingControl = bufferingControl;
|
this.loadControl = loadControl;
|
||||||
this.playWhenReady = playWhenReady;
|
this.playWhenReady = playWhenReady;
|
||||||
this.eventHandler = eventHandler;
|
this.eventHandler = eventHandler;
|
||||||
this.state = ExoPlayer.STATE_IDLE;
|
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) {
|
private void setSourceProviderInternal(SampleSourceProvider sourceProvider) {
|
||||||
try {
|
try {
|
||||||
resetInternal();
|
resetInternal();
|
||||||
|
|
@ -524,7 +531,8 @@ import java.util.ArrayList;
|
||||||
enabledRenderers = new TrackRenderer[0];
|
enabledRenderers = new TrackRenderer[0];
|
||||||
sampleSourceProvider = null;
|
sampleSourceProvider = null;
|
||||||
timeline.reset();
|
timeline.reset();
|
||||||
bufferingControl.reset();
|
loadControl.reset();
|
||||||
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessagesInternal(ExoPlayerMessage[] messages) throws ExoPlaybackException {
|
private void sendMessagesInternal(ExoPlayerMessage[] messages) throws ExoPlaybackException {
|
||||||
|
|
@ -572,7 +580,7 @@ import java.util.ArrayList;
|
||||||
|
|
||||||
private Source playingSource;
|
private Source playingSource;
|
||||||
private Source readingSource;
|
private Source readingSource;
|
||||||
private Source bufferingSource;
|
private Source loadingSource;
|
||||||
|
|
||||||
private int pendingSourceIndex;
|
private int pendingSourceIndex;
|
||||||
private long playingSourceEndPositionUs;
|
private long playingSourceEndPositionUs;
|
||||||
|
|
@ -587,62 +595,65 @@ import java.util.ArrayList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean haveSufficientBuffer(boolean rebuffering) {
|
public boolean haveSufficientBuffer(boolean rebuffering) {
|
||||||
if (bufferingSource == null) {
|
if (loadingSource == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
long positionUs = internalPositionUs - bufferingSource.offsetUs;
|
long positionUs = internalPositionUs - loadingSource.offsetUs;
|
||||||
long bufferedPositionUs = !bufferingSource.prepared ? 0
|
long bufferedPositionUs = !loadingSource.prepared ? 0
|
||||||
: bufferingSource.sampleSource.getBufferedPositionUs();
|
: loadingSource.sampleSource.getBufferedPositionUs();
|
||||||
if (bufferedPositionUs == C.END_OF_SOURCE_US) {
|
if (bufferedPositionUs == C.END_OF_SOURCE_US) {
|
||||||
int sourceCount = sampleSourceProvider.getSourceCount();
|
int sourceCount = sampleSourceProvider.getSourceCount();
|
||||||
if (sourceCount != SampleSourceProvider.UNKNOWN_SOURCE_COUNT
|
if (sourceCount != SampleSourceProvider.UNKNOWN_SOURCE_COUNT
|
||||||
&& bufferingSource.index == sourceCount - 1) {
|
&& loadingSource.index == sourceCount - 1) {
|
||||||
return true;
|
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 {
|
public void maybeThrowSourcePrepareError() throws IOException {
|
||||||
if (bufferingSource != null && !bufferingSource.prepared
|
if (loadingSource != null && !loadingSource.prepared
|
||||||
&& (readingSource == null || readingSource.nextSource == bufferingSource)) {
|
&& (readingSource == null || readingSource.nextSource == loadingSource)) {
|
||||||
for (TrackRenderer renderer : enabledRenderers) {
|
for (TrackRenderer renderer : enabledRenderers) {
|
||||||
if (!renderer.hasReadStreamToEnd()) {
|
if (!renderer.hasReadStreamToEnd()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bufferingSource.sampleSource.maybeThrowPrepareError();
|
loadingSource.sampleSource.maybeThrowPrepareError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateSources() throws ExoPlaybackException, IOException {
|
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();
|
int sourceCount = sampleSourceProvider.getSourceCount();
|
||||||
if (bufferingSource == null
|
if (loadingSource == null
|
||||||
|| (bufferingSource.isFullyBuffered() && bufferingSource.index
|
|| (loadingSource.isFullyBuffered() && loadingSource.index
|
||||||
- (playingSource != null ? playingSource.index : 0) < MAXIMUM_BUFFER_AHEAD_SOURCES)) {
|
- (playingSource != null ? playingSource.index : 0) < MAXIMUM_BUFFER_AHEAD_SOURCES)) {
|
||||||
// Try and obtain the next source to start buffering.
|
// Try and obtain the next source to start loading.
|
||||||
int sourceIndex = bufferingSource == null ? pendingSourceIndex : bufferingSource.index + 1;
|
int sourceIndex = loadingSource == null ? pendingSourceIndex : loadingSource.index + 1;
|
||||||
if (sourceCount == SampleSourceProvider.UNKNOWN_SOURCE_COUNT || sourceIndex < sourceCount) {
|
if (sourceCount == SampleSourceProvider.UNKNOWN_SOURCE_COUNT || sourceIndex < sourceCount) {
|
||||||
// Attempt to create the next source.
|
// Attempt to create the next source.
|
||||||
SampleSource sampleSource = sampleSourceProvider.createSource(sourceIndex);
|
SampleSource sampleSource = sampleSourceProvider.createSource(sourceIndex);
|
||||||
if (sampleSource != null) {
|
if (sampleSource != null) {
|
||||||
Source newSource = new Source(renderers, trackSelector, sampleSource, sourceIndex);
|
Source newSource = new Source(renderers, trackSelector, sampleSource, sourceIndex);
|
||||||
if (bufferingSource != null) {
|
if (loadingSource != null) {
|
||||||
bufferingSource.setNextSource(newSource);
|
loadingSource.setNextSource(newSource);
|
||||||
}
|
}
|
||||||
bufferingSource = newSource;
|
loadingSource = newSource;
|
||||||
long startPositionUs = playingSource == null ? playbackInfo.positionUs : 0;
|
long startPositionUs = playingSource == null ? playbackInfo.positionUs : 0;
|
||||||
bufferingSource.sampleSource.prepare(ExoPlayerImplInternal.this,
|
setIsLoading(true);
|
||||||
bufferingControl.getAllocator(), startPositionUs);
|
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();
|
maybeContinueLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -713,15 +724,15 @@ import java.util.ArrayList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleSourcePrepared(SampleSource source) throws ExoPlaybackException {
|
public void handleSourcePrepared(SampleSource source) throws ExoPlaybackException {
|
||||||
if (bufferingSource == null || bufferingSource.sampleSource != source) {
|
if (loadingSource == null || loadingSource.sampleSource != source) {
|
||||||
// Stale event.
|
// Stale event.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
long startPositionUs = playingSource == null ? playbackInfo.positionUs : 0;
|
long startPositionUs = playingSource == null ? playbackInfo.positionUs : 0;
|
||||||
bufferingSource.handlePrepared(startPositionUs, bufferingControl);
|
loadingSource.handlePrepared(startPositionUs, loadControl);
|
||||||
if (playingSource == null) {
|
if (playingSource == null) {
|
||||||
// This is the first prepared source, so start playing it.
|
// This is the first prepared source, so start playing it.
|
||||||
readingSource = bufferingSource;
|
readingSource = loadingSource;
|
||||||
setPlayingSource(readingSource);
|
setPlayingSource(readingSource);
|
||||||
updateTimelineState();
|
updateTimelineState();
|
||||||
}
|
}
|
||||||
|
|
@ -729,24 +740,27 @@ import java.util.ArrayList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleContinueLoadingRequested(SampleSource source) {
|
public void handleContinueLoadingRequested(SampleSource source) {
|
||||||
if (bufferingSource == null || bufferingSource.sampleSource != source) {
|
if (loadingSource == null || loadingSource.sampleSource != source) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
maybeContinueLoading();
|
maybeContinueLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeContinueLoading() {
|
private void maybeContinueLoading() {
|
||||||
long nextLoadPositionUs = bufferingSource.sampleSource.getNextLoadPositionUs();
|
long nextLoadPositionUs = loadingSource.sampleSource.getNextLoadPositionUs();
|
||||||
if (nextLoadPositionUs != C.END_OF_SOURCE_US) {
|
if (nextLoadPositionUs != C.END_OF_SOURCE_US) {
|
||||||
long positionUs = internalPositionUs - bufferingSource.offsetUs;
|
long positionUs = internalPositionUs - loadingSource.offsetUs;
|
||||||
long bufferedDurationUs = nextLoadPositionUs - positionUs;
|
long bufferedDurationUs = nextLoadPositionUs - positionUs;
|
||||||
boolean continueBuffering = bufferingControl.shouldContinueBuffering(bufferedDurationUs);
|
boolean continueLoading = loadControl.shouldContinueLoading(bufferedDurationUs);
|
||||||
if (continueBuffering) {
|
setIsLoading(continueLoading);
|
||||||
bufferingSource.needsContinueLoading = false;
|
if (continueLoading) {
|
||||||
bufferingSource.sampleSource.continueLoading(positionUs);
|
loadingSource.needsContinueLoading = false;
|
||||||
|
loadingSource.sampleSource.continueLoading(positionUs);
|
||||||
} else {
|
} else {
|
||||||
bufferingSource.needsContinueLoading = true;
|
loadingSource.needsContinueLoading = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -768,7 +782,7 @@ import java.util.ArrayList;
|
||||||
setPlayingSource(newPlayingSource);
|
setPlayingSource(newPlayingSource);
|
||||||
updateTimelineState();
|
updateTimelineState();
|
||||||
readingSource = playingSource;
|
readingSource = playingSource;
|
||||||
bufferingSource = playingSource;
|
loadingSource = playingSource;
|
||||||
if (playingSource.hasEnabledTracks) {
|
if (playingSource.hasEnabledTracks) {
|
||||||
seekPositionUs = playingSource.sampleSource.seekToUs(seekPositionUs);
|
seekPositionUs = playingSource.sampleSource.seekToUs(seekPositionUs);
|
||||||
}
|
}
|
||||||
|
|
@ -782,7 +796,7 @@ import java.util.ArrayList;
|
||||||
enabledRenderers = new TrackRenderer[0];
|
enabledRenderers = new TrackRenderer[0];
|
||||||
playingSource = null;
|
playingSource = null;
|
||||||
readingSource = null;
|
readingSource = null;
|
||||||
bufferingSource = null;
|
loadingSource = null;
|
||||||
pendingSourceIndex = sourceIndex;
|
pendingSourceIndex = sourceIndex;
|
||||||
resetInternalPosition(seekPositionUs);
|
resetInternalPosition(seekPositionUs);
|
||||||
}
|
}
|
||||||
|
|
@ -818,13 +832,13 @@ import java.util.ArrayList;
|
||||||
}
|
}
|
||||||
playingSource.nextSource = null;
|
playingSource.nextSource = null;
|
||||||
readingSource = playingSource;
|
readingSource = playingSource;
|
||||||
bufferingSource = playingSource;
|
loadingSource = playingSource;
|
||||||
playingSourceEndPositionUs = C.UNSET_TIME_US;
|
playingSourceEndPositionUs = C.UNSET_TIME_US;
|
||||||
|
|
||||||
// Update streams for the new selection, recreating all streams if reading ahead.
|
// Update streams for the new selection, recreating all streams if reading ahead.
|
||||||
boolean recreateStreams = readingSource != playingSource;
|
boolean recreateStreams = readingSource != playingSource;
|
||||||
TrackSelectionArray playingSourceOldTrackSelections = playingSource.sourceTrackSelections;
|
TrackSelectionArray playingSourceOldTrackSelections = playingSource.sourceTrackSelections;
|
||||||
playingSource.updateSourceTrackSelection(playbackInfo.positionUs, bufferingControl,
|
playingSource.updateSourceTrackSelection(playbackInfo.positionUs, loadControl,
|
||||||
recreateStreams);
|
recreateStreams);
|
||||||
|
|
||||||
int enabledRendererCount = 0;
|
int enabledRendererCount = 0;
|
||||||
|
|
@ -858,21 +872,21 @@ import java.util.ArrayList;
|
||||||
enableRenderers(rendererWasEnabledFlags, enabledRendererCount);
|
enableRenderers(rendererWasEnabledFlags, enabledRendererCount);
|
||||||
} else {
|
} else {
|
||||||
// Release and re-prepare/buffer sources after the one whose selection changed.
|
// Release and re-prepare/buffer sources after the one whose selection changed.
|
||||||
bufferingSource = source;
|
loadingSource = source;
|
||||||
source = bufferingSource.nextSource;
|
source = loadingSource.nextSource;
|
||||||
while (source != null) {
|
while (source != null) {
|
||||||
source.release();
|
source.release();
|
||||||
source = source.nextSource;
|
source = source.nextSource;
|
||||||
}
|
}
|
||||||
bufferingSource.nextSource = null;
|
loadingSource.nextSource = null;
|
||||||
long positionUs = Math.max(0, internalPositionUs - bufferingSource.offsetUs);
|
long positionUs = Math.max(0, internalPositionUs - loadingSource.offsetUs);
|
||||||
bufferingSource.updateSourceTrackSelection(positionUs, bufferingControl, false);
|
loadingSource.updateSourceTrackSelection(positionUs, loadControl, false);
|
||||||
}
|
}
|
||||||
maybeContinueLoading();
|
maybeContinueLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
Source source = playingSource != null ? playingSource : bufferingSource;
|
Source source = playingSource != null ? playingSource : loadingSource;
|
||||||
while (source != null) {
|
while (source != null) {
|
||||||
source.release();
|
source.release();
|
||||||
source = source.nextSource;
|
source = source.nextSource;
|
||||||
|
|
@ -881,7 +895,7 @@ import java.util.ArrayList;
|
||||||
isEnded = false;
|
isEnded = false;
|
||||||
playingSource = null;
|
playingSource = null;
|
||||||
readingSource = null;
|
readingSource = null;
|
||||||
bufferingSource = null;
|
loadingSource = null;
|
||||||
playingSourceEndPositionUs = C.UNSET_TIME_US;
|
playingSourceEndPositionUs = C.UNSET_TIME_US;
|
||||||
pendingSourceIndex = 0;
|
pendingSourceIndex = 0;
|
||||||
playbackInfo = new PlaybackInfo(0);
|
playbackInfo = new PlaybackInfo(0);
|
||||||
|
|
@ -995,7 +1009,6 @@ import java.util.ArrayList;
|
||||||
int index) {
|
int index) {
|
||||||
this.renderers = renderers;
|
this.renderers = renderers;
|
||||||
this.trackSelector = trackSelector;
|
this.trackSelector = trackSelector;
|
||||||
|
|
||||||
this.sampleSource = sampleSource;
|
this.sampleSource = sampleSource;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
trackStreams = new TrackStream[renderers.length];
|
trackStreams = new TrackStream[renderers.length];
|
||||||
|
|
@ -1011,11 +1024,11 @@ import java.util.ArrayList;
|
||||||
|| sampleSource.getBufferedPositionUs() == C.END_OF_SOURCE_US);
|
|| sampleSource.getBufferedPositionUs() == C.END_OF_SOURCE_US);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handlePrepared(long positionUs, BufferingControl bufferingControl)
|
public void handlePrepared(long positionUs, LoadControl loadControl)
|
||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
prepared = true;
|
prepared = true;
|
||||||
selectTracks();
|
selectTracks();
|
||||||
updateSourceTrackSelection(positionUs, bufferingControl, false);
|
updateSourceTrackSelection(positionUs, loadControl, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean selectTracks() throws ExoPlaybackException {
|
public boolean selectTracks() throws ExoPlaybackException {
|
||||||
|
|
@ -1030,7 +1043,7 @@ import java.util.ArrayList;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateSourceTrackSelection(long positionUs, BufferingControl bufferingControl,
|
public void updateSourceTrackSelection(long positionUs, LoadControl loadControl,
|
||||||
boolean forceRecreateStreams) throws ExoPlaybackException {
|
boolean forceRecreateStreams) throws ExoPlaybackException {
|
||||||
// Populate lists of streams that are being disabled/newly enabled.
|
// Populate lists of streams that are being disabled/newly enabled.
|
||||||
ArrayList<TrackStream> oldStreams = new ArrayList<>();
|
ArrayList<TrackStream> oldStreams = new ArrayList<>();
|
||||||
|
|
@ -1069,7 +1082,7 @@ import java.util.ArrayList;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The track selection has changed.
|
// The track selection has changed.
|
||||||
bufferingControl.onTrackSelections(renderers, sampleSource.getTrackGroups(), trackSelections);
|
loadControl.onTrackSelections(renderers, sampleSource.getTrackGroups(), trackSelections);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void release() {
|
public void release() {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import com.google.android.exoplayer.upstream.Allocator;
|
||||||
/**
|
/**
|
||||||
* Controls buffering of media.
|
* Controls buffering of media.
|
||||||
*/
|
*/
|
||||||
public interface BufferingControl {
|
public interface LoadControl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked by the player when a track selection occurs.
|
* Invoked by the player when a track selection occurs.
|
||||||
|
|
@ -55,11 +55,11 @@ public interface BufferingControl {
|
||||||
boolean shouldStartPlayback(long bufferedDurationUs, boolean rebuffering);
|
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.
|
* @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;
|
private CodecCounters audioCodecCounters;
|
||||||
|
|
||||||
/* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector,
|
/* package */ SimpleExoPlayer(Context context, TrackSelector trackSelector,
|
||||||
BufferingControl bufferingControl, DrmSessionManager drmSessionManager,
|
LoadControl loadControl, DrmSessionManager drmSessionManager,
|
||||||
boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) {
|
boolean preferExtensionDecoders, long allowedVideoJoiningTimeMs) {
|
||||||
mainHandler = new Handler();
|
mainHandler = new Handler();
|
||||||
bandwidthMeter = new DefaultBandwidthMeter();
|
bandwidthMeter = new DefaultBandwidthMeter();
|
||||||
|
|
@ -145,7 +145,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||||
this.audioRendererCount = audioRendererCount;
|
this.audioRendererCount = audioRendererCount;
|
||||||
|
|
||||||
// Build the player and associated objects.
|
// Build the player and associated objects.
|
||||||
player = new ExoPlayerImpl(renderers, trackSelector, bufferingControl);
|
player = new ExoPlayerImpl(renderers, trackSelector, loadControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -338,6 +338,11 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||||
return player.isPlayWhenReadyCommitted();
|
return player.isPlayWhenReadyCommitted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLoading() {
|
||||||
|
return player.isLoading();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seekTo(long positionMs) {
|
public void seekTo(long positionMs) {
|
||||||
player.seekTo(positionMs);
|
player.seekTo(positionMs);
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,11 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe
|
||||||
|
|
||||||
// ExoPlayer.EventListener implementation
|
// ExoPlayer.EventListener implementation
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadingChanged(boolean isLoading) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||||
updateTextView();
|
updateTextView();
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,12 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
|
||||||
assertPassed(audioCodecCounters, videoCodecCounters);
|
assertPassed(audioCodecCounters, videoCodecCounters);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExoPlayer.Listener
|
// ExoPlayer.EventListener
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadingChanged(boolean isLoading) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
public final void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue