mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Remove initial seek counting in ExoPlayerImplInternal.
We can acknoledge seeks before preparation finished immediately now, because ExoPlayerImpl won't leave the masking state until the first prepare operation is processed. As a side effect, it also cleans up the responsibility of the callbacks. Prepares are always acknowledged with a SOURCE_INFO_REFRESHED, while seeks are always acknowledged with a SEEK_ACK. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=177144089
This commit is contained in:
parent
1ae50cb9e5
commit
efc709f366
3 changed files with 49 additions and 43 deletions
|
|
@ -306,11 +306,19 @@ public final class ExoPlayerTest extends TestCase {
|
||||||
public void testSeekProcessedCallback() throws Exception {
|
public void testSeekProcessedCallback() throws Exception {
|
||||||
Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
|
Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
|
||||||
ActionSchedule actionSchedule = new ActionSchedule.Builder("testSeekProcessedCallback")
|
ActionSchedule actionSchedule = new ActionSchedule.Builder("testSeekProcessedCallback")
|
||||||
// Initial seek before timeline preparation finished.
|
// Initial seek before timeline preparation started. Expect immediate seek processed while
|
||||||
.pause().seek(10).waitForPlaybackState(Player.STATE_READY)
|
// the player is still in STATE_IDLE.
|
||||||
// Re-seek to same position, start playback and wait until playback reaches second window.
|
.pause().seek(5)
|
||||||
.seek(10).play().waitForPositionDiscontinuity()
|
// Wait until the media source starts preparing and issue more initial seeks. Expect only
|
||||||
// Seek twice in concession, expecting the first seek to be replaced.
|
// one seek processed after the source has been prepared.
|
||||||
|
.waitForPlaybackState(Player.STATE_BUFFERING).seek(2).seek(10)
|
||||||
|
// Wait until media source prepared and re-seek to same position. Expect a seek processed
|
||||||
|
// while still being in STATE_READY.
|
||||||
|
.waitForPlaybackState(Player.STATE_READY).seek(10)
|
||||||
|
// Start playback and wait until playback reaches second window.
|
||||||
|
.play().waitForPositionDiscontinuity()
|
||||||
|
// Seek twice in concession, expecting the first seek to be replaced (and thus except only
|
||||||
|
// on seek processed callback).
|
||||||
.seek(5).seek(60).build();
|
.seek(5).seek(60).build();
|
||||||
final List<Integer> playbackStatesWhenSeekProcessed = new ArrayList<>();
|
final List<Integer> playbackStatesWhenSeekProcessed = new ArrayList<>();
|
||||||
Player.EventListener eventListener = new Player.DefaultEventListener() {
|
Player.EventListener eventListener = new Player.DefaultEventListener() {
|
||||||
|
|
@ -329,10 +337,11 @@ public final class ExoPlayerTest extends TestCase {
|
||||||
new ExoPlayerTestRunner.Builder()
|
new ExoPlayerTestRunner.Builder()
|
||||||
.setTimeline(timeline).setEventListener(eventListener).setActionSchedule(actionSchedule)
|
.setTimeline(timeline).setEventListener(eventListener).setActionSchedule(actionSchedule)
|
||||||
.build().start().blockUntilEnded(TIMEOUT_MS);
|
.build().start().blockUntilEnded(TIMEOUT_MS);
|
||||||
assertEquals(3, playbackStatesWhenSeekProcessed.size());
|
assertEquals(4, playbackStatesWhenSeekProcessed.size());
|
||||||
assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(0));
|
assertEquals(Player.STATE_IDLE, (int) playbackStatesWhenSeekProcessed.get(0));
|
||||||
assertEquals(Player.STATE_READY, (int) playbackStatesWhenSeekProcessed.get(1));
|
assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(1));
|
||||||
assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(2));
|
assertEquals(Player.STATE_READY, (int) playbackStatesWhenSeekProcessed.get(2));
|
||||||
|
assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSeekDiscontinuity() throws Exception {
|
public void testSeekDiscontinuity() throws Exception {
|
||||||
|
|
@ -742,7 +751,7 @@ public final class ExoPlayerTest extends TestCase {
|
||||||
.waitForPlaybackState(Player.STATE_IDLE)
|
.waitForPlaybackState(Player.STATE_IDLE)
|
||||||
// If we were still using the first timeline, this would throw.
|
// If we were still using the first timeline, this would throw.
|
||||||
.seek(/* windowIndex= */ 1, /* positionMs= */ 0)
|
.seek(/* windowIndex= */ 1, /* positionMs= */ 0)
|
||||||
.prepareSource(secondSource)
|
.prepareSource(secondSource, /* resetPosition= */ false, /* resetState= */ true)
|
||||||
.build();
|
.build();
|
||||||
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
|
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
|
||||||
.setTimeline(timeline)
|
.setTimeline(timeline)
|
||||||
|
|
|
||||||
|
|
@ -450,8 +450,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
}
|
}
|
||||||
case ExoPlayerImplInternal.MSG_SOURCE_INFO_REFRESHED: {
|
case ExoPlayerImplInternal.MSG_SOURCE_INFO_REFRESHED: {
|
||||||
int prepareOrStopAcks = msg.arg1;
|
int prepareOrStopAcks = msg.arg1;
|
||||||
int seekAcks = msg.arg2;
|
handlePlaybackInfo((PlaybackInfo) msg.obj, prepareOrStopAcks, 0, false,
|
||||||
handlePlaybackInfo((PlaybackInfo) msg.obj, prepareOrStopAcks, seekAcks, false,
|
|
||||||
/* ignored */ DISCONTINUITY_REASON_INTERNAL);
|
/* ignored */ DISCONTINUITY_REASON_INTERNAL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -510,13 +509,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
boolean timelineOrManifestChanged = this.playbackInfo.timeline != playbackInfo.timeline
|
boolean timelineOrManifestChanged = this.playbackInfo.timeline != playbackInfo.timeline
|
||||||
|| this.playbackInfo.manifest != playbackInfo.manifest;
|
|| this.playbackInfo.manifest != playbackInfo.manifest;
|
||||||
this.playbackInfo = playbackInfo;
|
this.playbackInfo = playbackInfo;
|
||||||
if (playbackInfo.timeline.isEmpty()) {
|
|
||||||
// Update the masking variables, which are used when the timeline is empty.
|
|
||||||
maskingPeriodIndex = 0;
|
|
||||||
maskingWindowIndex = 0;
|
|
||||||
maskingWindowPositionMs = 0;
|
|
||||||
}
|
|
||||||
if (timelineOrManifestChanged || waitingForInitialTimeline) {
|
if (timelineOrManifestChanged || waitingForInitialTimeline) {
|
||||||
|
if (playbackInfo.timeline.isEmpty()) {
|
||||||
|
// Update the masking variables, which are used when the timeline becomes empty.
|
||||||
|
maskingPeriodIndex = 0;
|
||||||
|
maskingWindowIndex = 0;
|
||||||
|
maskingWindowPositionMs = 0;
|
||||||
|
}
|
||||||
@Player.TimelineChangeReason int reason = waitingForInitialTimeline
|
@Player.TimelineChangeReason int reason = waitingForInitialTimeline
|
||||||
? Player.TIMELINE_CHANGE_REASON_PREPARED : Player.TIMELINE_CHANGE_REASON_DYNAMIC;
|
? Player.TIMELINE_CHANGE_REASON_PREPARED : Player.TIMELINE_CHANGE_REASON_DYNAMIC;
|
||||||
waitingForInitialTimeline = false;
|
waitingForInitialTimeline = false;
|
||||||
|
|
|
||||||
|
|
@ -125,8 +125,7 @@ import java.io.IOException;
|
||||||
private long elapsedRealtimeUs;
|
private long elapsedRealtimeUs;
|
||||||
|
|
||||||
private int pendingPrepareCount;
|
private int pendingPrepareCount;
|
||||||
private int pendingInitialSeekCount;
|
private SeekPosition pendingInitialSeekPosition;
|
||||||
private SeekPosition pendingSeekPosition;
|
|
||||||
private long rendererPositionUs;
|
private long rendererPositionUs;
|
||||||
|
|
||||||
private MediaPeriodHolder loadingPeriodHolder;
|
private MediaPeriodHolder loadingPeriodHolder;
|
||||||
|
|
@ -631,8 +630,9 @@ import java.io.IOException;
|
||||||
private void seekToInternal(SeekPosition seekPosition) throws ExoPlaybackException {
|
private void seekToInternal(SeekPosition seekPosition) throws ExoPlaybackException {
|
||||||
Timeline timeline = playbackInfo.timeline;
|
Timeline timeline = playbackInfo.timeline;
|
||||||
if (timeline == null) {
|
if (timeline == null) {
|
||||||
pendingInitialSeekCount++;
|
pendingInitialSeekPosition = seekPosition;
|
||||||
pendingSeekPosition = seekPosition;
|
eventHandler.obtainMessage(MSG_SEEK_ACK, /* seekAdjusted = */ 0, 0,
|
||||||
|
playbackInfo.copyWithTimeline(Timeline.EMPTY, null)).sendToTarget();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -781,7 +781,7 @@ import java.io.IOException;
|
||||||
}
|
}
|
||||||
int prepareOrStopAcks = pendingPrepareCount + 1;
|
int prepareOrStopAcks = pendingPrepareCount + 1;
|
||||||
pendingPrepareCount = 0;
|
pendingPrepareCount = 0;
|
||||||
notifySourceInfoRefresh(prepareOrStopAcks, 0, publicPlaybackInfo);
|
notifySourceInfoRefresh(prepareOrStopAcks, publicPlaybackInfo);
|
||||||
loadControl.onStopped();
|
loadControl.onStopped();
|
||||||
setState(Player.STATE_IDLE);
|
setState(Player.STATE_IDLE);
|
||||||
}
|
}
|
||||||
|
|
@ -825,6 +825,7 @@ import java.io.IOException;
|
||||||
? 0
|
? 0
|
||||||
: timeline.getWindow(timeline.getFirstWindowIndex(shuffleModeEnabled), window)
|
: timeline.getWindow(timeline.getFirstWindowIndex(shuffleModeEnabled), window)
|
||||||
.firstPeriodIndex;
|
.firstPeriodIndex;
|
||||||
|
pendingInitialSeekPosition = null;
|
||||||
playbackInfo = playbackInfo.fromNewPosition(firstPeriodIndex, C.TIME_UNSET, C.TIME_UNSET);
|
playbackInfo = playbackInfo.fromNewPosition(firstPeriodIndex, C.TIME_UNSET, C.TIME_UNSET);
|
||||||
} else {
|
} else {
|
||||||
// The new start position is the current playback position.
|
// The new start position is the current playback position.
|
||||||
|
|
@ -1009,15 +1010,13 @@ import java.io.IOException;
|
||||||
if (oldTimeline == null) {
|
if (oldTimeline == null) {
|
||||||
int processedPrepareAcks = pendingPrepareCount;
|
int processedPrepareAcks = pendingPrepareCount;
|
||||||
pendingPrepareCount = 0;
|
pendingPrepareCount = 0;
|
||||||
if (pendingInitialSeekCount > 0) {
|
if (pendingInitialSeekPosition != null) {
|
||||||
Pair<Integer, Long> periodPosition = resolveSeekPosition(pendingSeekPosition);
|
Pair<Integer, Long> periodPosition = resolveSeekPosition(pendingInitialSeekPosition);
|
||||||
int processedInitialSeekCount = pendingInitialSeekCount;
|
pendingInitialSeekPosition = null;
|
||||||
pendingInitialSeekCount = 0;
|
|
||||||
pendingSeekPosition = null;
|
|
||||||
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(processedPrepareAcks, processedInitialSeekCount);
|
handleSourceInfoRefreshEndedPlayback(processedPrepareAcks);
|
||||||
} else {
|
} else {
|
||||||
int periodIndex = periodPosition.first;
|
int periodIndex = periodPosition.first;
|
||||||
long positionUs = periodPosition.second;
|
long positionUs = periodPosition.second;
|
||||||
|
|
@ -1025,11 +1024,11 @@ import java.io.IOException;
|
||||||
mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, positionUs);
|
mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, positionUs);
|
||||||
playbackInfo = playbackInfo.fromNewPosition(periodId, periodId.isAd() ? 0 : positionUs,
|
playbackInfo = playbackInfo.fromNewPosition(periodId, periodId.isAd() ? 0 : positionUs,
|
||||||
positionUs);
|
positionUs);
|
||||||
notifySourceInfoRefresh(processedPrepareAcks, processedInitialSeekCount);
|
notifySourceInfoRefresh(processedPrepareAcks);
|
||||||
}
|
}
|
||||||
} else if (playbackInfo.startPositionUs == C.TIME_UNSET) {
|
} else if (playbackInfo.startPositionUs == C.TIME_UNSET) {
|
||||||
if (timeline.isEmpty()) {
|
if (timeline.isEmpty()) {
|
||||||
handleSourceInfoRefreshEndedPlayback(processedPrepareAcks, 0);
|
handleSourceInfoRefreshEndedPlayback(processedPrepareAcks);
|
||||||
} else {
|
} else {
|
||||||
Pair<Integer, Long> defaultPosition = getPeriodPosition(timeline,
|
Pair<Integer, Long> defaultPosition = getPeriodPosition(timeline,
|
||||||
timeline.getFirstWindowIndex(shuffleModeEnabled), C.TIME_UNSET);
|
timeline.getFirstWindowIndex(shuffleModeEnabled), C.TIME_UNSET);
|
||||||
|
|
@ -1039,10 +1038,10 @@ import java.io.IOException;
|
||||||
startPositionUs);
|
startPositionUs);
|
||||||
playbackInfo = playbackInfo.fromNewPosition(periodId,
|
playbackInfo = playbackInfo.fromNewPosition(periodId,
|
||||||
periodId.isAd() ? 0 : startPositionUs, startPositionUs);
|
periodId.isAd() ? 0 : startPositionUs, startPositionUs);
|
||||||
notifySourceInfoRefresh(processedPrepareAcks, 0);
|
notifySourceInfoRefresh(processedPrepareAcks);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
notifySourceInfoRefresh(processedPrepareAcks, 0);
|
notifySourceInfoRefresh(processedPrepareAcks);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1169,30 +1168,29 @@ import java.io.IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSourceInfoRefreshEndedPlayback() {
|
private void handleSourceInfoRefreshEndedPlayback() {
|
||||||
handleSourceInfoRefreshEndedPlayback(0, 0);
|
handleSourceInfoRefreshEndedPlayback(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSourceInfoRefreshEndedPlayback(int prepareAcks, int seekAcks) {
|
private void handleSourceInfoRefreshEndedPlayback(int prepareAcks) {
|
||||||
setState(Player.STATE_ENDED);
|
setState(Player.STATE_ENDED);
|
||||||
// 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, true);
|
resetInternal(false, true);
|
||||||
// Set the playback position to 0 for notifying the eventHandler (instead of C.TIME_UNSET).
|
// Set the playback position to 0 for notifying the eventHandler (instead of C.TIME_UNSET).
|
||||||
notifySourceInfoRefresh(prepareAcks, seekAcks,
|
notifySourceInfoRefresh(prepareAcks,
|
||||||
playbackInfo.fromNewPosition(playbackInfo.periodId.periodIndex, 0, C.TIME_UNSET));
|
playbackInfo.fromNewPosition(playbackInfo.periodId.periodIndex, 0, C.TIME_UNSET));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifySourceInfoRefresh() {
|
private void notifySourceInfoRefresh() {
|
||||||
notifySourceInfoRefresh(0, 0);
|
notifySourceInfoRefresh(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifySourceInfoRefresh(int prepareOrStopAcks, int seekAcks) {
|
private void notifySourceInfoRefresh(int prepareOrStopAcks) {
|
||||||
notifySourceInfoRefresh(prepareOrStopAcks, seekAcks, playbackInfo);
|
notifySourceInfoRefresh(prepareOrStopAcks, playbackInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifySourceInfoRefresh(int prepareOrStopAcks, int seekAcks,
|
private void notifySourceInfoRefresh(int prepareOrStopAcks, PlaybackInfo playbackInfo) {
|
||||||
PlaybackInfo playbackInfo) {
|
eventHandler.obtainMessage(MSG_SOURCE_INFO_REFRESHED, prepareOrStopAcks, 0, playbackInfo)
|
||||||
eventHandler.obtainMessage(MSG_SOURCE_INFO_REFRESHED, prepareOrStopAcks, seekAcks,
|
.sendToTarget();
|
||||||
playbackInfo).sendToTarget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue