From c0a3feee00cbf201ba7c15a03d4afb3aaba241f5 Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 27 Jan 2020 13:51:11 +0000 Subject: [PATCH] Add window.isPlaceHolder flag There are existing bugs that need this flag to know whether the current information in the window is still a placeholder or can already be relied on for further calculation. This flag will probably only ever be set in DummyTimeline, so it's not added to the window.set method to avoid updating all clients. Issue:#5924 PiperOrigin-RevId: 291705637 --- .../google/android/exoplayer2/Timeline.java | 9 ++ .../exoplayer2/source/MaskingMediaSource.java | 4 +- .../android/exoplayer2/ExoPlayerTest.java | 90 +++---------------- .../android/exoplayer2/TimelineTest.java | 4 + .../exoplayer2/testutil/FakeTimeline.java | 32 ++++++- 5 files changed, 54 insertions(+), 85 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java b/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java index b07a9792a1..7c416c544f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java @@ -176,6 +176,12 @@ public abstract class Timeline { */ public boolean isLive; + /** + * Whether the information in this window is used as a placeholder for as long as the media + * hasn't provided the real information. + */ + public boolean isPlaceholder; + /** The index of the first period that belongs to this window. */ public int firstPeriodIndex; @@ -238,6 +244,7 @@ public abstract class Timeline { this.firstPeriodIndex = firstPeriodIndex; this.lastPeriodIndex = lastPeriodIndex; this.positionInFirstPeriodUs = positionInFirstPeriodUs; + this.isPlaceholder = false; return this; } @@ -319,6 +326,7 @@ public abstract class Timeline { && isSeekable == that.isSeekable && isDynamic == that.isDynamic && isLive == that.isLive + && isPlaceholder == that.isPlaceholder && defaultPositionUs == that.defaultPositionUs && durationUs == that.durationUs && firstPeriodIndex == that.firstPeriodIndex @@ -340,6 +348,7 @@ public abstract class Timeline { result = 31 * result + (isSeekable ? 1 : 0); result = 31 * result + (isDynamic ? 1 : 0); result = 31 * result + (isLive ? 1 : 0); + result = 31 * result + (isPlaceholder ? 1 : 0); result = 31 * result + (int) (defaultPositionUs ^ (defaultPositionUs >>> 32)); result = 31 * result + (int) (durationUs ^ (durationUs >>> 32)); result = 31 * result + firstPeriodIndex; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java index aff40d891d..1049d266f9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java @@ -331,7 +331,7 @@ public final class MaskingMediaSource extends CompositeMediaSource { @Override public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) { - return window.set( + window.set( Window.SINGLE_WINDOW_UID, tag, /* manifest= */ null, @@ -347,6 +347,8 @@ public final class MaskingMediaSource extends CompositeMediaSource { /* firstPeriodIndex= */ 0, /* lastPeriodIndex= */ 0, /* positionInFirstPeriodUs= */ 0); + window.isPlaceholder = true; + return window; } @Override diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 8e42309e89..ced29798db 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -3486,22 +3486,8 @@ public final class ExoPlayerTest { MediaSource mediaSource2 = new FakeMediaSource(timeline2); Timeline expectedDummyTimeline = new FakeTimeline( - new TimelineWindowDefinition( - /* periodCount= */ 1, - /* id= */ 1, - /* isSeekable= */ false, - /* isDynamic= */ true, - /* isLive= */ false, - /* durationUs= */ C.TIME_UNSET, - AdPlaybackState.NONE), - new TimelineWindowDefinition( - /* periodCount= */ 1, - /* id= */ 2, - /* isSeekable= */ false, - /* isDynamic= */ true, - /* isLive= */ false, - /* durationUs= */ C.TIME_UNSET, - AdPlaybackState.NONE)); + TimelineWindowDefinition.createDummy(/* tag= */ 1), + TimelineWindowDefinition.createDummy(/* tag= */ 2)); ActionSchedule actionSchedule = new ActionSchedule.Builder("testMoveMediaItem") .waitForTimelineChanged( @@ -3573,30 +3559,9 @@ public final class ExoPlayerTest { Timeline expectedDummyTimeline = new FakeTimeline( - new TimelineWindowDefinition( - /* periodCount= */ 1, - /* id= */ 1, - /* isSeekable= */ false, - /* isDynamic= */ true, - /* isLive= */ false, - /* durationUs= */ C.TIME_UNSET, - AdPlaybackState.NONE), - new TimelineWindowDefinition( - /* periodCount= */ 1, - /* id= */ 2, - /* isSeekable= */ false, - /* isDynamic= */ true, - /* isLive= */ false, - /* durationUs= */ C.TIME_UNSET, - AdPlaybackState.NONE), - new TimelineWindowDefinition( - /* periodCount= */ 1, - /* id= */ 3, - /* isSeekable= */ false, - /* isDynamic= */ true, - /* isLive= */ false, - /* durationUs= */ C.TIME_UNSET, - AdPlaybackState.NONE)); + TimelineWindowDefinition.createDummy(/* tag= */ 1), + TimelineWindowDefinition.createDummy(/* tag= */ 2), + TimelineWindowDefinition.createDummy(/* tag= */ 3)); Timeline expectedRealTimeline = new FakeTimeline(firstWindowDefinition, secondWindowDefinition, thirdWindowDefinition); Timeline expectedRealTimelineAfterRemove = @@ -3654,30 +3619,9 @@ public final class ExoPlayerTest { Timeline expectedDummyTimeline = new FakeTimeline( - new TimelineWindowDefinition( - /* periodCount= */ 1, - /* id= */ 1, - /* isSeekable= */ false, - /* isDynamic= */ true, - /* isLive= */ false, - /* durationUs= */ C.TIME_UNSET, - AdPlaybackState.NONE), - new TimelineWindowDefinition( - /* periodCount= */ 1, - /* id= */ 2, - /* isSeekable= */ false, - /* isDynamic= */ true, - /* isLive= */ false, - /* durationUs= */ C.TIME_UNSET, - AdPlaybackState.NONE), - new TimelineWindowDefinition( - /* periodCount= */ 1, - /* id= */ 3, - /* isSeekable= */ false, - /* isDynamic= */ true, - /* isLive= */ false, - /* durationUs= */ C.TIME_UNSET, - AdPlaybackState.NONE)); + TimelineWindowDefinition.createDummy(/* tag= */ 1), + TimelineWindowDefinition.createDummy(/* tag= */ 2), + TimelineWindowDefinition.createDummy(/* tag= */ 3)); Timeline expectedRealTimeline = new FakeTimeline(firstWindowDefinition, secondWindowDefinition, thirdWindowDefinition); Timeline expectedRealTimelineAfterRemove = new FakeTimeline(firstWindowDefinition); @@ -3819,22 +3763,8 @@ public final class ExoPlayerTest { Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE /* source update after prepare */); Timeline expectedSecondDummyTimeline = new FakeTimeline( - new TimelineWindowDefinition( - /* periodCount= */ 1, - /* id= */ 0, - /* isSeekable= */ false, - /* isDynamic= */ true, - /* isLive= */ false, - /* durationUs= */ C.TIME_UNSET, - AdPlaybackState.NONE), - new TimelineWindowDefinition( - /* periodCount= */ 1, - /* id= */ 0, - /* isSeekable= */ false, - /* isDynamic= */ true, - /* isLive= */ false, - /* durationUs= */ C.TIME_UNSET, - AdPlaybackState.NONE)); + TimelineWindowDefinition.createDummy(/* tag= */ 0), + TimelineWindowDefinition.createDummy(/* tag= */ 0)); Timeline expectedSecondRealTimeline = new FakeTimeline( new TimelineWindowDefinition( diff --git a/library/core/src/test/java/com/google/android/exoplayer2/TimelineTest.java b/library/core/src/test/java/com/google/android/exoplayer2/TimelineTest.java index 6bc70e3188..a7c0df2572 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/TimelineTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/TimelineTest.java @@ -94,6 +94,10 @@ public class TimelineTest { otherWindow.isLive = true; assertThat(window).isNotEqualTo(otherWindow); + otherWindow = new Timeline.Window(); + otherWindow.isPlaceholder = true; + assertThat(window).isNotEqualTo(otherWindow); + otherWindow = new Timeline.Window(); otherWindow.defaultPositionUs = C.TIME_UNSET; assertThat(window).isNotEqualTo(otherWindow); diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTimeline.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTimeline.java index 6b738ec075..ce58aa5ca8 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTimeline.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeTimeline.java @@ -42,12 +42,31 @@ public final class FakeTimeline extends Timeline { public final boolean isSeekable; public final boolean isDynamic; public final boolean isLive; + public final boolean isPlaceholder; public final long durationUs; public final AdPlaybackState adPlaybackState; /** - * Creates a seekable, non-dynamic window definition with a duration of - * {@link #DEFAULT_WINDOW_DURATION_US}. + * Creates a window definition that corresponds to a dummy placeholder timeline using the given + * tag. + * + * @param tag The tag to use in the timeline. + */ + public static TimelineWindowDefinition createDummy(Object tag) { + return new TimelineWindowDefinition( + /* periodCount= */ 1, + /* id= */ tag, + /* isSeekable= */ false, + /* isDynamic= */ true, + /* isLive= */ false, + /* isPlaceholder= */ true, + /* durationUs= */ C.TIME_UNSET, + AdPlaybackState.NONE); + } + + /** + * Creates a seekable, non-dynamic window definition with a duration of {@link + * #DEFAULT_WINDOW_DURATION_US}. * * @param periodCount The number of periods in the window. Each period get an equal slice of the * total window duration. @@ -107,6 +126,7 @@ public final class FakeTimeline extends Timeline { isSeekable, isDynamic, /* isLive= */ isDynamic, + /* isPlaceholder= */ false, durationUs, adPlaybackState); } @@ -120,6 +140,7 @@ public final class FakeTimeline extends Timeline { * @param isSeekable Whether the window is seekable. * @param isDynamic Whether the window is dynamic. * @param isLive Whether the window is live. + * @param isPlaceholder Whether the window is a placeholder. * @param durationUs The duration of the window in microseconds. * @param adPlaybackState The ad playback state. */ @@ -129,6 +150,7 @@ public final class FakeTimeline extends Timeline { boolean isSeekable, boolean isDynamic, boolean isLive, + boolean isPlaceholder, long durationUs, AdPlaybackState adPlaybackState) { this.periodCount = periodCount; @@ -136,10 +158,10 @@ public final class FakeTimeline extends Timeline { this.isSeekable = isSeekable; this.isDynamic = isDynamic; this.isLive = isLive; + this.isPlaceholder = isPlaceholder; this.durationUs = durationUs; this.adPlaybackState = adPlaybackState; } - } private static final long AD_DURATION_US = 10 * C.MICROS_PER_SECOND; @@ -222,7 +244,7 @@ public final class FakeTimeline extends Timeline { @Override public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) { TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; - return window.set( + window.set( /* uid= */ windowDefinition.id, /* tag= */ windowDefinition.id, manifests[windowIndex], @@ -237,6 +259,8 @@ public final class FakeTimeline extends Timeline { periodOffsets[windowIndex], periodOffsets[windowIndex + 1] - 1, /* positionInFirstPeriodUs= */ 0); + window.isPlaceholder = windowDefinition.isPlaceholder; + return window; } @Override