mirror of
https://github.com/androidx/media.git
synced 2025-05-18 04:59:54 +08:00
Allow resetInternal to release MediaSource but keep timeline.
This allows to keep the state synced with ExoPlayerImpl after stopping the player, but still releases the media source immediately as it needs to be reprepared. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=177167980
This commit is contained in:
parent
f2d5541752
commit
54a1bb186e
@ -784,4 +784,28 @@ public final class ExoPlayerTest extends TestCase {
|
||||
testRunner.assertNoPositionDiscontinuities();
|
||||
}
|
||||
|
||||
public void testStopAndSeekAfterStopDoesNotResetTimeline() throws Exception {
|
||||
// Combining additional stop and seek after initial stop in one test to get the seek processed
|
||||
// callback which ensures that all operations have been processed by the player.
|
||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
ActionSchedule actionSchedule =
|
||||
new ActionSchedule.Builder("testStopTwice")
|
||||
.waitForPlaybackState(Player.STATE_READY)
|
||||
.stop(false)
|
||||
.stop(false)
|
||||
.seek(0)
|
||||
.waitForSeekProcessed()
|
||||
.build();
|
||||
ExoPlayerTestRunner testRunner =
|
||||
new ExoPlayerTestRunner.Builder()
|
||||
.setTimeline(timeline)
|
||||
.setActionSchedule(actionSchedule)
|
||||
.build()
|
||||
.start()
|
||||
.blockUntilActionScheduleFinished(TIMEOUT_MS)
|
||||
.blockUntilEnded(TIMEOUT_MS);
|
||||
testRunner.assertTimelinesEqual(timeline);
|
||||
testRunner.assertTimelineChangeReasonsEqual(Player.TIMELINE_CHANGE_REASON_PREPARED);
|
||||
testRunner.assertPositionDiscontinuityReasonsEqual(Player.DISCONTINUITY_REASON_SEEK);
|
||||
}
|
||||
}
|
||||
|
@ -390,11 +390,11 @@ import java.io.IOException;
|
||||
|
||||
private void prepareInternal(MediaSource mediaSource, boolean resetPosition) {
|
||||
pendingPrepareCount++;
|
||||
resetInternal(/* releaseMediaSource= */ true, resetPosition);
|
||||
resetInternal(/* releaseMediaSource= */ true, resetPosition, /* resetState= */ true);
|
||||
loadControl.onPrepared();
|
||||
this.mediaSource = mediaSource;
|
||||
setState(Player.STATE_BUFFERING);
|
||||
mediaSource.prepareSource(player, /* isTopLevelSource= */ true, /* listener = */ this);
|
||||
mediaSource.prepareSource(player, /* isTopLevelSource= */ true, /* listener= */ this);
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
}
|
||||
|
||||
@ -629,10 +629,15 @@ import java.io.IOException;
|
||||
|
||||
private void seekToInternal(SeekPosition seekPosition) throws ExoPlaybackException {
|
||||
Timeline timeline = playbackInfo.timeline;
|
||||
if (timeline == null) {
|
||||
if (mediaSource == null || timeline == null) {
|
||||
pendingInitialSeekPosition = seekPosition;
|
||||
eventHandler.obtainMessage(MSG_SEEK_ACK, /* seekAdjusted = */ 0, 0,
|
||||
playbackInfo.copyWithTimeline(Timeline.EMPTY, null)).sendToTarget();
|
||||
eventHandler
|
||||
.obtainMessage(
|
||||
MSG_SEEK_ACK,
|
||||
/* seekAdjusted */ 0,
|
||||
0,
|
||||
timeline == null ? playbackInfo.copyWithTimeline(Timeline.EMPTY, null) : playbackInfo)
|
||||
.sendToTarget();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -642,10 +647,11 @@ import java.io.IOException;
|
||||
// timeline has changed and a suitable seek position could not be resolved in the new one.
|
||||
setState(Player.STATE_ENDED);
|
||||
// Reset, but retain the source so that it can still be used should a seek occur.
|
||||
resetInternal(false, true);
|
||||
resetInternal(
|
||||
/* releaseMediaSource= */ false, /* resetPosition= */ true, /* resetState= */ false);
|
||||
// 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,
|
||||
eventHandler.obtainMessage(MSG_SEEK_ACK, /* seekAdjusted */ 1, 0,
|
||||
playbackInfo.fromNewPosition(playbackInfo.periodId.periodIndex, /* startPositionUs= */ 0,
|
||||
/* contentPositionUs= */ C.TIME_UNSET))
|
||||
.sendToTarget();
|
||||
return;
|
||||
@ -766,14 +772,15 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
private void stopInternal(boolean reset) {
|
||||
// Releasing the internal player sets the timeline to null. Use the current timeline or
|
||||
// Timeline.EMPTY for notifying the eventHandler.
|
||||
Timeline publicTimeline = reset || playbackInfo.timeline == null
|
||||
? Timeline.EMPTY : playbackInfo.timeline;
|
||||
Object publicManifest = reset ? null : playbackInfo.manifest;
|
||||
resetInternal(/* releaseMediaSource= */ true, reset);
|
||||
PlaybackInfo publicPlaybackInfo = playbackInfo.copyWithTimeline(publicTimeline, publicManifest);
|
||||
if (reset) {
|
||||
resetInternal(
|
||||
/* releaseMediaSource= */ true, /* resetPosition= */ reset, /* resetState= */ reset);
|
||||
PlaybackInfo publicPlaybackInfo = playbackInfo;
|
||||
if (playbackInfo.timeline == null) {
|
||||
// Resetting the state sets the timeline to null. Use Timeline.EMPTY for notifying the
|
||||
// eventHandler.
|
||||
publicPlaybackInfo = publicPlaybackInfo.copyWithTimeline(Timeline.EMPTY, null);
|
||||
}
|
||||
if (playbackInfo.startPositionUs == C.TIME_UNSET) {
|
||||
// When resetting the state, set the playback position to 0 (instead of C.TIME_UNSET) for
|
||||
// notifying the eventHandler.
|
||||
publicPlaybackInfo =
|
||||
@ -787,7 +794,8 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
private void releaseInternal() {
|
||||
resetInternal(/* releaseMediaSource= */ true, /* resetPosition= */ true);
|
||||
resetInternal(
|
||||
/* releaseMediaSource= */ true, /* resetPosition= */ true, /* resetState= */ true);
|
||||
loadControl.onReleased();
|
||||
setState(Player.STATE_IDLE);
|
||||
internalPlaybackThread.quit();
|
||||
@ -797,7 +805,8 @@ import java.io.IOException;
|
||||
}
|
||||
}
|
||||
|
||||
private void resetInternal(boolean releaseMediaSource, boolean resetPosition) {
|
||||
private void resetInternal(
|
||||
boolean releaseMediaSource, boolean resetPosition, boolean resetState) {
|
||||
handler.removeMessages(MSG_DO_SOME_WORK);
|
||||
rebuffering = false;
|
||||
mediaClock.stop();
|
||||
@ -832,13 +841,15 @@ import java.io.IOException;
|
||||
playbackInfo = playbackInfo.fromNewPosition(playbackInfo.periodId, playbackInfo.positionUs,
|
||||
playbackInfo.contentPositionUs);
|
||||
}
|
||||
if (resetState) {
|
||||
mediaPeriodInfoSequence.setTimeline(null);
|
||||
playbackInfo = playbackInfo.copyWithTimeline(null, null);
|
||||
}
|
||||
if (releaseMediaSource) {
|
||||
if (mediaSource != null) {
|
||||
mediaSource.releaseSource();
|
||||
mediaSource = null;
|
||||
}
|
||||
mediaPeriodInfoSequence.setTimeline(null);
|
||||
playbackInfo = playbackInfo.copyWithTimeline(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1174,7 +1185,8 @@ import java.io.IOException;
|
||||
private void handleSourceInfoRefreshEndedPlayback(int prepareAcks) {
|
||||
setState(Player.STATE_ENDED);
|
||||
// Reset, but retain the source so that it can still be used should a seek occur.
|
||||
resetInternal(false, true);
|
||||
resetInternal(
|
||||
/* releaseMediaSource= */ false, /* resetPosition= */ true, /* resetState= */ false);
|
||||
// Set the playback position to 0 for notifying the eventHandler (instead of C.TIME_UNSET).
|
||||
notifySourceInfoRefresh(prepareAcks,
|
||||
playbackInfo.fromNewPosition(playbackInfo.periodId.periodIndex, 0, C.TIME_UNSET));
|
||||
@ -1279,6 +1291,10 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
private void updatePeriods() throws ExoPlaybackException, IOException {
|
||||
if (mediaSource == null) {
|
||||
// The player has no media source yet.
|
||||
return;
|
||||
}
|
||||
if (playbackInfo.timeline == null) {
|
||||
// We're waiting to get information about periods.
|
||||
mediaSource.maybeThrowSourceInfoRefreshError();
|
||||
|
@ -183,6 +183,15 @@ public final class ActionSchedule {
|
||||
.apply(new WaitForPlaybackState(tag, Player.STATE_READY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a delay until the player indicates that a seek has been processed.
|
||||
*
|
||||
* @return The builder, for convenience.
|
||||
*/
|
||||
public Builder waitForSeekProcessed() {
|
||||
return apply(new WaitForSeekProcessed(tag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a playback parameters setting action to be executed.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user