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:
parent
e15633e906
commit
f7eba77ee0
@ -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++) {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user