Commit d38c6c84 by tonihei Committed by Oliver Woodman

Fix bug in MaskingMediaSource caused by removed periods.

If MaskingMediaSource masks a multi-window media source, it may be that a period
is removed while we are using an initial unprepared masking MediaPeriod. That
means it's not guaranteed that a timeline update still contains our
unpreparedMaskingMediaPeriod and we should ignore timeline updates where the
period is no longer present because the it will be removed anyway.

PiperOrigin-RevId: 302383787
parent af00d91f
...@@ -234,7 +234,15 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> { ...@@ -234,7 +234,15 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
@RequiresNonNull("unpreparedMaskingMediaPeriod") @RequiresNonNull("unpreparedMaskingMediaPeriod")
private void setPreparePositionOverrideToUnpreparedMaskingPeriod(long preparePositionOverrideUs) { private void setPreparePositionOverrideToUnpreparedMaskingPeriod(long preparePositionOverrideUs) {
MaskingMediaPeriod maskingPeriod = unpreparedMaskingMediaPeriod; MaskingMediaPeriod maskingPeriod = unpreparedMaskingMediaPeriod;
long periodDurationUs = timeline.getPeriodByUid(maskingPeriod.id.periodUid, period).durationUs; int maskingPeriodIndex = timeline.getIndexOfPeriod(maskingPeriod.id.periodUid);
if (maskingPeriodIndex == C.INDEX_UNSET) {
// The new timeline doesn't contain this period anymore. This can happen if the media source
// has multiple periods and removed the first period with a timeline update. Ignore the
// update, as the non-existing period will be released anyway as soon as the player receives
// this new timeline.
return;
}
long periodDurationUs = timeline.getPeriod(maskingPeriodIndex, period).durationUs;
if (periodDurationUs != C.TIME_UNSET) { if (periodDurationUs != C.TIME_UNSET) {
// Ensure the overridden position doesn't exceed the period duration. // Ensure the overridden position doesn't exceed the period duration.
if (preparePositionOverrideUs >= periodDurationUs) { if (preparePositionOverrideUs >= periodDurationUs) {
......
...@@ -4277,6 +4277,40 @@ public final class ExoPlayerTest { ...@@ -4277,6 +4277,40 @@ public final class ExoPlayerTest {
} }
@Test @Test
public void
timelineUpdateInMultiWindowMediaSource_removingPeriod_withUnpreparedMaskingMediaPeriod_doesNotThrow()
throws Exception {
TimelineWindowDefinition window1 =
new TimelineWindowDefinition(/* periodCount= */ 1, /* id= */ 1);
TimelineWindowDefinition window2 =
new TimelineWindowDefinition(/* periodCount= */ 1, /* id= */ 2);
FakeMediaSource mediaSource = new FakeMediaSource(/* timeline= */ null);
ActionSchedule actionSchedule =
new ActionSchedule.Builder(TAG)
.waitForPlaybackState(Player.STATE_BUFFERING)
// Do something and wait so that the player can create its unprepared MaskingMediaPeriod
.seek(/* positionMs= */ 0)
.waitForSeekProcessed()
// Let the player assign the unprepared period to window1.
.executeRunnable(() -> mediaSource.setNewSourceInfo(new FakeTimeline(window1, window2)))
.waitForTimelineChanged()
// Remove window1 and assume the update is handled without throwing.
.executeRunnable(() -> mediaSource.setNewSourceInfo(new FakeTimeline(window2)))
.waitForTimelineChanged()
.stop()
.build();
new ExoPlayerTestRunner.Builder()
.setMediaSources(mediaSource)
.setActionSchedule(actionSchedule)
.build(context)
.start()
.blockUntilActionScheduleFinished(TIMEOUT_MS)
.blockUntilEnded(TIMEOUT_MS);
// Assertion is to not throw while running the action schedule above.
}
@Test
public void setPlayWhenReady_keepsCurrentPosition() throws Exception { public void setPlayWhenReady_keepsCurrentPosition() throws Exception {
AtomicLong positionAfterSetPlayWhenReady = new AtomicLong(C.TIME_UNSET); AtomicLong positionAfterSetPlayWhenReady = new AtomicLong(C.TIME_UNSET);
ActionSchedule actionSchedule = ActionSchedule actionSchedule =
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment