diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d95288bb4d..f56a511f6a 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -27,6 +27,8 @@ * Added callbacks to `MediaSourceEventListener` to get notified when media periods are created, released and being read from. * Support live stream clipping with `ClippingMediaSource`. + * Allow setting custom tags for all media sources in their factories. The tag + of the current window can be retrieved with `ExoPlayer.getCurrentTag`. * Audio: * Factor out `AudioTrack` position tracking from `DefaultAudioSink`. * Fix an issue where the playback position would pause just after playback diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java index 50c883c3f6..7e0753ba10 100644 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java +++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java @@ -481,6 +481,14 @@ public final class CastPlayer implements Player { : currentTimeline.getPreviousWindowIndex(getCurrentWindowIndex(), repeatMode, false); } + @Override + public @Nullable Object getCurrentTag() { + int windowIndex = getCurrentWindowIndex(); + return windowIndex > currentTimeline.getWindowCount() + ? null + : currentTimeline.getWindow(windowIndex, window, /* setTag= */ true).tag; + } + // TODO: Fill the cast timeline information with ProgressListener's duration updates. // See [Internal: b/65152553]. @Override diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastTimeline.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastTimeline.java index a0be844439..24d815bae2 100644 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastTimeline.java +++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastTimeline.java @@ -73,12 +73,22 @@ import java.util.Map; } @Override - public Window getWindow(int windowIndex, Window window, boolean setIds, - long defaultPositionProjectionUs) { + public Window getWindow( + int windowIndex, Window window, boolean setTag, long defaultPositionProjectionUs) { long durationUs = durationsUs[windowIndex]; boolean isDynamic = durationUs == C.TIME_UNSET; - return window.set(ids[windowIndex], C.TIME_UNSET, C.TIME_UNSET, !isDynamic, isDynamic, - defaultPositionsUs[windowIndex], durationUs, windowIndex, windowIndex, 0); + Object tag = setTag ? ids[windowIndex] : null; + return window.set( + tag, + /* presentationStartTimeMs= */ C.TIME_UNSET, + /* windowStartTimeMs= */ C.TIME_UNSET, + /* isSeekable= */ !isDynamic, + isDynamic, + defaultPositionsUs[windowIndex], + durationUs, + /* firstPeriodIndex= */ windowIndex, + /* lastPeriodIndex= */ windowIndex, + /* positionInFirstPeriodUs= */ 0); } @Override diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index ae55870baf..e33b7358a4 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -311,6 +311,14 @@ import java.util.concurrent.CopyOnWriteArraySet; internalPlayer.setSeekParameters(seekParameters); } + @Override + public @Nullable Object getCurrentTag() { + int windowIndex = getCurrentWindowIndex(); + return windowIndex > playbackInfo.timeline.getWindowCount() + ? null + : playbackInfo.timeline.getWindow(windowIndex, window, /* setTag= */ true).tag; + } + @Override public void stop() { stop(/* reset= */ false); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Player.java b/library/core/src/main/java/com/google/android/exoplayer2/Player.java index 443ff8a2ea..85872339a3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Player.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Player.java @@ -655,6 +655,12 @@ public interface Player { */ int getPreviousWindowIndex(); + /** + * Returns the tag of the currently playing window in the timeline. May be null if no tag is set + * or the timeline is not yet available. + */ + @Nullable Object getCurrentTag(); + /** * Returns the duration of the current window in milliseconds, or {@link C#TIME_UNSET} if the * duration is not known. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index 419e082dea..e6979b4a60 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -664,6 +664,11 @@ public class SimpleExoPlayer implements ExoPlayer, Player.VideoComponent, Player player.setSeekParameters(seekParameters); } + @Override + public @Nullable Object getCurrentTag() { + return player.getCurrentTag(); + } + @Override public void stop() { player.stop(); 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 50a3e66880..884bdc0f4d 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 @@ -118,10 +118,8 @@ public abstract class Timeline { */ public static final class Window { - /** - * An identifier for the window. Not necessarily unique. - */ - public Object id; + /** A custom tag for the window. Not necessarily unique. */ + public Object tag; /** * The start time of the presentation to which this window belongs in milliseconds since the @@ -174,13 +172,19 @@ public abstract class Timeline { */ public long positionInFirstPeriodUs; - /** - * Sets the data held by this window. - */ - public Window set(Object id, long presentationStartTimeMs, long windowStartTimeMs, - boolean isSeekable, boolean isDynamic, long defaultPositionUs, long durationUs, - int firstPeriodIndex, int lastPeriodIndex, long positionInFirstPeriodUs) { - this.id = id; + /** Sets the data held by this window. */ + public Window set( + Object tag, + long presentationStartTimeMs, + long windowStartTimeMs, + boolean isSeekable, + boolean isDynamic, + long defaultPositionUs, + long durationUs, + int firstPeriodIndex, + int lastPeriodIndex, + long positionInFirstPeriodUs) { + this.tag = tag; this.presentationStartTimeMs = presentationStartTimeMs; this.windowStartTimeMs = windowStartTimeMs; this.isSeekable = isSeekable; @@ -486,38 +490,36 @@ public abstract class Timeline { } - /** - * An empty timeline. - */ - public static final Timeline EMPTY = new Timeline() { + /** An empty timeline. */ + public static final Timeline EMPTY = + new Timeline() { - @Override - public int getWindowCount() { - return 0; - } + @Override + public int getWindowCount() { + return 0; + } - @Override - public Window getWindow(int windowIndex, Window window, boolean setIds, - long defaultPositionProjectionUs) { - throw new IndexOutOfBoundsException(); - } + @Override + public Window getWindow( + int windowIndex, Window window, boolean setTag, long defaultPositionProjectionUs) { + throw new IndexOutOfBoundsException(); + } - @Override - public int getPeriodCount() { - return 0; - } + @Override + public int getPeriodCount() { + return 0; + } - @Override - public Period getPeriod(int periodIndex, Period period, boolean setIds) { - throw new IndexOutOfBoundsException(); - } + @Override + public Period getPeriod(int periodIndex, Period period, boolean setIds) { + throw new IndexOutOfBoundsException(); + } - @Override - public int getIndexOfPeriod(Object uid) { - return C.INDEX_UNSET; - } - - }; + @Override + public int getIndexOfPeriod(Object uid) { + return C.INDEX_UNSET; + } + }; /** * Returns whether the timeline is empty. @@ -607,7 +609,7 @@ public abstract class Timeline { /** * Populates a {@link Window} with data for the window at the specified index. Does not populate - * {@link Window#id}. + * {@link Window#tag}. * * @param windowIndex The index of the window. * @param window The {@link Window} to populate. Must not be null. @@ -622,12 +624,12 @@ public abstract class Timeline { * * @param windowIndex The index of the window. * @param window The {@link Window} to populate. Must not be null. - * @param setIds Whether {@link Window#id} should be populated. If false, the field will be set to - * null. The caller should pass false for efficiency reasons unless the field is required. + * @param setTag Whether {@link Window#tag} should be populated. If false, the field will be set + * to null. The caller should pass false for efficiency reasons unless the field is required. * @return The populated {@link Window}, for convenience. */ - public final Window getWindow(int windowIndex, Window window, boolean setIds) { - return getWindow(windowIndex, window, setIds, 0); + public final Window getWindow(int windowIndex, Window window, boolean setTag) { + return getWindow(windowIndex, window, setTag, 0); } /** @@ -635,14 +637,14 @@ public abstract class Timeline { * * @param windowIndex The index of the window. * @param window The {@link Window} to populate. Must not be null. - * @param setIds Whether {@link Window#id} should be populated. If false, the field will be set to - * null. The caller should pass false for efficiency reasons unless the field is required. + * @param setTag Whether {@link Window#tag} should be populated. If false, the field will be set + * to null. The caller should pass false for efficiency reasons unless the field is required. * @param defaultPositionProjectionUs A duration into the future that the populated window's * default start position should be projected. * @return The populated {@link Window}, for convenience. */ - public abstract Window getWindow(int windowIndex, Window window, boolean setIds, - long defaultPositionProjectionUs); + public abstract Window getWindow( + int windowIndex, Window window, boolean setTag, long defaultPositionProjectionUs); /** * Returns the number of periods in the timeline. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java b/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java index 696a6f6fad..8663b4c05c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java @@ -155,13 +155,14 @@ import com.google.android.exoplayer2.Timeline; } @Override - public final Window getWindow(int windowIndex, Window window, boolean setIds, - long defaultPositionProjectionUs) { + public final Window getWindow( + int windowIndex, Window window, boolean setTag, long defaultPositionProjectionUs) { int childIndex = getChildIndexByWindowIndex(windowIndex); int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex); int firstPeriodIndexInChild = getFirstPeriodIndexByChildIndex(childIndex); - getTimelineByChildIndex(childIndex).getWindow(windowIndex - firstWindowIndexInChild, window, - setIds, defaultPositionProjectionUs); + getTimelineByChildIndex(childIndex) + .getWindow( + windowIndex - firstWindowIndexInChild, window, setTag, defaultPositionProjectionUs); window.firstPeriodIndex += firstPeriodIndexInChild; window.lastPeriodIndex += firstPeriodIndexInChild; return window; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java index a3f1b10d30..f633dd8f15 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ClippingMediaSource.java @@ -363,10 +363,10 @@ public final class ClippingMediaSource extends CompositeMediaSource { } @Override - public Window getWindow(int windowIndex, Window window, boolean setIds, - long defaultPositionProjectionUs) { + public Window getWindow( + int windowIndex, Window window, boolean setTag, long defaultPositionProjectionUs) { timeline.getWindow( - /* windowIndex= */ 0, window, setIds, /* defaultPositionProjectionUs= */ 0); + /* windowIndex= */ 0, window, setTag, /* defaultPositionProjectionUs= */ 0); window.positionInFirstPeriodUs += startUs; window.durationUs = durationUs; window.isDynamic = isDynamic; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java index 7e44895ac7..3e39139918 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java @@ -865,9 +865,9 @@ public class ConcatenatingMediaSource extends CompositeMediaSourceasList(childSources[4], childSources[5], childSources[6])); timeline = testRunner.assertTimelineChangeBlocking(); TimelineAsserts.assertPeriodCounts(timeline, 2, 4, 1, 5, 6, 7, 3); - TimelineAsserts.assertWindowIds(timeline, 222, 444, 111, 555, 666, 777, 333); + TimelineAsserts.assertWindowTags(timeline, 222, 444, 111, 555, 666, 777, 333); // Move sources. mediaSource.moveMediaSource(2, 3); timeline = testRunner.assertTimelineChangeBlocking(); TimelineAsserts.assertPeriodCounts(timeline, 2, 4, 5, 1, 6, 7, 3); - TimelineAsserts.assertWindowIds(timeline, 222, 444, 555, 111, 666, 777, 333); + TimelineAsserts.assertWindowTags(timeline, 222, 444, 555, 111, 666, 777, 333); mediaSource.moveMediaSource(3, 2); timeline = testRunner.assertTimelineChangeBlocking(); TimelineAsserts.assertPeriodCounts(timeline, 2, 4, 1, 5, 6, 7, 3); - TimelineAsserts.assertWindowIds(timeline, 222, 444, 111, 555, 666, 777, 333); + TimelineAsserts.assertWindowTags(timeline, 222, 444, 111, 555, 666, 777, 333); mediaSource.moveMediaSource(0, 6); timeline = testRunner.assertTimelineChangeBlocking(); TimelineAsserts.assertPeriodCounts(timeline, 4, 1, 5, 6, 7, 3, 2); - TimelineAsserts.assertWindowIds(timeline, 444, 111, 555, 666, 777, 333, 222); + TimelineAsserts.assertWindowTags(timeline, 444, 111, 555, 666, 777, 333, 222); mediaSource.moveMediaSource(6, 0); timeline = testRunner.assertTimelineChangeBlocking(); TimelineAsserts.assertPeriodCounts(timeline, 2, 4, 1, 5, 6, 7, 3); - TimelineAsserts.assertWindowIds(timeline, 222, 444, 111, 555, 666, 777, 333); + TimelineAsserts.assertWindowTags(timeline, 222, 444, 111, 555, 666, 777, 333); // Remove in the middle. mediaSource.removeMediaSource(3); @@ -130,7 +130,7 @@ public final class ConcatenatingMediaSourceTest { mediaSource.removeMediaSource(1); timeline = testRunner.assertTimelineChangeBlocking(); TimelineAsserts.assertPeriodCounts(timeline, 2, 1, 3); - TimelineAsserts.assertWindowIds(timeline, 222, 111, 333); + TimelineAsserts.assertWindowTags(timeline, 222, 111, 333); for (int i = 3; i <= 6; i++) { childSources[i].assertReleased(); } @@ -169,14 +169,14 @@ public final class ConcatenatingMediaSourceTest { mediaSource.removeMediaSource(0); timeline = testRunner.assertTimelineChangeBlocking(); TimelineAsserts.assertPeriodCounts(timeline, 1, 3); - TimelineAsserts.assertWindowIds(timeline, 111, 333); + TimelineAsserts.assertWindowTags(timeline, 111, 333); childSources[1].assertReleased(); // Remove at back of queue. mediaSource.removeMediaSource(1); timeline = testRunner.assertTimelineChangeBlocking(); TimelineAsserts.assertPeriodCounts(timeline, 1); - TimelineAsserts.assertWindowIds(timeline, 111); + TimelineAsserts.assertWindowTags(timeline, 111); childSources[2].assertReleased(); // Remove last source. @@ -200,7 +200,7 @@ public final class ConcatenatingMediaSourceTest { Timeline timeline = testRunner.prepareSource(); TimelineAsserts.assertPeriodCounts(timeline, 3, 4, 2); - TimelineAsserts.assertWindowIds(timeline, 333, 444, 222); + TimelineAsserts.assertWindowTags(timeline, 333, 444, 222); TimelineAsserts.assertNextWindowIndices( timeline, Player.REPEAT_MODE_OFF, false, 1, 2, C.INDEX_UNSET); TimelineAsserts.assertPreviousWindowIndices( @@ -241,7 +241,7 @@ public final class ConcatenatingMediaSourceTest { // placeholder information for lazy sources. Timeline timeline = testRunner.prepareSource(); TimelineAsserts.assertPeriodCounts(timeline, 1, 1); - TimelineAsserts.assertWindowIds(timeline, 111, null); + TimelineAsserts.assertWindowTags(timeline, 111, null); TimelineAsserts.assertWindowIsDynamic(timeline, false, true); // Trigger source info refresh for lazy source and check that the timeline now contains all @@ -255,7 +255,7 @@ public final class ConcatenatingMediaSourceTest { }); timeline = testRunner.assertTimelineChangeBlocking(); TimelineAsserts.assertPeriodCounts(timeline, 1, 9); - TimelineAsserts.assertWindowIds(timeline, 111, 999); + TimelineAsserts.assertWindowTags(timeline, 111, 999); TimelineAsserts.assertWindowIsDynamic(timeline, false, false); testRunner.assertPrepareAndReleaseAllPeriods(); testRunner.assertCompletedManifestLoads(0, 1); @@ -272,7 +272,7 @@ public final class ConcatenatingMediaSourceTest { mediaSource.removeMediaSource(2); timeline = testRunner.assertTimelineChangeBlocking(); TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 2, 9); - TimelineAsserts.assertWindowIds(timeline, null, 111, 222, 999); + TimelineAsserts.assertWindowTags(timeline, null, 111, 222, 999); TimelineAsserts.assertWindowIsDynamic(timeline, true, false, false, false); // Create a period from an unprepared lazy media source and assert Callback.onPrepared is not @@ -300,7 +300,7 @@ public final class ConcatenatingMediaSourceTest { }); timeline = testRunner.assertTimelineChangeBlocking(); TimelineAsserts.assertPeriodCounts(timeline, 8, 1, 2, 9); - TimelineAsserts.assertWindowIds(timeline, 888, 111, 222, 999); + TimelineAsserts.assertWindowTags(timeline, 888, 111, 222, 999); TimelineAsserts.assertWindowIsDynamic(timeline, false, false, false, false); assertThat(preparedCondition.getCount()).isEqualTo(0); @@ -346,7 +346,7 @@ public final class ConcatenatingMediaSourceTest { testRunner.assertTimelineChangeBlocking(); mediaSource.addMediaSource(6, mediaSources[2]); timeline = testRunner.assertTimelineChangeBlocking(); - TimelineAsserts.assertWindowIds(timeline, 111, 222, 333); + TimelineAsserts.assertWindowTags(timeline, 111, 222, 333); TimelineAsserts.assertPeriodCounts(timeline, 1, 2, 3); TimelineAsserts.assertPreviousWindowIndices( timeline, Player.REPEAT_MODE_OFF, false, C.INDEX_UNSET, 0, 1); @@ -685,7 +685,7 @@ public final class ConcatenatingMediaSourceTest { testRunner = new MediaSourceTestRunner(mediaSource, null); mediaSource.addMediaSources(Arrays.asList(createMediaSources(3))); Timeline timeline = testRunner.prepareSource(); - TimelineAsserts.assertWindowIds(timeline, 111, 222, 333); + TimelineAsserts.assertWindowTags(timeline, 111, 222, 333); TimelineAsserts.assertPeriodCounts(timeline, 1, 2, 3); TimelineAsserts.assertPreviousWindowIndices( timeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ false, C.INDEX_UNSET, 0, 1); @@ -736,7 +736,7 @@ public final class ConcatenatingMediaSourceTest { nestedSource2.addMediaSource(childSources[3]); Timeline timeline = testRunner.assertTimelineChangeBlocking(); - TimelineAsserts.assertWindowIds(timeline, 111, 222, 333, 444); + TimelineAsserts.assertWindowTags(timeline, 111, 222, 333, 444); TimelineAsserts.assertPeriodCounts(timeline, 1, 2, 3, 4); TimelineAsserts.assertPreviousWindowIndices( timeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ false, C.INDEX_UNSET, 0, 1, 2); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/LoopingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/LoopingMediaSourceTest.java index 6aa710aff4..d639bc168a 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/LoopingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/LoopingMediaSourceTest.java @@ -50,7 +50,7 @@ public class LoopingMediaSourceTest { @Test public void testSingleLoop() throws IOException { Timeline timeline = getLoopingTimeline(multiWindowTimeline, 1); - TimelineAsserts.assertWindowIds(timeline, 111, 222, 333); + TimelineAsserts.assertWindowTags(timeline, 111, 222, 333); TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1); for (boolean shuffled : new boolean[] {false, true}) { TimelineAsserts.assertPreviousWindowIndices( @@ -69,7 +69,7 @@ public class LoopingMediaSourceTest { @Test public void testMultiLoop() throws IOException { Timeline timeline = getLoopingTimeline(multiWindowTimeline, 3); - TimelineAsserts.assertWindowIds(timeline, 111, 222, 333, 111, 222, 333, 111, 222, 333); + TimelineAsserts.assertWindowTags(timeline, 111, 222, 333, 111, 222, 333, 111, 222, 333); TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1, 1, 1, 1, 1, 1, 1); for (boolean shuffled : new boolean[] {false, true}) { TimelineAsserts.assertPreviousWindowIndices( @@ -90,7 +90,7 @@ public class LoopingMediaSourceTest { @Test public void testInfiniteLoop() throws IOException { Timeline timeline = getLoopingTimeline(multiWindowTimeline, Integer.MAX_VALUE); - TimelineAsserts.assertWindowIds(timeline, 111, 222, 333); + TimelineAsserts.assertWindowTags(timeline, 111, 222, 333); TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1); for (boolean shuffled : new boolean[] {false, true}) { TimelineAsserts.assertPreviousWindowIndices( diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/SinglePeriodTimelineTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/SinglePeriodTimelineTest.java index 2627052cc5..2587b78d99 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/SinglePeriodTimelineTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/SinglePeriodTimelineTest.java @@ -56,8 +56,15 @@ public final class SinglePeriodTimelineTest { @Test public void testGetPeriodPositionDynamicWindowKnownDuration() { long windowDurationUs = 1000; - SinglePeriodTimeline timeline = new SinglePeriodTimeline(windowDurationUs, windowDurationUs, 0, - 0, false, true); + SinglePeriodTimeline timeline = + new SinglePeriodTimeline( + windowDurationUs, + windowDurationUs, + /* windowPositionInPeriodUs= */ 0, + /* windowDefaultStartPositionUs= */ 0, + /* isSeekable= */ false, + /* isDynamic= */ true, + /* tag= */ null); // Should return null with a positive position projection beyond window duration. Pair position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, windowDurationUs + 1); @@ -72,4 +79,48 @@ public final class SinglePeriodTimelineTest { assertThat(position.second).isEqualTo(0); } + @Test + public void setNullTag_returnsNullTag_butUsesDefaultUid() { + SinglePeriodTimeline timeline = + new SinglePeriodTimeline( + /* durationUs= */ C.TIME_UNSET, + /* isSeekable= */ false, + /* isDynamic= */ false, + /* tag= */ null); + + assertThat(timeline.getWindow(/* windowIndex= */ 0, window, /* setTag= */ false).tag).isNull(); + assertThat(timeline.getWindow(/* windowIndex= */ 0, window, /* setTag= */ true).tag).isNull(); + assertThat(timeline.getPeriod(/* periodIndex= */ 0, period, /* setIds= */ false).id).isNull(); + assertThat(timeline.getPeriod(/* periodIndex= */ 0, period, /* setIds= */ true).id).isNull(); + assertThat(timeline.getPeriod(/* periodIndex= */ 0, period, /* setIds= */ false).uid).isNull(); + assertThat(timeline.getPeriod(/* periodIndex= */ 0, period, /* setIds= */ true).uid) + .isNotNull(); + } + + @Test + public void setTag_isUsedForWindowTag() { + Object tag = new Object(); + SinglePeriodTimeline timeline = + new SinglePeriodTimeline( + /* durationUs= */ C.TIME_UNSET, /* isSeekable= */ false, /* isDynamic= */ false, tag); + + assertThat(timeline.getWindow(/* windowIndex= */ 0, window, /* setTag= */ false).tag).isNull(); + assertThat(timeline.getWindow(/* windowIndex= */ 0, window, /* setTag= */ true).tag) + .isEqualTo(tag); + } + + @Test + public void getIndexOfPeriod_returnsPeriod() { + SinglePeriodTimeline timeline = + new SinglePeriodTimeline( + /* durationUs= */ C.TIME_UNSET, + /* isSeekable= */ false, + /* isDynamic= */ false, + /* tag= */ null); + Object uid = timeline.getPeriod(/* periodIndex= */ 0, period, /* setIds= */ true).uid; + + assertThat(timeline.getIndexOfPeriod(uid)).isEqualTo(0); + assertThat(timeline.getIndexOfPeriod(/* uid= */ null)).isEqualTo(C.INDEX_UNSET); + assertThat(timeline.getIndexOfPeriod(/* uid= */ new Object())).isEqualTo(C.INDEX_UNSET); + } } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index b6371c2219..7b854e9d29 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -77,6 +77,7 @@ public final class DashMediaSource extends BaseMediaSource { private int minLoadableRetryCount; private long livePresentationDelayMs; private boolean isCreateCalled; + private @Nullable Object tag; /** * Creates a new factory for {@link DashMediaSource}s. @@ -97,6 +98,21 @@ public final class DashMediaSource extends BaseMediaSource { compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); } + /** + * Sets a tag for the media source which will be published in the {@link + * com.google.android.exoplayer2.Timeline} of the source as {@link + * com.google.android.exoplayer2.Timeline.Window#tag}. + * + * @param tag A tag for the media source. + * @return This factory, for convenience. + * @throws IllegalStateException If one of the {@code create} methods has already been called. + */ + public Factory setTag(Object tag) { + Assertions.checkState(!isCreateCalled); + this.tag = tag; + return this; + } + /** * Sets the minimum number of times to retry if a loading error occurs. The default value is * {@link #DEFAULT_MIN_LOADABLE_RETRY_COUNT}. @@ -175,13 +191,14 @@ public final class DashMediaSource extends BaseMediaSource { isCreateCalled = true; return new DashMediaSource( manifest, - null, - null, - null, + /* manifestUri= */ null, + /* manifestDataSourceFactory= */ null, + /* manifestParser= */ null, chunkSourceFactory, compositeSequenceableLoaderFactory, minLoadableRetryCount, - livePresentationDelayMs); + livePresentationDelayMs, + tag); } /** @@ -213,14 +230,15 @@ public final class DashMediaSource extends BaseMediaSource { manifestParser = new DashManifestParser(); } return new DashMediaSource( - null, + /* manifest= */ null, Assertions.checkNotNull(manifestUri), manifestDataSourceFactory, manifestParser, chunkSourceFactory, compositeSequenceableLoaderFactory, minLoadableRetryCount, - livePresentationDelayMs); + livePresentationDelayMs, + tag); } /** @@ -290,6 +308,7 @@ public final class DashMediaSource extends BaseMediaSource { private final Runnable simulateManifestRefreshRunnable; private final PlayerEmsgCallback playerEmsgCallback; private final LoaderErrorThrower manifestLoadErrorThrower; + private final @Nullable Object tag; private DataSource dataSource; private Loader loader; @@ -349,13 +368,14 @@ public final class DashMediaSource extends BaseMediaSource { MediaSourceEventListener eventListener) { this( manifest, - null, - null, - null, + /* manifestUri= */ null, + /* manifestDataSourceFactory= */ null, + /* manifestParser= */ null, chunkSourceFactory, new DefaultCompositeSequenceableLoaderFactory(), minLoadableRetryCount, - DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS); + DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS, + /* tag= */ null); if (eventHandler != null && eventListener != null) { addEventListener(eventHandler, eventListener); } @@ -444,14 +464,15 @@ public final class DashMediaSource extends BaseMediaSource { Handler eventHandler, MediaSourceEventListener eventListener) { this( - null, + /* manifest= */ null, manifestUri, manifestDataSourceFactory, manifestParser, chunkSourceFactory, new DefaultCompositeSequenceableLoaderFactory(), minLoadableRetryCount, - livePresentationDelayMs); + livePresentationDelayMs, + /* tag= */ null); if (eventHandler != null && eventListener != null) { addEventListener(eventHandler, eventListener); } @@ -465,7 +486,8 @@ public final class DashMediaSource extends BaseMediaSource { DashChunkSource.Factory chunkSourceFactory, CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, int minLoadableRetryCount, - long livePresentationDelayMs) { + long livePresentationDelayMs, + @Nullable Object tag) { this.initialManifestUri = manifestUri; this.manifest = manifest; this.manifestUri = manifestUri; @@ -475,6 +497,7 @@ public final class DashMediaSource extends BaseMediaSource { this.minLoadableRetryCount = minLoadableRetryCount; this.livePresentationDelayMs = livePresentationDelayMs; this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; + this.tag = tag; sideloadedManifest = manifest != null; manifestEventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null); manifestUriLock = new Object(); @@ -862,9 +885,16 @@ public final class DashMediaSource extends BaseMediaSource { } long windowStartTimeMs = manifest.availabilityStartTimeMs + manifest.getPeriod(0).startMs + C.usToMs(currentStartTimeUs); - DashTimeline timeline = new DashTimeline(manifest.availabilityStartTimeMs, windowStartTimeMs, - firstPeriodId, currentStartTimeUs, windowDurationUs, windowDefaultStartPositionUs, - manifest); + DashTimeline timeline = + new DashTimeline( + manifest.availabilityStartTimeMs, + windowStartTimeMs, + firstPeriodId, + currentStartTimeUs, + windowDurationUs, + windowDefaultStartPositionUs, + manifest, + tag); refreshSourceInfo(timeline, manifest); if (!sideloadedManifest) { @@ -993,10 +1023,17 @@ public final class DashMediaSource extends BaseMediaSource { private final long windowDurationUs; private final long windowDefaultStartPositionUs; private final DashManifest manifest; + private final @Nullable Object windowTag; - public DashTimeline(long presentationStartTimeMs, long windowStartTimeMs, int firstPeriodId, - long offsetInFirstPeriodUs, long windowDurationUs, long windowDefaultStartPositionUs, - DashManifest manifest) { + public DashTimeline( + long presentationStartTimeMs, + long windowStartTimeMs, + int firstPeriodId, + long offsetInFirstPeriodUs, + long windowDurationUs, + long windowDefaultStartPositionUs, + DashManifest manifest, + @Nullable Object windowTag) { this.presentationStartTimeMs = presentationStartTimeMs; this.windowStartTimeMs = windowStartTimeMs; this.firstPeriodId = firstPeriodId; @@ -1004,6 +1041,7 @@ public final class DashMediaSource extends BaseMediaSource { this.windowDurationUs = windowDurationUs; this.windowDefaultStartPositionUs = windowDefaultStartPositionUs; this.manifest = manifest; + this.windowTag = windowTag; } @Override @@ -1028,14 +1066,23 @@ public final class DashMediaSource extends BaseMediaSource { } @Override - public Window getWindow(int windowIndex, Window window, boolean setIdentifier, - long defaultPositionProjectionUs) { + public Window getWindow( + int windowIndex, Window window, boolean setTag, long defaultPositionProjectionUs) { Assertions.checkIndex(windowIndex, 0, 1); long windowDefaultStartPositionUs = getAdjustedWindowDefaultStartPositionUs( defaultPositionProjectionUs); - return window.set(null, presentationStartTimeMs, windowStartTimeMs, true /* isSeekable */, - manifest.dynamic, windowDefaultStartPositionUs, windowDurationUs, 0, - manifest.getPeriodCount() - 1, offsetInFirstPeriodUs); + Object tag = setTag ? windowTag : null; + return window.set( + tag, + presentationStartTimeMs, + windowStartTimeMs, + /* isSeekable= */ true, + manifest.dynamic, + windowDefaultStartPositionUs, + windowDurationUs, + /* firstPeriodIndex= */ 0, + manifest.getPeriodCount() - 1, + offsetInFirstPeriodUs); } @Override diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index 37d37575c2..01bb36f6ce 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -62,6 +62,7 @@ public final class HlsMediaSource extends BaseMediaSource private int minLoadableRetryCount; private boolean allowChunklessPreparation; private boolean isCreateCalled; + private @Nullable Object tag; /** * Creates a new factory for {@link HlsMediaSource}s. @@ -87,6 +88,21 @@ public final class HlsMediaSource extends BaseMediaSource compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); } + /** + * Sets a tag for the media source which will be published in the {@link + * com.google.android.exoplayer2.Timeline} of the source as {@link + * com.google.android.exoplayer2.Timeline.Window#tag}. + * + * @param tag A tag for the media source. + * @return This factory, for convenience. + * @throws IllegalStateException If one of the {@code create} methods has already been called. + */ + public Factory setTag(Object tag) { + Assertions.checkState(!isCreateCalled); + this.tag = tag; + return this; + } + /** * Sets the factory for {@link Extractor}s for the segments. The default value is {@link * HlsExtractorFactory#DEFAULT}. @@ -181,7 +197,8 @@ public final class HlsMediaSource extends BaseMediaSource compositeSequenceableLoaderFactory, minLoadableRetryCount, playlistParser, - allowChunklessPreparation); + allowChunklessPreparation, + tag); } /** @@ -218,6 +235,7 @@ public final class HlsMediaSource extends BaseMediaSource private final int minLoadableRetryCount; private final ParsingLoadable.Parser playlistParser; private final boolean allowChunklessPreparation; + private final @Nullable Object tag; private HlsPlaylistTracker playlistTracker; @@ -292,7 +310,8 @@ public final class HlsMediaSource extends BaseMediaSource new DefaultCompositeSequenceableLoaderFactory(), minLoadableRetryCount, playlistParser, - false); + /* allowChunklessPreparation= */ false, + /* tag= */ null); if (eventHandler != null && eventListener != null) { addEventListener(eventHandler, eventListener); } @@ -305,7 +324,8 @@ public final class HlsMediaSource extends BaseMediaSource CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, int minLoadableRetryCount, ParsingLoadable.Parser playlistParser, - boolean allowChunklessPreparation) { + boolean allowChunklessPreparation, + @Nullable Object tag) { this.manifestUri = manifestUri; this.dataSourceFactory = dataSourceFactory; this.extractorFactory = extractorFactory; @@ -313,6 +333,7 @@ public final class HlsMediaSource extends BaseMediaSource this.minLoadableRetryCount = minLoadableRetryCount; this.playlistParser = playlistParser; this.allowChunklessPreparation = allowChunklessPreparation; + this.tag = tag; } @Override @@ -388,7 +409,8 @@ public final class HlsMediaSource extends BaseMediaSource /* windowPositionInPeriodUs= */ offsetFromInitialStartTimeUs, windowDefaultStartPositionUs, /* isSeekable= */ true, - /* isDynamic= */ !playlist.hasEndTag); + /* isDynamic= */ !playlist.hasEndTag, + tag); } else /* not live */ { if (windowDefaultStartPositionUs == C.TIME_UNSET) { windowDefaultStartPositionUs = 0; @@ -402,7 +424,8 @@ public final class HlsMediaSource extends BaseMediaSource /* windowPositionInPeriodUs= */ 0, windowDefaultStartPositionUs, /* isSeekable= */ true, - /* isDynamic= */ false); + /* isDynamic= */ false, + tag); } refreshSourceInfo(timeline, new HlsManifest(playlistTracker.getMasterPlaylist(), playlist)); } diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java index d07cfd116b..72d1ba1efd 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java @@ -66,6 +66,7 @@ public final class SsMediaSource extends BaseMediaSource private int minLoadableRetryCount; private long livePresentationDelayMs; private boolean isCreateCalled; + private @Nullable Object tag; /** * Creates a new factory for {@link SsMediaSource}s. @@ -86,6 +87,20 @@ public final class SsMediaSource extends BaseMediaSource compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory(); } + /** + * Sets a tag for the media source which will be published in the {@link Timeline} of the source + * as {@link Timeline.Window#tag}. + * + * @param tag A tag for the media source. + * @return This factory, for convenience. + * @throws IllegalStateException If one of the {@code create} methods has already been called. + */ + public Factory setTag(Object tag) { + Assertions.checkState(!isCreateCalled); + this.tag = tag; + return this; + } + /** * Sets the minimum number of times to retry if a loading error occurs. The default value is * {@link #DEFAULT_MIN_LOADABLE_RETRY_COUNT}. @@ -161,13 +176,14 @@ public final class SsMediaSource extends BaseMediaSource isCreateCalled = true; return new SsMediaSource( manifest, - null, - null, - null, + /* manifestUri= */ null, + /* manifestDataSourceFactory= */ null, + /* manifestParser= */ null, chunkSourceFactory, compositeSequenceableLoaderFactory, minLoadableRetryCount, - livePresentationDelayMs); + livePresentationDelayMs, + tag); } /** @@ -199,14 +215,15 @@ public final class SsMediaSource extends BaseMediaSource manifestParser = new SsManifestParser(); } return new SsMediaSource( - null, + /* manifest= */ null, Assertions.checkNotNull(manifestUri), manifestDataSourceFactory, manifestParser, chunkSourceFactory, compositeSequenceableLoaderFactory, minLoadableRetryCount, - livePresentationDelayMs); + livePresentationDelayMs, + tag); } /** @@ -261,6 +278,7 @@ public final class SsMediaSource extends BaseMediaSource private final EventDispatcher manifestEventDispatcher; private final ParsingLoadable.Parser manifestParser; private final ArrayList mediaPeriods; + private final @Nullable Object tag; private DataSource manifestDataSource; private Loader manifestLoader; @@ -309,13 +327,14 @@ public final class SsMediaSource extends BaseMediaSource MediaSourceEventListener eventListener) { this( manifest, - null, - null, - null, + /* manifestUri= */ null, + /* manifestDataSourceFactory= */ null, + /* manifestParser= */ null, chunkSourceFactory, new DefaultCompositeSequenceableLoaderFactory(), minLoadableRetryCount, - DEFAULT_LIVE_PRESENTATION_DELAY_MS); + DEFAULT_LIVE_PRESENTATION_DELAY_MS, + /* tag= */ null); if (eventHandler != null && eventListener != null) { addEventListener(eventHandler, eventListener); } @@ -400,14 +419,15 @@ public final class SsMediaSource extends BaseMediaSource Handler eventHandler, MediaSourceEventListener eventListener) { this( - null, + /* manifest= */ null, manifestUri, manifestDataSourceFactory, manifestParser, chunkSourceFactory, new DefaultCompositeSequenceableLoaderFactory(), minLoadableRetryCount, - livePresentationDelayMs); + livePresentationDelayMs, + /* tag= */ null); if (eventHandler != null && eventListener != null) { addEventListener(eventHandler, eventListener); } @@ -421,7 +441,8 @@ public final class SsMediaSource extends BaseMediaSource SsChunkSource.Factory chunkSourceFactory, CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, int minLoadableRetryCount, - long livePresentationDelayMs) { + long livePresentationDelayMs, + @Nullable Object tag) { Assertions.checkState(manifest == null || !manifest.isLive); this.manifest = manifest; this.manifestUri = manifestUri == null ? null : SsUtil.fixManifestUri(manifestUri); @@ -432,6 +453,7 @@ public final class SsMediaSource extends BaseMediaSource this.minLoadableRetryCount = minLoadableRetryCount; this.livePresentationDelayMs = livePresentationDelayMs; this.manifestEventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null); + this.tag = tag; sideloadedManifest = manifest != null; mediaPeriods = new ArrayList<>(); } @@ -555,8 +577,15 @@ public final class SsMediaSource extends BaseMediaSource Timeline timeline; if (startTimeUs == Long.MAX_VALUE) { long periodDurationUs = manifest.isLive ? C.TIME_UNSET : 0; - timeline = new SinglePeriodTimeline(periodDurationUs, 0, 0, 0, true /* isSeekable */, - manifest.isLive /* isDynamic */); + timeline = + new SinglePeriodTimeline( + periodDurationUs, + /* windowDurationUs= */ 0, + /* windowPositionInPeriodUs= */ 0, + /* windowDefaultStartPositionUs= */ 0, + /* isSeekable= */ true, + manifest.isLive, + tag); } else if (manifest.isLive) { if (manifest.dvrWindowLengthUs != C.TIME_UNSET && manifest.dvrWindowLengthUs > 0) { startTimeUs = Math.max(startTimeUs, endTimeUs - manifest.dvrWindowLengthUs); @@ -569,13 +598,27 @@ public final class SsMediaSource extends BaseMediaSource // it to the middle of the window. defaultStartPositionUs = Math.min(MIN_LIVE_DEFAULT_START_POSITION_US, durationUs / 2); } - timeline = new SinglePeriodTimeline(C.TIME_UNSET, durationUs, startTimeUs, - defaultStartPositionUs, true /* isSeekable */, true /* isDynamic */); + timeline = + new SinglePeriodTimeline( + /* periodDurationUs= */ C.TIME_UNSET, + durationUs, + startTimeUs, + defaultStartPositionUs, + /* isSeekable= */ true, + /* isDynamic= */ true, + tag); } else { long durationUs = manifest.durationUs != C.TIME_UNSET ? manifest.durationUs : endTimeUs - startTimeUs; - timeline = new SinglePeriodTimeline(startTimeUs + durationUs, durationUs, startTimeUs, 0, - true /* isSeekable */, false /* isDynamic */); + timeline = + new SinglePeriodTimeline( + startTimeUs + durationUs, + durationUs, + startTimeUs, + /* windowDefaultStartPositionUs= */ 0, + /* isSeekable= */ true, + /* isDynamic= */ false, + tag); } refreshSourceInfo(timeline, manifest); } 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 521c8ee52a..4e118366d7 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 @@ -164,13 +164,21 @@ public final class FakeTimeline extends Timeline { } @Override - public Window getWindow(int windowIndex, Window window, boolean setIds, - long defaultPositionProjectionUs) { + public Window getWindow( + int windowIndex, Window window, boolean setTag, long defaultPositionProjectionUs) { TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; - Object id = setIds ? windowDefinition.id : null; - return window.set(id, C.TIME_UNSET, C.TIME_UNSET, windowDefinition.isSeekable, - windowDefinition.isDynamic, 0, windowDefinition.durationUs, periodOffsets[windowIndex], - periodOffsets[windowIndex + 1] - 1, 0); + Object tag = setTag ? windowDefinition.id : null; + return window.set( + tag, + /* presentationStartTimeMs= */ C.TIME_UNSET, + /* windowStartTimeMs= */ C.TIME_UNSET, + windowDefinition.isSeekable, + windowDefinition.isDynamic, + /* defaultPositionUs= */ 0, + windowDefinition.durationUs, + periodOffsets[windowIndex], + periodOffsets[windowIndex + 1] - 1, + /* positionInFirstPeriodUs= */ 0); } @Override diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java index af8b10e6d3..8de92ddfa9 100644 --- a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer2.testutil; import android.os.Looper; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; @@ -142,6 +143,11 @@ public abstract class StubExoPlayer implements ExoPlayer { throw new UnsupportedOperationException(); } + @Override + public @Nullable Object getCurrentTag() { + throw new UnsupportedOperationException(); + } + @Override public void stop() { throw new UnsupportedOperationException(); diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java index 17045f749a..a0ca6af8a9 100644 --- a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/TimelineAsserts.java @@ -34,7 +34,7 @@ public final class TimelineAsserts { /** Assert that timeline is empty (i.e. has no windows or periods). */ public static void assertEmpty(Timeline timeline) { - assertWindowIds(timeline); + assertWindowTags(timeline); assertPeriodCounts(timeline); for (boolean shuffled : new boolean[] {false, true}) { assertThat(timeline.getFirstWindowIndex(shuffled)).isEqualTo(C.INDEX_UNSET); @@ -43,18 +43,18 @@ public final class TimelineAsserts { } /** - * Asserts that window IDs are set correctly. + * Asserts that window tags are set correctly. * - * @param expectedWindowIds A list of expected window IDs. If an ID is unknown or not important + * @param expectedWindowTags A list of expected window tags. If a tag is unknown or not important * {@code null} can be passed to skip this window. */ - public static void assertWindowIds(Timeline timeline, Object... expectedWindowIds) { + public static void assertWindowTags(Timeline timeline, Object... expectedWindowTags) { Window window = new Window(); - assertThat(timeline.getWindowCount()).isEqualTo(expectedWindowIds.length); + assertThat(timeline.getWindowCount()).isEqualTo(expectedWindowTags.length); for (int i = 0; i < timeline.getWindowCount(); i++) { timeline.getWindow(i, window, true); - if (expectedWindowIds[i] != null) { - assertThat(window.id).isEqualTo(expectedWindowIds[i]); + if (expectedWindowTags[i] != null) { + assertThat(window.tag).isEqualTo(expectedWindowTags[i]); } } }