Commit e15633e9 by tonihei Committed by Oliver Woodman

Add shuffle support to ConcatenatingMediaSource.

The media source is initialized with a DefaultShuffleOrder which can be changed at
any time. This shuffle order is then used within the corresponding timeline.

The isRepeatOneAtomic flag is extended to also suppress shuffling (now called
isAtomic only).

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=166197184
parent 8115e114
......@@ -19,6 +19,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.testutil.FakeMediaSource;
import com.google.android.exoplayer2.testutil.FakeShuffleOrder;
import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.testutil.TestUtil;
......@@ -34,26 +35,30 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
Timeline timeline = getConcatenatedTimeline(false, createFakeTimeline(3, 111));
TimelineAsserts.assertWindowIds(timeline, 111);
TimelineAsserts.assertPeriodCounts(timeline, 3);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_OFF, false,
C.INDEX_UNSET);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ONE, false, 0);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, false,
C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, false, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 0);
for (boolean shuffled : new boolean[] { false, true }) {
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_OFF, shuffled,
C.INDEX_UNSET);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ONE, shuffled, 0);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ALL, shuffled, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, shuffled,
C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, shuffled, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, shuffled, 0);
}
timeline = getConcatenatedTimeline(true, createFakeTimeline(3, 111));
TimelineAsserts.assertWindowIds(timeline, 111);
TimelineAsserts.assertPeriodCounts(timeline, 3);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_OFF, false,
C.INDEX_UNSET);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ONE, false, 0);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, false,
C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, false, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 0);
for (boolean shuffled : new boolean[] { false, true }) {
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_OFF, shuffled,
C.INDEX_UNSET);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ONE, shuffled, 0);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ALL, shuffled, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, shuffled,
C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, shuffled, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, shuffled, 0);
}
}
public void testMultipleMediaSources() {
......@@ -70,18 +75,36 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
1, 2, C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, false, 0, 1, 2);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 1, 2, 0);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_OFF, true,
1, 2, C.INDEX_UNSET);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ONE, true, 0, 1, 2);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ALL, true, 1, 2, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, true,
C.INDEX_UNSET, 0, 1);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, true, 0, 1, 2);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, true, 2, 0, 1);
assertEquals(0, timeline.getFirstWindowIndex(false));
assertEquals(2, timeline.getLastWindowIndex(false));
assertEquals(2, timeline.getFirstWindowIndex(true));
assertEquals(0, timeline.getLastWindowIndex(true));
timeline = getConcatenatedTimeline(true, timelines);
TimelineAsserts.assertWindowIds(timeline, 111, 222, 333);
TimelineAsserts.assertPeriodCounts(timeline, 3, 1, 3);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_OFF, false,
C.INDEX_UNSET, 0, 1);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ONE, false, 2, 0, 1);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 2, 0, 1);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, false,
1, 2, C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, false, 1, 2, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 1, 2, 0);
for (boolean shuffled : new boolean[] { false, true }) {
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_OFF, shuffled,
C.INDEX_UNSET, 0, 1);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ONE, shuffled,
2, 0, 1);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ALL, shuffled,
2, 0, 1);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, shuffled,
1, 2, C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, shuffled, 1, 2, 0);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, shuffled, 1, 2, 0);
assertEquals(0, timeline.getFirstWindowIndex(shuffled));
assertEquals(2, timeline.getLastWindowIndex(shuffled));
}
}
public void testNestedMediaSources() {
......@@ -100,6 +123,16 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
1, 2, 3, C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, false, 0, 1, 3, 2);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 1, 2, 3, 0);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_OFF, true,
1, 3, C.INDEX_UNSET, 2);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ONE, true,
0, 1, 3, 2);
TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ALL, true,
1, 3, 0, 2);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, true,
C.INDEX_UNSET, 0, 3, 1);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ONE, true, 0, 1, 3, 2);
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, true, 2, 0, 3, 1);
}
/**
......@@ -112,8 +145,9 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
for (int i = 0; i < timelines.length; i++) {
mediaSources[i] = new FakeMediaSource(timelines[i], null);
}
return TestUtil.extractTimelineFromMediaSource(
new ConcatenatingMediaSource(isRepeatOneAtomic, mediaSources));
ConcatenatingMediaSource mediaSource = new ConcatenatingMediaSource(isRepeatOneAtomic,
new FakeShuffleOrder(mediaSources.length), mediaSources);
return TestUtil.extractTimelineFromMediaSource(mediaSource);
}
private static FakeTimeline createFakeTimeline(int periodCount, int windowId) {
......
......@@ -19,7 +19,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ShuffleOrder.UnshuffledShuffleOrder;
import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
......@@ -39,10 +39,11 @@ public final class ConcatenatingMediaSource implements MediaSource {
private final Object[] manifests;
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private final boolean[] duplicateFlags;
private final boolean isRepeatOneAtomic;
private final boolean isAtomic;
private Listener listener;
private ConcatenatedTimeline timeline;
private ShuffleOrder shuffleOrder;
/**
* @param mediaSources The {@link MediaSource}s to concatenate. It is valid for the same
......@@ -53,17 +54,33 @@ public final class ConcatenatingMediaSource implements MediaSource {
}
/**
* @param isRepeatOneAtomic Whether the concatenated media source shall be treated as atomic
* (i.e., repeated in its entirety) when repeat mode is set to {@code Player.REPEAT_MODE_ONE}.
* @param isAtomic Whether the concatenated media source shall be treated as atomic,
* i.e., treated as a single item for repeating and shuffling.
* @param mediaSources The {@link MediaSource}s to concatenate. It is valid for the same
* {@link MediaSource} instance to be present more than once in the array.
*/
public ConcatenatingMediaSource(boolean isRepeatOneAtomic, MediaSource... mediaSources) {
public ConcatenatingMediaSource(boolean isAtomic, MediaSource... mediaSources) {
this(isAtomic, new DefaultShuffleOrder(mediaSources.length), mediaSources);
}
/**
* @param isAtomic Whether the concatenated media source shall be treated as atomic,
* i.e., treated as a single item for repeating and shuffling.
* @param shuffleOrder The {@link ShuffleOrder} to use when shuffling the child media sources. The
* number of elements in the shuffle order must match the number of concatenated
* {@link MediaSource}s.
* @param mediaSources The {@link MediaSource}s to concatenate. It is valid for the same
* {@link MediaSource} instance to be present more than once in the array.
*/
public ConcatenatingMediaSource(boolean isAtomic, ShuffleOrder shuffleOrder,
MediaSource... mediaSources) {
for (MediaSource mediaSource : mediaSources) {
Assertions.checkNotNull(mediaSource);
}
Assertions.checkArgument(shuffleOrder.getLength() == mediaSources.length);
this.mediaSources = mediaSources;
this.isRepeatOneAtomic = isRepeatOneAtomic;
this.isAtomic = isAtomic;
this.shuffleOrder = shuffleOrder;
timelines = new Timeline[mediaSources.length];
manifests = new Object[mediaSources.length];
sourceIndexByMediaPeriod = new HashMap<>();
......@@ -139,7 +156,7 @@ public final class ConcatenatingMediaSource implements MediaSource {
return;
}
}
timeline = new ConcatenatedTimeline(timelines.clone(), isRepeatOneAtomic);
timeline = new ConcatenatedTimeline(timelines.clone(), isAtomic, shuffleOrder);
listener.onSourceInfoRefreshed(timeline, manifests.clone());
}
......@@ -165,10 +182,10 @@ public final class ConcatenatingMediaSource implements MediaSource {
private final Timeline[] timelines;
private final int[] sourcePeriodOffsets;
private final int[] sourceWindowOffsets;
private final boolean isRepeatOneAtomic;
private final boolean isAtomic;
public ConcatenatedTimeline(Timeline[] timelines, boolean isRepeatOneAtomic) {
super(new UnshuffledShuffleOrder(timelines.length));
public ConcatenatedTimeline(Timeline[] timelines, boolean isAtomic, ShuffleOrder shuffleOrder) {
super(shuffleOrder);
int[] sourcePeriodOffsets = new int[timelines.length];
int[] sourceWindowOffsets = new int[timelines.length];
long periodCount = 0;
......@@ -185,7 +202,7 @@ public final class ConcatenatingMediaSource implements MediaSource {
this.timelines = timelines;
this.sourcePeriodOffsets = sourcePeriodOffsets;
this.sourceWindowOffsets = sourceWindowOffsets;
this.isRepeatOneAtomic = isRepeatOneAtomic;
this.isAtomic = isAtomic;
}
@Override
......@@ -201,19 +218,29 @@ public final class ConcatenatingMediaSource implements MediaSource {
@Override
public int getNextWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled) {
if (isRepeatOneAtomic && repeatMode == Player.REPEAT_MODE_ONE) {
if (isAtomic && repeatMode == Player.REPEAT_MODE_ONE) {
repeatMode = Player.REPEAT_MODE_ALL;
}
return super.getNextWindowIndex(windowIndex, repeatMode, shuffleModeEnabled);
return super.getNextWindowIndex(windowIndex, repeatMode, !isAtomic && shuffleModeEnabled);
}
@Override
public int getPreviousWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled) {
if (isRepeatOneAtomic && repeatMode == Player.REPEAT_MODE_ONE) {
if (isAtomic && repeatMode == Player.REPEAT_MODE_ONE) {
repeatMode = Player.REPEAT_MODE_ALL;
}
return super.getPreviousWindowIndex(windowIndex, repeatMode, shuffleModeEnabled);
return super.getPreviousWindowIndex(windowIndex, repeatMode, !isAtomic && shuffleModeEnabled);
}
@Override
public int getLastWindowIndex(boolean shuffleModeEnabled) {
return super.getLastWindowIndex(!isAtomic && shuffleModeEnabled);
}
@Override
public int getFirstWindowIndex(boolean shuffleModeEnabled) {
return super.getFirstWindowIndex(!isAtomic && shuffleModeEnabled);
}
@Override
......@@ -257,3 +284,4 @@ public final class ConcatenatingMediaSource implements MediaSource {
}
}
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