mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Unify internal reset method to support state and position resets.
The ExoPlayerImplInternal.reset method now takes the same set of options as the ExoPlayer.prepare method. This also allows to - Remove some code duplication within ExoPlayerImplInternal - Fix calls to prepare(sameSource, resetPosition=true, resetState=false) with enabled shuffle mode where the position was not correctly reset to the first period index. - Keep the current timeline when calling stop (in line with ExoPlayerImpl). ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=176481878
This commit is contained in:
parent
fdb53ac8d1
commit
e1d960db68
2 changed files with 55 additions and 31 deletions
|
|
@ -17,7 +17,6 @@ package com.google.android.exoplayer2;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
import com.google.android.exoplayer2.source.MediaSource.Listener;
|
|
||||||
import com.google.android.exoplayer2.source.TrackGroup;
|
import com.google.android.exoplayer2.source.TrackGroup;
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
import com.google.android.exoplayer2.testutil.ActionSchedule;
|
import com.google.android.exoplayer2.testutil.ActionSchedule;
|
||||||
|
|
@ -541,4 +540,30 @@ public final class ExoPlayerTest extends TestCase {
|
||||||
Player.TIMELINE_CHANGE_REASON_DYNAMIC);
|
Player.TIMELINE_CHANGE_REASON_DYNAMIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testRepreparationWithPositionResetAndShufflingUsesFirstPeriod() throws Exception {
|
||||||
|
Timeline fakeTimeline = new FakeTimeline(new TimelineWindowDefinition(/* isSeekable= */ true,
|
||||||
|
/* isDynamic= */ false, /* durationUs= */ 100000));
|
||||||
|
ConcatenatingMediaSource firstMediaSource = new ConcatenatingMediaSource(/* isAtomic= */ false,
|
||||||
|
new FakeShuffleOrder(/* length= */ 2),
|
||||||
|
new FakeMediaSource(fakeTimeline, null, Builder.VIDEO_FORMAT),
|
||||||
|
new FakeMediaSource(fakeTimeline, null, Builder.VIDEO_FORMAT)
|
||||||
|
);
|
||||||
|
ConcatenatingMediaSource secondMediaSource = new ConcatenatingMediaSource(/* isAtomic= */ false,
|
||||||
|
new FakeShuffleOrder(/* length= */ 2),
|
||||||
|
new FakeMediaSource(fakeTimeline, null, Builder.VIDEO_FORMAT),
|
||||||
|
new FakeMediaSource(fakeTimeline, null, Builder.VIDEO_FORMAT)
|
||||||
|
);
|
||||||
|
ActionSchedule actionSchedule = new ActionSchedule.Builder("testRepreparationWithShuffle")
|
||||||
|
// Wait for first preparation and enable shuffling. Plays period 0.
|
||||||
|
.waitForPlaybackState(Player.STATE_READY).setShuffleModeEnabled(true)
|
||||||
|
// Reprepare with second media source (keeping state, but with position reset).
|
||||||
|
// Plays period 1 and 0 because of the reversed fake shuffle order.
|
||||||
|
.prepareSource(secondMediaSource, /* resetPosition= */ true, /* resetState= */ false)
|
||||||
|
.build();
|
||||||
|
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
|
||||||
|
.setMediaSource(firstMediaSource).setActionSchedule(actionSchedule)
|
||||||
|
.build().start().blockUntilEnded(TIMEOUT_MS);
|
||||||
|
testRunner.assertPlayedPeriodIndices(0, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -393,17 +393,10 @@ import java.io.IOException;
|
||||||
|
|
||||||
private void prepareInternal(MediaSource mediaSource, boolean resetPosition) {
|
private void prepareInternal(MediaSource mediaSource, boolean resetPosition) {
|
||||||
pendingPrepareCount++;
|
pendingPrepareCount++;
|
||||||
resetInternal(true);
|
resetInternal(/* releaseMediaSource= */ true, resetPosition);
|
||||||
loadControl.onPrepared();
|
loadControl.onPrepared();
|
||||||
if (resetPosition) {
|
|
||||||
playbackInfo = new PlaybackInfo(null, null, 0, C.TIME_UNSET);
|
|
||||||
} else {
|
|
||||||
// The new start position is the current playback position.
|
|
||||||
playbackInfo = new PlaybackInfo(null, null, playbackInfo.periodId, playbackInfo.positionUs,
|
|
||||||
playbackInfo.contentPositionUs);
|
|
||||||
}
|
|
||||||
this.mediaSource = mediaSource;
|
this.mediaSource = mediaSource;
|
||||||
mediaSource.prepareSource(player, true, this);
|
mediaSource.prepareSource(player, /* isTopLevelSource= */ true, /* listener = */ this);
|
||||||
setState(Player.STATE_BUFFERING);
|
setState(Player.STATE_BUFFERING);
|
||||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||||
}
|
}
|
||||||
|
|
@ -638,18 +631,16 @@ import java.io.IOException;
|
||||||
|
|
||||||
Pair<Integer, Long> periodPosition = resolveSeekPosition(seekPosition);
|
Pair<Integer, Long> periodPosition = resolveSeekPosition(seekPosition);
|
||||||
if (periodPosition == null) {
|
if (periodPosition == null) {
|
||||||
int firstPeriodIndex = timeline.isEmpty() ? 0 : timeline.getWindow(
|
|
||||||
timeline.getFirstWindowIndex(shuffleModeEnabled), window).firstPeriodIndex;
|
|
||||||
// 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.
|
||||||
// Set the internal position to (firstPeriodIndex,TIME_UNSET) so that a subsequent seek to
|
|
||||||
// (firstPeriodIndex,0) isn't ignored.
|
|
||||||
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,
|
|
||||||
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, true);
|
||||||
|
// Set the playback position to 0 for notifying the eventHandler (instead of C.TIME_UNSET).
|
||||||
|
eventHandler.obtainMessage(MSG_SEEK_ACK, /* seekAdjusted = */ 1, 0,
|
||||||
|
playbackInfo.fromNewPosition(playbackInfo.periodId.periodIndex, /* startPositionUs = */ 0,
|
||||||
|
/* contentPositionUs= */ C.TIME_UNSET))
|
||||||
|
.sendToTarget();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -768,13 +759,13 @@ import java.io.IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopInternal() {
|
private void stopInternal() {
|
||||||
resetInternal(true);
|
resetInternal(/* releaseMediaSource= */ false, /* resetPosition= */ false);
|
||||||
loadControl.onStopped();
|
loadControl.onStopped();
|
||||||
setState(Player.STATE_IDLE);
|
setState(Player.STATE_IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void releaseInternal() {
|
private void releaseInternal() {
|
||||||
resetInternal(true);
|
resetInternal(/* releaseMediaSource= */ true, /* resetPosition= */ true);
|
||||||
loadControl.onReleased();
|
loadControl.onReleased();
|
||||||
setState(Player.STATE_IDLE);
|
setState(Player.STATE_IDLE);
|
||||||
internalPlaybackThread.quit();
|
internalPlaybackThread.quit();
|
||||||
|
|
@ -784,7 +775,7 @@ import java.io.IOException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetInternal(boolean releaseMediaSource) {
|
private void resetInternal(boolean releaseMediaSource, boolean resetPosition) {
|
||||||
handler.removeMessages(MSG_DO_SOME_WORK);
|
handler.removeMessages(MSG_DO_SOME_WORK);
|
||||||
rebuffering = false;
|
rebuffering = false;
|
||||||
mediaClock.stop();
|
mediaClock.stop();
|
||||||
|
|
@ -804,6 +795,20 @@ import java.io.IOException;
|
||||||
readingPeriodHolder = null;
|
readingPeriodHolder = null;
|
||||||
playingPeriodHolder = null;
|
playingPeriodHolder = null;
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
if (resetPosition) {
|
||||||
|
// Set the internal position to (firstPeriodIndex,TIME_UNSET) so that a subsequent seek to
|
||||||
|
// (firstPeriodIndex,0) isn't ignored.
|
||||||
|
Timeline timeline = playbackInfo.timeline;
|
||||||
|
int firstPeriodIndex = timeline == null || timeline.isEmpty()
|
||||||
|
? 0
|
||||||
|
: timeline.getWindow(timeline.getFirstWindowIndex(shuffleModeEnabled), window)
|
||||||
|
.firstPeriodIndex;
|
||||||
|
playbackInfo = playbackInfo.fromNewPosition(firstPeriodIndex, C.TIME_UNSET, C.TIME_UNSET);
|
||||||
|
} else {
|
||||||
|
// The new start position is the current playback position.
|
||||||
|
playbackInfo = playbackInfo.fromNewPosition(playbackInfo.periodId, playbackInfo.positionUs,
|
||||||
|
playbackInfo.contentPositionUs);
|
||||||
|
}
|
||||||
if (releaseMediaSource) {
|
if (releaseMediaSource) {
|
||||||
if (mediaSource != null) {
|
if (mediaSource != null) {
|
||||||
mediaSource.releaseSource();
|
mediaSource.releaseSource();
|
||||||
|
|
@ -1129,18 +1134,12 @@ import java.io.IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSourceInfoRefreshEndedPlayback(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 = 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.
|
|
||||||
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, true);
|
||||||
|
// Set the playback position to 0 for notifying the eventHandler (instead of C.TIME_UNSET).
|
||||||
|
notifySourceInfoRefresh(prepareAcks, seekAcks,
|
||||||
|
playbackInfo.fromNewPosition(playbackInfo.periodId.periodIndex, 0, C.TIME_UNSET));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifySourceInfoRefresh() {
|
private void notifySourceInfoRefresh() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue