mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Expose source indices via ExoPlayer (playlists #5).
ExoPlayer.EventListener.onPositionDiscontinuity is notified during seeking and transitioning from one source to the next. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=125578976
This commit is contained in:
parent
f9fa54cd5d
commit
2073f3fce3
@ -72,6 +72,11 @@ public class EventLogger implements ExoPlayer.EventListener, SimpleExoPlayer.Deb
|
||||
+ getStateString(state) + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(int sourceIndex, long positionMs) {
|
||||
Log.d(TAG, "discontinuity [" + sourceIndex + ", " + positionMs + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayWhenReadyCommitted() {
|
||||
// Do nothing.
|
||||
|
@ -391,6 +391,14 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(int sourceIndex, long positionMs) {
|
||||
if (mediaController.isShowing()) {
|
||||
// The MediaController is visible, so force it to show the updated position immediately.
|
||||
mediaController.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException e) {
|
||||
String errorString = null;
|
||||
|
@ -92,6 +92,11 @@ public class FlacPlaybackTest extends InstrumentationTestCase {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(int sourceIndex, long positionMs) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
playbackException = error;
|
||||
|
@ -92,6 +92,11 @@ public class OpusPlaybackTest extends InstrumentationTestCase {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(int sourceIndex, long positionMs) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
playbackException = error;
|
||||
|
@ -111,6 +111,11 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(int sourceIndex, long positionMs) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
playbackException = error;
|
||||
|
@ -118,6 +118,16 @@ public interface ExoPlayer {
|
||||
*/
|
||||
void onPlayWhenReadyCommitted();
|
||||
|
||||
// TODO[playlists]: Should source-initiated resets also cause this to be invoked?
|
||||
/**
|
||||
* Invoked when the player's position changes due to a discontinuity (seeking or playback
|
||||
* transitioning to the next source).
|
||||
*
|
||||
* @param sourceIndex The index of the source being played.
|
||||
* @param positionMs The playback position in that source, in milliseconds.
|
||||
*/
|
||||
void onPositionDiscontinuity(int sourceIndex, long positionMs);
|
||||
|
||||
/**
|
||||
* Invoked when an error occurs. The playback state will transition to
|
||||
* {@link ExoPlayer#STATE_IDLE} immediately after this method is invoked. The player instance
|
||||
@ -227,8 +237,9 @@ public interface ExoPlayer {
|
||||
void setSource(SampleSource sampleSource);
|
||||
|
||||
/**
|
||||
* Sets the player's source provider. The player will transition to {@link #STATE_BUFFERING} until
|
||||
* it is ready to play the first source.
|
||||
* Sets the player's source provider. The player's position will be reset to the start of the
|
||||
* first source and the player will transition to {@link #STATE_BUFFERING} until it is ready to
|
||||
* play it.
|
||||
*
|
||||
* @param sourceProvider The provider of {@link SampleSource}s to play.
|
||||
*/
|
||||
@ -259,12 +270,20 @@ public interface ExoPlayer {
|
||||
boolean isPlayWhenReadyCommitted();
|
||||
|
||||
/**
|
||||
* Seeks to a position specified in milliseconds.
|
||||
* Seeks to a position specified in milliseconds in the current source.
|
||||
*
|
||||
* @param positionMs The seek position.
|
||||
*/
|
||||
void seekTo(long positionMs);
|
||||
|
||||
/**
|
||||
* Seeks to a position specified in milliseconds in the specified source.
|
||||
*
|
||||
* @param sourceIndex The index of the source to seek to.
|
||||
* @param positionMs The seek position relative to the start of the specified source.
|
||||
*/
|
||||
void seekTo(int sourceIndex, long positionMs);
|
||||
|
||||
/**
|
||||
* Stops playback. Use {@code setPlayWhenReady(false)} rather than this method if the intention
|
||||
* is to pause playback.
|
||||
@ -312,12 +331,19 @@ public interface ExoPlayer {
|
||||
long getDuration();
|
||||
|
||||
/**
|
||||
* Gets the current playback position in milliseconds.
|
||||
* Gets the playback position in the current source, in milliseconds.
|
||||
*
|
||||
* @return The current playback position in milliseconds.
|
||||
* @return The playback position in the current source, in milliseconds.
|
||||
*/
|
||||
long getCurrentPosition();
|
||||
|
||||
/**
|
||||
* Gets the index of the current source.
|
||||
*
|
||||
* @return The index of the current source.
|
||||
*/
|
||||
int getCurrentSourceIndex();
|
||||
|
||||
/**
|
||||
* Gets an estimate of the absolute position in milliseconds up to which data is buffered.
|
||||
*
|
||||
|
@ -39,6 +39,15 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
private boolean playWhenReady;
|
||||
private int playbackState;
|
||||
private int pendingPlayWhenReadyAcks;
|
||||
private int pendingSetSourceProviderAndSeekAcks;
|
||||
|
||||
// Playback information when there is no pending seek/set source operation.
|
||||
private ExoPlayerImplInternal.PlaybackInfo playbackInfo;
|
||||
|
||||
// Playback information when there is a pending seek/set source operation.
|
||||
private int sourceIndex;
|
||||
private long position;
|
||||
private long duration;
|
||||
|
||||
/**
|
||||
* Constructs an instance. Must be invoked from a thread that has an associated {@link Looper}.
|
||||
@ -68,6 +77,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
};
|
||||
internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, minBufferMs, minRebufferMs,
|
||||
playWhenReady, eventHandler);
|
||||
playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -87,12 +97,20 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
@Override
|
||||
public void setSource(final SampleSource sampleSource) {
|
||||
internalPlayer.setSourceProvider(new SingleSampleSourceProvider(sampleSource));
|
||||
setSourceProvider(new SingleSampleSourceProvider(sampleSource));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSourceProvider(SampleSourceProvider sourceProvider) {
|
||||
duration = ExoPlayer.UNKNOWN_TIME;
|
||||
position = 0;
|
||||
sourceIndex = 0;
|
||||
|
||||
pendingSetSourceProviderAndSeekAcks++;
|
||||
internalPlayer.setSourceProvider(sourceProvider);
|
||||
for (EventListener listener : listeners) {
|
||||
listener.onPositionDiscontinuity(sourceIndex, position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -119,7 +137,20 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
@Override
|
||||
public void seekTo(long positionMs) {
|
||||
internalPlayer.seekTo(positionMs);
|
||||
seekTo(getCurrentSourceIndex(), positionMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekTo(int sourceIndex, long positionMs) {
|
||||
duration = sourceIndex == getCurrentSourceIndex() ? getDuration() : ExoPlayer.UNKNOWN_TIME;
|
||||
position = positionMs;
|
||||
this.sourceIndex = sourceIndex;
|
||||
|
||||
pendingSetSourceProviderAndSeekAcks++;
|
||||
internalPlayer.seekTo(sourceIndex, position);
|
||||
for (EventListener listener : listeners) {
|
||||
listener.onPositionDiscontinuity(sourceIndex, position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -145,17 +176,33 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return internalPlayer.getDuration();
|
||||
if (pendingSetSourceProviderAndSeekAcks == 0) {
|
||||
long durationUs = playbackInfo.durationUs;
|
||||
return durationUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : durationUs / 1000;
|
||||
} else {
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCurrentPosition() {
|
||||
return internalPlayer.getCurrentPosition();
|
||||
return pendingSetSourceProviderAndSeekAcks == 0 ? playbackInfo.positionUs / 1000 : position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentSourceIndex() {
|
||||
return pendingSetSourceProviderAndSeekAcks == 0 ? playbackInfo.sourceIndex : sourceIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBufferedPosition() {
|
||||
return internalPlayer.getBufferedPosition();
|
||||
if (pendingSetSourceProviderAndSeekAcks == 0) {
|
||||
long bufferedPositionUs = playbackInfo.bufferedPositionUs;
|
||||
return bufferedPositionUs == C.UNSET_TIME_US || bufferedPositionUs == C.END_OF_SOURCE_US
|
||||
? ExoPlayer.UNKNOWN_TIME : bufferedPositionUs / 1000;
|
||||
} else {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -185,6 +232,20 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExoPlayerImplInternal.MSG_SET_SOURCE_PROVIDER_ACK: // Fall through.
|
||||
case ExoPlayerImplInternal.MSG_SEEK_ACK: {
|
||||
pendingSetSourceProviderAndSeekAcks--;
|
||||
break;
|
||||
}
|
||||
case ExoPlayerImplInternal.MSG_SOURCE_CHANGED: {
|
||||
playbackInfo = (ExoPlayerImplInternal.PlaybackInfo) msg.obj;
|
||||
if (pendingSetSourceProviderAndSeekAcks == 0) {
|
||||
for (EventListener listener : listeners) {
|
||||
listener.onPositionDiscontinuity(playbackInfo.sourceIndex, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExoPlayerImplInternal.MSG_ERROR: {
|
||||
ExoPlaybackException exception = (ExoPlaybackException) msg.obj;
|
||||
for (EventListener listener : listeners) {
|
||||
|
@ -31,7 +31,6 @@ import android.util.Pair;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Implements the internal behavior of {@link ExoPlayerImpl}.
|
||||
@ -40,12 +39,35 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
// always propagated properly.
|
||||
/* package */ final class ExoPlayerImplInternal implements Handler.Callback, InvalidationListener {
|
||||
|
||||
/**
|
||||
* Playback position information which is read on the application's thread by
|
||||
* {@link ExoPlayerImpl} and read/written internally on the player's thread.
|
||||
*/
|
||||
public static final class PlaybackInfo {
|
||||
|
||||
public final int sourceIndex;
|
||||
|
||||
public volatile long positionUs;
|
||||
public volatile long bufferedPositionUs;
|
||||
public volatile long durationUs;
|
||||
|
||||
public PlaybackInfo(int sourceIndex) {
|
||||
this.sourceIndex = sourceIndex;
|
||||
bufferedPositionUs = C.UNSET_TIME_US;
|
||||
durationUs = C.UNSET_TIME_US;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final String TAG = "ExoPlayerImplInternal";
|
||||
|
||||
// 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_ERROR = 3;
|
||||
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;
|
||||
|
||||
// Internal messages
|
||||
private static final int MSG_SET_SOURCE_PROVIDER = 0;
|
||||
@ -69,12 +91,13 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
private final Handler handler;
|
||||
private final HandlerThread internalPlaybackThread;
|
||||
private final Handler eventHandler;
|
||||
private final AtomicInteger pendingSeekCount;
|
||||
private final Timeline timeline;
|
||||
|
||||
private PlaybackInfo playbackInfo;
|
||||
private TrackRenderer rendererMediaClockSource;
|
||||
private MediaClock rendererMediaClock;
|
||||
private SampleSourceProvider sampleSourceProvider;
|
||||
// TODO[playlists]: Use timeline.playingSource.sampleSource instead.
|
||||
private SampleSource sampleSource;
|
||||
private TrackRenderer[] enabledRenderers;
|
||||
private boolean released;
|
||||
@ -83,17 +106,10 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
private int state;
|
||||
private int customMessagesSent;
|
||||
private int customMessagesProcessed;
|
||||
private long lastSeekPositionMs;
|
||||
private int lastSeekSourceIndex;
|
||||
private long elapsedRealtimeUs;
|
||||
|
||||
private long sourceOffsetUs;
|
||||
private long internalPositionUs;
|
||||
private int sourceIndex;
|
||||
|
||||
private volatile long durationUs;
|
||||
private volatile long positionUs;
|
||||
private volatile long bufferedPositionUs;
|
||||
|
||||
public ExoPlayerImplInternal(TrackRenderer[] renderers, TrackSelector trackSelector,
|
||||
int minBufferMs, int minRebufferMs, boolean playWhenReady, Handler eventHandler) {
|
||||
@ -104,17 +120,15 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
this.playWhenReady = playWhenReady;
|
||||
this.eventHandler = eventHandler;
|
||||
this.state = ExoPlayer.STATE_IDLE;
|
||||
this.durationUs = C.UNSET_TIME_US;
|
||||
this.bufferedPositionUs = C.UNSET_TIME_US;
|
||||
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
renderers[i].setIndex(i);
|
||||
}
|
||||
|
||||
standaloneMediaClock = new StandaloneMediaClock();
|
||||
pendingSeekCount = new AtomicInteger();
|
||||
enabledRenderers = new TrackRenderer[0];
|
||||
timeline = new Timeline();
|
||||
playbackInfo = new PlaybackInfo(0);
|
||||
|
||||
trackSelector.init(this);
|
||||
|
||||
@ -126,21 +140,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
handler = new Handler(internalPlaybackThread.getLooper(), this);
|
||||
}
|
||||
|
||||
public long getCurrentPosition() {
|
||||
return pendingSeekCount.get() > 0 ? lastSeekPositionMs : (positionUs / 1000);
|
||||
}
|
||||
|
||||
public long getBufferedPosition() {
|
||||
long bufferedPositionUs = this.bufferedPositionUs;
|
||||
return bufferedPositionUs == C.UNSET_TIME_US || bufferedPositionUs == C.END_OF_SOURCE_US
|
||||
? ExoPlayer.UNKNOWN_TIME : bufferedPositionUs / 1000;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
long durationUs = this.durationUs;
|
||||
return durationUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : durationUs / 1000;
|
||||
}
|
||||
|
||||
public void setSourceProvider(SampleSourceProvider sourceProvider) {
|
||||
handler.obtainMessage(MSG_SET_SOURCE_PROVIDER, sourceProvider).sendToTarget();
|
||||
}
|
||||
@ -149,21 +148,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
handler.obtainMessage(MSG_SET_PLAY_WHEN_READY, playWhenReady ? 1 : 0, 0).sendToTarget();
|
||||
}
|
||||
|
||||
public void seekTo(long positionMs) {
|
||||
// TODO[playlists]: Move to ExoPlayerImpl.
|
||||
int sourceIndex;
|
||||
synchronized (timeline) {
|
||||
sourceIndex = this.sourceIndex;
|
||||
}
|
||||
seekTo(sourceIndex, positionMs);
|
||||
}
|
||||
|
||||
public void seekTo(int sourceIndex, long positionMs) {
|
||||
// TODO[playlists]: Expose the current source index and seeking to sources in ExoPlayer.
|
||||
lastSeekSourceIndex = sourceIndex;
|
||||
lastSeekPositionMs = positionMs;
|
||||
pendingSeekCount.incrementAndGet();
|
||||
handler.obtainMessage(MSG_SEEK_TO, lastSeekSourceIndex, -1, positionMs).sendToTarget();
|
||||
handler.obtainMessage(MSG_SEEK_TO, sourceIndex, -1, positionMs).sendToTarget();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@ -294,17 +280,22 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
// TODO[playlists]: Take into account the buffered position in the timeline.
|
||||
long minBufferDurationUs = rebuffering ? minRebufferUs : minBufferUs;
|
||||
return minBufferDurationUs <= 0
|
||||
|| bufferedPositionUs == C.UNSET_TIME_US
|
||||
|| bufferedPositionUs == C.END_OF_SOURCE_US
|
||||
|| bufferedPositionUs >= positionUs + minBufferDurationUs
|
||||
|| (durationUs != C.UNSET_TIME_US && bufferedPositionUs >= durationUs);
|
||||
|| playbackInfo.bufferedPositionUs == C.UNSET_TIME_US
|
||||
|| 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) {
|
||||
resetInternal();
|
||||
sampleSourceProvider = sourceProvider;
|
||||
setState(ExoPlayer.STATE_BUFFERING);
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
try {
|
||||
resetInternal();
|
||||
sampleSourceProvider = sourceProvider;
|
||||
setState(ExoPlayer.STATE_BUFFERING);
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
} finally {
|
||||
eventHandler.sendEmptyMessage(MSG_SET_SOURCE_PROVIDER_ACK);
|
||||
}
|
||||
}
|
||||
|
||||
private void setPlayWhenReadyInternal(boolean playWhenReady) throws ExoPlaybackException {
|
||||
@ -323,7 +314,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
eventHandler.obtainMessage(MSG_SET_PLAY_WHEN_READY_ACK).sendToTarget();
|
||||
eventHandler.sendEmptyMessage(MSG_SET_PLAY_WHEN_READY_ACK);
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,14 +340,15 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
} else {
|
||||
internalPositionUs = standaloneMediaClock.getPositionUs();
|
||||
}
|
||||
positionUs = internalPositionUs - sourceOffsetUs;
|
||||
playbackInfo.positionUs = internalPositionUs - sourceOffsetUs;
|
||||
elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
|
||||
}
|
||||
|
||||
private void updateBufferedPositionUs() {
|
||||
long sourceBufferedPositionUs = enabledRenderers.length > 0
|
||||
? sampleSource.getBufferedPositionUs() : C.END_OF_SOURCE_US;
|
||||
bufferedPositionUs = sourceBufferedPositionUs == C.END_OF_SOURCE_US
|
||||
? timeline.playingSource.sampleSource.getBufferedPositionUs() : C.END_OF_SOURCE_US;
|
||||
long durationUs = playbackInfo.durationUs;
|
||||
playbackInfo.bufferedPositionUs = sourceBufferedPositionUs == C.END_OF_SOURCE_US
|
||||
&& durationUs != C.UNSET_TIME_US ? durationUs : sourceBufferedPositionUs;
|
||||
}
|
||||
|
||||
@ -406,7 +398,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
}
|
||||
|
||||
boolean timelineIsReady = timeline.isReady();
|
||||
if (allRenderersEnded && (durationUs == C.UNSET_TIME_US || durationUs <= positionUs)) {
|
||||
if (allRenderersEnded && (playbackInfo.durationUs == C.UNSET_TIME_US
|
||||
|| playbackInfo.durationUs <= playbackInfo.positionUs)) {
|
||||
setState(ExoPlayer.STATE_ENDED);
|
||||
stopRenderers();
|
||||
} else if (state == ExoPlayer.STATE_BUFFERING && allRenderersReadyOrEnded
|
||||
@ -444,11 +437,17 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
private void seekToInternal(int sourceIndex, long seekPositionMs) throws ExoPlaybackException {
|
||||
try {
|
||||
if (seekPositionMs == (positionUs / 1000)) {
|
||||
if (sourceIndex == playbackInfo.sourceIndex
|
||||
&& seekPositionMs == (playbackInfo.positionUs / 1000)) {
|
||||
// Seek is to the current position. Do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
if (sourceIndex != playbackInfo.sourceIndex) {
|
||||
playbackInfo = new PlaybackInfo(sourceIndex);
|
||||
eventHandler.obtainMessage(MSG_SOURCE_CHANGED, playbackInfo).sendToTarget();
|
||||
}
|
||||
|
||||
long seekPositionUs = seekPositionMs * 1000;
|
||||
rebuffering = false;
|
||||
standaloneMediaClock.stop();
|
||||
@ -470,7 +469,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
setNewSourcePositionInternal(seekPositionUs);
|
||||
resumeInternal();
|
||||
} finally {
|
||||
pendingSeekCount.decrementAndGet();
|
||||
eventHandler.sendEmptyMessage(MSG_SEEK_ACK);
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,7 +482,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
}
|
||||
|
||||
updateBufferedPositionUs();
|
||||
if (allRenderersEnded && (durationUs == C.UNSET_TIME_US || durationUs <= positionUs)) {
|
||||
if (allRenderersEnded && (playbackInfo.durationUs == C.UNSET_TIME_US
|
||||
|| playbackInfo.durationUs <= playbackInfo.positionUs)) {
|
||||
setState(ExoPlayer.STATE_ENDED);
|
||||
} else {
|
||||
setState(allRenderersReadyOrEnded && haveSufficientBuffer() && timeline.isReady()
|
||||
@ -507,7 +507,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
}
|
||||
|
||||
private void setNewSourcePositionInternal(long sourcePositionUs) throws ExoPlaybackException {
|
||||
positionUs = sourcePositionUs;
|
||||
playbackInfo.positionUs = sourcePositionUs;
|
||||
internalPositionUs = sourceOffsetUs + sourcePositionUs;
|
||||
standaloneMediaClock.setPositionUs(internalPositionUs);
|
||||
for (TrackRenderer renderer : enabledRenderers) {
|
||||
@ -636,7 +636,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
if (!bufferingSource.prepared) {
|
||||
// Continue preparation.
|
||||
// TODO[playlists]: Add support for setting the start position to play in a source.
|
||||
long startPositionUs = playingSource == null ? positionUs : 0;
|
||||
long startPositionUs = playingSource == null ? playbackInfo.positionUs : 0;
|
||||
if (bufferingSource.prepare(startPositionUs)) {
|
||||
Pair<TrackSelectionArray, Object> result = trackSelector.selectTracks(renderers,
|
||||
bufferingSource.sampleSource.getTrackGroups());
|
||||
@ -710,7 +710,9 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
if (readingSource != playingSource && playingSourceEndPositionUs != C.UNSET_TIME_US
|
||||
&& internalPositionUs >= playingSourceEndPositionUs) {
|
||||
playingSource.release();
|
||||
playbackInfo = new PlaybackInfo(readingSource.index);
|
||||
setPlayingSource(readingSource, playingSourceEndPositionUs);
|
||||
eventHandler.obtainMessage(MSG_SOURCE_CHANGED, playbackInfo).sendToTarget();
|
||||
}
|
||||
return playingSource.sampleSource;
|
||||
}
|
||||
@ -727,23 +729,24 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
}
|
||||
source = source.nextSource;
|
||||
}
|
||||
|
||||
if (newPlayingSource != null) {
|
||||
nextSourceIndex = sourceIndex + 1;
|
||||
newPlayingSource.nextSource = null;
|
||||
setPlayingSource(newPlayingSource, sourceOffsetUs);
|
||||
bufferingSource = playingSource;
|
||||
bufferingSourceOffsetUs = sourceOffsetUs;
|
||||
nextSourceIndex = sourceIndex + 1;
|
||||
} else {
|
||||
// TODO[REFACTOR]: Presumably we need to disable the renderers somewhere in here?
|
||||
playingSource = null;
|
||||
readingSource = null;
|
||||
bufferingSource = null;
|
||||
bufferingSourceOffsetUs = 0;
|
||||
durationUs = C.UNSET_TIME_US;
|
||||
sampleSource = null;
|
||||
// Set the next source index so that the required source is created in updateSources.
|
||||
nextSourceIndex = sourceIndex;
|
||||
}
|
||||
|
||||
return sampleSource;
|
||||
}
|
||||
|
||||
@ -794,7 +797,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
int enabledRendererCount = disableRenderers(false, newTrackSelections);
|
||||
TrackStream[] newStreams = playingSource.updateTrackStreams(oldStreams, newTrackSelections,
|
||||
newSelections, positionUs);
|
||||
newSelections, playbackInfo.positionUs);
|
||||
trackSelector.onSelectionActivated(trackSelectionData);
|
||||
|
||||
// Update the stored TrackStreams.
|
||||
@ -819,32 +822,16 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
playingSource = null;
|
||||
readingSource = null;
|
||||
bufferingSource = null;
|
||||
durationUs = C.UNSET_TIME_US;
|
||||
playingSourceEndPositionUs = C.UNSET_TIME_US;
|
||||
nextSourceIndex = 0;
|
||||
sourceOffsetUs = 0;
|
||||
bufferingSourceOffsetUs = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Timeline[");
|
||||
Source source = playingSource != null ? playingSource : bufferingSource;
|
||||
while (source != null) {
|
||||
sb.append(source);
|
||||
source = source.nextSource;
|
||||
if (source != null) {
|
||||
sb.append(", ");
|
||||
}
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
playbackInfo = new PlaybackInfo(0);
|
||||
eventHandler.obtainMessage(MSG_SOURCE_CHANGED, playbackInfo).sendToTarget();
|
||||
}
|
||||
|
||||
private void setPlayingSource(Source source, long offsetUs) throws ExoPlaybackException {
|
||||
sourceOffsetUs = offsetUs;
|
||||
durationUs = source.sampleSource.getDurationUs();
|
||||
|
||||
// Disable/enable renderers for the new source.
|
||||
int enabledRendererCount = disableRenderers(true, source.trackSelections);
|
||||
@ -856,11 +843,10 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
playingSourceEndPositionUs = C.UNSET_TIME_US;
|
||||
enableRenderers(source.trackSelections, enabledRendererCount);
|
||||
|
||||
// Update the timeline position for the new source index.
|
||||
synchronized (timeline) {
|
||||
sourceIndex = source.index;
|
||||
updatePositionUs();
|
||||
}
|
||||
// Update playback information.
|
||||
playbackInfo.durationUs = source.sampleSource.getDurationUs();
|
||||
updateBufferedPositionUs();
|
||||
updatePositionUs();
|
||||
}
|
||||
|
||||
private int disableRenderers(boolean sourceTransition, TrackSelectionArray newTrackSelections)
|
||||
|
@ -343,6 +343,11 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||
player.seekTo(positionMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekTo(int sourceIndex, long positionMs) {
|
||||
player.seekTo(sourceIndex, positionMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
player.stop();
|
||||
@ -373,6 +378,11 @@ public final class SimpleExoPlayer implements ExoPlayer {
|
||||
return player.getCurrentPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentSourceIndex() {
|
||||
return player.getCurrentSourceIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBufferedPosition() {
|
||||
return player.getBufferedPosition();
|
||||
|
@ -81,11 +81,11 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe
|
||||
}
|
||||
|
||||
private void updateTextView() {
|
||||
textView.setText(getPlayerStateString() + getBandwidthString() + getVideoString()
|
||||
+ getAudioString());
|
||||
textView.setText(getPlayerStateString() + getPlayerSourceIndexString() + getBandwidthString()
|
||||
+ getVideoString() + getAudioString());
|
||||
}
|
||||
|
||||
public String getPlayerStateString() {
|
||||
private String getPlayerStateString() {
|
||||
String text = "playWhenReady:" + player.getPlayWhenReady() + " playbackState:";
|
||||
switch(player.getPlaybackState()) {
|
||||
case ExoPlayer.STATE_BUFFERING:
|
||||
@ -107,6 +107,10 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe
|
||||
return text;
|
||||
}
|
||||
|
||||
private String getPlayerSourceIndexString() {
|
||||
return " source:" + player.getCurrentSourceIndex();
|
||||
}
|
||||
|
||||
private String getBandwidthString() {
|
||||
BandwidthMeter bandwidthMeter = player.getBandwidthMeter();
|
||||
if (bandwidthMeter == null
|
||||
@ -159,6 +163,11 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(int sourceIndex, long positionMs) {
|
||||
updateTextView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
// Do nothing.
|
||||
|
@ -204,6 +204,11 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onPositionDiscontinuity(int sourceIndex, long positionMs) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
// SimpleExoPlayer.DebugListener
|
||||
|
||||
@Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user