Commit b6113763 by tonihei Committed by Oliver Woodman

Allow lazy preparation of child source in a concatenation.

This adds an optional parameter to ConcatenatingMediaSource to prepare
child sources only lazily when are needed. This is helpful for long playlists
of media sources with manifests to prevent a lot of simultaneous manifest
loads.

Issue:#3972

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=198855676
parent 7f866b2a
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
changes ([#4276](https://github.com/google/ExoPlayer/issues/4276)). changes ([#4276](https://github.com/google/ExoPlayer/issues/4276)).
* IMA: Don't advertise support for video/mpeg ad media, as we don't have an * IMA: Don't advertise support for video/mpeg ad media, as we don't have an
extractor for this ([#4297](https://github.com/google/ExoPlayer/issues/4297)). extractor for this ([#4297](https://github.com/google/ExoPlayer/issues/4297)).
* Add support for lazy preparation of playlist media sources in
`ConcatenatingMediaSource`
([#3972](https://github.com/google/ExoPlayer/issues/3972)).
* HLS: * HLS:
* Allow injection of custom playlist trackers. * Allow injection of custom playlist trackers.
......
...@@ -35,8 +35,8 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource { ...@@ -35,8 +35,8 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
private final HashMap<T, MediaSourceAndListener> childSources; private final HashMap<T, MediaSourceAndListener> childSources;
private ExoPlayer player; private @Nullable ExoPlayer player;
private Handler eventHandler; private @Nullable Handler eventHandler;
/** Create composite media source without child sources. */ /** Create composite media source without child sources. */
protected CompositeMediaSource() { protected CompositeMediaSource() {
...@@ -78,7 +78,7 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource { ...@@ -78,7 +78,7 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
* @param manifest The manifest of the child source. * @param manifest The manifest of the child source.
*/ */
protected abstract void onChildSourceInfoRefreshed( protected abstract void onChildSourceInfoRefreshed(
@Nullable T id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest); T id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest);
/** /**
* Prepares a child source. * Prepares a child source.
...@@ -93,7 +93,7 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource { ...@@ -93,7 +93,7 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
* @param id A unique id to identify the child source preparation. Null is allowed as an id. * @param id A unique id to identify the child source preparation. Null is allowed as an id.
* @param mediaSource The child {@link MediaSource}. * @param mediaSource The child {@link MediaSource}.
*/ */
protected final void prepareChildSource(@Nullable final T id, MediaSource mediaSource) { protected final void prepareChildSource(final T id, MediaSource mediaSource) {
Assertions.checkArgument(!childSources.containsKey(id)); Assertions.checkArgument(!childSources.containsKey(id));
SourceInfoRefreshListener sourceListener = SourceInfoRefreshListener sourceListener =
new SourceInfoRefreshListener() { new SourceInfoRefreshListener() {
...@@ -105,8 +105,9 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource { ...@@ -105,8 +105,9 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
}; };
MediaSourceEventListener eventListener = new ForwardingEventListener(id); MediaSourceEventListener eventListener = new ForwardingEventListener(id);
childSources.put(id, new MediaSourceAndListener(mediaSource, sourceListener, eventListener)); childSources.put(id, new MediaSourceAndListener(mediaSource, sourceListener, eventListener));
mediaSource.addEventListener(eventHandler, eventListener); mediaSource.addEventListener(Assertions.checkNotNull(eventHandler), eventListener);
mediaSource.prepareSource(player, /* isTopLevelSource= */ false, sourceListener); mediaSource.prepareSource(
Assertions.checkNotNull(player), /* isTopLevelSource= */ false, sourceListener);
} }
/** /**
...@@ -114,8 +115,8 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource { ...@@ -114,8 +115,8 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
* *
* @param id The unique id used to prepare the child source. * @param id The unique id used to prepare the child source.
*/ */
protected final void releaseChildSource(@Nullable T id) { protected final void releaseChildSource(T id) {
MediaSourceAndListener removedChild = childSources.remove(id); MediaSourceAndListener removedChild = Assertions.checkNotNull(childSources.remove(id));
removedChild.mediaSource.releaseSource(removedChild.listener); removedChild.mediaSource.releaseSource(removedChild.listener);
removedChild.mediaSource.removeEventListener(removedChild.eventListener); removedChild.mediaSource.removeEventListener(removedChild.eventListener);
} }
...@@ -128,7 +129,7 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource { ...@@ -128,7 +129,7 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
* @param windowIndex A window index of the child source. * @param windowIndex A window index of the child source.
* @return The corresponding window index in the composite source. * @return The corresponding window index in the composite source.
*/ */
protected int getWindowIndexForChildWindowIndex(@Nullable T id, int windowIndex) { protected int getWindowIndexForChildWindowIndex(T id, int windowIndex) {
return windowIndex; return windowIndex;
} }
...@@ -143,7 +144,7 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource { ...@@ -143,7 +144,7 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
* corresponding media period id can be determined. * corresponding media period id can be determined.
*/ */
protected @Nullable MediaPeriodId getMediaPeriodIdForChildMediaPeriodId( protected @Nullable MediaPeriodId getMediaPeriodIdForChildMediaPeriodId(
@Nullable T id, MediaPeriodId mediaPeriodId) { T id, MediaPeriodId mediaPeriodId) {
return mediaPeriodId; return mediaPeriodId;
} }
...@@ -177,10 +178,10 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource { ...@@ -177,10 +178,10 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
private final class ForwardingEventListener implements MediaSourceEventListener { private final class ForwardingEventListener implements MediaSourceEventListener {
private final @Nullable T id; private final T id;
private EventDispatcher eventDispatcher; private EventDispatcher eventDispatcher;
public ForwardingEventListener(@Nullable T id) { public ForwardingEventListener(T id) {
this.eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null); this.eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null);
this.id = id; this.id = id;
} }
......
...@@ -63,40 +63,16 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -63,40 +63,16 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
private final Map<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod; private final Map<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod;
private final List<EventDispatcher> pendingOnCompletionActions; private final List<EventDispatcher> pendingOnCompletionActions;
private final boolean isAtomic; private final boolean isAtomic;
private final boolean useLazyPreparation;
private final Timeline.Window window; private final Timeline.Window window;
private final Timeline.Period period;
private ExoPlayer player; private @Nullable ExoPlayer player;
private boolean listenerNotificationScheduled; private boolean listenerNotificationScheduled;
private ShuffleOrder shuffleOrder; private ShuffleOrder shuffleOrder;
private int windowCount; private int windowCount;
private int periodCount; private int periodCount;
/** Creates a new concatenating media source. */
public ConcatenatingMediaSource() {
this(/* isAtomic= */ false, new DefaultShuffleOrder(0));
}
/**
* Creates a new 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 ConcatenatingMediaSource(boolean isAtomic) {
this(isAtomic, new DefaultShuffleOrder(0));
}
/**
* Creates a new 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.
*/
public ConcatenatingMediaSource(boolean isAtomic, ShuffleOrder shuffleOrder) {
this(isAtomic, shuffleOrder, new MediaSource[0]);
}
/** /**
* @param mediaSources The {@link MediaSource}s to concatenate. It is valid for the same * @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. * {@link MediaSource} instance to be present more than once in the array.
...@@ -124,6 +100,25 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -124,6 +100,25 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
*/ */
public ConcatenatingMediaSource( public ConcatenatingMediaSource(
boolean isAtomic, ShuffleOrder shuffleOrder, MediaSource... mediaSources) { boolean isAtomic, ShuffleOrder shuffleOrder, MediaSource... mediaSources) {
this(isAtomic, /* useLazyPreparation= */ false, shuffleOrder, mediaSources);
}
/**
* @param isAtomic Whether the concatenating media source will be treated as atomic, i.e., treated
* as a single item for repeating and shuffling.
* @param useLazyPreparation Whether playlist items are prepared lazily. If false, all manifest
* loads and other initial preparation steps happen immediately. If true, these initial
* preparations are triggered only when the player starts buffering the media.
* @param shuffleOrder The {@link ShuffleOrder} to use when shuffling the child media sources.
* @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.
*/
@SuppressWarnings("initialization")
public ConcatenatingMediaSource(
boolean isAtomic,
boolean useLazyPreparation,
ShuffleOrder shuffleOrder,
MediaSource... mediaSources) {
for (MediaSource mediaSource : mediaSources) { for (MediaSource mediaSource : mediaSources) {
Assertions.checkNotNull(mediaSource); Assertions.checkNotNull(mediaSource);
} }
...@@ -134,7 +129,9 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -134,7 +129,9 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
this.pendingOnCompletionActions = new ArrayList<>(); this.pendingOnCompletionActions = new ArrayList<>();
this.query = new MediaSourceHolder(/* mediaSource= */ null); this.query = new MediaSourceHolder(/* mediaSource= */ null);
this.isAtomic = isAtomic; this.isAtomic = isAtomic;
this.useLazyPreparation = useLazyPreparation;
window = new Timeline.Window(); window = new Timeline.Window();
period = new Timeline.Period();
addMediaSources(Arrays.asList(mediaSources)); addMediaSources(Arrays.asList(mediaSources));
} }
...@@ -293,7 +290,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -293,7 +290,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
player player
.createMessage(this) .createMessage(this)
.setType(MSG_REMOVE) .setType(MSG_REMOVE)
.setPayload(new MessageData<>(index, null, actionOnCompletion)) .setPayload(new MessageData<Void>(index, null, actionOnCompletion))
.send(); .send();
} else if (actionOnCompletion != null) { } else if (actionOnCompletion != null) {
actionOnCompletion.run(); actionOnCompletion.run();
...@@ -402,7 +399,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -402,7 +399,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
new DeferredMediaPeriod(holder.mediaSource, idInSource, allocator); new DeferredMediaPeriod(holder.mediaSource, idInSource, allocator);
mediaSourceByMediaPeriod.put(mediaPeriod, holder); mediaSourceByMediaPeriod.put(mediaPeriod, holder);
holder.activeMediaPeriods.add(mediaPeriod); holder.activeMediaPeriods.add(mediaPeriod);
if (holder.isPrepared) { if (!holder.hasStartedPreparing) {
holder.hasStartedPreparing = true;
prepareChildSource(holder, holder.mediaSource);
} else if (holder.isPrepared) {
mediaPeriod.createPeriod(); mediaPeriod.createPeriod();
} }
return mediaPeriod; return mediaPeriod;
...@@ -410,7 +410,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -410,7 +410,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
@Override @Override
public final void releasePeriod(MediaPeriod mediaPeriod) { public final void releasePeriod(MediaPeriod mediaPeriod) {
MediaSourceHolder holder = mediaSourceByMediaPeriod.remove(mediaPeriod); MediaSourceHolder holder =
Assertions.checkNotNull(mediaSourceByMediaPeriod.remove(mediaPeriod));
((DeferredMediaPeriod) mediaPeriod).releasePeriod(); ((DeferredMediaPeriod) mediaPeriod).releasePeriod();
holder.activeMediaPeriods.remove(mediaPeriod); holder.activeMediaPeriods.remove(mediaPeriod);
if (holder.activeMediaPeriods.isEmpty() && holder.isRemoved) { if (holder.activeMediaPeriods.isEmpty() && holder.isRemoved) {
...@@ -510,7 +511,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -510,7 +511,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
private void scheduleListenerNotification(@Nullable EventDispatcher actionOnCompletion) { private void scheduleListenerNotification(@Nullable EventDispatcher actionOnCompletion) {
if (!listenerNotificationScheduled) { if (!listenerNotificationScheduled) {
player.createMessage(this).setType(MSG_NOTIFY_LISTENER).send(); Assertions.checkNotNull(player).createMessage(this).setType(MSG_NOTIFY_LISTENER).send();
listenerNotificationScheduled = true; listenerNotificationScheduled = true;
} }
if (actionOnCompletion != null) { if (actionOnCompletion != null) {
...@@ -530,7 +531,11 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -530,7 +531,11 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
mediaSourceHolders, windowCount, periodCount, shuffleOrder, isAtomic), mediaSourceHolders, windowCount, periodCount, shuffleOrder, isAtomic),
/* manifest= */ null); /* manifest= */ null);
if (!actionsOnCompletion.isEmpty()) { if (!actionsOnCompletion.isEmpty()) {
player.createMessage(this).setType(MSG_ON_COMPLETION).setPayload(actionsOnCompletion).send(); Assertions.checkNotNull(player)
.createMessage(this)
.setType(MSG_ON_COMPLETION)
.setPayload(actionsOnCompletion)
.send();
} }
} }
...@@ -551,7 +556,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -551,7 +556,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
newMediaSourceHolder.timeline.getWindowCount(), newMediaSourceHolder.timeline.getWindowCount(),
newMediaSourceHolder.timeline.getPeriodCount()); newMediaSourceHolder.timeline.getPeriodCount());
mediaSourceHolders.add(newIndex, newMediaSourceHolder); mediaSourceHolders.add(newIndex, newMediaSourceHolder);
prepareChildSource(newMediaSourceHolder, newMediaSourceHolder.mediaSource); if (!useLazyPreparation) {
newMediaSourceHolder.hasStartedPreparing = true;
prepareChildSource(newMediaSourceHolder, newMediaSourceHolder.mediaSource);
}
} }
private void addMediaSourcesInternal( private void addMediaSourcesInternal(
...@@ -578,7 +586,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -578,7 +586,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
windowOffsetUpdate, windowOffsetUpdate,
periodOffsetUpdate); periodOffsetUpdate);
} }
mediaSourceHolder.timeline = deferredTimeline.cloneWithNewTimeline(timeline); mediaSourceHolder.timeline = deferredTimeline.cloneWithNewTimeline(timeline, period);
if (!mediaSourceHolder.isPrepared && !timeline.isEmpty()) { if (!mediaSourceHolder.isPrepared && !timeline.isEmpty()) {
timeline.getWindow(/* windowIndex= */ 0, window); timeline.getWindow(/* windowIndex= */ 0, window);
long defaultPeriodPositionUs = long defaultPeriodPositionUs =
...@@ -662,21 +670,23 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -662,21 +670,23 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
public int childIndex; public int childIndex;
public int firstWindowIndexInChild; public int firstWindowIndexInChild;
public int firstPeriodIndexInChild; public int firstPeriodIndexInChild;
public boolean hasStartedPreparing;
public boolean isPrepared; public boolean isPrepared;
public boolean isRemoved; public boolean isRemoved;
public List<DeferredMediaPeriod> activeMediaPeriods; public List<DeferredMediaPeriod> activeMediaPeriods;
public MediaSourceHolder(MediaSource mediaSource) { public MediaSourceHolder(MediaSource mediaSource) {
this.mediaSource = mediaSource; this.mediaSource = mediaSource;
this.uid = System.identityHashCode(this);
this.timeline = new DeferredTimeline(); this.timeline = new DeferredTimeline();
this.activeMediaPeriods = new ArrayList<>(); this.activeMediaPeriods = new ArrayList<>();
this.uid = System.identityHashCode(this);
} }
public void reset(int childIndex, int firstWindowIndexInChild, int firstPeriodIndexInChild) { public void reset(int childIndex, int firstWindowIndexInChild, int firstPeriodIndexInChild) {
this.childIndex = childIndex; this.childIndex = childIndex;
this.firstWindowIndexInChild = firstWindowIndexInChild; this.firstWindowIndexInChild = firstWindowIndexInChild;
this.firstPeriodIndexInChild = firstPeriodIndexInChild; this.firstPeriodIndexInChild = firstPeriodIndexInChild;
this.hasStartedPreparing = false;
this.isPrepared = false; this.isPrepared = false;
this.isRemoved = false; this.isRemoved = false;
this.activeMediaPeriods.clear(); this.activeMediaPeriods.clear();
...@@ -814,13 +824,12 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -814,13 +824,12 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
private static final class DeferredTimeline extends ForwardingTimeline { 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 DummyTimeline dummyTimeline = new DummyTimeline(); private static final DummyTimeline dummyTimeline = new DummyTimeline();
private final Object replacedId; private final Object replacedId;
public DeferredTimeline() { public DeferredTimeline() {
this(dummyTimeline, /* replacedId= */ null); this(dummyTimeline, DUMMY_ID);
} }
private DeferredTimeline(Timeline timeline, Object replacedId) { private DeferredTimeline(Timeline timeline, Object replacedId) {
...@@ -828,10 +837,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -828,10 +837,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
this.replacedId = replacedId; this.replacedId = replacedId;
} }
public DeferredTimeline cloneWithNewTimeline(Timeline timeline) { public DeferredTimeline cloneWithNewTimeline(Timeline timeline, Period period) {
return new DeferredTimeline( return new DeferredTimeline(
timeline, timeline,
replacedId == null && timeline.getPeriodCount() > 0 replacedId == DUMMY_ID && timeline.getPeriodCount() > 0
? timeline.getPeriod(0, period, true).uid ? timeline.getPeriod(0, period, true).uid
: replacedId); : replacedId);
} }
...@@ -873,8 +882,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -873,8 +882,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
/* isSeekable= */ false, /* isSeekable= */ false,
// Dynamic window to indicate pending timeline updates. // Dynamic window to indicate pending timeline updates.
/* isDynamic= */ true, /* isDynamic= */ true,
// Position can't be projected yet as the default position is still unknown. /* defaultPositionUs= */ 0,
/* defaultPositionUs= */ defaultPositionProjectionUs > 0 ? C.TIME_UNSET : 0,
/* durationUs= */ C.TIME_UNSET, /* durationUs= */ C.TIME_UNSET,
/* firstPeriodIndex= */ 0, /* firstPeriodIndex= */ 0,
/* lastPeriodIndex= */ 0, /* lastPeriodIndex= */ 0,
...@@ -889,8 +897,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -889,8 +897,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
@Override @Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) { public Period getPeriod(int periodIndex, Period period, boolean setIds) {
return period.set( return period.set(
/* id= */ null, /* id= */ 0,
/* uid= */ null, /* uid= */ DeferredTimeline.DUMMY_ID,
/* windowIndex= */ 0, /* windowIndex= */ 0,
/* durationUs = */ C.TIME_UNSET, /* durationUs = */ C.TIME_UNSET,
/* positionInWindowUs= */ 0); /* positionInWindowUs= */ 0);
...@@ -898,7 +906,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo ...@@ -898,7 +906,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
@Override @Override
public int getIndexOfPeriod(Object uid) { public int getIndexOfPeriod(Object uid) {
return uid == null ? 0 : C.INDEX_UNSET; return uid == DeferredTimeline.DUMMY_ID ? 0 : C.INDEX_UNSET;
} }
} }
} }
......
...@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.Player; ...@@ -25,6 +25,7 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder;
import com.google.android.exoplayer2.testutil.DummyMainThread; import com.google.android.exoplayer2.testutil.DummyMainThread;
import com.google.android.exoplayer2.testutil.FakeMediaSource; import com.google.android.exoplayer2.testutil.FakeMediaSource;
import com.google.android.exoplayer2.testutil.FakeShuffleOrder; import com.google.android.exoplayer2.testutil.FakeShuffleOrder;
...@@ -913,6 +914,58 @@ public final class ConcatenatingMediaSourceTest { ...@@ -913,6 +914,58 @@ public final class ConcatenatingMediaSourceTest {
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0)); new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0));
} }
@Test
public void testChildSourceIsNotPreparedWithLazyPreparation() throws IOException {
FakeMediaSource[] childSources = createMediaSources(/* count= */ 2);
mediaSource =
new ConcatenatingMediaSource(
/* isAtomic= */ false,
/* useLazyPreparation= */ true,
new DefaultShuffleOrder(0),
childSources);
testRunner = new MediaSourceTestRunner(mediaSource, /* allocator= */ null);
testRunner.prepareSource();
assertThat(childSources[0].isPrepared()).isFalse();
assertThat(childSources[1].isPrepared()).isFalse();
}
@Test
public void testChildSourceIsPreparedWithLazyPreparationAfterPeriodCreation() throws IOException {
FakeMediaSource[] childSources = createMediaSources(/* count= */ 2);
mediaSource =
new ConcatenatingMediaSource(
/* isAtomic= */ false,
/* useLazyPreparation= */ true,
new DefaultShuffleOrder(0),
childSources);
testRunner = new MediaSourceTestRunner(mediaSource, /* allocator= */ null);
testRunner.prepareSource();
testRunner.createPeriod(new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0));
assertThat(childSources[0].isPrepared()).isTrue();
assertThat(childSources[1].isPrepared()).isFalse();
}
@Test
public void testChildSourceWithLazyPreparationOnlyPreparesSourceOnce() throws IOException {
FakeMediaSource[] childSources = createMediaSources(/* count= */ 2);
mediaSource =
new ConcatenatingMediaSource(
/* isAtomic= */ false,
/* useLazyPreparation= */ true,
new DefaultShuffleOrder(0),
childSources);
testRunner = new MediaSourceTestRunner(mediaSource, /* allocator= */ null);
testRunner.prepareSource();
// The lazy preparation must only be triggered once, even if we create multiple periods from
// the media source. FakeMediaSource.prepareSource asserts that it's not called twice, so
// creating two periods shouldn't throw.
testRunner.createPeriod(new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0));
testRunner.createPeriod(new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0));
}
private void assertCompletedAllMediaPeriodLoads(Timeline timeline) { private void assertCompletedAllMediaPeriodLoads(Timeline timeline) {
Timeline.Period period = new Timeline.Period(); Timeline.Period period = new Timeline.Period();
Timeline.Window window = new Timeline.Window(); Timeline.Window window = new Timeline.Window();
......
...@@ -159,9 +159,9 @@ public class FakeMediaSource extends BaseMediaSource { ...@@ -159,9 +159,9 @@ public class FakeMediaSource extends BaseMediaSource {
} }
} }
/** Asserts that the source has been prepared. */ /** Returns whether the source is currently prepared. */
public void assertPrepared() { public boolean isPrepared() {
assertThat(preparedSource).isTrue(); return preparedSource;
} }
/** /**
......
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