Commit 7b4b5cbf by gyumin Committed by Ian Baker

Preserve window indices of Timeline when bundling

PiperOrigin-RevId: 364324490
parent 10a8c603
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import static com.google.android.exoplayer2.util.Assertions.checkState; import static com.google.android.exoplayer2.util.Assertions.checkState;
import android.net.Uri; import android.net.Uri;
...@@ -1255,11 +1256,16 @@ public abstract class Timeline implements Bundleable { ...@@ -1255,11 +1256,16 @@ public abstract class Timeline implements Bundleable {
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({FIELD_WINDOWS, FIELD_PERIODS}) @IntDef({
FIELD_WINDOWS,
FIELD_PERIODS,
FIELD_SHUFFLED_WINDOW_INDICES,
})
private @interface FieldNumber {} private @interface FieldNumber {}
private static final int FIELD_WINDOWS = 0; private static final int FIELD_WINDOWS = 0;
private static final int FIELD_PERIODS = 1; private static final int FIELD_PERIODS = 1;
private static final int FIELD_SHUFFLED_WINDOW_INDICES = 2;
/** /**
* {@inheritDoc} * {@inheritDoc}
...@@ -1272,18 +1278,24 @@ public abstract class Timeline implements Bundleable { ...@@ -1272,18 +1278,24 @@ public abstract class Timeline implements Bundleable {
public final Bundle toBundle() { public final Bundle toBundle() {
List<Bundle> windowBundles = new ArrayList<>(); List<Bundle> windowBundles = new ArrayList<>();
int windowCount = getWindowCount(); int windowCount = getWindowCount();
for (int i = 0; i < windowCount; i++) {
Window window = new Window(); Window window = new Window();
getWindow(i, window, /* defaultPositionProjectionUs= */ 0); for (int i = 0; i < windowCount; i++) {
windowBundles.add(window.toBundle()); windowBundles.add(getWindow(i, window, /* defaultPositionProjectionUs= */ 0).toBundle());
} }
List<Bundle> periodBundles = new ArrayList<>(); List<Bundle> periodBundles = new ArrayList<>();
int periodCount = getPeriodCount(); int periodCount = getPeriodCount();
for (int i = 0; i < periodCount; i++) {
Period period = new Period(); Period period = new Period();
getPeriod(i, period, /* setIds= */ false); for (int i = 0; i < periodCount; i++) {
periodBundles.add(period.toBundle()); periodBundles.add(getPeriod(i, period, /* setIds= */ false).toBundle());
}
int[] shuffledWindowIndices = new int[windowCount];
shuffledWindowIndices[0] = getFirstWindowIndex(/* shuffleModeEnabled= */ true);
for (int i = 1; i < windowCount; i++) {
shuffledWindowIndices[i] =
getNextWindowIndex(
shuffledWindowIndices[i - 1], Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ true);
} }
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
...@@ -1291,6 +1303,7 @@ public abstract class Timeline implements Bundleable { ...@@ -1291,6 +1303,7 @@ public abstract class Timeline implements Bundleable {
bundle, keyForField(FIELD_WINDOWS), new BundleListRetriever(windowBundles)); bundle, keyForField(FIELD_WINDOWS), new BundleListRetriever(windowBundles));
BundleCompat.putBinder( BundleCompat.putBinder(
bundle, keyForField(FIELD_PERIODS), new BundleListRetriever(periodBundles)); bundle, keyForField(FIELD_PERIODS), new BundleListRetriever(periodBundles));
bundle.putIntArray(keyForField(FIELD_SHUFFLED_WINDOW_INDICES), shuffledWindowIndices);
return bundle; return bundle;
} }
...@@ -1310,7 +1323,14 @@ public abstract class Timeline implements Bundleable { ...@@ -1310,7 +1323,14 @@ public abstract class Timeline implements Bundleable {
ImmutableList<Period> periods = ImmutableList<Period> periods =
fromBundleListRetriever( fromBundleListRetriever(
Period.CREATOR, BundleCompat.getBinder(bundle, keyForField(FIELD_PERIODS))); Period.CREATOR, BundleCompat.getBinder(bundle, keyForField(FIELD_PERIODS)));
return new RemotableTimeline(windows, periods); @Nullable
int[] shuffledWindowIndices = bundle.getIntArray(keyForField(FIELD_SHUFFLED_WINDOW_INDICES));
return new RemotableTimeline(
windows,
periods,
shuffledWindowIndices == null
? generateUnshuffledIndices(windows.size())
: shuffledWindowIndices);
} }
private static <T extends Bundleable> ImmutableList<T> fromBundleListRetriever( private static <T extends Bundleable> ImmutableList<T> fromBundleListRetriever(
...@@ -1330,6 +1350,14 @@ public abstract class Timeline implements Bundleable { ...@@ -1330,6 +1350,14 @@ public abstract class Timeline implements Bundleable {
return Integer.toString(field, Character.MAX_RADIX); return Integer.toString(field, Character.MAX_RADIX);
} }
private static int[] generateUnshuffledIndices(int n) {
int[] indices = new int[n];
for (int i = 0; i < n; i++) {
indices[i] = i;
}
return indices;
}
/** /**
* A concrete class of {@link Timeline} to restore a {@link Timeline} instance from a {@link * A concrete class of {@link Timeline} to restore a {@link Timeline} instance from a {@link
* Bundle} sent by another process via {@link IBinder}. * Bundle} sent by another process via {@link IBinder}.
...@@ -1338,10 +1366,19 @@ public abstract class Timeline implements Bundleable { ...@@ -1338,10 +1366,19 @@ public abstract class Timeline implements Bundleable {
private final ImmutableList<Window> windows; private final ImmutableList<Window> windows;
private final ImmutableList<Period> periods; private final ImmutableList<Period> periods;
private final int[] shuffledWindowIndices;
private final int[] windowIndicesInShuffled;
public RemotableTimeline(ImmutableList<Window> windows, ImmutableList<Period> periods) { public RemotableTimeline(
ImmutableList<Window> windows, ImmutableList<Period> periods, int[] shuffledWindowIndices) {
checkArgument(windows.size() == shuffledWindowIndices.length);
this.windows = windows; this.windows = windows;
this.periods = periods; this.periods = periods;
this.shuffledWindowIndices = shuffledWindowIndices;
windowIndicesInShuffled = new int[shuffledWindowIndices.length];
for (int i = 0; i < shuffledWindowIndices.length; i++) {
windowIndicesInShuffled[shuffledWindowIndices[i]] = i;
}
} }
@Override @Override
...@@ -1373,6 +1410,56 @@ public abstract class Timeline implements Bundleable { ...@@ -1373,6 +1410,56 @@ public abstract class Timeline implements Bundleable {
} }
@Override @Override
public int getNextWindowIndex(
int windowIndex, @Player.RepeatMode int repeatMode, boolean shuffleModeEnabled) {
if (repeatMode == Player.REPEAT_MODE_ONE) {
return windowIndex;
}
if (windowIndex == getLastWindowIndex(shuffleModeEnabled)) {
return repeatMode == Player.REPEAT_MODE_ALL
? getFirstWindowIndex(shuffleModeEnabled)
: C.INDEX_UNSET;
}
return shuffleModeEnabled
? shuffledWindowIndices[windowIndicesInShuffled[windowIndex] + 1]
: windowIndex + 1;
}
@Override
public int getPreviousWindowIndex(
int windowIndex, @Player.RepeatMode int repeatMode, boolean shuffleModeEnabled) {
if (repeatMode == Player.REPEAT_MODE_ONE) {
return windowIndex;
}
if (windowIndex == getFirstWindowIndex(shuffleModeEnabled)) {
return repeatMode == Player.REPEAT_MODE_ALL
? getLastWindowIndex(shuffleModeEnabled)
: C.INDEX_UNSET;
}
return shuffleModeEnabled
? shuffledWindowIndices[windowIndicesInShuffled[windowIndex] - 1]
: windowIndex - 1;
}
@Override
public int getLastWindowIndex(boolean shuffleModeEnabled) {
if (isEmpty()) {
return C.INDEX_UNSET;
}
return shuffleModeEnabled
? shuffledWindowIndices[getWindowCount() - 1]
: getWindowCount() - 1;
}
@Override
public int getFirstWindowIndex(boolean shuffleModeEnabled) {
if (isEmpty()) {
return C.INDEX_UNSET;
}
return shuffleModeEnabled ? shuffledWindowIndices[0] : 0;
}
@Override
public int getPeriodCount() { public int getPeriodCount() {
return periods.size(); return periods.size();
} }
......
...@@ -238,6 +238,47 @@ public class TimelineTest { ...@@ -238,6 +238,47 @@ public class TimelineTest {
} }
@Test @Test
public void roundtripViaBundle_ofTimeline_preservesWindowIndices() {
int windowCount = 10;
FakeTimeline timeline = new FakeTimeline(windowCount);
Timeline restoredTimeline = Timeline.CREATOR.fromBundle(timeline.toBundle());
assertThat(restoredTimeline.getLastWindowIndex(/* shuffleModeEnabled= */ false))
.isEqualTo(timeline.getLastWindowIndex(/* shuffleModeEnabled= */ false));
assertThat(restoredTimeline.getLastWindowIndex(/* shuffleModeEnabled= */ true))
.isEqualTo(timeline.getLastWindowIndex(/* shuffleModeEnabled= */ true));
assertThat(restoredTimeline.getFirstWindowIndex(/* shuffleModeEnabled= */ false))
.isEqualTo(timeline.getFirstWindowIndex(/* shuffleModeEnabled= */ false));
assertThat(restoredTimeline.getFirstWindowIndex(/* shuffleModeEnabled= */ true))
.isEqualTo(timeline.getFirstWindowIndex(/* shuffleModeEnabled= */ true));
TimelineAsserts.assertEqualNextWindowIndices(
timeline, restoredTimeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ false);
TimelineAsserts.assertEqualNextWindowIndices(
timeline, restoredTimeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ true);
TimelineAsserts.assertEqualNextWindowIndices(
timeline, restoredTimeline, Player.REPEAT_MODE_ONE, /* shuffleModeEnabled= */ false);
TimelineAsserts.assertEqualNextWindowIndices(
timeline, restoredTimeline, Player.REPEAT_MODE_ONE, /* shuffleModeEnabled= */ true);
TimelineAsserts.assertEqualNextWindowIndices(
timeline, restoredTimeline, Player.REPEAT_MODE_ALL, /* shuffleModeEnabled= */ false);
TimelineAsserts.assertEqualNextWindowIndices(
timeline, restoredTimeline, Player.REPEAT_MODE_ALL, /* shuffleModeEnabled= */ true);
TimelineAsserts.assertEqualPreviousWindowIndices(
timeline, restoredTimeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ false);
TimelineAsserts.assertEqualPreviousWindowIndices(
timeline, restoredTimeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ true);
TimelineAsserts.assertEqualPreviousWindowIndices(
timeline, restoredTimeline, Player.REPEAT_MODE_ONE, /* shuffleModeEnabled= */ false);
TimelineAsserts.assertEqualPreviousWindowIndices(
timeline, restoredTimeline, Player.REPEAT_MODE_ONE, /* shuffleModeEnabled= */ true);
TimelineAsserts.assertEqualPreviousWindowIndices(
timeline, restoredTimeline, Player.REPEAT_MODE_ALL, /* shuffleModeEnabled= */ false);
TimelineAsserts.assertEqualPreviousWindowIndices(
timeline, restoredTimeline, Player.REPEAT_MODE_ALL, /* shuffleModeEnabled= */ true);
}
@Test
public void roundtripViaBundle_ofWindow_yieldsEqualInstanceExceptUidAndManifest() { public void roundtripViaBundle_ofWindow_yieldsEqualInstanceExceptUidAndManifest() {
Timeline.Window window = new Timeline.Window(); Timeline.Window window = new Timeline.Window();
window.uid = new Object(); window.uid = new Object();
......
...@@ -1707,40 +1707,6 @@ public final class ExoPlayerTest { ...@@ -1707,40 +1707,6 @@ public final class ExoPlayerTest {
} }
@Test @Test
public void
testInvalidSeekPositionAfterSourceInfoRefreshWithShuffleModeEnabledUsesCorrectFirstPeriod()
throws Exception {
FakeMediaSource mediaSource = new FakeMediaSource(new FakeTimeline(/* windowCount= */ 2));
AtomicInteger windowIndexAfterUpdate = new AtomicInteger();
ActionSchedule actionSchedule =
new ActionSchedule.Builder(TAG)
.setShuffleOrder(new FakeShuffleOrder(/* length= */ 0))
.setShuffleModeEnabled(true)
.waitForPlaybackState(Player.STATE_BUFFERING)
// Seeking to an invalid position will end playback.
.seek(
/* windowIndex= */ 100, /* positionMs= */ 0, /* catchIllegalSeekException= */ true)
.waitForPlaybackState(Player.STATE_ENDED)
.executeRunnable(
new PlayerRunnable() {
@Override
public void run(SimpleExoPlayer player) {
windowIndexAfterUpdate.set(player.getCurrentWindowIndex());
}
})
.build();
new ExoPlayerTestRunner.Builder(context)
.setMediaSources(mediaSource)
.setActionSchedule(actionSchedule)
.build()
.start()
.blockUntilActionScheduleFinished(TIMEOUT_MS)
.blockUntilEnded(TIMEOUT_MS);
assertThat(windowIndexAfterUpdate.get()).isEqualTo(1);
}
@Test
public void restartAfterEmptyTimelineWithShuffleModeEnabledUsesCorrectFirstPeriod() public void restartAfterEmptyTimelineWithShuffleModeEnabledUsesCorrectFirstPeriod()
throws Exception { throws Exception {
ConcatenatingMediaSource concatenatingMediaSource = ConcatenatingMediaSource concatenatingMediaSource =
......
...@@ -49,7 +49,7 @@ public class LoopingMediaSourceTest { ...@@ -49,7 +49,7 @@ public class LoopingMediaSourceTest {
Timeline timeline = getLoopingTimeline(multiWindowTimeline, 1); Timeline timeline = getLoopingTimeline(multiWindowTimeline, 1);
TimelineAsserts.assertWindowTags(timeline, 111, 222, 333); TimelineAsserts.assertWindowTags(timeline, 111, 222, 333);
TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1); TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1);
for (boolean shuffled : new boolean[] {false, true}) { boolean shuffled = false;
TimelineAsserts.assertPreviousWindowIndices( TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_OFF, shuffled, C.INDEX_UNSET, 0, 1); timeline, Player.REPEAT_MODE_OFF, shuffled, C.INDEX_UNSET, 0, 1);
TimelineAsserts.assertPreviousWindowIndices( TimelineAsserts.assertPreviousWindowIndices(
...@@ -60,7 +60,17 @@ public class LoopingMediaSourceTest { ...@@ -60,7 +60,17 @@ public class LoopingMediaSourceTest {
timeline, Player.REPEAT_MODE_OFF, shuffled, 1, 2, C.INDEX_UNSET); timeline, Player.REPEAT_MODE_OFF, shuffled, 1, 2, C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2); TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, shuffled, 1, 2, 0); TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, shuffled, 1, 2, 0);
} shuffled = true; // FakeTimeline has FakeShuffleOrder which returns a reverse order.
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_OFF, shuffled, 1, 2, C.INDEX_UNSET);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ALL, shuffled, 1, 2, 0);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_OFF, shuffled, C.INDEX_UNSET, 0, 1);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, shuffled, 2, 0, 1);
} }
@Test @Test
...@@ -68,9 +78,9 @@ public class LoopingMediaSourceTest { ...@@ -68,9 +78,9 @@ public class LoopingMediaSourceTest {
Timeline timeline = getLoopingTimeline(multiWindowTimeline, 3); Timeline timeline = getLoopingTimeline(multiWindowTimeline, 3);
TimelineAsserts.assertWindowTags(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); TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1, 1, 1, 1, 1, 1, 1);
for (boolean shuffled : new boolean[] {false, true}) { boolean shuffled = false;
TimelineAsserts.assertPreviousWindowIndices( TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_OFF, shuffled, C.INDEX_UNSET, 0, 1, 2, 3, 4, 5, 6, 7, 8); timeline, Player.REPEAT_MODE_OFF, shuffled, C.INDEX_UNSET, 0, 1, 2, 3, 4, 5, 6, 7);
TimelineAsserts.assertPreviousWindowIndices( TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2, 3, 4, 5, 6, 7, 8); timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2, 3, 4, 5, 6, 7, 8);
TimelineAsserts.assertPreviousWindowIndices( TimelineAsserts.assertPreviousWindowIndices(
...@@ -81,7 +91,19 @@ public class LoopingMediaSourceTest { ...@@ -81,7 +91,19 @@ public class LoopingMediaSourceTest {
timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2, 3, 4, 5, 6, 7, 8); timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2, 3, 4, 5, 6, 7, 8);
TimelineAsserts.assertNextWindowIndices( TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_ALL, shuffled, 1, 2, 3, 4, 5, 6, 7, 8, 0); timeline, Player.REPEAT_MODE_ALL, shuffled, 1, 2, 3, 4, 5, 6, 7, 8, 0);
} shuffled = true; // FakeTimeline has FakeShuffleOrder which returns a reverse order.
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_OFF, shuffled, 1, 2, C.INDEX_UNSET, 4, 5, 0, 7, 8, 3);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2, 3, 4, 5, 6, 7, 8);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ALL, shuffled, 1, 2, 6, 4, 5, 0, 7, 8, 3);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_OFF, shuffled, 5, 0, 1, 8, 3, 4, C.INDEX_UNSET, 6, 7);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2, 3, 4, 5, 6, 7, 8);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_ALL, shuffled, 5, 0, 1, 8, 3, 4, 2, 6, 7);
} }
@Test @Test
...@@ -89,7 +111,7 @@ public class LoopingMediaSourceTest { ...@@ -89,7 +111,7 @@ public class LoopingMediaSourceTest {
Timeline timeline = getLoopingTimeline(multiWindowTimeline, Integer.MAX_VALUE); Timeline timeline = getLoopingTimeline(multiWindowTimeline, Integer.MAX_VALUE);
TimelineAsserts.assertWindowTags(timeline, 111, 222, 333); TimelineAsserts.assertWindowTags(timeline, 111, 222, 333);
TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1); TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1);
for (boolean shuffled : new boolean[] {false, true}) { boolean shuffled = false;
TimelineAsserts.assertPreviousWindowIndices( TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_OFF, shuffled, 2, 0, 1); timeline, Player.REPEAT_MODE_OFF, shuffled, 2, 0, 1);
TimelineAsserts.assertPreviousWindowIndices( TimelineAsserts.assertPreviousWindowIndices(
...@@ -99,7 +121,16 @@ public class LoopingMediaSourceTest { ...@@ -99,7 +121,16 @@ public class LoopingMediaSourceTest {
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, shuffled, 1, 2, 0); TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, shuffled, 1, 2, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2); TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, shuffled, 1, 2, 0); TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, shuffled, 1, 2, 0);
} shuffled = true; // FakeTimeline has FakeShuffleOrder which returns a reverse order.
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_OFF, shuffled, 1, 2, 0);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ALL, shuffled, 1, 2, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, shuffled, 2, 0, 1);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, shuffled, 2, 0, 1);
} }
@Test @Test
......
...@@ -21,6 +21,7 @@ import android.net.Uri; ...@@ -21,6 +21,7 @@ import android.net.Uri;
import android.util.Pair; import android.util.Pair;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ads.AdPlaybackState; import com.google.android.exoplayer2.source.ads.AdPlaybackState;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
...@@ -235,6 +236,7 @@ public final class FakeTimeline extends Timeline { ...@@ -235,6 +236,7 @@ public final class FakeTimeline extends Timeline {
private final TimelineWindowDefinition[] windowDefinitions; private final TimelineWindowDefinition[] windowDefinitions;
private final Object[] manifests; private final Object[] manifests;
private final int[] periodOffsets; private final int[] periodOffsets;
private final FakeShuffleOrder fakeShuffleOrder;
/** /**
* Returns an ad playback state with the specified number of ads in each of the specified ad * Returns an ad playback state with the specified number of ads in each of the specified ad
...@@ -308,6 +310,7 @@ public final class FakeTimeline extends Timeline { ...@@ -308,6 +310,7 @@ public final class FakeTimeline extends Timeline {
for (int i = 0; i < windowDefinitions.length; i++) { for (int i = 0; i < windowDefinitions.length; i++) {
periodOffsets[i + 1] = periodOffsets[i] + windowDefinitions[i].periodCount; periodOffsets[i + 1] = periodOffsets[i] + windowDefinitions[i].periodCount;
} }
fakeShuffleOrder = new FakeShuffleOrder(windowDefinitions.length);
} }
@Override @Override
...@@ -316,6 +319,48 @@ public final class FakeTimeline extends Timeline { ...@@ -316,6 +319,48 @@ public final class FakeTimeline extends Timeline {
} }
@Override @Override
public int getNextWindowIndex(
int windowIndex, @Player.RepeatMode int repeatMode, boolean shuffleModeEnabled) {
if (repeatMode == Player.REPEAT_MODE_ONE) {
return windowIndex;
}
if (windowIndex == getLastWindowIndex(shuffleModeEnabled)) {
return repeatMode == Player.REPEAT_MODE_ALL
? getFirstWindowIndex(shuffleModeEnabled)
: C.INDEX_UNSET;
}
return shuffleModeEnabled ? fakeShuffleOrder.getNextIndex(windowIndex) : windowIndex + 1;
}
@Override
public int getPreviousWindowIndex(
int windowIndex, @Player.RepeatMode int repeatMode, boolean shuffleModeEnabled) {
if (repeatMode == Player.REPEAT_MODE_ONE) {
return windowIndex;
}
if (windowIndex == getFirstWindowIndex(shuffleModeEnabled)) {
return repeatMode == Player.REPEAT_MODE_ALL
? getLastWindowIndex(shuffleModeEnabled)
: C.INDEX_UNSET;
}
return shuffleModeEnabled ? fakeShuffleOrder.getPreviousIndex(windowIndex) : windowIndex - 1;
}
@Override
public int getLastWindowIndex(boolean shuffleModeEnabled) {
return shuffleModeEnabled
? fakeShuffleOrder.getLastIndex()
: super.getLastWindowIndex(/* shuffleModeEnabled= */ false);
}
@Override
public int getFirstWindowIndex(boolean shuffleModeEnabled) {
return shuffleModeEnabled
? fakeShuffleOrder.getFirstIndex()
: super.getFirstWindowIndex(/* shuffleModeEnabled= */ false);
}
@Override
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) { public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex];
window.set( window.set(
......
...@@ -105,6 +105,38 @@ public final class TimelineAsserts { ...@@ -105,6 +105,38 @@ public final class TimelineAsserts {
} }
/** /**
* Asserts that previous window indices for each window of the actual timeline are equal to the
* indices of the expected timeline depending on the repeat mode and the shuffle mode.
*/
public static void assertEqualPreviousWindowIndices(
Timeline expectedTimeline,
Timeline actualTimeline,
@Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled) {
for (int windowIndex = 0; windowIndex < actualTimeline.getWindowCount(); windowIndex++) {
assertThat(actualTimeline.getPreviousWindowIndex(windowIndex, repeatMode, shuffleModeEnabled))
.isEqualTo(
expectedTimeline.getPreviousWindowIndex(windowIndex, repeatMode, shuffleModeEnabled));
}
}
/**
* Asserts that next window indices for each window of the actual timeline are equal to the
* indices of the expected timeline depending on the repeat mode and the shuffle mode.
*/
public static void assertEqualNextWindowIndices(
Timeline expectedTimeline,
Timeline actualTimeline,
@Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled) {
for (int windowIndex = 0; windowIndex < actualTimeline.getWindowCount(); windowIndex++) {
assertThat(actualTimeline.getNextWindowIndex(windowIndex, repeatMode, shuffleModeEnabled))
.isEqualTo(
expectedTimeline.getNextWindowIndex(windowIndex, repeatMode, shuffleModeEnabled));
}
}
/**
* Asserts that the durations of the periods in the {@link Timeline} and the durations in the * Asserts that the durations of the periods in the {@link Timeline} and the durations in the
* given sequence are equal. * given sequence are equal.
*/ */
......
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