Fix wrong start position in resetInternal.

The start position was set to the old start position instead of the current
playback position. We need to set the start position to the current playback
position to ensure a repreperation with the same media starts from the last
playback position.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=188025439
This commit is contained in:
tonihei 2018-03-06 08:22:41 -08:00 committed by Oliver Woodman
parent b40e5d8c0f
commit 4e69f71e52
4 changed files with 116 additions and 13 deletions

View file

@ -792,7 +792,7 @@ import java.util.Collections;
resetState ? null : playbackInfo.manifest,
resetPosition ? new MediaPeriodId(getFirstPeriodIndex()) : playbackInfo.periodId,
// Set the start position to TIME_UNSET so that a subsequent seek to 0 isn't ignored.
resetPosition ? C.TIME_UNSET : playbackInfo.startPositionUs,
resetPosition ? C.TIME_UNSET : playbackInfo.positionUs,
resetPosition ? C.TIME_UNSET : playbackInfo.contentPositionUs,
playbackInfo.playbackState,
/* isLoading= */ false,

View file

@ -44,6 +44,7 @@ import com.google.android.exoplayer2.testutil.FakeTrackSelection;
import com.google.android.exoplayer2.testutil.FakeTrackSelector;
import com.google.android.exoplayer2.testutil.RobolectricUtil;
import com.google.android.exoplayer2.upstream.Allocator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -1170,10 +1171,8 @@ public final class ExoPlayerTest {
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
ActionSchedule actionSchedule =
new ActionSchedule.Builder("testReprepareAfterPlaybackError")
.waitForPlaybackState(Player.STATE_BUFFERING)
// Cause an internal exception by seeking to an invalid position while the media source
// is still being prepared and the player doesn't immediately know it will fail.
.seek(/* windowIndex= */ 100, /* positionMs= */ 0)
.waitForPlaybackState(Player.STATE_READY)
.throwPlaybackException(ExoPlaybackException.createForSource(new IOException()))
.waitForPlaybackState(Player.STATE_IDLE)
.prepareSource(
new FakeMediaSource(timeline, /* manifest= */ null),
@ -1204,11 +1203,8 @@ public final class ExoPlayerTest {
ActionSchedule actionSchedule =
new ActionSchedule.Builder("testReprepareAfterPlaybackError")
.pause()
.waitForPlaybackState(Player.STATE_BUFFERING)
// Cause an internal exception by seeking to an invalid position while the media source
// is still being prepared and the player doesn't immediately know it will fail.
.seek(/* windowIndex= */ 100, /* positionMs= */ 0)
.waitForSeekProcessed()
.waitForPlaybackState(Player.STATE_READY)
.throwPlaybackException(ExoPlaybackException.createForSource(new IOException()))
.waitForPlaybackState(Player.STATE_IDLE)
.seek(/* positionMs= */ 50)
.waitForSeekProcessed()
@ -1247,8 +1243,7 @@ public final class ExoPlayerTest {
testRunner.assertTimelinesEqual(timeline, timeline);
testRunner.assertTimelineChangeReasonsEqual(
Player.TIMELINE_CHANGE_REASON_PREPARED, Player.TIMELINE_CHANGE_REASON_PREPARED);
testRunner.assertPositionDiscontinuityReasonsEqual(
Player.DISCONTINUITY_REASON_SEEK, Player.DISCONTINUITY_REASON_SEEK);
testRunner.assertPositionDiscontinuityReasonsEqual(Player.DISCONTINUITY_REASON_SEEK);
assertThat(positionHolder[0]).isEqualTo(50);
assertThat(positionHolder[1]).isEqualTo(50);
}
@ -1289,6 +1284,72 @@ public final class ExoPlayerTest {
testRunner.assertTimelineChangeReasonsEqual(Player.TIMELINE_CHANGE_REASON_PREPARED);
}
@Test
public void testPlaybackErrorAndReprepareDoesNotResetPosition() throws Exception {
final Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
final long[] positionHolder = new long[3];
final int[] windowIndexHolder = new int[3];
final FakeMediaSource secondMediaSource =
new FakeMediaSource(/* timeline= */ null, /* manifest= */ null);
ActionSchedule actionSchedule =
new ActionSchedule.Builder("testPlaybackErrorDoesNotResetPosition")
.pause()
.waitForPlaybackState(Player.STATE_READY)
.playUntilPosition(/* windowIndex= */ 1, /* positionMs= */ 500)
.throwPlaybackException(ExoPlaybackException.createForSource(new IOException()))
.waitForPlaybackState(Player.STATE_IDLE)
.executeRunnable(
new PlayerRunnable() {
@Override
public void run(SimpleExoPlayer player) {
// Position while in error state
positionHolder[0] = player.getCurrentPosition();
windowIndexHolder[0] = player.getCurrentWindowIndex();
}
})
.prepareSource(secondMediaSource, /* resetPosition= */ false, /* resetState= */ false)
.waitForPlaybackState(Player.STATE_BUFFERING)
.executeRunnable(
new PlayerRunnable() {
@Override
public void run(SimpleExoPlayer player) {
// Position while repreparing.
positionHolder[1] = player.getCurrentPosition();
windowIndexHolder[1] = player.getCurrentWindowIndex();
secondMediaSource.setNewSourceInfo(timeline, /* newManifest= */ null);
}
})
.waitForPlaybackState(Player.STATE_READY)
.executeRunnable(
new PlayerRunnable() {
@Override
public void run(SimpleExoPlayer player) {
// Position after repreparation finished.
positionHolder[2] = player.getCurrentPosition();
windowIndexHolder[2] = player.getCurrentWindowIndex();
}
})
.play()
.build();
ExoPlayerTestRunner testRunner =
new ExoPlayerTestRunner.Builder()
.setTimeline(timeline)
.setActionSchedule(actionSchedule)
.build();
try {
testRunner.start().blockUntilActionScheduleFinished(TIMEOUT_MS).blockUntilEnded(TIMEOUT_MS);
fail();
} catch (ExoPlaybackException e) {
// Expected exception.
}
assertThat(positionHolder[0]).isAtLeast(500L);
assertThat(positionHolder[1]).isEqualTo(positionHolder[0]);
assertThat(positionHolder[2]).isEqualTo(positionHolder[0]);
assertThat(windowIndexHolder[0]).isEqualTo(1);
assertThat(windowIndexHolder[1]).isEqualTo(1);
assertThat(windowIndexHolder[2]).isEqualTo(1);
}
@Test
public void testSendMessagesDuringPreparation() throws Exception {
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
@ -1422,7 +1483,8 @@ public final class ExoPlayerTest {
new FakeMediaSource(timeline, null),
/* resetPosition= */ false,
/* resetState= */ true)
.waitForPlaybackState(Player.STATE_READY)
.waitForPlaybackState(Player.STATE_BUFFERING)
.waitForPlaybackState(Player.STATE_ENDED)
.build();
new Builder()
.setTimeline(timeline)

View file

@ -447,6 +447,36 @@ public abstract class Action {
}
/** Throws a playback exception on the playback thread. */
public static final class ThrowPlaybackException extends Action {
private final ExoPlaybackException exception;
/**
* @param tag A tag to use for logging.
* @param exception The exception to throw.
*/
public ThrowPlaybackException(String tag, ExoPlaybackException exception) {
super(tag, "ThrowPlaybackException:" + exception);
this.exception = exception;
}
@Override
protected void doActionImpl(
SimpleExoPlayer player, MappingTrackSelector trackSelector, Surface surface) {
player
.createMessage(
new Target() {
@Override
public void handleMessage(int messageType, Object payload)
throws ExoPlaybackException {
throw exception;
}
})
.send();
}
}
/**
* Schedules a play action to be executed, waits until the player reaches the specified position,
* and pauses the player again.

View file

@ -40,6 +40,7 @@ import com.google.android.exoplayer2.testutil.Action.SetRepeatMode;
import com.google.android.exoplayer2.testutil.Action.SetShuffleModeEnabled;
import com.google.android.exoplayer2.testutil.Action.SetVideoSurface;
import com.google.android.exoplayer2.testutil.Action.Stop;
import com.google.android.exoplayer2.testutil.Action.ThrowPlaybackException;
import com.google.android.exoplayer2.testutil.Action.WaitForPlaybackState;
import com.google.android.exoplayer2.testutil.Action.WaitForPositionDiscontinuity;
import com.google.android.exoplayer2.testutil.Action.WaitForSeekProcessed;
@ -412,6 +413,16 @@ public final class ActionSchedule {
return apply(new ExecuteRunnable(tag, runnable));
}
/**
* Schedules to throw a playback exception on the playback thread.
*
* @param exception The exception to throw.
* @return The builder, for convenience.
*/
public Builder throwPlaybackException(ExoPlaybackException exception) {
return apply(new ThrowPlaybackException(tag, exception));
}
public ActionSchedule build() {
CallbackAction callbackAction = new CallbackAction(tag);
apply(callbackAction);