Commit f1508565 by olly Committed by Oliver Woodman

Fix masking step 1

1. Move Timeline/Manifest into PlaybackInfo
2. Don't update externally visible Timeline/Manifest during preparation
3. Ignore MSG_POSITION_DISCONTINUITY during preparation
4. Correctly set masking variables at start of preparation, and use them

Once this change goes in, PlaybackInfo will contain timeline, manifest
and position, which should always be self-consistent with one another.
The next step would then be to move a bunch of logic in ExoPlayerImpl
that derives state from timeline and position into PlaybackInfo, and
split that into its own top level class that can be easily tested to make
sure it never IndexOutOfBounds.

I think we could also replace the masking variables and instead just assign
a new PlaybackInfo to the playbackInfo variable whenever we're doing
something that requires masking. This should be possible because we no
longer update playbackInfo whenever we have pending acks. It would
require allowing PlaybackInfo to mask the window position internally when
the timeline is empty, but I think this is ok, and again is something we
could test pretty easily.

Issue: #3362

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