Commit 784e8a63 by tonihei Committed by Oliver Woodman

Support isAtomic flag in DynamicConcatenatingMediaSource.

This feature is supported in the ConcatenatingMediaSource and is easily copied to this
media source. Also adding tests to check whether the atomic property works in normal
concatenation and in also in nested use.

Also fixes a bug where timeline methods of the DeferredTimeline were not correctly
forwarded.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=184526881
parent fe984770
...@@ -47,7 +47,8 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -47,7 +47,8 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
@Override @Override
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
mediaSource = new DynamicConcatenatingMediaSource(new FakeShuffleOrder(0)); mediaSource =
new DynamicConcatenatingMediaSource(/* isAtomic= */ false, new FakeShuffleOrder(0));
testRunner = new MediaSourceTestRunner(mediaSource, null); testRunner = new MediaSourceTestRunner(mediaSource, null);
} }
...@@ -627,6 +628,92 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase { ...@@ -627,6 +628,92 @@ public final class DynamicConcatenatingMediaSourceTest extends TestCase {
mediaSourceWithAds.assertMediaPeriodCreated(new MediaPeriodId(1, 0, 0)); mediaSourceWithAds.assertMediaPeriodCreated(new MediaPeriodId(1, 0, 0));
} }
public void testAtomicTimelineWindowOrder() throws IOException {
// Release default test runner with non-atomic media source and replace with new test runner.
testRunner.release();
DynamicConcatenatingMediaSource mediaSource =
new DynamicConcatenatingMediaSource(/* isAtomic= */ true, new FakeShuffleOrder(0));
testRunner = new MediaSourceTestRunner(mediaSource, null);
mediaSource.addMediaSources(Arrays.<MediaSource>asList(createMediaSources(3)));
Timeline timeline = testRunner.prepareSource();
TimelineAsserts.assertWindowIds(timeline, 111, 222, 333);
TimelineAsserts.assertPeriodCounts(timeline, 1, 2, 3);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ false, C.INDEX_UNSET, 0, 1);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ true, C.INDEX_UNSET, 0, 1);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ONE, /* shuffleModeEnabled= */ false, 2, 0, 1);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ONE, /* shuffleModeEnabled= */ true, 2, 0, 1);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ALL, /* shuffleModeEnabled= */ false, 2, 0, 1);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ALL, /* shuffleModeEnabled= */ true, 2, 0, 1);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ false, 1, 2, C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ true, 1, 2, C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_ONE, /* shuffleModeEnabled= */ false, 1, 2, 0);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_ONE, /* shuffleModeEnabled= */ true, 1, 2, 0);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_ALL, /* shuffleModeEnabled= */ false, 1, 2, 0);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_ALL, /* shuffleModeEnabled= */ true, 1, 2, 0);
assertThat(timeline.getFirstWindowIndex(/* shuffleModeEnabled= */ false)).isEqualTo(0);
assertThat(timeline.getFirstWindowIndex(/* shuffleModeEnabled= */ true)).isEqualTo(0);
assertThat(timeline.getLastWindowIndex(/* shuffleModeEnabled= */ false)).isEqualTo(2);
assertThat(timeline.getLastWindowIndex(/* shuffleModeEnabled= */ true)).isEqualTo(2);
}
public void testNestedTimeline() throws IOException {
DynamicConcatenatingMediaSource nestedSource1 =
new DynamicConcatenatingMediaSource(/* isAtomic= */ false, new FakeShuffleOrder(0));
DynamicConcatenatingMediaSource nestedSource2 =
new DynamicConcatenatingMediaSource(/* isAtomic= */ true, new FakeShuffleOrder(0));
mediaSource.addMediaSource(nestedSource1);
mediaSource.addMediaSource(nestedSource2);
testRunner.prepareSource();
FakeMediaSource[] childSources = createMediaSources(4);
nestedSource1.addMediaSource(childSources[0]);
testRunner.assertTimelineChangeBlocking();
nestedSource1.addMediaSource(childSources[1]);
testRunner.assertTimelineChangeBlocking();
nestedSource2.addMediaSource(childSources[2]);
testRunner.assertTimelineChangeBlocking();
nestedSource2.addMediaSource(childSources[3]);
Timeline timeline = testRunner.assertTimelineChangeBlocking();
TimelineAsserts.assertWindowIds(timeline, 111, 222, 333, 444);
TimelineAsserts.assertPeriodCounts(timeline, 1, 2, 3, 4);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ false, C.INDEX_UNSET, 0, 1, 2);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ONE, /* shuffleModeEnabled= */ false, 0, 1, 3, 2);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ALL, /* shuffleModeEnabled= */ false, 3, 0, 1, 2);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ false, 1, 2, 3, C.INDEX_UNSET);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_ONE, /* shuffleModeEnabled= */ false, 0, 1, 3, 2);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_ALL, /* shuffleModeEnabled= */ false, 1, 2, 3, 0);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ true, 1, 3, C.INDEX_UNSET, 2);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ONE, /* shuffleModeEnabled= */ true, 0, 1, 3, 2);
TimelineAsserts.assertPreviousWindowIndices(
timeline, Player.REPEAT_MODE_ALL, /* shuffleModeEnabled= */ true, 1, 3, 0, 2);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ true, C.INDEX_UNSET, 0, 3, 1);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_ONE, /* shuffleModeEnabled= */ true, 0, 1, 3, 2);
TimelineAsserts.assertNextWindowIndices(
timeline, Player.REPEAT_MODE_ALL, /* shuffleModeEnabled= */ true, 2, 0, 3, 1);
}
public void testRemoveChildSourceWithActiveMediaPeriod() throws IOException { public void testRemoveChildSourceWithActiveMediaPeriod() throws IOException {
FakeMediaSource childSource = createFakeMediaSource(); FakeMediaSource childSource = createFakeMediaSource();
mediaSource.addMediaSource(childSource); mediaSource.addMediaSource(childSource);
......
...@@ -27,14 +27,18 @@ import com.google.android.exoplayer2.Timeline; ...@@ -27,14 +27,18 @@ import com.google.android.exoplayer2.Timeline;
private final int childCount; private final int childCount;
private final ShuffleOrder shuffleOrder; private final ShuffleOrder shuffleOrder;
private final boolean isAtomic;
/** /**
* Sets up a concatenated timeline with a shuffle order of child timelines. * Sets up a concatenated timeline with a shuffle order of child timelines.
* *
* @param isAtomic Whether the child timelines shall be treated as atomic, i.e., treated as a
* single item for repeating and shuffling.
* @param shuffleOrder A shuffle order of child timelines. The number of child timelines must * @param shuffleOrder A shuffle order of child timelines. The number of child timelines must
* match the number of elements in the shuffle order. * match the number of elements in the shuffle order.
*/ */
public AbstractConcatenatedTimeline(ShuffleOrder shuffleOrder) { public AbstractConcatenatedTimeline(boolean isAtomic, ShuffleOrder shuffleOrder) {
this.isAtomic = isAtomic;
this.shuffleOrder = shuffleOrder; this.shuffleOrder = shuffleOrder;
this.childCount = shuffleOrder.getLength(); this.childCount = shuffleOrder.getLength();
} }
...@@ -42,6 +46,11 @@ import com.google.android.exoplayer2.Timeline; ...@@ -42,6 +46,11 @@ import com.google.android.exoplayer2.Timeline;
@Override @Override
public int getNextWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode, public int getNextWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled) { boolean shuffleModeEnabled) {
if (isAtomic) {
// Adapt repeat and shuffle mode to atomic concatenation.
repeatMode = repeatMode == Player.REPEAT_MODE_ONE ? Player.REPEAT_MODE_ALL : repeatMode;
shuffleModeEnabled = false;
}
// Find next window within current child. // Find next window within current child.
int childIndex = getChildIndexByWindowIndex(windowIndex); int childIndex = getChildIndexByWindowIndex(windowIndex);
int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex); int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex);
...@@ -71,6 +80,11 @@ import com.google.android.exoplayer2.Timeline; ...@@ -71,6 +80,11 @@ import com.google.android.exoplayer2.Timeline;
@Override @Override
public int getPreviousWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode, public int getPreviousWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled) { boolean shuffleModeEnabled) {
if (isAtomic) {
// Adapt repeat and shuffle mode to atomic concatenation.
repeatMode = repeatMode == Player.REPEAT_MODE_ONE ? Player.REPEAT_MODE_ALL : repeatMode;
shuffleModeEnabled = false;
}
// Find previous window within current child. // Find previous window within current child.
int childIndex = getChildIndexByWindowIndex(windowIndex); int childIndex = getChildIndexByWindowIndex(windowIndex);
int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex); int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex);
...@@ -103,6 +117,9 @@ import com.google.android.exoplayer2.Timeline; ...@@ -103,6 +117,9 @@ import com.google.android.exoplayer2.Timeline;
if (childCount == 0) { if (childCount == 0) {
return C.INDEX_UNSET; return C.INDEX_UNSET;
} }
if (isAtomic) {
shuffleModeEnabled = false;
}
// Find last non-empty child. // Find last non-empty child.
int lastChildIndex = shuffleModeEnabled ? shuffleOrder.getLastIndex() : childCount - 1; int lastChildIndex = shuffleModeEnabled ? shuffleOrder.getLastIndex() : childCount - 1;
while (getTimelineByChildIndex(lastChildIndex).isEmpty()) { while (getTimelineByChildIndex(lastChildIndex).isEmpty()) {
...@@ -121,6 +138,9 @@ import com.google.android.exoplayer2.Timeline; ...@@ -121,6 +138,9 @@ import com.google.android.exoplayer2.Timeline;
if (childCount == 0) { if (childCount == 0) {
return C.INDEX_UNSET; return C.INDEX_UNSET;
} }
if (isAtomic) {
shuffleModeEnabled = false;
}
// Find first non-empty child. // Find first non-empty child.
int firstChildIndex = shuffleModeEnabled ? shuffleOrder.getFirstIndex() : 0; int firstChildIndex = shuffleModeEnabled ? shuffleOrder.getFirstIndex() : 0;
while (getTimelineByChildIndex(firstChildIndex).isEmpty()) { while (getTimelineByChildIndex(firstChildIndex).isEmpty()) {
......
...@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.source; ...@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.source;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder; import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
...@@ -173,10 +172,9 @@ public final class ConcatenatingMediaSource extends CompositeMediaSource<Integer ...@@ -173,10 +172,9 @@ public final class ConcatenatingMediaSource extends CompositeMediaSource<Integer
private final Timeline[] timelines; private final Timeline[] timelines;
private final int[] sourcePeriodOffsets; private final int[] sourcePeriodOffsets;
private final int[] sourceWindowOffsets; private final int[] sourceWindowOffsets;
private final boolean isAtomic;
public ConcatenatedTimeline(Timeline[] timelines, boolean isAtomic, ShuffleOrder shuffleOrder) { public ConcatenatedTimeline(Timeline[] timelines, boolean isAtomic, ShuffleOrder shuffleOrder) {
super(shuffleOrder); super(isAtomic, shuffleOrder);
int[] sourcePeriodOffsets = new int[timelines.length]; int[] sourcePeriodOffsets = new int[timelines.length];
int[] sourceWindowOffsets = new int[timelines.length]; int[] sourceWindowOffsets = new int[timelines.length];
long periodCount = 0; long periodCount = 0;
...@@ -193,7 +191,6 @@ public final class ConcatenatingMediaSource extends CompositeMediaSource<Integer ...@@ -193,7 +191,6 @@ public final class ConcatenatingMediaSource extends CompositeMediaSource<Integer
this.timelines = timelines; this.timelines = timelines;
this.sourcePeriodOffsets = sourcePeriodOffsets; this.sourcePeriodOffsets = sourcePeriodOffsets;
this.sourceWindowOffsets = sourceWindowOffsets; this.sourceWindowOffsets = sourceWindowOffsets;
this.isAtomic = isAtomic;
} }
@Override @Override
...@@ -207,34 +204,6 @@ public final class ConcatenatingMediaSource extends CompositeMediaSource<Integer ...@@ -207,34 +204,6 @@ public final class ConcatenatingMediaSource extends CompositeMediaSource<Integer
} }
@Override @Override
public int getNextWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled) {
if (isAtomic && repeatMode == Player.REPEAT_MODE_ONE) {
repeatMode = Player.REPEAT_MODE_ALL;
}
return super.getNextWindowIndex(windowIndex, repeatMode, !isAtomic && shuffleModeEnabled);
}
@Override
public int getPreviousWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode,
boolean shuffleModeEnabled) {
if (isAtomic && repeatMode == Player.REPEAT_MODE_ONE) {
repeatMode = Player.REPEAT_MODE_ALL;
}
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
protected int getChildIndexByPeriodIndex(int periodIndex) { protected int getChildIndexByPeriodIndex(int periodIndex) {
return Util.binarySearchFloor(sourcePeriodOffsets, periodIndex + 1, false, false) + 1; return Util.binarySearchFloor(sourcePeriodOffsets, periodIndex + 1, false, false) + 1;
} }
......
...@@ -58,6 +58,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -58,6 +58,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
private final MediaSourceHolder query; private final MediaSourceHolder query;
private final Map<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod; private final Map<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod;
private final List<DeferredMediaPeriod> deferredMediaPeriods; private final List<DeferredMediaPeriod> deferredMediaPeriods;
private final boolean isAtomic;
private ExoPlayer player; private ExoPlayer player;
private Listener listener; private Listener listener;
...@@ -70,22 +71,35 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -70,22 +71,35 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
* Creates a new dynamic concatenating media source. * Creates a new dynamic concatenating media source.
*/ */
public DynamicConcatenatingMediaSource() { public DynamicConcatenatingMediaSource() {
this(new DefaultShuffleOrder(0)); this(/* isAtomic= */ false, new DefaultShuffleOrder(0));
}
/**
* Creates a new dynamic concatenating media source.
*
* @param isAtomic Whether the concatenating media source will be treated as atomic, i.e., treated
* as a single item for repeating and shuffling.
*/
public DynamicConcatenatingMediaSource(boolean isAtomic) {
this(isAtomic, new DefaultShuffleOrder(0));
} }
/** /**
* Creates a new dynamic concatenating media source with a custom shuffle order. * Creates a new dynamic concatenating media source with a custom shuffle order.
* *
* @param isAtomic Whether the concatenating media source will 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. * @param shuffleOrder The {@link ShuffleOrder} to use when shuffling the child media sources.
* This shuffle order must be empty. * This shuffle order must be empty.
*/ */
public DynamicConcatenatingMediaSource(ShuffleOrder shuffleOrder) { public DynamicConcatenatingMediaSource(boolean isAtomic, ShuffleOrder shuffleOrder) {
this.shuffleOrder = shuffleOrder; this.shuffleOrder = shuffleOrder;
this.mediaSourceByMediaPeriod = new IdentityHashMap<>(); this.mediaSourceByMediaPeriod = new IdentityHashMap<>();
this.mediaSourcesPublic = new ArrayList<>(); this.mediaSourcesPublic = new ArrayList<>();
this.mediaSourceHolders = new ArrayList<>(); this.mediaSourceHolders = new ArrayList<>();
this.deferredMediaPeriods = new ArrayList<>(1); this.deferredMediaPeriods = new ArrayList<>(1);
this.query = new MediaSourceHolder(null, null, -1, -1, -1); this.query = new MediaSourceHolder(null, null, -1, -1, -1);
this.isAtomic = isAtomic;
} }
/** /**
...@@ -446,8 +460,10 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -446,8 +460,10 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
private void maybeNotifyListener(@Nullable EventDispatcher actionOnCompletion) { private void maybeNotifyListener(@Nullable EventDispatcher actionOnCompletion) {
if (!preventListenerNotification) { if (!preventListenerNotification) {
listener.onSourceInfoRefreshed(this, listener.onSourceInfoRefreshed(
new ConcatenatedTimeline(mediaSourceHolders, windowCount, periodCount, shuffleOrder), this,
new ConcatenatedTimeline(
mediaSourceHolders, windowCount, periodCount, shuffleOrder, isAtomic),
null); null);
if (actionOnCompletion != null) { if (actionOnCompletion != null) {
player.createMessage(this).setType(MSG_ON_COMPLETION).setPayload(actionOnCompletion).send(); player.createMessage(this).setType(MSG_ON_COMPLETION).setPayload(actionOnCompletion).send();
...@@ -652,9 +668,13 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -652,9 +668,13 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
private final int[] uids; private final int[] uids;
private final SparseIntArray childIndexByUid; private final SparseIntArray childIndexByUid;
public ConcatenatedTimeline(Collection<MediaSourceHolder> mediaSourceHolders, int windowCount, public ConcatenatedTimeline(
int periodCount, ShuffleOrder shuffleOrder) { Collection<MediaSourceHolder> mediaSourceHolders,
super(shuffleOrder); int windowCount,
int periodCount,
ShuffleOrder shuffleOrder,
boolean isAtomic) {
super(isAtomic, shuffleOrder);
this.windowCount = windowCount; this.windowCount = windowCount;
this.periodCount = periodCount; this.periodCount = periodCount;
int childCount = mediaSourceHolders.size(); int childCount = mediaSourceHolders.size();
...@@ -728,27 +748,29 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -728,27 +748,29 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
* Timeline used as placeholder for an unprepared media source. After preparation, a copy of the * Timeline used as placeholder for an unprepared media source. After preparation, a copy of the
* DeferredTimeline is used to keep the originally assigned first period ID. * DeferredTimeline is used to keep the originally assigned first period ID.
*/ */
private static final class DeferredTimeline extends Timeline { private static final class DeferredTimeline extends ForwardingTimeline {
private static final Object DUMMY_ID = new Object(); private static final Object DUMMY_ID = new Object();
private static final Period period = new Period(); private static final Period period = new Period();
private static final DummyTimeline dummyTimeline = new DummyTimeline();
private final Timeline timeline; private final Object replacedId;
private final Object replacedID;
public DeferredTimeline() { public DeferredTimeline() {
timeline = null; this(dummyTimeline, /* replacedId= */ null);
replacedID = null;
} }
private DeferredTimeline(Timeline timeline, Object replacedID) { private DeferredTimeline(Timeline timeline, Object replacedId) {
this.timeline = timeline; super(timeline);
this.replacedID = replacedID; this.replacedId = replacedId;
} }
public DeferredTimeline cloneWithNewTimeline(Timeline timeline) { public DeferredTimeline cloneWithNewTimeline(Timeline timeline) {
return new DeferredTimeline(timeline, replacedID == null && timeline.getPeriodCount() > 0 return new DeferredTimeline(
? timeline.getPeriod(0, period, true).uid : replacedID); timeline,
replacedId == null && timeline.getPeriodCount() > 0
? timeline.getPeriod(0, period, true).uid
: replacedId);
} }
public Timeline getTimeline() { public Timeline getTimeline() {
...@@ -756,45 +778,64 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -756,45 +778,64 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
} }
@Override @Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
timeline.getPeriod(periodIndex, period, setIds);
if (period.uid == replacedId) {
period.uid = DUMMY_ID;
}
return period;
}
@Override
public int getIndexOfPeriod(Object uid) {
return timeline.getIndexOfPeriod(uid == DUMMY_ID ? replacedId : uid);
}
}
/** Dummy placeholder timeline with one dynamic window with a period of indeterminate duration. */
private static final class DummyTimeline extends Timeline {
@Override
public int getWindowCount() { public int getWindowCount() {
return timeline == null ? 1 : timeline.getWindowCount(); return 1;
} }
@Override @Override
public Window getWindow(int windowIndex, Window window, boolean setIds, public Window getWindow(int windowIndex, Window window, boolean setIds,
long defaultPositionProjectionUs) { long defaultPositionProjectionUs) {
return timeline == null // Dynamic window to indicate pending timeline updates.
// Dynamic window to indicate pending timeline updates. return window.set(
? window.set(setIds ? DUMMY_ID : null, C.TIME_UNSET, C.TIME_UNSET, false, true, 0, /* id= */ null,
C.TIME_UNSET, 0, 0, 0) /* presentationStartTimeMs= */ C.TIME_UNSET,
: timeline.getWindow(windowIndex, window, setIds, defaultPositionProjectionUs); /* windowStartTimeMs= */ C.TIME_UNSET,
/* isSeekable= */ false,
/* isDynamic= */ true,
/* defaultPositionUs= */ 0,
/* durationUs= */ C.TIME_UNSET,
/* firstPeriodIndex= */ 0,
/* lastPeriodIndex= */ 0,
/* positionInFirstPeriodUs= */ 0);
} }
@Override @Override
public int getPeriodCount() { public int getPeriodCount() {
return timeline == null ? 1 : timeline.getPeriodCount(); return 1;
} }
@Override @Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) { public Period getPeriod(int periodIndex, Period period, boolean setIds) {
if (timeline == null) { return period.set(
return period.set(setIds ? DUMMY_ID : null, setIds ? DUMMY_ID : null, 0, C.TIME_UNSET, /* id= */ null,
C.TIME_UNSET); /* uid= */ null,
} /* windowIndex= */ 0,
timeline.getPeriod(periodIndex, period, setIds); /* durationUs = */ C.TIME_UNSET,
if (period.uid == replacedID) { /* positionInWindowUs= */ C.TIME_UNSET);
period.uid = DUMMY_ID;
}
return period;
} }
@Override @Override
public int getIndexOfPeriod(Object uid) { public int getIndexOfPeriod(Object uid) {
return timeline == null ? (uid == DUMMY_ID ? 0 : C.INDEX_UNSET) return uid == null ? 0 : C.INDEX_UNSET;
: timeline.getIndexOfPeriod(uid == DUMMY_ID ? replacedID : uid);
} }
} }
} }
...@@ -106,7 +106,7 @@ public final class LoopingMediaSource extends CompositeMediaSource<Void> { ...@@ -106,7 +106,7 @@ public final class LoopingMediaSource extends CompositeMediaSource<Void> {
private final int loopCount; private final int loopCount;
public LoopingTimeline(Timeline childTimeline, int loopCount) { public LoopingTimeline(Timeline childTimeline, int loopCount) {
super(new UnshuffledShuffleOrder(loopCount)); super(/* isAtomic= */ false, new UnshuffledShuffleOrder(loopCount));
this.childTimeline = childTimeline; this.childTimeline = childTimeline;
childPeriodCount = childTimeline.getPeriodCount(); childPeriodCount = childTimeline.getPeriodCount();
childWindowCount = childTimeline.getWindowCount(); childWindowCount = childTimeline.getWindowCount();
......
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