Ensure sessions without MediaPeriodId are ended after seek to new item

We already have logic to end all session except the current one if the
current one doesn't have a MediaPeriodId yet. This is assuming that this
only happens after a seek on the app side where the player doesn't have
detailled knowledge about the MediaPeriodIds yet.

Currently this logic isn't triggered if the window we are coming from
doesn't have its MediaPeriodId either as we run into another check that
keeps sessions around until we have a valid windowSequenceNumber.

Swapping both conditions fixes this case without breaking any of the
other known transition scenarios.

Issue: androidx/media#180
PiperOrigin-RevId: 480866465
This commit is contained in:
tonihei 2022-10-13 12:29:51 +00:00 committed by Marc Baechinger
parent 006a519a0e
commit 409c9f874c
3 changed files with 32 additions and 5 deletions

View File

@ -21,6 +21,8 @@
([#15](https://github.com/androidx/media/issues/15)).
* Prefer other tracks to Dolby Vision if display does not support it.
([#8944](https://github.com/google/ExoPlayer/issues/8944)).
* Fix session tracking problem with fast seeks in `PlaybackStatsListener`
([#180](https://github.com/androidx/media/issues/180)).
* Downloads:
* Fix potential infinite loop in `ProgressiveDownloader` caused by
simultaneous download and playback with the same `PriorityTaskManager`

View File

@ -382,15 +382,15 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
}
public boolean isFinishedAtEventTime(EventTime eventTime) {
if (windowSequenceNumber == C.INDEX_UNSET) {
// Sessions with unspecified window sequence number are kept until we know more.
return false;
}
if (eventTime.mediaPeriodId == null) {
// For event times without media period id (e.g. after seek to new window), we only keep
// sessions of this window.
return windowIndex != eventTime.windowIndex;
}
if (windowSequenceNumber == C.INDEX_UNSET) {
// Sessions with unspecified window sequence number are kept until we know more.
return false;
}
if (eventTime.mediaPeriodId.windowSequenceNumber > windowSequenceNumber) {
// All past window sequence numbers are finished.
return true;

View File

@ -925,7 +925,8 @@ public final class DefaultPlaybackSessionManagerTest {
}
@Test
public void positionDiscontinuity_toNewWindow_withSeekTransitionReason_finishesSession() {
public void
positionDiscontinuity_toNewWindow_withMediaPeriodIds_withSeekTransitionReason_finishesSession() {
Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
EventTime eventTime1 =
createEventTime(
@ -957,6 +958,30 @@ public final class DefaultPlaybackSessionManagerTest {
verifyNoMoreInteractions(mockListener);
}
@Test
public void
positionDiscontinuity_toNewWindow_withoutMediaPeriodIds_withSeekTransitionReason_finishesSession() {
Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
EventTime eventTime1 =
createEventTime(timeline, /* windowIndex= */ 0, /* mediaPeriodId= */ null);
EventTime eventTime2 =
createEventTime(timeline, /* windowIndex= */ 1, /* mediaPeriodId= */ null);
sessionManager.updateSessionsWithTimelineChange(eventTime1);
sessionManager.updateSessionsWithDiscontinuity(eventTime2, Player.DISCONTINUITY_REASON_SEEK);
ArgumentCaptor<String> sessionId1 = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> sessionId2 = ArgumentCaptor.forClass(String.class);
verify(mockListener).onSessionCreated(eq(eventTime1), sessionId1.capture());
verify(mockListener).onSessionActive(eventTime1, sessionId1.getValue());
verify(mockListener).onSessionCreated(eq(eventTime2), sessionId2.capture());
verify(mockListener)
.onSessionFinished(
eventTime2, sessionId1.getValue(), /* automaticTransitionToNextPlayback= */ false);
verify(mockListener).onSessionActive(eventTime2, sessionId2.getValue());
verifyNoMoreInteractions(mockListener);
}
@Test
public void positionDiscontinuity_toSameWindow_withoutMediaPeriodId_doesNotFinishSession() {
Timeline timeline = new FakeTimeline();