diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b79f2bafd1..cd9939c05c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -10,6 +10,9 @@ 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 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: * Allow injection of custom playlist trackers. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java index f5c4b3a16d..c36cc1e26a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java @@ -35,8 +35,8 @@ public abstract class CompositeMediaSource extends BaseMediaSource { private final HashMap childSources; - private ExoPlayer player; - private Handler eventHandler; + private @Nullable ExoPlayer player; + private @Nullable Handler eventHandler; /** Create composite media source without child sources. */ protected CompositeMediaSource() { @@ -78,7 +78,7 @@ public abstract class CompositeMediaSource extends BaseMediaSource { * @param manifest The manifest of the child source. */ 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. @@ -93,7 +93,7 @@ public abstract class CompositeMediaSource extends BaseMediaSource { * @param id A unique id to identify the child source preparation. Null is allowed as an id. * @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)); SourceInfoRefreshListener sourceListener = new SourceInfoRefreshListener() { @@ -105,8 +105,9 @@ public abstract class CompositeMediaSource extends BaseMediaSource { }; MediaSourceEventListener eventListener = new ForwardingEventListener(id); childSources.put(id, new MediaSourceAndListener(mediaSource, sourceListener, eventListener)); - mediaSource.addEventListener(eventHandler, eventListener); - mediaSource.prepareSource(player, /* isTopLevelSource= */ false, sourceListener); + mediaSource.addEventListener(Assertions.checkNotNull(eventHandler), eventListener); + mediaSource.prepareSource( + Assertions.checkNotNull(player), /* isTopLevelSource= */ false, sourceListener); } /** @@ -114,8 +115,8 @@ public abstract class CompositeMediaSource extends BaseMediaSource { * * @param id The unique id used to prepare the child source. */ - protected final void releaseChildSource(@Nullable T id) { - MediaSourceAndListener removedChild = childSources.remove(id); + protected final void releaseChildSource(T id) { + MediaSourceAndListener removedChild = Assertions.checkNotNull(childSources.remove(id)); removedChild.mediaSource.releaseSource(removedChild.listener); removedChild.mediaSource.removeEventListener(removedChild.eventListener); } @@ -128,7 +129,7 @@ public abstract class CompositeMediaSource extends BaseMediaSource { * @param windowIndex A window index of the child 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; } @@ -143,7 +144,7 @@ public abstract class CompositeMediaSource extends BaseMediaSource { * corresponding media period id can be determined. */ protected @Nullable MediaPeriodId getMediaPeriodIdForChildMediaPeriodId( - @Nullable T id, MediaPeriodId mediaPeriodId) { + T id, MediaPeriodId mediaPeriodId) { return mediaPeriodId; } @@ -177,10 +178,10 @@ public abstract class CompositeMediaSource extends BaseMediaSource { private final class ForwardingEventListener implements MediaSourceEventListener { - private final @Nullable T id; + private final T id; private EventDispatcher eventDispatcher; - public ForwardingEventListener(@Nullable T id) { + public ForwardingEventListener(T id) { this.eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null); this.id = id; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java index 3e39139918..61fc3a1813 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java @@ -63,40 +63,16 @@ public class ConcatenatingMediaSource extends CompositeMediaSource mediaSourceByMediaPeriod; private final List pendingOnCompletionActions; private final boolean isAtomic; + private final boolean useLazyPreparation; private final Timeline.Window window; + private final Timeline.Period period; - private ExoPlayer player; + private @Nullable ExoPlayer player; private boolean listenerNotificationScheduled; private ShuffleOrder shuffleOrder; private int windowCount; 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 * {@link MediaSource} instance to be present more than once in the array. @@ -124,6 +100,25 @@ public class ConcatenatingMediaSource extends CompositeMediaSource(); this.query = new MediaSourceHolder(/* mediaSource= */ null); this.isAtomic = isAtomic; + this.useLazyPreparation = useLazyPreparation; window = new Timeline.Window(); + period = new Timeline.Period(); addMediaSources(Arrays.asList(mediaSources)); } @@ -293,7 +290,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource(index, null, actionOnCompletion)) + .setPayload(new MessageData(index, null, actionOnCompletion)) .send(); } else if (actionOnCompletion != null) { actionOnCompletion.run(); @@ -402,7 +399,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource activeMediaPeriods; public MediaSourceHolder(MediaSource mediaSource) { this.mediaSource = mediaSource; - this.uid = System.identityHashCode(this); this.timeline = new DeferredTimeline(); this.activeMediaPeriods = new ArrayList<>(); + this.uid = System.identityHashCode(this); } public void reset(int childIndex, int firstWindowIndexInChild, int firstPeriodIndexInChild) { this.childIndex = childIndex; this.firstWindowIndexInChild = firstWindowIndexInChild; this.firstPeriodIndexInChild = firstPeriodIndexInChild; + this.hasStartedPreparing = false; this.isPrepared = false; this.isRemoved = false; this.activeMediaPeriods.clear(); @@ -814,13 +824,12 @@ public class ConcatenatingMediaSource extends CompositeMediaSource 0 + replacedId == DUMMY_ID && timeline.getPeriodCount() > 0 ? timeline.getPeriod(0, period, true).uid : replacedId); } @@ -873,8 +882,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource 0 ? C.TIME_UNSET : 0, + /* defaultPositionUs= */ 0, /* durationUs= */ C.TIME_UNSET, /* firstPeriodIndex= */ 0, /* lastPeriodIndex= */ 0, @@ -889,8 +897,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource