diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Playlist.java b/library/core/src/main/java/com/google/android/exoplayer2/Playlist.java
deleted file mode 100644
index 351c9d5780..0000000000
--- a/library/core/src/main/java/com/google/android/exoplayer2/Playlist.java
+++ /dev/null
@@ -1,707 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.android.exoplayer2;
-
-import android.os.Handler;
-import androidx.annotation.Nullable;
-import com.google.android.exoplayer2.analytics.AnalyticsCollector;
-import com.google.android.exoplayer2.source.MaskingMediaPeriod;
-import com.google.android.exoplayer2.source.MaskingMediaSource;
-import com.google.android.exoplayer2.source.MediaPeriod;
-import com.google.android.exoplayer2.source.MediaSource;
-import com.google.android.exoplayer2.source.MediaSourceEventListener;
-import com.google.android.exoplayer2.source.ShuffleOrder;
-import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder;
-import com.google.android.exoplayer2.upstream.Allocator;
-import com.google.android.exoplayer2.upstream.TransferListener;
-import com.google.android.exoplayer2.util.Assertions;
-import com.google.android.exoplayer2.util.Util;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Concatenates multiple {@link MediaSource}s. The list of {@link MediaSource}s can be modified
- * during playback. It is valid for the same {@link MediaSource} instance to be present more than
- * once in the playlist.
- *
- *
With the exception of the constructor, all methods are called on the playback thread.
- */
-/* package */ class Playlist {
-
- /** Listener for source events. */
- public interface PlaylistInfoRefreshListener {
-
- /**
- * Called when the timeline of a media item has changed and a new timeline that reflects the
- * current playlist state needs to be created by calling {@link #createTimeline()}.
- *
- *
Called on the playback thread.
- */
- void onPlaylistUpdateRequested();
- }
-
- private final List mediaSourceHolders;
- private final Map mediaSourceByMediaPeriod;
- private final Map mediaSourceByUid;
- private final PlaylistInfoRefreshListener playlistInfoListener;
- private final MediaSourceEventListener.EventDispatcher eventDispatcher;
- private final HashMap childSources;
- private final Set enabledMediaSourceHolders;
-
- private ShuffleOrder shuffleOrder;
- private boolean isPrepared;
-
- @Nullable private TransferListener mediaTransferListener;
-
- @SuppressWarnings("initialization")
- public Playlist(PlaylistInfoRefreshListener listener) {
- playlistInfoListener = listener;
- shuffleOrder = new DefaultShuffleOrder(0);
- mediaSourceByMediaPeriod = new IdentityHashMap<>();
- mediaSourceByUid = new HashMap<>();
- mediaSourceHolders = new ArrayList<>();
- eventDispatcher = new MediaSourceEventListener.EventDispatcher();
- childSources = new HashMap<>();
- enabledMediaSourceHolders = new HashSet<>();
- }
-
- /**
- * Sets the media sources replacing any sources previously contained in the playlist.
- *
- * @param holders The list of {@link MediaSourceHolder}s to set.
- * @param shuffleOrder The new shuffle order.
- * @return The new {@link Timeline}.
- */
- public final Timeline setMediaSources(
- List holders, ShuffleOrder shuffleOrder) {
- removeMediaSourcesInternal(/* fromIndex= */ 0, /* toIndex= */ mediaSourceHolders.size());
- return addMediaSources(/* index= */ this.mediaSourceHolders.size(), holders, shuffleOrder);
- }
-
- /**
- * Adds multiple {@link MediaSourceHolder}s to the playlist.
- *
- * @param index The index at which the new {@link MediaSourceHolder}s will be inserted. This index
- * must be in the range of 0 <= index <= {@link #getSize()}.
- * @param holders A list of {@link MediaSourceHolder}s to be added.
- * @param shuffleOrder The new shuffle order.
- * @return The new {@link Timeline}.
- */
- public final Timeline addMediaSources(
- int index, List holders, ShuffleOrder shuffleOrder) {
- if (!holders.isEmpty()) {
- this.shuffleOrder = shuffleOrder;
- for (int insertionIndex = index; insertionIndex < index + holders.size(); insertionIndex++) {
- MediaSourceHolder holder = holders.get(insertionIndex - index);
- if (insertionIndex > 0) {
- MediaSourceHolder previousHolder = mediaSourceHolders.get(insertionIndex - 1);
- Timeline previousTimeline = previousHolder.mediaSource.getTimeline();
- holder.reset(
- /* firstWindowInChildIndex= */ previousHolder.firstWindowIndexInChild
- + previousTimeline.getWindowCount());
- } else {
- holder.reset(/* firstWindowIndexInChild= */ 0);
- }
- Timeline newTimeline = holder.mediaSource.getTimeline();
- correctOffsets(
- /* startIndex= */ insertionIndex,
- /* windowOffsetUpdate= */ newTimeline.getWindowCount());
- mediaSourceHolders.add(insertionIndex, holder);
- mediaSourceByUid.put(holder.uid, holder);
- if (isPrepared) {
- prepareChildSource(holder);
- if (mediaSourceByMediaPeriod.isEmpty()) {
- enabledMediaSourceHolders.add(holder);
- } else {
- disableChildSource(holder);
- }
- }
- }
- }
- return createTimeline();
- }
-
- /**
- * Removes a range of {@link MediaSourceHolder}s from the playlist, by specifying an initial index
- * (included) and a final index (excluded).
- *
- * Note: when specified range is empty, no actual media source is removed and no exception is
- * thrown.
- *
- * @param fromIndex The initial range index, pointing to the first media source that will be
- * removed. This index must be in the range of 0 <= index <= {@link #getSize()}.
- * @param toIndex The final range index, pointing to the first media source that will be left
- * untouched. This index must be in the range of 0 <= index <= {@link #getSize()}.
- * @param shuffleOrder The new shuffle order.
- * @return The new {@link Timeline}.
- * @throws IllegalArgumentException When the range is malformed, i.e. {@code fromIndex} < 0,
- * {@code toIndex} > {@link #getSize()}, {@code fromIndex} > {@code toIndex}
- */
- public final Timeline removeMediaSourceRange(
- int fromIndex, int toIndex, ShuffleOrder shuffleOrder) {
- Assertions.checkArgument(fromIndex >= 0 && fromIndex <= toIndex && toIndex <= getSize());
- this.shuffleOrder = shuffleOrder;
- removeMediaSourcesInternal(fromIndex, toIndex);
- return createTimeline();
- }
-
- /**
- * Moves an existing media source within the playlist.
- *
- * @param currentIndex The current index of the media source in the playlist. This index must be
- * in the range of 0 <= index < {@link #getSize()}.
- * @param newIndex The target index of the media source in the playlist. This index must be in the
- * range of 0 <= index < {@link #getSize()}.
- * @param shuffleOrder The new shuffle order.
- * @return The new {@link Timeline}.
- * @throws IllegalArgumentException When an index is invalid, i.e. {@code currentIndex} < 0,
- * {@code currentIndex} >= {@link #getSize()}, {@code newIndex} < 0
- */
- public final Timeline moveMediaSource(int currentIndex, int newIndex, ShuffleOrder shuffleOrder) {
- return moveMediaSourceRange(currentIndex, currentIndex + 1, newIndex, shuffleOrder);
- }
-
- /**
- * Moves a range of media sources within the playlist.
- *
- *
Note: when specified range is empty or the from index equals the new from index, no actual
- * media source is moved and no exception is thrown.
- *
- * @param fromIndex The initial range index, pointing to the first media source of the range that
- * will be moved. This index must be in the range of 0 <= index <= {@link #getSize()}.
- * @param toIndex The final range index, pointing to the first media source that will be left
- * untouched. This index must be larger or equals than {@code fromIndex}.
- * @param newFromIndex The target index of the first media source of the range that will be moved.
- * @param shuffleOrder The new shuffle order.
- * @return The new {@link Timeline}.
- * @throws IllegalArgumentException When the range is malformed, i.e. {@code fromIndex} < 0,
- * {@code toIndex} < {@code fromIndex}, {@code fromIndex} > {@code toIndex}, {@code
- * newFromIndex} < 0
- */
- public Timeline moveMediaSourceRange(
- int fromIndex, int toIndex, int newFromIndex, ShuffleOrder shuffleOrder) {
- Assertions.checkArgument(
- fromIndex >= 0 && fromIndex <= toIndex && toIndex <= getSize() && newFromIndex >= 0);
- this.shuffleOrder = shuffleOrder;
- if (fromIndex == toIndex || fromIndex == newFromIndex) {
- return createTimeline();
- }
- int startIndex = Math.min(fromIndex, newFromIndex);
- int newEndIndex = newFromIndex + (toIndex - fromIndex) - 1;
- int endIndex = Math.max(newEndIndex, toIndex - 1);
- int windowOffset = mediaSourceHolders.get(startIndex).firstWindowIndexInChild;
- moveMediaSourceHolders(mediaSourceHolders, fromIndex, toIndex, newFromIndex);
- for (int i = startIndex; i <= endIndex; i++) {
- MediaSourceHolder holder = mediaSourceHolders.get(i);
- holder.firstWindowIndexInChild = windowOffset;
- windowOffset += holder.mediaSource.getTimeline().getWindowCount();
- }
- return createTimeline();
- }
-
- /** Clears the playlist. */
- public final Timeline clear(@Nullable ShuffleOrder shuffleOrder) {
- this.shuffleOrder = shuffleOrder != null ? shuffleOrder : this.shuffleOrder.cloneAndClear();
- removeMediaSourcesInternal(/* fromIndex= */ 0, /* toIndex= */ getSize());
- return createTimeline();
- }
-
- /** Whether the playlist is prepared. */
- public final boolean isPrepared() {
- return isPrepared;
- }
-
- /** Returns the number of media sources in the playlist. */
- public final int getSize() {
- return mediaSourceHolders.size();
- }
-
- /**
- * Sets the {@link AnalyticsCollector}.
- *
- * @param handler The handler on which to call the collector.
- * @param analyticsCollector The analytics collector.
- */
- public final void setAnalyticsCollector(Handler handler, AnalyticsCollector analyticsCollector) {
- eventDispatcher.addEventListener(handler, analyticsCollector);
- }
-
- /**
- * Sets a new shuffle order to use when shuffling the child media sources.
- *
- * @param shuffleOrder A {@link ShuffleOrder}.
- */
- public final Timeline setShuffleOrder(ShuffleOrder shuffleOrder) {
- int size = getSize();
- if (shuffleOrder.getLength() != size) {
- shuffleOrder =
- shuffleOrder
- .cloneAndClear()
- .cloneAndInsert(/* insertionIndex= */ 0, /* insertionCount= */ size);
- }
- this.shuffleOrder = shuffleOrder;
- return createTimeline();
- }
-
- /** Prepares the playlist. */
- public final void prepare(@Nullable TransferListener mediaTransferListener) {
- Assertions.checkState(!isPrepared);
- this.mediaTransferListener = mediaTransferListener;
- for (int i = 0; i < mediaSourceHolders.size(); i++) {
- MediaSourceHolder mediaSourceHolder = mediaSourceHolders.get(i);
- prepareChildSource(mediaSourceHolder);
- enabledMediaSourceHolders.add(mediaSourceHolder);
- }
- isPrepared = true;
- }
-
- /**
- * Returns a new {@link MediaPeriod} identified by {@code periodId}.
- *
- * @param id The identifier of the period.
- * @param allocator An {@link Allocator} from which to obtain media buffer allocations.
- * @param startPositionUs The expected start position, in microseconds.
- * @return A new {@link MediaPeriod}.
- */
- public MediaPeriod createPeriod(
- MediaSource.MediaPeriodId id, Allocator allocator, long startPositionUs) {
- Object mediaSourceHolderUid = getMediaSourceHolderUid(id.periodUid);
- MediaSource.MediaPeriodId childMediaPeriodId =
- id.copyWithPeriodUid(getChildPeriodUid(id.periodUid));
- MediaSourceHolder holder = Assertions.checkNotNull(mediaSourceByUid.get(mediaSourceHolderUid));
- enableMediaSource(holder);
- holder.activeMediaPeriodIds.add(childMediaPeriodId);
- MediaPeriod mediaPeriod =
- holder.mediaSource.createPeriod(childMediaPeriodId, allocator, startPositionUs);
- mediaSourceByMediaPeriod.put(mediaPeriod, holder);
- disableUnusedMediaSources();
- return mediaPeriod;
- }
-
- /**
- * Releases the period.
- *
- * @param mediaPeriod The period to release.
- */
- public final void releasePeriod(MediaPeriod mediaPeriod) {
- MediaSourceHolder holder =
- Assertions.checkNotNull(mediaSourceByMediaPeriod.remove(mediaPeriod));
- holder.mediaSource.releasePeriod(mediaPeriod);
- holder.activeMediaPeriodIds.remove(((MaskingMediaPeriod) mediaPeriod).id);
- if (!mediaSourceByMediaPeriod.isEmpty()) {
- disableUnusedMediaSources();
- }
- maybeReleaseChildSource(holder);
- }
-
- /** Releases the playlist. */
- public final void release() {
- for (MediaSourceAndListener childSource : childSources.values()) {
- childSource.mediaSource.releaseSource(childSource.caller);
- childSource.mediaSource.removeEventListener(childSource.eventListener);
- }
- childSources.clear();
- enabledMediaSourceHolders.clear();
- isPrepared = false;
- }
-
- /** Throws any pending error encountered while loading or refreshing. */
- public final void maybeThrowSourceInfoRefreshError() throws IOException {
- for (MediaSourceAndListener childSource : childSources.values()) {
- childSource.mediaSource.maybeThrowSourceInfoRefreshError();
- }
- }
-
- /** Creates a timeline reflecting the current state of the playlist. */
- public final Timeline createTimeline() {
- if (mediaSourceHolders.isEmpty()) {
- return Timeline.EMPTY;
- }
- int windowOffset = 0;
- for (int i = 0; i < mediaSourceHolders.size(); i++) {
- MediaSourceHolder mediaSourceHolder = mediaSourceHolders.get(i);
- mediaSourceHolder.firstWindowIndexInChild = windowOffset;
- windowOffset += mediaSourceHolder.mediaSource.getTimeline().getWindowCount();
- }
- return new PlaylistTimeline(mediaSourceHolders, shuffleOrder);
- }
-
- // Internal methods.
-
- private void enableMediaSource(MediaSourceHolder mediaSourceHolder) {
- enabledMediaSourceHolders.add(mediaSourceHolder);
- @Nullable MediaSourceAndListener enabledChild = childSources.get(mediaSourceHolder);
- if (enabledChild != null) {
- enabledChild.mediaSource.enable(enabledChild.caller);
- }
- }
-
- private void disableUnusedMediaSources() {
- Iterator iterator = enabledMediaSourceHolders.iterator();
- while (iterator.hasNext()) {
- MediaSourceHolder holder = iterator.next();
- if (holder.activeMediaPeriodIds.isEmpty()) {
- disableChildSource(holder);
- iterator.remove();
- }
- }
- }
-
- private void disableChildSource(MediaSourceHolder holder) {
- @Nullable MediaSourceAndListener disabledChild = childSources.get(holder);
- if (disabledChild != null) {
- disabledChild.mediaSource.disable(disabledChild.caller);
- }
- }
-
- private void removeMediaSourcesInternal(int fromIndex, int toIndex) {
- for (int index = toIndex - 1; index >= fromIndex; index--) {
- MediaSourceHolder holder = mediaSourceHolders.remove(index);
- mediaSourceByUid.remove(holder.uid);
- Timeline oldTimeline = holder.mediaSource.getTimeline();
- correctOffsets(
- /* startIndex= */ index, /* windowOffsetUpdate= */ -oldTimeline.getWindowCount());
- holder.isRemoved = true;
- if (isPrepared) {
- maybeReleaseChildSource(holder);
- }
- }
- }
-
- private void correctOffsets(int startIndex, int windowOffsetUpdate) {
- for (int i = startIndex; i < mediaSourceHolders.size(); i++) {
- MediaSourceHolder mediaSourceHolder = mediaSourceHolders.get(i);
- mediaSourceHolder.firstWindowIndexInChild += windowOffsetUpdate;
- }
- }
-
- // Internal methods to manage child sources.
-
- @Nullable
- private static MediaSource.MediaPeriodId getMediaPeriodIdForChildMediaPeriodId(
- MediaSourceHolder mediaSourceHolder, MediaSource.MediaPeriodId mediaPeriodId) {
- for (int i = 0; i < mediaSourceHolder.activeMediaPeriodIds.size(); i++) {
- // Ensure the reported media period id has the same window sequence number as the one created
- // by this media source. Otherwise it does not belong to this child source.
- if (mediaSourceHolder.activeMediaPeriodIds.get(i).windowSequenceNumber
- == mediaPeriodId.windowSequenceNumber) {
- Object periodUid = getPeriodUid(mediaSourceHolder, mediaPeriodId.periodUid);
- return mediaPeriodId.copyWithPeriodUid(periodUid);
- }
- }
- return null;
- }
-
- private static int getWindowIndexForChildWindowIndex(
- MediaSourceHolder mediaSourceHolder, int windowIndex) {
- return windowIndex + mediaSourceHolder.firstWindowIndexInChild;
- }
-
- private void prepareChildSource(MediaSourceHolder holder) {
- MediaSource mediaSource = holder.mediaSource;
- MediaSource.MediaSourceCaller caller =
- (source, timeline) -> playlistInfoListener.onPlaylistUpdateRequested();
- MediaSourceEventListener eventListener = new ForwardingEventListener(holder);
- childSources.put(holder, new MediaSourceAndListener(mediaSource, caller, eventListener));
- mediaSource.addEventListener(new Handler(), eventListener);
- mediaSource.prepareSource(caller, mediaTransferListener);
- }
-
- private void maybeReleaseChildSource(MediaSourceHolder mediaSourceHolder) {
- // Release if the source has been removed from the playlist and no periods are still active.
- if (mediaSourceHolder.isRemoved && mediaSourceHolder.activeMediaPeriodIds.isEmpty()) {
- MediaSourceAndListener removedChild =
- Assertions.checkNotNull(childSources.remove(mediaSourceHolder));
- removedChild.mediaSource.releaseSource(removedChild.caller);
- removedChild.mediaSource.removeEventListener(removedChild.eventListener);
- enabledMediaSourceHolders.remove(mediaSourceHolder);
- }
- }
-
- /** Return uid of media source holder from period uid of concatenated source. */
- private static Object getMediaSourceHolderUid(Object periodUid) {
- return PlaylistTimeline.getChildTimelineUidFromConcatenatedUid(periodUid);
- }
-
- /** Return uid of child period from period uid of concatenated source. */
- private static Object getChildPeriodUid(Object periodUid) {
- return PlaylistTimeline.getChildPeriodUidFromConcatenatedUid(periodUid);
- }
-
- private static Object getPeriodUid(MediaSourceHolder holder, Object childPeriodUid) {
- return PlaylistTimeline.getConcatenatedUid(holder.uid, childPeriodUid);
- }
-
- /* package */ static void moveMediaSourceHolders(
- List mediaSourceHolders, int fromIndex, int toIndex, int newFromIndex) {
- MediaSourceHolder[] removedItems = new MediaSourceHolder[toIndex - fromIndex];
- for (int i = removedItems.length - 1; i >= 0; i--) {
- removedItems[i] = mediaSourceHolders.remove(fromIndex + i);
- }
- mediaSourceHolders.addAll(
- Math.min(newFromIndex, mediaSourceHolders.size()), Arrays.asList(removedItems));
- }
-
- /** Data class to hold playlist media sources together with meta data needed to process them. */
- /* package */ static final class MediaSourceHolder {
-
- public final MaskingMediaSource mediaSource;
- public final Object uid;
- public final List activeMediaPeriodIds;
-
- public int firstWindowIndexInChild;
- public boolean isRemoved;
-
- public MediaSourceHolder(MediaSource mediaSource, boolean useLazyPreparation) {
- this.mediaSource = new MaskingMediaSource(mediaSource, useLazyPreparation);
- this.activeMediaPeriodIds = new ArrayList<>();
- this.uid = new Object();
- }
-
- public void reset(int firstWindowIndexInChild) {
- this.firstWindowIndexInChild = firstWindowIndexInChild;
- this.isRemoved = false;
- this.activeMediaPeriodIds.clear();
- }
- }
-
- /** Timeline exposing concatenated timelines of playlist media sources. */
- /* package */ static final class PlaylistTimeline extends AbstractConcatenatedTimeline {
-
- private final int windowCount;
- private final int periodCount;
- private final int[] firstPeriodInChildIndices;
- private final int[] firstWindowInChildIndices;
- private final Timeline[] timelines;
- private final Object[] uids;
- private final HashMap childIndexByUid;
-
- public PlaylistTimeline(
- Collection mediaSourceHolders, ShuffleOrder shuffleOrder) {
- super(/* isAtomic= */ false, shuffleOrder);
- int childCount = mediaSourceHolders.size();
- firstPeriodInChildIndices = new int[childCount];
- firstWindowInChildIndices = new int[childCount];
- timelines = new Timeline[childCount];
- uids = new Object[childCount];
- childIndexByUid = new HashMap<>();
- int index = 0;
- int windowCount = 0;
- int periodCount = 0;
- for (MediaSourceHolder mediaSourceHolder : mediaSourceHolders) {
- timelines[index] = mediaSourceHolder.mediaSource.getTimeline();
- firstWindowInChildIndices[index] = windowCount;
- firstPeriodInChildIndices[index] = periodCount;
- windowCount += timelines[index].getWindowCount();
- periodCount += timelines[index].getPeriodCount();
- uids[index] = mediaSourceHolder.uid;
- childIndexByUid.put(uids[index], index++);
- }
- this.windowCount = windowCount;
- this.periodCount = periodCount;
- }
-
- @Override
- protected int getChildIndexByPeriodIndex(int periodIndex) {
- return Util.binarySearchFloor(firstPeriodInChildIndices, periodIndex + 1, false, false);
- }
-
- @Override
- protected int getChildIndexByWindowIndex(int windowIndex) {
- return Util.binarySearchFloor(firstWindowInChildIndices, windowIndex + 1, false, false);
- }
-
- @Override
- protected int getChildIndexByChildUid(Object childUid) {
- Integer index = childIndexByUid.get(childUid);
- return index == null ? C.INDEX_UNSET : index;
- }
-
- @Override
- protected Timeline getTimelineByChildIndex(int childIndex) {
- return timelines[childIndex];
- }
-
- @Override
- protected int getFirstPeriodIndexByChildIndex(int childIndex) {
- return firstPeriodInChildIndices[childIndex];
- }
-
- @Override
- protected int getFirstWindowIndexByChildIndex(int childIndex) {
- return firstWindowInChildIndices[childIndex];
- }
-
- @Override
- protected Object getChildUidByChildIndex(int childIndex) {
- return uids[childIndex];
- }
-
- @Override
- public int getWindowCount() {
- return windowCount;
- }
-
- @Override
- public int getPeriodCount() {
- return periodCount;
- }
- }
-
- private static final class MediaSourceAndListener {
-
- public final MediaSource mediaSource;
- public final MediaSource.MediaSourceCaller caller;
- public final MediaSourceEventListener eventListener;
-
- public MediaSourceAndListener(
- MediaSource mediaSource,
- MediaSource.MediaSourceCaller caller,
- MediaSourceEventListener eventListener) {
- this.mediaSource = mediaSource;
- this.caller = caller;
- this.eventListener = eventListener;
- }
- }
-
- private final class ForwardingEventListener implements MediaSourceEventListener {
-
- private final Playlist.MediaSourceHolder id;
- private EventDispatcher eventDispatcher;
-
- public ForwardingEventListener(Playlist.MediaSourceHolder id) {
- eventDispatcher = Playlist.this.eventDispatcher;
- this.id = id;
- }
-
- @Override
- public void onMediaPeriodCreated(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
- if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
- eventDispatcher.mediaPeriodCreated();
- }
- }
-
- @Override
- public void onMediaPeriodReleased(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
- if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
- eventDispatcher.mediaPeriodReleased();
- }
- }
-
- @Override
- public void onLoadStarted(
- int windowIndex,
- @Nullable MediaSource.MediaPeriodId mediaPeriodId,
- LoadEventInfo loadEventData,
- MediaLoadData mediaLoadData) {
- if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
- eventDispatcher.loadStarted(loadEventData, mediaLoadData);
- }
- }
-
- @Override
- public void onLoadCompleted(
- int windowIndex,
- @Nullable MediaSource.MediaPeriodId mediaPeriodId,
- LoadEventInfo loadEventData,
- MediaLoadData mediaLoadData) {
- if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
- eventDispatcher.loadCompleted(loadEventData, mediaLoadData);
- }
- }
-
- @Override
- public void onLoadCanceled(
- int windowIndex,
- @Nullable MediaSource.MediaPeriodId mediaPeriodId,
- LoadEventInfo loadEventData,
- MediaLoadData mediaLoadData) {
- if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
- eventDispatcher.loadCanceled(loadEventData, mediaLoadData);
- }
- }
-
- @Override
- public void onLoadError(
- int windowIndex,
- @Nullable MediaSource.MediaPeriodId mediaPeriodId,
- LoadEventInfo loadEventData,
- MediaLoadData mediaLoadData,
- IOException error,
- boolean wasCanceled) {
- if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
- eventDispatcher.loadError(loadEventData, mediaLoadData, error, wasCanceled);
- }
- }
-
- @Override
- public void onReadingStarted(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
- if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
- eventDispatcher.readingStarted();
- }
- }
-
- @Override
- public void onUpstreamDiscarded(
- int windowIndex,
- @Nullable MediaSource.MediaPeriodId mediaPeriodId,
- MediaLoadData mediaLoadData) {
- if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
- eventDispatcher.upstreamDiscarded(mediaLoadData);
- }
- }
-
- @Override
- public void onDownstreamFormatChanged(
- int windowIndex,
- @Nullable MediaSource.MediaPeriodId mediaPeriodId,
- MediaLoadData mediaLoadData) {
- if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
- eventDispatcher.downstreamFormatChanged(mediaLoadData);
- }
- }
-
- /** Updates the event dispatcher and returns whether the event should be dispatched. */
- private boolean maybeUpdateEventDispatcher(
- int childWindowIndex, @Nullable MediaSource.MediaPeriodId childMediaPeriodId) {
- @Nullable MediaSource.MediaPeriodId mediaPeriodId = null;
- if (childMediaPeriodId != null) {
- mediaPeriodId = getMediaPeriodIdForChildMediaPeriodId(id, childMediaPeriodId);
- if (mediaPeriodId == null) {
- // Media period not found. Ignore event.
- return false;
- }
- }
- int windowIndex = getWindowIndexForChildWindowIndex(id, childWindowIndex);
- if (eventDispatcher.windowIndex != windowIndex
- || !Util.areEqual(eventDispatcher.mediaPeriodId, mediaPeriodId)) {
- eventDispatcher =
- Playlist.this.eventDispatcher.withParameters(
- windowIndex, mediaPeriodId, /* mediaTimeOffsetMs= */ 0L);
- }
- return true;
- }
- }
-}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/AbstractConcatenatedTimeline.java b/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java
similarity index 98%
rename from library/core/src/main/java/com/google/android/exoplayer2/AbstractConcatenatedTimeline.java
rename to library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java
index 73bb49ed40..29ef1faa80 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/AbstractConcatenatedTimeline.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.google.android.exoplayer2;
+package com.google.android.exoplayer2.source;
import android.util.Pair;
-import com.google.android.exoplayer2.source.ShuffleOrder;
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.Player;
+import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.util.Assertions;
/** Abstract base class for the concatenation of one or more {@link Timeline}s. */
-public abstract class AbstractConcatenatedTimeline extends Timeline {
+/* package */ abstract class AbstractConcatenatedTimeline extends Timeline {
private final int childCount;
private final ShuffleOrder shuffleOrder;
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 c1ab78a9bc..545b8f5155 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
@@ -19,7 +19,6 @@ import android.os.Handler;
import android.os.Message;
import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
-import com.google.android.exoplayer2.AbstractConcatenatedTimeline;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource.MediaSourceHolder;
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java
index 8769a84d95..cedc6f911d 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java
@@ -16,7 +16,6 @@
package com.google.android.exoplayer2.source;
import androidx.annotation.Nullable;
-import com.google.android.exoplayer2.AbstractConcatenatedTimeline;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Player;
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/PlaylistTest.java b/library/core/src/test/java/com/google/android/exoplayer2/PlaylistTest.java
deleted file mode 100644
index cc551db8ac..0000000000
--- a/library/core/src/test/java/com/google/android/exoplayer2/PlaylistTest.java
+++ /dev/null
@@ -1,510 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.android.exoplayer2;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertSame;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import com.google.android.exoplayer2.source.MediaSource;
-import com.google.android.exoplayer2.source.ShuffleOrder;
-import com.google.android.exoplayer2.testutil.FakeMediaSource;
-import com.google.android.exoplayer2.testutil.FakeShuffleOrder;
-import com.google.android.exoplayer2.testutil.FakeTimeline;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/** Unit test for {@link Playlist}. */
-@RunWith(AndroidJUnit4.class)
-public class PlaylistTest {
-
- private static final int PLAYLIST_SIZE = 4;
-
- private Playlist playlist;
-
- @Before
- public void setUp() {
- playlist = new Playlist(mock(Playlist.PlaylistInfoRefreshListener.class));
- }
-
- @Test
- public void testEmptyPlaylist_expectConstantTimelineInstanceEMPTY() {
- ShuffleOrder.DefaultShuffleOrder shuffleOrder =
- new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0);
- List fakeHolders = createFakeHolders();
-
- Timeline timeline = playlist.setMediaSources(fakeHolders, shuffleOrder);
- assertNotSame(timeline, Timeline.EMPTY);
-
- // Remove all media sources.
- timeline =
- playlist.removeMediaSourceRange(
- /* fromIndex= */ 0, /* toIndex= */ timeline.getWindowCount(), shuffleOrder);
- assertSame(timeline, Timeline.EMPTY);
-
- timeline = playlist.setMediaSources(fakeHolders, shuffleOrder);
- assertNotSame(timeline, Timeline.EMPTY);
- // Clear.
- timeline = playlist.clear(shuffleOrder);
- assertSame(timeline, Timeline.EMPTY);
- }
-
- @Test
- public void testPrepareAndReprepareAfterRelease_expectSourcePreparationAfterPlaylistPrepare() {
- MediaSource mockMediaSource1 = mock(MediaSource.class);
- MediaSource mockMediaSource2 = mock(MediaSource.class);
- playlist.setMediaSources(
- createFakeHoldersWithSources(
- /* useLazyPreparation= */ false, mockMediaSource1, mockMediaSource2),
- new ShuffleOrder.DefaultShuffleOrder(/* length= */ 2));
- // Verify prepare is called once on prepare.
- verify(mockMediaSource1, times(0))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
- verify(mockMediaSource2, times(0))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
-
- playlist.prepare(/* mediaTransferListener= */ null);
- assertThat(playlist.isPrepared()).isTrue();
- // Verify prepare is called once on prepare.
- verify(mockMediaSource1, times(1))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
- verify(mockMediaSource2, times(1))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
-
- playlist.release();
- playlist.prepare(/* mediaTransferListener= */ null);
- // Verify prepare is called a second time on re-prepare.
- verify(mockMediaSource1, times(2))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
- verify(mockMediaSource2, times(2))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
- }
-
- @Test
- public void testSetMediaSources_playlistUnprepared_notUsingLazyPreparation() {
- ShuffleOrder.DefaultShuffleOrder shuffleOrder =
- new ShuffleOrder.DefaultShuffleOrder(/* length= */ 2);
- MediaSource mockMediaSource1 = mock(MediaSource.class);
- MediaSource mockMediaSource2 = mock(MediaSource.class);
- List mediaSources =
- createFakeHoldersWithSources(
- /* useLazyPreparation= */ false, mockMediaSource1, mockMediaSource2);
- Timeline timeline = playlist.setMediaSources(mediaSources, shuffleOrder);
-
- assertThat(timeline.getWindowCount()).isEqualTo(2);
- assertThat(playlist.getSize()).isEqualTo(2);
-
- // Assert holder offsets have been set properly
- for (int i = 0; i < mediaSources.size(); i++) {
- Playlist.MediaSourceHolder mediaSourceHolder = mediaSources.get(i);
- assertThat(mediaSourceHolder.isRemoved).isFalse();
- assertThat(mediaSourceHolder.firstWindowIndexInChild).isEqualTo(i);
- }
-
- // Set media items again. The second holder is re-used.
- List moreMediaSources =
- createFakeHoldersWithSources(/* useLazyPreparation= */ false, mock(MediaSource.class));
- moreMediaSources.add(mediaSources.get(1));
- timeline = playlist.setMediaSources(moreMediaSources, shuffleOrder);
-
- assertThat(playlist.getSize()).isEqualTo(2);
- assertThat(timeline.getWindowCount()).isEqualTo(2);
- for (int i = 0; i < moreMediaSources.size(); i++) {
- Playlist.MediaSourceHolder mediaSourceHolder = moreMediaSources.get(i);
- assertThat(mediaSourceHolder.isRemoved).isFalse();
- assertThat(mediaSourceHolder.firstWindowIndexInChild).isEqualTo(i);
- }
- // Expect removed holders and sources to be removed without releasing.
- verify(mockMediaSource1, times(0)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- assertThat(mediaSources.get(0).isRemoved).isTrue();
- // Expect re-used holder and source not to be removed.
- verify(mockMediaSource2, times(0)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- assertThat(mediaSources.get(1).isRemoved).isFalse();
- }
-
- @Test
- public void testSetMediaSources_playlistPrepared_notUsingLazyPreparation() {
- ShuffleOrder.DefaultShuffleOrder shuffleOrder =
- new ShuffleOrder.DefaultShuffleOrder(/* length= */ 2);
- MediaSource mockMediaSource1 = mock(MediaSource.class);
- MediaSource mockMediaSource2 = mock(MediaSource.class);
- List mediaSources =
- createFakeHoldersWithSources(
- /* useLazyPreparation= */ false, mockMediaSource1, mockMediaSource2);
-
- playlist.prepare(/* mediaTransferListener= */ null);
- playlist.setMediaSources(mediaSources, shuffleOrder);
-
- // Verify sources are prepared.
- verify(mockMediaSource1, times(1))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
- verify(mockMediaSource2, times(1))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
-
- // Set media items again. The second holder is re-used.
- List moreMediaSources =
- createFakeHoldersWithSources(/* useLazyPreparation= */ false, mock(MediaSource.class));
- moreMediaSources.add(mediaSources.get(1));
- playlist.setMediaSources(moreMediaSources, shuffleOrder);
-
- // Expect removed holders and sources to be removed and released.
- verify(mockMediaSource1, times(1)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- assertThat(mediaSources.get(0).isRemoved).isTrue();
- // Expect re-used holder and source not to be removed but released.
- verify(mockMediaSource2, times(1)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- assertThat(mediaSources.get(1).isRemoved).isFalse();
- verify(mockMediaSource2, times(2))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
- }
-
- @Test
- public void testAddMediaSources_playlistUnprepared_notUsingLazyPreparation_expectUnprepared() {
- MediaSource mockMediaSource1 = mock(MediaSource.class);
- MediaSource mockMediaSource2 = mock(MediaSource.class);
- List mediaSources =
- createFakeHoldersWithSources(
- /* useLazyPreparation= */ false, mockMediaSource1, mockMediaSource2);
- playlist.addMediaSources(/* index= */ 0, mediaSources, new ShuffleOrder.DefaultShuffleOrder(2));
-
- assertThat(playlist.getSize()).isEqualTo(2);
- // Verify lazy initialization does not call prepare on sources.
- verify(mockMediaSource1, times(0))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
- verify(mockMediaSource2, times(0))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
-
- for (int i = 0; i < mediaSources.size(); i++) {
- assertThat(mediaSources.get(i).firstWindowIndexInChild).isEqualTo(i);
- assertThat(mediaSources.get(i).isRemoved).isFalse();
- }
-
- // Add for more sources in between.
- List moreMediaSources = createFakeHolders();
- playlist.addMediaSources(
- /* index= */ 1, moreMediaSources, new ShuffleOrder.DefaultShuffleOrder(/* length= */ 3));
-
- assertThat(mediaSources.get(0).firstWindowIndexInChild).isEqualTo(0);
- assertThat(moreMediaSources.get(0).firstWindowIndexInChild).isEqualTo(1);
- assertThat(moreMediaSources.get(3).firstWindowIndexInChild).isEqualTo(4);
- assertThat(mediaSources.get(1).firstWindowIndexInChild).isEqualTo(5);
- }
-
- @Test
- public void testAddMediaSources_playlistPrepared_notUsingLazyPreparation_expectPrepared() {
- MediaSource mockMediaSource1 = mock(MediaSource.class);
- MediaSource mockMediaSource2 = mock(MediaSource.class);
- playlist.prepare(/* mediaTransferListener= */ null);
- playlist.addMediaSources(
- /* index= */ 0,
- createFakeHoldersWithSources(
- /* useLazyPreparation= */ false, mockMediaSource1, mockMediaSource2),
- new ShuffleOrder.DefaultShuffleOrder(/* length= */ 2));
-
- // Verify prepare is called on sources when added.
- verify(mockMediaSource1, times(1))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
- verify(mockMediaSource2, times(1))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
- }
-
- @Test
- public void testMoveMediaSources() {
- ShuffleOrder.DefaultShuffleOrder shuffleOrder =
- new ShuffleOrder.DefaultShuffleOrder(/* length= */ 4);
- List holders = createFakeHolders();
- playlist.addMediaSources(/* index= */ 0, holders, shuffleOrder);
-
- assertDefaultFirstWindowInChildIndexOrder(holders);
- playlist.moveMediaSource(/* currentIndex= */ 0, /* newIndex= */ 3, shuffleOrder);
- assertFirstWindowInChildIndices(holders, 3, 0, 1, 2);
- playlist.moveMediaSource(/* currentIndex= */ 3, /* newIndex= */ 0, shuffleOrder);
- assertDefaultFirstWindowInChildIndexOrder(holders);
-
- playlist.moveMediaSourceRange(
- /* fromIndex= */ 0, /* toIndex= */ 2, /* newFromIndex= */ 2, shuffleOrder);
- assertFirstWindowInChildIndices(holders, 2, 3, 0, 1);
- playlist.moveMediaSourceRange(
- /* fromIndex= */ 2, /* toIndex= */ 4, /* newFromIndex= */ 0, shuffleOrder);
- assertDefaultFirstWindowInChildIndexOrder(holders);
-
- playlist.moveMediaSourceRange(
- /* fromIndex= */ 0, /* toIndex= */ 2, /* newFromIndex= */ 2, shuffleOrder);
- assertFirstWindowInChildIndices(holders, 2, 3, 0, 1);
- playlist.moveMediaSourceRange(
- /* fromIndex= */ 2, /* toIndex= */ 3, /* newFromIndex= */ 0, shuffleOrder);
- assertFirstWindowInChildIndices(holders, 0, 3, 1, 2);
- playlist.moveMediaSourceRange(
- /* fromIndex= */ 3, /* toIndex= */ 4, /* newFromIndex= */ 1, shuffleOrder);
- assertDefaultFirstWindowInChildIndexOrder(holders);
-
- // No-ops.
- playlist.moveMediaSourceRange(
- /* fromIndex= */ 0, /* toIndex= */ 4, /* newFromIndex= */ 0, shuffleOrder);
- assertDefaultFirstWindowInChildIndexOrder(holders);
- playlist.moveMediaSourceRange(
- /* fromIndex= */ 0, /* toIndex= */ 0, /* newFromIndex= */ 3, shuffleOrder);
- assertDefaultFirstWindowInChildIndexOrder(holders);
- }
-
- @Test
- public void testRemoveMediaSources_whenUnprepared_expectNoRelease() {
- MediaSource mockMediaSource1 = mock(MediaSource.class);
- MediaSource mockMediaSource2 = mock(MediaSource.class);
- MediaSource mockMediaSource3 = mock(MediaSource.class);
- MediaSource mockMediaSource4 = mock(MediaSource.class);
- ShuffleOrder.DefaultShuffleOrder shuffleOrder =
- new ShuffleOrder.DefaultShuffleOrder(/* length= */ 4);
-
- List holders =
- createFakeHoldersWithSources(
- /* useLazyPreparation= */ false,
- mockMediaSource1,
- mockMediaSource2,
- mockMediaSource3,
- mockMediaSource4);
- playlist.addMediaSources(/* index= */ 0, holders, shuffleOrder);
- playlist.removeMediaSourceRange(/* fromIndex= */ 1, /* toIndex= */ 3, shuffleOrder);
-
- assertThat(playlist.getSize()).isEqualTo(2);
- Playlist.MediaSourceHolder removedHolder1 = holders.remove(1);
- Playlist.MediaSourceHolder removedHolder2 = holders.remove(1);
-
- assertDefaultFirstWindowInChildIndexOrder(holders);
- assertThat(removedHolder1.isRemoved).isTrue();
- assertThat(removedHolder2.isRemoved).isTrue();
- verify(mockMediaSource1, times(0)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- verify(mockMediaSource2, times(0)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- verify(mockMediaSource3, times(0)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- verify(mockMediaSource4, times(0)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- }
-
- @Test
- public void testRemoveMediaSources_whenPrepared_expectRelease() {
- MediaSource mockMediaSource1 = mock(MediaSource.class);
- MediaSource mockMediaSource2 = mock(MediaSource.class);
- MediaSource mockMediaSource3 = mock(MediaSource.class);
- MediaSource mockMediaSource4 = mock(MediaSource.class);
- ShuffleOrder.DefaultShuffleOrder shuffleOrder =
- new ShuffleOrder.DefaultShuffleOrder(/* length= */ 4);
-
- List holders =
- createFakeHoldersWithSources(
- /* useLazyPreparation= */ false,
- mockMediaSource1,
- mockMediaSource2,
- mockMediaSource3,
- mockMediaSource4);
- playlist.prepare(/* mediaTransferListener */ null);
- playlist.addMediaSources(/* index= */ 0, holders, shuffleOrder);
- playlist.removeMediaSourceRange(/* fromIndex= */ 1, /* toIndex= */ 3, shuffleOrder);
-
- assertThat(playlist.getSize()).isEqualTo(2);
- holders.remove(2);
- holders.remove(1);
-
- assertDefaultFirstWindowInChildIndexOrder(holders);
- verify(mockMediaSource1, times(0)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- verify(mockMediaSource2, times(1)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- verify(mockMediaSource3, times(1)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- verify(mockMediaSource4, times(0)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- }
-
- @Test
- public void testRelease_playlistUnprepared_expectSourcesNotReleased() {
- MediaSource mockMediaSource = mock(MediaSource.class);
- Playlist.MediaSourceHolder mediaSourceHolder =
- new Playlist.MediaSourceHolder(mockMediaSource, /* useLazyPreparation= */ false);
-
- playlist.setMediaSources(
- Collections.singletonList(mediaSourceHolder),
- new ShuffleOrder.DefaultShuffleOrder(/* length= */ 1));
- verify(mockMediaSource, times(0))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
- playlist.release();
- verify(mockMediaSource, times(0)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- assertThat(mediaSourceHolder.isRemoved).isFalse();
- }
-
- @Test
- public void testRelease_playlistPrepared_expectSourcesReleasedNotRemoved() {
- MediaSource mockMediaSource = mock(MediaSource.class);
- Playlist.MediaSourceHolder mediaSourceHolder =
- new Playlist.MediaSourceHolder(mockMediaSource, /* useLazyPreparation= */ false);
-
- playlist.prepare(/* mediaTransferListener= */ null);
- playlist.setMediaSources(
- Collections.singletonList(mediaSourceHolder),
- new ShuffleOrder.DefaultShuffleOrder(/* length= */ 1));
- verify(mockMediaSource, times(1))
- .prepareSource(
- any(MediaSource.MediaSourceCaller.class), /* mediaTransferListener= */ isNull());
- playlist.release();
- verify(mockMediaSource, times(1)).releaseSource(any(MediaSource.MediaSourceCaller.class));
- assertThat(mediaSourceHolder.isRemoved).isFalse();
- }
-
- @Test
- public void testClearPlaylist_expectSourcesReleasedAndRemoved() {
- ShuffleOrder.DefaultShuffleOrder shuffleOrder =
- new ShuffleOrder.DefaultShuffleOrder(/* length= */ 4);
- MediaSource mockMediaSource1 = mock(MediaSource.class);
- MediaSource mockMediaSource2 = mock(MediaSource.class);
- List holders =
- createFakeHoldersWithSources(
- /* useLazyPreparation= */ false, mockMediaSource1, mockMediaSource2);
- playlist.setMediaSources(holders, shuffleOrder);
- playlist.prepare(/* mediaTransferListener= */ null);
-
- Timeline timeline = playlist.clear(shuffleOrder);
- assertThat(timeline.isEmpty()).isTrue();
- assertThat(holders.get(0).isRemoved).isTrue();
- assertThat(holders.get(1).isRemoved).isTrue();
- verify(mockMediaSource1, times(1)).releaseSource(any());
- verify(mockMediaSource2, times(1)).releaseSource(any());
- }
-
- @Test
- public void testSetMediaSources_expectTimelineUsesCustomShuffleOrder() {
- Timeline timeline =
- playlist.setMediaSources(createFakeHolders(), new FakeShuffleOrder(/* length=*/ 4));
- assertTimelineUsesFakeShuffleOrder(timeline);
- }
-
- @Test
- public void testAddMediaSources_expectTimelineUsesCustomShuffleOrder() {
- Timeline timeline =
- playlist.addMediaSources(
- /* index= */ 0, createFakeHolders(), new FakeShuffleOrder(PLAYLIST_SIZE));
- assertTimelineUsesFakeShuffleOrder(timeline);
- }
-
- @Test
- public void testMoveMediaSources_expectTimelineUsesCustomShuffleOrder() {
- ShuffleOrder shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ PLAYLIST_SIZE);
- playlist.addMediaSources(/* index= */ 0, createFakeHolders(), shuffleOrder);
- Timeline timeline =
- playlist.moveMediaSource(
- /* currentIndex= */ 0, /* newIndex= */ 1, new FakeShuffleOrder(PLAYLIST_SIZE));
- assertTimelineUsesFakeShuffleOrder(timeline);
- }
-
- @Test
- public void testMoveMediaSourceRange_expectTimelineUsesCustomShuffleOrder() {
- ShuffleOrder shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ PLAYLIST_SIZE);
- playlist.addMediaSources(/* index= */ 0, createFakeHolders(), shuffleOrder);
- Timeline timeline =
- playlist.moveMediaSourceRange(
- /* fromIndex= */ 0,
- /* toIndex= */ 2,
- /* newFromIndex= */ 2,
- new FakeShuffleOrder(PLAYLIST_SIZE));
- assertTimelineUsesFakeShuffleOrder(timeline);
- }
-
- @Test
- public void testRemoveMediaSourceRange_expectTimelineUsesCustomShuffleOrder() {
- ShuffleOrder shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ PLAYLIST_SIZE);
- playlist.addMediaSources(/* index= */ 0, createFakeHolders(), shuffleOrder);
- Timeline timeline =
- playlist.removeMediaSourceRange(
- /* fromIndex= */ 0, /* toIndex= */ 2, new FakeShuffleOrder(/* length= */ 2));
- assertTimelineUsesFakeShuffleOrder(timeline);
- }
-
- @Test
- public void testSetShuffleOrder_expectTimelineUsesCustomShuffleOrder() {
- playlist.setMediaSources(
- createFakeHolders(), new ShuffleOrder.DefaultShuffleOrder(/* length= */ PLAYLIST_SIZE));
- assertTimelineUsesFakeShuffleOrder(
- playlist.setShuffleOrder(new FakeShuffleOrder(PLAYLIST_SIZE)));
- }
-
- // Internal methods.
-
- private static void assertTimelineUsesFakeShuffleOrder(Timeline timeline) {
- assertThat(
- timeline.getNextWindowIndex(
- /* windowIndex= */ 0, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ true))
- .isEqualTo(-1);
- assertThat(
- timeline.getPreviousWindowIndex(
- /* windowIndex= */ timeline.getWindowCount() - 1,
- Player.REPEAT_MODE_OFF,
- /* shuffleModeEnabled= */ true))
- .isEqualTo(-1);
- }
-
- private static void assertDefaultFirstWindowInChildIndexOrder(
- List holders) {
- int[] indices = new int[holders.size()];
- for (int i = 0; i < indices.length; i++) {
- indices[i] = i;
- }
- assertFirstWindowInChildIndices(holders, indices);
- }
-
- private static void assertFirstWindowInChildIndices(
- List holders, int... firstWindowInChildIndices) {
- assertThat(holders).hasSize(firstWindowInChildIndices.length);
- for (int i = 0; i < holders.size(); i++) {
- assertThat(holders.get(i).firstWindowIndexInChild).isEqualTo(firstWindowInChildIndices[i]);
- }
- }
-
- private static List createFakeHolders() {
- MediaSource fakeMediaSource = new FakeMediaSource(new FakeTimeline(1));
- List holders = new ArrayList<>();
- for (int i = 0; i < PLAYLIST_SIZE; i++) {
- holders.add(new Playlist.MediaSourceHolder(fakeMediaSource, /* useLazyPreparation= */ true));
- }
- return holders;
- }
-
- private static List createFakeHoldersWithSources(
- boolean useLazyPreparation, MediaSource... sources) {
- List holders = new ArrayList<>();
- for (MediaSource mediaSource : sources) {
- holders.add(
- new Playlist.MediaSourceHolder(
- mediaSource, /* useLazyPreparation= */ useLazyPreparation));
- }
- return holders;
- }
-}