diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index f7abd4dd9d..441bb94d68 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -186,10 +186,7 @@ import java.util.concurrent.atomic.AtomicBoolean; internalPlaybackThread.start(); handler = clock.createHandler(internalPlaybackThread.getLooper(), this); deliverPendingMessageAtStartPositionRequired = true; - mediaSourceList = new MediaSourceList(this); - if (analyticsCollector != null) { - mediaSourceList.setAnalyticsCollector(eventHandler, analyticsCollector); - } + mediaSourceList = new MediaSourceList(/* listener= */ this, analyticsCollector, eventHandler); } public void experimental_setReleaseTimeoutMs(long releaseTimeoutMs) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/MediaSourceList.java b/library/core/src/main/java/com/google/android/exoplayer2/MediaSourceList.java index cffad118ad..518f7bc6cb 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/MediaSourceList.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/MediaSourceList.java @@ -52,7 +52,7 @@ import java.util.Set; * *

With the exception of the constructor, all methods are called on the playback thread. */ -/* package */ class MediaSourceList { +/* package */ final class MediaSourceList { /** Listener for source events. */ public interface MediaSourceListInfoRefreshListener { @@ -81,8 +81,20 @@ import java.util.Set; @Nullable private TransferListener mediaTransferListener; - @SuppressWarnings("initialization") - public MediaSourceList(MediaSourceListInfoRefreshListener listener) { + /** + * Creates the media source list. + * + * @param listener The {@link MediaSourceListInfoRefreshListener} to be informed of timeline + * changes. + * @param analyticsCollector An optional {@link AnalyticsCollector} to be registered for media + * source events. + * @param analyticsCollectorHandler The {@link Handler} to call {@link AnalyticsCollector} methods + * on. + */ + public MediaSourceList( + MediaSourceListInfoRefreshListener listener, + @Nullable AnalyticsCollector analyticsCollector, + Handler analyticsCollectorHandler) { mediaSourceListInfoListener = listener; shuffleOrder = new DefaultShuffleOrder(0); mediaSourceByMediaPeriod = new IdentityHashMap<>(); @@ -91,6 +103,12 @@ import java.util.Set; eventDispatcher = new MediaSourceEventListener.EventDispatcher(); childSources = new HashMap<>(); enabledMediaSourceHolders = new HashSet<>(); + if (analyticsCollector != null) { + eventDispatcher.addEventListener( + analyticsCollectorHandler, analyticsCollector, MediaSourceEventListener.class); + eventDispatcher.addEventListener( + analyticsCollectorHandler, analyticsCollector, DrmSessionEventListener.class); + } } /** @@ -100,8 +118,7 @@ import java.util.Set; * @param shuffleOrder The new shuffle order. * @return The new {@link Timeline}. */ - public final Timeline setMediaSources( - List holders, ShuffleOrder shuffleOrder) { + public Timeline setMediaSources(List holders, ShuffleOrder shuffleOrder) { removeMediaSourcesInternal(/* fromIndex= */ 0, /* toIndex= */ mediaSourceHolders.size()); return addMediaSources(/* index= */ this.mediaSourceHolders.size(), holders, shuffleOrder); } @@ -115,7 +132,7 @@ import java.util.Set; * @param shuffleOrder The new shuffle order. * @return The new {@link Timeline}. */ - public final Timeline addMediaSources( + public Timeline addMediaSources( int index, List holders, ShuffleOrder shuffleOrder) { if (!holders.isEmpty()) { this.shuffleOrder = shuffleOrder; @@ -165,8 +182,7 @@ import java.util.Set; * @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) { + public Timeline removeMediaSourceRange(int fromIndex, int toIndex, ShuffleOrder shuffleOrder) { Assertions.checkArgument(fromIndex >= 0 && fromIndex <= toIndex && toIndex <= getSize()); this.shuffleOrder = shuffleOrder; removeMediaSourcesInternal(fromIndex, toIndex); @@ -185,7 +201,7 @@ import java.util.Set; * @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) { + public Timeline moveMediaSource(int currentIndex, int newIndex, ShuffleOrder shuffleOrder) { return moveMediaSourceRange(currentIndex, currentIndex + 1, newIndex, shuffleOrder); } @@ -228,39 +244,28 @@ import java.util.Set; } /** Clears the playlist. */ - public final Timeline clear(@Nullable ShuffleOrder shuffleOrder) { + public 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() { + public boolean isPrepared() { return isPrepared; } /** Returns the number of media sources in the playlist. */ - public final int getSize() { + public 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, MediaSourceEventListener.class); - eventDispatcher.addEventListener(handler, analyticsCollector, DrmSessionEventListener.class); - } - /** * Sets a new shuffle order to use when shuffling the child media sources. * * @param shuffleOrder A {@link ShuffleOrder}. */ - public final Timeline setShuffleOrder(ShuffleOrder shuffleOrder) { + public Timeline setShuffleOrder(ShuffleOrder shuffleOrder) { int size = getSize(); if (shuffleOrder.getLength() != size) { shuffleOrder = @@ -273,7 +278,7 @@ import java.util.Set; } /** Prepares the playlist. */ - public final void prepare(@Nullable TransferListener mediaTransferListener) { + public void prepare(@Nullable TransferListener mediaTransferListener) { Assertions.checkState(!isPrepared); this.mediaTransferListener = mediaTransferListener; for (int i = 0; i < mediaSourceHolders.size(); i++) { @@ -312,7 +317,7 @@ import java.util.Set; * * @param mediaPeriod The period to release. */ - public final void releasePeriod(MediaPeriod mediaPeriod) { + public void releasePeriod(MediaPeriod mediaPeriod) { MediaSourceHolder holder = Assertions.checkNotNull(mediaSourceByMediaPeriod.remove(mediaPeriod)); holder.mediaSource.releasePeriod(mediaPeriod); @@ -324,7 +329,7 @@ import java.util.Set; } /** Releases the playlist. */ - public final void release() { + public void release() { for (MediaSourceAndListener childSource : childSources.values()) { try { childSource.mediaSource.releaseSource(childSource.caller); @@ -340,14 +345,14 @@ import java.util.Set; } /** Throws any pending error encountered while loading or refreshing. */ - public final void maybeThrowSourceInfoRefreshError() throws IOException { + public 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() { + public Timeline createTimeline() { if (mediaSourceHolders.isEmpty()) { return Timeline.EMPTY; } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java b/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java index 0ca5dd60ef..efd88aacaf 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java @@ -23,18 +23,19 @@ import static org.robolectric.annotation.LooperMode.Mode.LEGACY; import android.net.Uri; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; -import com.google.android.exoplayer2.source.ShuffleOrder; import com.google.android.exoplayer2.source.SinglePeriodTimeline; import com.google.android.exoplayer2.source.ads.AdPlaybackState; import com.google.android.exoplayer2.source.ads.SinglePeriodAdTimeline; import com.google.android.exoplayer2.testutil.FakeMediaSource; +import com.google.android.exoplayer2.testutil.FakeShuffleOrder; import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelectorResult; import com.google.android.exoplayer2.upstream.Allocator; -import java.util.Collections; +import com.google.android.exoplayer2.util.Util; +import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -70,12 +71,15 @@ public final class MediaPeriodQueueTest { private Allocator allocator; private MediaSourceList mediaSourceList; private FakeMediaSource fakeMediaSource; - private MediaSourceList.MediaSourceHolder mediaSourceHolder; @Before public void setUp() { mediaPeriodQueue = new MediaPeriodQueue(); - mediaSourceList = mock(MediaSourceList.class); + mediaSourceList = + new MediaSourceList( + mock(MediaSourceList.MediaSourceListInfoRefreshListener.class), + /* analyticsCollector= */ null, + Util.createHandlerForCurrentOrMainLooper()); rendererCapabilities = new RendererCapabilities[0]; trackSelector = mock(TrackSelector.class); allocator = mock(Allocator.class); @@ -408,10 +412,13 @@ public final class MediaPeriodQueueTest { private void setupTimeline(Timeline timeline) { fakeMediaSource = new FakeMediaSource(timeline); - mediaSourceHolder = new MediaSourceList.MediaSourceHolder(fakeMediaSource, false); + MediaSourceList.MediaSourceHolder mediaSourceHolder = + new MediaSourceList.MediaSourceHolder(fakeMediaSource, /* useLazyPreparation= */ false); + mediaSourceList.setMediaSources( + ImmutableList.of(mediaSourceHolder), new FakeShuffleOrder(/* length= */ 1)); mediaSourceHolder.mediaSource.prepareSourceInternal(/* mediaTransferListener */ null); - Timeline playlistTimeline = createPlaylistTimeline(); + Timeline playlistTimeline = mediaSourceList.createTimeline(); firstPeriodUid = playlistTimeline.getUidOfPeriod(/* periodIndex= */ 0); playbackInfo = @@ -443,13 +450,7 @@ public final class MediaPeriodQueueTest { SinglePeriodAdTimeline adTimeline = new SinglePeriodAdTimeline(CONTENT_TIMELINE, adPlaybackState); fakeMediaSource.setNewSourceInfo(adTimeline); - playbackInfo = playbackInfo.copyWithTimeline(createPlaylistTimeline()); - } - - private MediaSourceList.PlaylistTimeline createPlaylistTimeline() { - return new MediaSourceList.PlaylistTimeline( - Collections.singleton(mediaSourceHolder), - new ShuffleOrder.DefaultShuffleOrder(/* length= */ 1)); + playbackInfo = playbackInfo.copyWithTimeline(mediaSourceList.createTimeline()); } private void advance() { diff --git a/library/core/src/test/java/com/google/android/exoplayer2/MediaSourceListTest.java b/library/core/src/test/java/com/google/android/exoplayer2/MediaSourceListTest.java index bcea053115..b3ff5e5c55 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/MediaSourceListTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/MediaSourceListTest.java @@ -31,6 +31,7 @@ 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 com.google.android.exoplayer2.util.Util; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -51,7 +52,10 @@ public class MediaSourceListTest { @Before public void setUp() { mediaSourceList = - new MediaSourceList(mock(MediaSourceList.MediaSourceListInfoRefreshListener.class)); + new MediaSourceList( + mock(MediaSourceList.MediaSourceListInfoRefreshListener.class), + /* analyticsCollector= */ null, + Util.createHandlerForCurrentOrMainLooper()); } @Test