diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/ExoPlayerTest.java index eb9dc6f9d8..56d5f05d00 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -56,7 +56,7 @@ public final class ExoPlayerTest extends TestCase { .setTimeline(timeline).setRenderers(renderer) .build().start().blockUntilEnded(TIMEOUT_MS); testRunner.assertPositionDiscontinuityCount(0); - testRunner.assertTimelinesEqual(timeline); + testRunner.assertTimelinesEqual(); assertEquals(0, renderer.formatReadCount); assertEquals(0, renderer.bufferReadCount); assertFalse(renderer.isEnded); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 7f9b25b606..92f5d0c844 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -22,7 +22,6 @@ import android.os.Message; import android.support.annotation.Nullable; import android.util.Log; import com.google.android.exoplayer2.ExoPlayerImplInternal.PlaybackInfo; -import com.google.android.exoplayer2.ExoPlayerImplInternal.SourceInfo; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.TrackGroupArray; @@ -105,9 +104,9 @@ import java.util.concurrent.CopyOnWriteArraySet; ExoPlayerImpl.this.handleEvent(msg); } }; - playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0, 0); + playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(timeline, manifest, 0, 0); internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, loadControl, playWhenReady, - repeatMode, shuffleModeEnabled, eventHandler, playbackInfo, this); + repeatMode, shuffleModeEnabled, eventHandler, this); } @Override @@ -137,6 +136,15 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { + if (!resetPosition) { + maskingWindowIndex = getCurrentWindowIndex(); + maskingPeriodIndex = getCurrentPeriodIndex(); + maskingWindowPositionMs = getCurrentPosition(); + } else { + maskingWindowIndex = 0; + maskingPeriodIndex = 0; + maskingWindowPositionMs = 0; + } if (resetState) { if (!timeline.isEmpty() || manifest != null) { timeline = Timeline.EMPTY; @@ -263,6 +271,7 @@ import java.util.concurrent.CopyOnWriteArraySet; maskingPeriodIndex = periodIndex; } if (positionMs == C.TIME_UNSET) { + // TODO: Work out when to call onPositionDiscontinuity on listeners for this case. maskingWindowPositionMs = 0; internalPlayer.seekTo(timeline, windowIndex, C.TIME_UNSET); } else { @@ -313,7 +322,7 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public int getCurrentPeriodIndex() { - if (timeline.isEmpty() || pendingSeekAcks > 0) { + if (shouldMaskPosition()) { return maskingPeriodIndex; } else { return playbackInfo.periodId.periodIndex; @@ -322,7 +331,7 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public int getCurrentWindowIndex() { - if (timeline.isEmpty() || pendingSeekAcks > 0) { + if (shouldMaskPosition()) { return maskingWindowIndex; } else { return timeline.getPeriod(playbackInfo.periodId.periodIndex, period).windowIndex; @@ -358,7 +367,7 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public long getCurrentPosition() { - if (timeline.isEmpty() || pendingSeekAcks > 0) { + if (shouldMaskPosition()) { return maskingWindowPositionMs; } else { return playbackInfoPositionUsToWindowPositionMs(playbackInfo.positionUs); @@ -368,7 +377,7 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public long getBufferedPosition() { // TODO - Implement this properly. - if (timeline.isEmpty() || pendingSeekAcks > 0) { + if (shouldMaskPosition()) { return maskingWindowPositionMs; } else { return playbackInfoPositionUsToWindowPositionMs(playbackInfo.bufferedPositionUs); @@ -398,7 +407,7 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public boolean isPlayingAd() { - return !timeline.isEmpty() && pendingSeekAcks == 0 && playbackInfo.periodId.isAd(); + return !shouldMaskPosition() && playbackInfo.periodId.isAd(); } @Override @@ -469,28 +478,9 @@ import java.util.concurrent.CopyOnWriteArraySet; break; } case ExoPlayerImplInternal.MSG_SOURCE_INFO_REFRESHED: { - SourceInfo sourceInfo = (SourceInfo) msg.obj; - pendingPrepareAcks -= sourceInfo.prepareAcks; - pendingSeekAcks -= sourceInfo.seekAcks; - if (pendingPrepareAcks == 0) { - timeline = sourceInfo.timeline; - manifest = sourceInfo.manifest; - playbackInfo = sourceInfo.playbackInfo; - if (pendingSeekAcks == 0 && timeline.isEmpty()) { - // Update the masking variables, which are used when the timeline is empty. - maskingPeriodIndex = 0; - maskingWindowIndex = 0; - maskingWindowPositionMs = 0; - } - for (Player.EventListener listener : listeners) { - listener.onTimelineChanged(timeline, manifest); - } - } - if (pendingSeekAcks == 0 && sourceInfo.seekAcks > 0) { - for (Player.EventListener listener : listeners) { - listener.onSeekProcessed(); - } - } + int prepareAcks = msg.arg1; + int seekAcks = msg.arg2; + handlePlaybackInfo((PlaybackInfo) msg.obj, prepareAcks, seekAcks, false); break; } case ExoPlayerImplInternal.MSG_TRACKS_CHANGED: { @@ -507,30 +497,12 @@ import java.util.concurrent.CopyOnWriteArraySet; break; } case ExoPlayerImplInternal.MSG_SEEK_ACK: { - if (--pendingSeekAcks == 0) { - playbackInfo = (ExoPlayerImplInternal.PlaybackInfo) msg.obj; - if (timeline.isEmpty()) { - // Update the masking variables, which are used when the timeline is empty. - maskingPeriodIndex = 0; - maskingWindowIndex = 0; - maskingWindowPositionMs = 0; - } - for (Player.EventListener listener : listeners) { - if (msg.arg1 != 0) { - listener.onPositionDiscontinuity(DISCONTINUITY_REASON_INTERNAL); - } - listener.onSeekProcessed(); - } - } + boolean seekPositionAdjusted = msg.arg1 != 0; + handlePlaybackInfo((PlaybackInfo) msg.obj, 0, 1, seekPositionAdjusted); break; } case ExoPlayerImplInternal.MSG_POSITION_DISCONTINUITY: { - if (pendingSeekAcks == 0) { - playbackInfo = (ExoPlayerImplInternal.PlaybackInfo) msg.obj; - for (Player.EventListener listener : listeners) { - listener.onPositionDiscontinuity(DISCONTINUITY_REASON_PERIOD_TRANSITION); - } - } + handlePlaybackInfo((PlaybackInfo) msg.obj, 0, 0, true); break; } case ExoPlayerImplInternal.MSG_PLAYBACK_PARAMETERS_CHANGED: { @@ -555,6 +527,43 @@ import java.util.concurrent.CopyOnWriteArraySet; } } + private void handlePlaybackInfo(PlaybackInfo playbackInfo, int prepareAcks, int seekAcks, + boolean positionDiscontinuity) { + Assertions.checkNotNull(playbackInfo.timeline); + pendingPrepareAcks -= prepareAcks; + pendingSeekAcks -= seekAcks; + if (pendingPrepareAcks == 0 && pendingSeekAcks == 0) { + this.playbackInfo = playbackInfo; + boolean timelineOrManifestChanged = timeline != playbackInfo.timeline + || manifest != playbackInfo.manifest; + timeline = playbackInfo.timeline; + manifest = playbackInfo.manifest; + if (timeline.isEmpty()) { + // Update the masking variables, which are used when the timeline is empty. + maskingPeriodIndex = 0; + maskingWindowIndex = 0; + maskingWindowPositionMs = 0; + } + if (timelineOrManifestChanged) { + for (Player.EventListener listener : listeners) { + listener.onTimelineChanged(timeline, manifest); + } + } + if (positionDiscontinuity) { + for (Player.EventListener listener : listeners) { + listener.onPositionDiscontinuity( + seekAcks > 0 ? DISCONTINUITY_REASON_INTERNAL : DISCONTINUITY_REASON_PERIOD_TRANSITION + ); + } + } + } + if (pendingSeekAcks == 0 && seekAcks > 0) { + for (Player.EventListener listener : listeners) { + listener.onSeekProcessed(); + } + } + } + private long playbackInfoPositionUsToWindowPositionMs(long positionUs) { long positionMs = C.usToMs(positionUs); if (!playbackInfo.periodId.isAd()) { @@ -564,4 +573,8 @@ import java.util.concurrent.CopyOnWriteArraySet; return positionMs; } + private boolean shouldMaskPosition() { + return timeline.isEmpty() || pendingSeekAcks > 0 || pendingPrepareAcks > 0; + } + } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index a9a289d597..fec3b3c65a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -54,6 +54,8 @@ import java.io.IOException; */ public static final class PlaybackInfo { + public final Timeline timeline; + public final Object manifest; public final MediaPeriodId periodId; public final long startPositionUs; public final long contentPositionUs; @@ -61,15 +63,14 @@ import java.io.IOException; public volatile long positionUs; public volatile long bufferedPositionUs; - public PlaybackInfo(int periodIndex, long startPositionUs) { - this(new MediaPeriodId(periodIndex), startPositionUs); + public PlaybackInfo(Timeline timeline, Object manifest, int periodIndex, long startPositionUs) { + this(timeline, manifest, new MediaPeriodId(periodIndex), startPositionUs, C.TIME_UNSET); } - public PlaybackInfo(MediaPeriodId periodId, long startPositionUs) { - this(periodId, startPositionUs, C.TIME_UNSET); - } - - public PlaybackInfo(MediaPeriodId periodId, long startPositionUs, long contentPositionUs) { + public PlaybackInfo(Timeline timeline, Object manifest, MediaPeriodId periodId, + long startPositionUs, long contentPositionUs) { + this.timeline = timeline; + this.manifest = manifest; this.periodId = periodId; this.startPositionUs = startPositionUs; this.contentPositionUs = contentPositionUs; @@ -77,31 +78,33 @@ import java.io.IOException; bufferedPositionUs = startPositionUs; } + public PlaybackInfo fromNewPosition(int periodIndex, long startPositionUs, + long contentPositionUs) { + return fromNewPosition(new MediaPeriodId(periodIndex), startPositionUs, contentPositionUs); + } + + public PlaybackInfo fromNewPosition(MediaPeriodId periodId, long startPositionUs, + long contentPositionUs) { + return new PlaybackInfo(timeline, manifest, periodId, startPositionUs, contentPositionUs); + } + public PlaybackInfo copyWithPeriodIndex(int periodIndex) { - PlaybackInfo playbackInfo = new PlaybackInfo(periodId.copyWithPeriodIndex(periodIndex), - startPositionUs, contentPositionUs); - playbackInfo.positionUs = positionUs; - playbackInfo.bufferedPositionUs = bufferedPositionUs; + PlaybackInfo playbackInfo = new PlaybackInfo(timeline, manifest, + periodId.copyWithPeriodIndex(periodIndex), startPositionUs, contentPositionUs); + copyMutablePositions(this, playbackInfo); return playbackInfo; } - } + public PlaybackInfo copyWithTimeline(Timeline timeline, Object manifest) { + PlaybackInfo playbackInfo = new PlaybackInfo(timeline, manifest, periodId, startPositionUs, + contentPositionUs); + copyMutablePositions(this, playbackInfo); + return playbackInfo; + } - public static final class SourceInfo { - - public final Timeline timeline; - public final Object manifest; - public final PlaybackInfo playbackInfo; - public final int prepareAcks; - public final int seekAcks; - - public SourceInfo(Timeline timeline, Object manifest, PlaybackInfo playbackInfo, - int prepareAcks, int seekAcks) { - this.timeline = timeline; - this.manifest = manifest; - this.playbackInfo = playbackInfo; - this.prepareAcks = prepareAcks; - this.seekAcks = seekAcks; + private static void copyMutablePositions(PlaybackInfo from, PlaybackInfo to) { + to.positionUs = from.positionUs; + to.bufferedPositionUs = from.bufferedPositionUs; } } @@ -192,12 +195,9 @@ import java.io.IOException; private MediaPeriodHolder readingPeriodHolder; private MediaPeriodHolder playingPeriodHolder; - private Timeline timeline; - public ExoPlayerImplInternal(Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl, boolean playWhenReady, @Player.RepeatMode int repeatMode, - boolean shuffleModeEnabled, Handler eventHandler, PlaybackInfo playbackInfo, - ExoPlayer player) { + boolean shuffleModeEnabled, Handler eventHandler, ExoPlayer player) { this.renderers = renderers; this.trackSelector = trackSelector; this.loadControl = loadControl; @@ -206,9 +206,9 @@ import java.io.IOException; this.shuffleModeEnabled = shuffleModeEnabled; this.eventHandler = eventHandler; this.state = Player.STATE_IDLE; - this.playbackInfo = playbackInfo; this.player = player; + playbackInfo = new PlaybackInfo(null, null, 0, C.TIME_UNSET); rendererCapabilities = new RendererCapabilities[renderers.length]; for (int i = 0; i < renderers.length; i++) { renderers[i].setIndex(i); @@ -446,10 +446,10 @@ import java.io.IOException; resetInternal(true); loadControl.onPrepared(); if (resetPosition) { - playbackInfo = new PlaybackInfo(0, C.TIME_UNSET); + playbackInfo = new PlaybackInfo(null, null, 0, C.TIME_UNSET); } else { // The new start position is the current playback position. - playbackInfo = new PlaybackInfo(playbackInfo.periodId, playbackInfo.positionUs, + playbackInfo = new PlaybackInfo(null, null, playbackInfo.periodId, playbackInfo.positionUs, playbackInfo.contentPositionUs); } this.mediaSource = mediaSource; @@ -496,8 +496,9 @@ import java.io.IOException; return; } while (true) { - int nextPeriodIndex = timeline.getNextPeriodIndex(lastValidPeriodHolder.info.id.periodIndex, - period, window, repeatMode, shuffleModeEnabled); + int nextPeriodIndex = playbackInfo.timeline.getNextPeriodIndex( + lastValidPeriodHolder.info.id.periodIndex, period, window, repeatMode, + shuffleModeEnabled); while (lastValidPeriodHolder.next != null && !lastValidPeriodHolder.info.isLastInTimelinePeriod) { lastValidPeriodHolder = lastValidPeriodHolder.next; @@ -534,7 +535,8 @@ import java.io.IOException; // position of the playing period to make sure none of the removed period is played. MediaPeriodId periodId = playingPeriodHolder.info.id; long newPositionUs = seekToPeriodPosition(periodId, playbackInfo.positionUs); - playbackInfo = new PlaybackInfo(periodId, newPositionUs, playbackInfo.contentPositionUs); + playbackInfo = playbackInfo.fromNewPosition(periodId, newPositionUs, + playbackInfo.contentPositionUs); } } @@ -696,6 +698,7 @@ import java.io.IOException; } private void seekToInternal(SeekPosition seekPosition) throws ExoPlaybackException { + Timeline timeline = playbackInfo.timeline; if (timeline == null) { pendingInitialSeekCount++; pendingSeekPosition = seekPosition; @@ -710,10 +713,10 @@ import java.io.IOException; // timeline has changed and a suitable seek position could not be resolved in the new one. // Set the internal position to (firstPeriodIndex,TIME_UNSET) so that a subsequent seek to // (firstPeriodIndex,0) isn't ignored. - playbackInfo = new PlaybackInfo(firstPeriodIndex, C.TIME_UNSET); + playbackInfo = playbackInfo.fromNewPosition(firstPeriodIndex, C.TIME_UNSET, C.TIME_UNSET); setState(Player.STATE_ENDED); - eventHandler.obtainMessage(MSG_SEEK_ACK, 1, 0, new PlaybackInfo(firstPeriodIndex, 0)) - .sendToTarget(); + eventHandler.obtainMessage(MSG_SEEK_ACK, 1, 0, + playbackInfo.fromNewPosition(firstPeriodIndex, 0, C.TIME_UNSET)).sendToTarget(); // Reset, but retain the source so that it can still be used should a seek occur. resetInternal(false); return; @@ -739,7 +742,7 @@ import java.io.IOException; seekPositionAdjusted |= periodPositionUs != newPeriodPositionUs; periodPositionUs = newPeriodPositionUs; } finally { - playbackInfo = new PlaybackInfo(periodId, periodPositionUs, contentPositionUs); + playbackInfo = playbackInfo.fromNewPosition(periodId, periodPositionUs, contentPositionUs); eventHandler.obtainMessage(MSG_SEEK_ACK, seekPositionAdjusted ? 1 : 0, 0, playbackInfo) .sendToTarget(); } @@ -809,7 +812,7 @@ import java.io.IOException; private boolean shouldKeepPeriodHolder(MediaPeriodId seekPeriodId, long positionUs, MediaPeriodHolder holder) { if (seekPeriodId.equals(holder.info.id) && holder.prepared) { - timeline.getPeriod(holder.info.id.periodIndex, period); + playbackInfo.timeline.getPeriod(holder.info.id.periodIndex, period); int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(positionUs); if (nextAdGroupIndex == C.INDEX_UNSET || period.getAdGroupTimeUs(nextAdGroupIndex) == holder.info.endPositionUs) { @@ -884,7 +887,7 @@ import java.io.IOException; mediaSource = null; } mediaPeriodInfoSequence.setTimeline(null); - timeline = null; + playbackInfo = playbackInfo.copyWithTimeline(null, null); } } @@ -1024,10 +1027,11 @@ import java.io.IOException; return; } - Timeline oldTimeline = timeline; - timeline = sourceRefreshInfo.timeline; - mediaPeriodInfoSequence.setTimeline(timeline); + Timeline oldTimeline = playbackInfo.timeline; + Timeline timeline = sourceRefreshInfo.timeline; Object manifest = sourceRefreshInfo.manifest; + mediaPeriodInfoSequence.setTimeline(timeline); + playbackInfo = playbackInfo.copyWithTimeline(timeline, manifest); if (oldTimeline == null) { int processedPrepareAcks = pendingPrepareCount; @@ -1040,32 +1044,32 @@ import java.io.IOException; if (periodPosition == null) { // The seek position was valid for the timeline that it was performed into, but the // timeline has changed and a suitable seek position could not be resolved in the new one. - handleSourceInfoRefreshEndedPlayback(manifest, processedPrepareAcks, - processedInitialSeekCount); + handleSourceInfoRefreshEndedPlayback(processedPrepareAcks, processedInitialSeekCount); } else { int periodIndex = periodPosition.first; long positionUs = periodPosition.second; MediaPeriodId periodId = mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, positionUs); - playbackInfo = new PlaybackInfo(periodId, periodId.isAd() ? 0 : positionUs, positionUs); - notifySourceInfoRefresh(manifest, processedPrepareAcks, processedInitialSeekCount); + playbackInfo = playbackInfo.fromNewPosition(periodId, periodId.isAd() ? 0 : positionUs, + positionUs); + notifySourceInfoRefresh(processedPrepareAcks, processedInitialSeekCount); } } else if (playbackInfo.startPositionUs == C.TIME_UNSET) { if (timeline.isEmpty()) { - handleSourceInfoRefreshEndedPlayback(manifest, processedPrepareAcks, 0); + handleSourceInfoRefreshEndedPlayback(processedPrepareAcks, 0); } else { - Pair defaultPosition = getPeriodPosition( + Pair defaultPosition = getPeriodPosition(timeline, timeline.getFirstWindowIndex(shuffleModeEnabled), C.TIME_UNSET); int periodIndex = defaultPosition.first; long startPositionUs = defaultPosition.second; MediaPeriodId periodId = mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, startPositionUs); - playbackInfo = new PlaybackInfo(periodId, periodId.isAd() ? 0 : startPositionUs, - startPositionUs); - notifySourceInfoRefresh(manifest, processedPrepareAcks, 0); + playbackInfo = playbackInfo.fromNewPosition(periodId, + periodId.isAd() ? 0 : startPositionUs, startPositionUs); + notifySourceInfoRefresh(processedPrepareAcks, 0); } } else { - notifySourceInfoRefresh(manifest, processedPrepareAcks, 0); + notifySourceInfoRefresh(processedPrepareAcks, 0); } return; } @@ -1074,7 +1078,7 @@ import java.io.IOException; MediaPeriodHolder periodHolder = playingPeriodHolder != null ? playingPeriodHolder : loadingPeriodHolder; if (periodHolder == null && playingPeriodIndex >= oldTimeline.getPeriodCount()) { - notifySourceInfoRefresh(manifest); + notifySourceInfoRefresh(); return; } Object playingPeriodUid = periodHolder == null @@ -1086,11 +1090,11 @@ import java.io.IOException; int newPeriodIndex = resolveSubsequentPeriod(playingPeriodIndex, oldTimeline, timeline); if (newPeriodIndex == C.INDEX_UNSET) { // We failed to resolve a suitable restart position. - handleSourceInfoRefreshEndedPlayback(manifest); + handleSourceInfoRefreshEndedPlayback(); return; } // We resolved a subsequent period. Seek to the default position in the corresponding window. - Pair defaultPosition = getPeriodPosition( + Pair defaultPosition = getPeriodPosition(timeline, timeline.getPeriod(newPeriodIndex, period).windowIndex, C.TIME_UNSET); newPeriodIndex = defaultPosition.first; long newPositionUs = defaultPosition.second; @@ -1113,8 +1117,8 @@ import java.io.IOException; // Actually do the seek. MediaPeriodId periodId = new MediaPeriodId(newPeriodIndex); newPositionUs = seekToPeriodPosition(periodId, newPositionUs); - playbackInfo = new PlaybackInfo(periodId, newPositionUs); - notifySourceInfoRefresh(manifest); + playbackInfo = playbackInfo.fromNewPosition(periodId, newPositionUs, C.TIME_UNSET); + notifySourceInfoRefresh(); return; } @@ -1130,15 +1134,15 @@ import java.io.IOException; if (!periodId.isAd() || periodId.adIndexInAdGroup != playbackInfo.periodId.adIndexInAdGroup) { long newPositionUs = seekToPeriodPosition(periodId, playbackInfo.contentPositionUs); long contentPositionUs = periodId.isAd() ? playbackInfo.contentPositionUs : C.TIME_UNSET; - playbackInfo = new PlaybackInfo(periodId, newPositionUs, contentPositionUs); - notifySourceInfoRefresh(manifest); + playbackInfo = playbackInfo.fromNewPosition(periodId, newPositionUs, contentPositionUs); + notifySourceInfoRefresh(); return; } } if (periodHolder == null) { // We don't have any period holders, so we're done. - notifySourceInfoRefresh(manifest); + notifySourceInfoRefresh(); return; } @@ -1163,7 +1167,7 @@ import java.io.IOException; // position of the playing period to make sure none of the removed period is played. long newPositionUs = seekToPeriodPosition(playingPeriodHolder.info.id, playbackInfo.positionUs); - playbackInfo = new PlaybackInfo(playingPeriodHolder.info.id, newPositionUs, + playbackInfo = playbackInfo.fromNewPosition(playingPeriodHolder.info.id, newPositionUs, playbackInfo.contentPositionUs); } else { // Update the loading period to be the last period that's still valid, and release all @@ -1177,7 +1181,7 @@ import java.io.IOException; } } - notifySourceInfoRefresh(manifest); + notifySourceInfoRefresh(); } private MediaPeriodHolder updatePeriodInfo(MediaPeriodHolder periodHolder, int periodIndex) { @@ -1191,36 +1195,36 @@ import java.io.IOException; } } - private void handleSourceInfoRefreshEndedPlayback(Object manifest) { - handleSourceInfoRefreshEndedPlayback(manifest, 0, 0); + private void handleSourceInfoRefreshEndedPlayback() { + handleSourceInfoRefreshEndedPlayback(0, 0); } - private void handleSourceInfoRefreshEndedPlayback(Object manifest, int prepareAcks, - int seekAcks) { + private void handleSourceInfoRefreshEndedPlayback(int prepareAcks, int seekAcks) { + Timeline timeline = playbackInfo.timeline; int firstPeriodIndex = timeline.isEmpty() ? 0 : timeline.getWindow( timeline.getFirstWindowIndex(shuffleModeEnabled), window).firstPeriodIndex; // Set the internal position to (firstPeriodIndex,TIME_UNSET) so that a subsequent seek to // (firstPeriodIndex,0) isn't ignored. - playbackInfo = new PlaybackInfo(firstPeriodIndex, C.TIME_UNSET); + playbackInfo = playbackInfo.fromNewPosition(firstPeriodIndex, C.TIME_UNSET, C.TIME_UNSET); setState(Player.STATE_ENDED); // Set the playback position to (firstPeriodIndex,0) for notifying the eventHandler. - notifySourceInfoRefresh(manifest, prepareAcks, seekAcks, new PlaybackInfo(firstPeriodIndex, 0)); + notifySourceInfoRefresh(prepareAcks, seekAcks, + playbackInfo.fromNewPosition(firstPeriodIndex, 0, C.TIME_UNSET)); // Reset, but retain the source so that it can still be used should a seek occur. resetInternal(false); } - private void notifySourceInfoRefresh(Object manifest) { - notifySourceInfoRefresh(manifest, 0, 0); + private void notifySourceInfoRefresh() { + notifySourceInfoRefresh(0, 0); } - private void notifySourceInfoRefresh(Object manifest, int prepareAcks, int seekAcks) { - notifySourceInfoRefresh(manifest, prepareAcks, seekAcks, playbackInfo); + private void notifySourceInfoRefresh(int prepareAcks, int seekAcks) { + notifySourceInfoRefresh(prepareAcks, seekAcks, playbackInfo); } - private void notifySourceInfoRefresh(Object manifest, int prepareAcks, int seekAcks, - PlaybackInfo playbackInfo) { - eventHandler.obtainMessage(MSG_SOURCE_INFO_REFRESHED, - new SourceInfo(timeline, manifest, playbackInfo, prepareAcks, seekAcks)).sendToTarget(); + private void notifySourceInfoRefresh(int prepareAcks, int seekAcks, PlaybackInfo playbackInfo) { + eventHandler.obtainMessage(MSG_SOURCE_INFO_REFRESHED, prepareAcks, seekAcks, playbackInfo) + .sendToTarget(); } /** @@ -1260,6 +1264,7 @@ import java.io.IOException; * bounds of the timeline. */ private Pair resolveSeekPosition(SeekPosition seekPosition) { + Timeline timeline = playbackInfo.timeline; Timeline seekTimeline = seekPosition.timeline; if (seekTimeline.isEmpty()) { // The application performed a blind seek without a non-empty timeline (most likely based on @@ -1291,7 +1296,8 @@ import java.io.IOException; periodIndex = resolveSubsequentPeriod(periodPosition.first, seekTimeline, timeline); if (periodIndex != C.INDEX_UNSET) { // We found one. Map the SeekPosition onto the corresponding default position. - return getPeriodPosition(timeline.getPeriod(periodIndex, period).windowIndex, C.TIME_UNSET); + return getPeriodPosition(timeline, timeline.getPeriod(periodIndex, period).windowIndex, + C.TIME_UNSET); } // We didn't find one. Give up. return null; @@ -1301,12 +1307,13 @@ import java.io.IOException; * Calls {@link Timeline#getPeriodPosition(Timeline.Window, Timeline.Period, int, long)} using the * current timeline. */ - private Pair getPeriodPosition(int windowIndex, long windowPositionUs) { + private Pair getPeriodPosition(Timeline timeline, int windowIndex, + long windowPositionUs) { return timeline.getPeriodPosition(window, period, windowIndex, windowPositionUs); } private void updatePeriods() throws ExoPlaybackException, IOException { - if (timeline == null) { + if (playbackInfo.timeline == null) { // We're waiting to get information about periods. mediaSource.maybeThrowSourceInfoRefreshError(); return; @@ -1332,7 +1339,7 @@ import java.io.IOException; // the end of the playing period, so advance playback to the next period. playingPeriodHolder.release(); setPlayingPeriodHolder(playingPeriodHolder.next); - playbackInfo = new PlaybackInfo(playingPeriodHolder.info.id, + playbackInfo = playbackInfo.fromNewPosition(playingPeriodHolder.info.id, playingPeriodHolder.info.startPositionUs, playingPeriodHolder.info.contentPositionUs); updatePlaybackPositions(); eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY, playbackInfo).sendToTarget(); @@ -1439,7 +1446,7 @@ import java.io.IOException; ? RENDERER_TIMESTAMP_OFFSET_US : (loadingPeriodHolder.getRendererOffset() + loadingPeriodHolder.info.durationUs); int holderIndex = loadingPeriodHolder == null ? 0 : loadingPeriodHolder.index + 1; - Object uid = timeline.getPeriod(info.id.periodIndex, period, true).uid; + Object uid = playbackInfo.timeline.getPeriod(info.id.periodIndex, period, true).uid; MediaPeriodHolder newPeriodHolder = new MediaPeriodHolder(renderers, rendererCapabilities, rendererPositionOffsetUs, trackSelector, loadControl, mediaSource, uid, holderIndex, info); if (loadingPeriodHolder != null) {