mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
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
This commit is contained in:
parent
7f866b2a07
commit
b6113763b4
5 changed files with 124 additions and 59 deletions
|
|
@ -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 {
|
||||||
|
|
||||||
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 {
|
||||||
* @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 {
|
||||||
* @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 {
|
||||||
};
|
};
|
||||||
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 {
|
||||||
*
|
*
|
||||||
* @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 {
|
||||||
* @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 {
|
||||||
* 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 {
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
*/
|
*/
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
@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
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
/* 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
|
||||||
@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
|
||||||
|
|
||||||
@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;
|
||||||
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 {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue