Add shuffle support to dynamic concatenating media source.

The media source is initialized with a DefaultShuffleOrder which can be changed at
any time. Whenever the list of media source is changed, the shuffle order is adapted
accordingly (either on the app thread if the player is not prepared yet, or on the
player thread). The shuffle order is then used to construct the timeline.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=166198488
This commit is contained in:
tonihei 2017-08-23 07:30:29 -07:00 committed by Oliver Woodman
parent e15633e906
commit f7eba77ee0
2 changed files with 52 additions and 7 deletions

View File

@ -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++) {

View File

@ -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<Integer, MediaSource> messageData = (Pair<Integer, MediaSource>) message;
shuffleOrder = shuffleOrder.cloneAndInsert(messageData.first, 1);
addMediaSourceInternal(messageData.first, messageData.second);
break;
}
case MSG_ADD_MULTIPLE: {
Pair<Integer, Collection<MediaSource>> messageData =
(Pair<Integer, Collection<MediaSource>>) 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<Integer, Integer> messageData = (Pair<Integer, Integer>) 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<MediaSourceHolder> 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
}
}