mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Preserve window indices of Timeline when bundling
PiperOrigin-RevId: 364324490
This commit is contained in:
parent
10a8c603f7
commit
7b4b5cbf5a
6 changed files with 282 additions and 80 deletions
|
|
@ -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 {
|
||||||
|
|
||||||
@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 {
|
||||||
public final Bundle toBundle() {
|
public final Bundle toBundle() {
|
||||||
List<Bundle> windowBundles = new ArrayList<>();
|
List<Bundle> windowBundles = new ArrayList<>();
|
||||||
int windowCount = getWindowCount();
|
int windowCount = getWindowCount();
|
||||||
|
Window window = new Window();
|
||||||
for (int i = 0; i < windowCount; i++) {
|
for (int i = 0; i < windowCount; i++) {
|
||||||
Window window = new Window();
|
windowBundles.add(getWindow(i, window, /* defaultPositionProjectionUs= */ 0).toBundle());
|
||||||
getWindow(i, window, /* defaultPositionProjectionUs= */ 0);
|
|
||||||
windowBundles.add(window.toBundle());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Bundle> periodBundles = new ArrayList<>();
|
List<Bundle> periodBundles = new ArrayList<>();
|
||||||
int periodCount = getPeriodCount();
|
int periodCount = getPeriodCount();
|
||||||
|
Period period = new Period();
|
||||||
for (int i = 0; i < periodCount; i++) {
|
for (int i = 0; i < periodCount; i++) {
|
||||||
Period period = new Period();
|
periodBundles.add(getPeriod(i, period, /* setIds= */ false).toBundle());
|
||||||
getPeriod(i, period, /* setIds= */ false);
|
}
|
||||||
periodBundles.add(period.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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -1372,6 +1409,56 @@ public abstract class Timeline implements Bundleable {
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Override
|
||||||
public int getPeriodCount() {
|
public int getPeriodCount() {
|
||||||
return periods.size();
|
return periods.size();
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,47 @@ public class TimelineTest {
|
||||||
/* expectedTimeline= */ timeline, /* actualTimeline= */ restoredTimeline);
|
/* expectedTimeline= */ timeline, /* actualTimeline= */ restoredTimeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Test
|
||||||
public void roundtripViaBundle_ofWindow_yieldsEqualInstanceExceptUidAndManifest() {
|
public void roundtripViaBundle_ofWindow_yieldsEqualInstanceExceptUidAndManifest() {
|
||||||
Timeline.Window window = new Timeline.Window();
|
Timeline.Window window = new Timeline.Window();
|
||||||
|
|
|
||||||
|
|
@ -1706,40 +1706,6 @@ public final class ExoPlayerTest {
|
||||||
assertThat(positionWhenFullyReadyAfterReprepare).isEqualTo(50);
|
assertThat(positionWhenFullyReadyAfterReprepare).isEqualTo(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
@Test
|
||||||
public void restartAfterEmptyTimelineWithShuffleModeEnabledUsesCorrectFirstPeriod()
|
public void restartAfterEmptyTimelineWithShuffleModeEnabledUsesCorrectFirstPeriod()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
|
||||||
|
|
@ -49,18 +49,28 @@ 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(
|
||||||
timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2);
|
timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2);
|
||||||
TimelineAsserts.assertPreviousWindowIndices(
|
TimelineAsserts.assertPreviousWindowIndices(
|
||||||
timeline, Player.REPEAT_MODE_ALL, shuffled, 2, 0, 1);
|
timeline, Player.REPEAT_MODE_ALL, shuffled, 2, 0, 1);
|
||||||
TimelineAsserts.assertNextWindowIndices(
|
TimelineAsserts.assertNextWindowIndices(
|
||||||
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,20 +78,32 @@ 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(
|
||||||
timeline, Player.REPEAT_MODE_ALL, shuffled, 8, 0, 1, 2, 3, 4, 5, 6, 7);
|
timeline, Player.REPEAT_MODE_ALL, shuffled, 8, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||||
TimelineAsserts.assertNextWindowIndices(
|
TimelineAsserts.assertNextWindowIndices(
|
||||||
timeline, Player.REPEAT_MODE_OFF, shuffled, 1, 2, 3, 4, 5, 6, 7, 8, C.INDEX_UNSET);
|
timeline, Player.REPEAT_MODE_OFF, shuffled, 1, 2, 3, 4, 5, 6, 7, 8, C.INDEX_UNSET);
|
||||||
TimelineAsserts.assertNextWindowIndices(
|
TimelineAsserts.assertNextWindowIndices(
|
||||||
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,17 +111,26 @@ 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(
|
||||||
timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2);
|
timeline, Player.REPEAT_MODE_ONE, shuffled, 0, 1, 2);
|
||||||
TimelineAsserts.assertPreviousWindowIndices(
|
TimelineAsserts.assertPreviousWindowIndices(
|
||||||
timeline, Player.REPEAT_MODE_ALL, shuffled, 2, 0, 1);
|
timeline, Player.REPEAT_MODE_ALL, shuffled, 2, 0, 1);
|
||||||
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;
|
||||||
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 {
|
||||||
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 {
|
||||||
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
|
||||||
|
|
@ -315,6 +318,48 @@ public final class FakeTimeline extends Timeline {
|
||||||
return windowDefinitions.length;
|
return windowDefinitions.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@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];
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,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.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue