diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSourceTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSourceTest.java index 9cdb461d7b..0e07e99978 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSourceTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSourceTest.java @@ -27,6 +27,7 @@ import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.MediaSource.Listener; 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.TimelineAsserts; @@ -49,7 +50,8 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { public void testPlaylistChangesAfterPreparation() throws InterruptedException { timeline = null; FakeMediaSource[] childSources = createMediaSources(7); - DynamicConcatenatingMediaSource mediaSource = new DynamicConcatenatingMediaSource(); + DynamicConcatenatingMediaSource mediaSource = new DynamicConcatenatingMediaSource( + new FakeShuffleOrder(0)); prepareAndListenToTimelineUpdates(mediaSource); waitForTimelineUpdate(); TimelineAsserts.assertEmpty(timeline); @@ -128,6 +130,18 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { C.INDEX_UNSET, 0, 1); TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ONE, false, 0, 1, 2); TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 2, 0, 1); + assertEquals(0, timeline.getFirstWindowIndex(false)); + assertEquals(timeline.getWindowCount() - 1, timeline.getLastWindowIndex(false)); + 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); + 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); + assertEquals(timeline.getWindowCount() - 1, timeline.getFirstWindowIndex(true)); + assertEquals(0, timeline.getLastWindowIndex(true)); // Remove at front of queue. mediaSource.removeMediaSource(0); @@ -153,7 +167,8 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { public void testPlaylistChangesBeforePreparation() throws InterruptedException { timeline = null; FakeMediaSource[] childSources = createMediaSources(4); - DynamicConcatenatingMediaSource mediaSource = new DynamicConcatenatingMediaSource(); + DynamicConcatenatingMediaSource mediaSource = new DynamicConcatenatingMediaSource( + new FakeShuffleOrder(0)); mediaSource.addMediaSource(childSources[0]); mediaSource.addMediaSource(childSources[1]); mediaSource.addMediaSource(0, childSources[2]); @@ -168,6 +183,14 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { assertNotNull(timeline); TimelineAsserts.assertPeriodCounts(timeline, 3, 4, 2); TimelineAsserts.assertWindowIds(timeline, 333, 444, 222); + TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, false, + 1, 2, C.INDEX_UNSET); + TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_OFF, false, + C.INDEX_UNSET, 0, 1); + TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_OFF, true, + C.INDEX_UNSET, 0, 1); + TimelineAsserts.assertPreviousWindowIndices(timeline, Player.REPEAT_MODE_OFF, true, + 1, 2, C.INDEX_UNSET); mediaSource.releaseSource(); for (int i = 1; i < 4; i++) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java index 02d4bad2bf..3d0df7dcb3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java @@ -23,7 +23,7 @@ import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer.ExoPlayerComponent; import com.google.android.exoplayer2.ExoPlayer.ExoPlayerMessage; 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.trackselection.TrackSelection; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.util.Assertions; @@ -58,11 +58,26 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, ExoPl private ExoPlayer player; private Listener listener; + private ShuffleOrder shuffleOrder; private boolean preventListenerNotification; private int windowCount; private int periodCount; + /** + * Creates a new dynamic concatenating media source. + */ public DynamicConcatenatingMediaSource() { + this(new DefaultShuffleOrder(0)); + } + + /** + * Creates a new dynamic concatenating media source with a custom shuffle order. + * + * @param shuffleOrder The {@link ShuffleOrder} to use when shuffling the child media sources. + * This shuffle order must be empty. + */ + public DynamicConcatenatingMediaSource(ShuffleOrder shuffleOrder) { + this.shuffleOrder = shuffleOrder; this.mediaSourceByMediaPeriod = new IdentityHashMap<>(); this.mediaSourcesPublic = new ArrayList<>(); this.mediaSourceHolders = new ArrayList<>(); @@ -180,6 +195,7 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, ExoPl this.player = player; this.listener = listener; preventListenerNotification = true; + shuffleOrder = shuffleOrder.cloneAndInsert(0, mediaSourcesPublic.size()); addMediaSourcesInternal(0, mediaSourcesPublic); preventListenerNotification = false; maybeNotifyListener(); @@ -234,21 +250,26 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, ExoPl switch (messageType) { case MSG_ADD: { Pair messageData = (Pair) message; + shuffleOrder = shuffleOrder.cloneAndInsert(messageData.first, 1); addMediaSourceInternal(messageData.first, messageData.second); break; } case MSG_ADD_MULTIPLE: { Pair> messageData = (Pair>) message; + shuffleOrder = shuffleOrder.cloneAndInsert(messageData.first, messageData.second.size()); addMediaSourcesInternal(messageData.first, messageData.second); break; } case MSG_REMOVE: { + shuffleOrder = shuffleOrder.cloneAndRemove((Integer) message); removeMediaSourceInternal((Integer) message); break; } case MSG_MOVE: { Pair messageData = (Pair) message; + shuffleOrder = shuffleOrder.cloneAndRemove(messageData.first); + shuffleOrder = shuffleOrder.cloneAndInsert(messageData.second, 1); moveMediaSourceInternal(messageData.first, messageData.second); break; } @@ -262,8 +283,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, ExoPl private void maybeNotifyListener() { if (!preventListenerNotification) { - listener.onSourceInfoRefreshed( - new ConcatenatedTimeline(mediaSourceHolders, windowCount, periodCount), null); + listener.onSourceInfoRefreshed(new ConcatenatedTimeline(mediaSourceHolders, windowCount, + periodCount, shuffleOrder), null); } } @@ -397,8 +418,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, ExoPl private final SparseIntArray childIndexByUid; public ConcatenatedTimeline(Collection mediaSourceHolders, int windowCount, - int periodCount) { - super(new UnshuffledShuffleOrder(mediaSourceHolders.size())); + int periodCount, ShuffleOrder shuffleOrder) { + super(shuffleOrder); this.windowCount = windowCount; this.periodCount = periodCount; int childCount = mediaSourceHolders.size(); @@ -638,3 +659,4 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, ExoPl } } +