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")
|
@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) {
|
||||||
|
@ -4276,6 +4276,40 @@ public final class ExoPlayerTest {
|
|||||||
assertArrayEquals(new long[] {5_000, 5_000}, currentPlaybackPositions);
|
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
|
@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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user