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
This commit is contained in:
parent
af00d91f79
commit
d38c6c84a6
@ -234,7 +234,15 @@ public final class MaskingMediaSource extends CompositeMediaSource<Void> {
|
||||
@RequiresNonNull("unpreparedMaskingMediaPeriod")
|
||||
private void setPreparePositionOverrideToUnpreparedMaskingPeriod(long preparePositionOverrideUs) {
|
||||
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) {
|
||||
// Ensure the overridden position doesn't exceed the period duration.
|
||||
if (preparePositionOverrideUs >= periodDurationUs) {
|
||||
|
@ -4276,6 +4276,40 @@ public final class ExoPlayerTest {
|
||||
assertArrayEquals(new long[] {5_000, 5_000}, currentPlaybackPositions);
|
||||
}
|
||||
|
||||
@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 {
|
||||
AtomicLong positionAfterSetPlayWhenReady = new AtomicLong(C.TIME_UNSET);
|
||||
|
Loading…
x
Reference in New Issue
Block a user