Simplify MediaSourceList setup and make class final.

The class was only non-final to allow mocking. Using the real class
in the test works equally well.

PiperOrigin-RevId: 317858805
This commit is contained in:
tonihei 2020-06-23 15:11:56 +01:00 committed by Christos Tsilopoulos
parent 093f9931b4
commit 9d8f54ab3a
4 changed files with 54 additions and 47 deletions

View File

@ -186,10 +186,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
internalPlaybackThread.start(); internalPlaybackThread.start();
handler = clock.createHandler(internalPlaybackThread.getLooper(), this); handler = clock.createHandler(internalPlaybackThread.getLooper(), this);
deliverPendingMessageAtStartPositionRequired = true; deliverPendingMessageAtStartPositionRequired = true;
mediaSourceList = new MediaSourceList(this); mediaSourceList = new MediaSourceList(/* listener= */ this, analyticsCollector, eventHandler);
if (analyticsCollector != null) {
mediaSourceList.setAnalyticsCollector(eventHandler, analyticsCollector);
}
} }
public void experimental_setReleaseTimeoutMs(long releaseTimeoutMs) { public void experimental_setReleaseTimeoutMs(long releaseTimeoutMs) {

View File

@ -52,7 +52,7 @@ import java.util.Set;
* *
* <p>With the exception of the constructor, all methods are called on the playback thread. * <p>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. */ /** Listener for source events. */
public interface MediaSourceListInfoRefreshListener { public interface MediaSourceListInfoRefreshListener {
@ -81,8 +81,20 @@ import java.util.Set;
@Nullable private TransferListener mediaTransferListener; @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; mediaSourceListInfoListener = listener;
shuffleOrder = new DefaultShuffleOrder(0); shuffleOrder = new DefaultShuffleOrder(0);
mediaSourceByMediaPeriod = new IdentityHashMap<>(); mediaSourceByMediaPeriod = new IdentityHashMap<>();
@ -91,6 +103,12 @@ import java.util.Set;
eventDispatcher = new MediaSourceEventListener.EventDispatcher(); eventDispatcher = new MediaSourceEventListener.EventDispatcher();
childSources = new HashMap<>(); childSources = new HashMap<>();
enabledMediaSourceHolders = new HashSet<>(); 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. * @param shuffleOrder The new shuffle order.
* @return The new {@link Timeline}. * @return The new {@link Timeline}.
*/ */
public final Timeline setMediaSources( public Timeline setMediaSources(List<MediaSourceHolder> holders, ShuffleOrder shuffleOrder) {
List<MediaSourceHolder> holders, ShuffleOrder shuffleOrder) {
removeMediaSourcesInternal(/* fromIndex= */ 0, /* toIndex= */ mediaSourceHolders.size()); removeMediaSourcesInternal(/* fromIndex= */ 0, /* toIndex= */ mediaSourceHolders.size());
return addMediaSources(/* index= */ this.mediaSourceHolders.size(), holders, shuffleOrder); return addMediaSources(/* index= */ this.mediaSourceHolders.size(), holders, shuffleOrder);
} }
@ -115,7 +132,7 @@ import java.util.Set;
* @param shuffleOrder The new shuffle order. * @param shuffleOrder The new shuffle order.
* @return The new {@link Timeline}. * @return The new {@link Timeline}.
*/ */
public final Timeline addMediaSources( public Timeline addMediaSources(
int index, List<MediaSourceHolder> holders, ShuffleOrder shuffleOrder) { int index, List<MediaSourceHolder> holders, ShuffleOrder shuffleOrder) {
if (!holders.isEmpty()) { if (!holders.isEmpty()) {
this.shuffleOrder = shuffleOrder; this.shuffleOrder = shuffleOrder;
@ -165,8 +182,7 @@ import java.util.Set;
* @throws IllegalArgumentException When the range is malformed, i.e. {@code fromIndex} &lt; 0, * @throws IllegalArgumentException When the range is malformed, i.e. {@code fromIndex} &lt; 0,
* {@code toIndex} &gt; {@link #getSize()}, {@code fromIndex} &gt; {@code toIndex} * {@code toIndex} &gt; {@link #getSize()}, {@code fromIndex} &gt; {@code toIndex}
*/ */
public final Timeline removeMediaSourceRange( public Timeline removeMediaSourceRange(int fromIndex, int toIndex, ShuffleOrder shuffleOrder) {
int fromIndex, int toIndex, ShuffleOrder shuffleOrder) {
Assertions.checkArgument(fromIndex >= 0 && fromIndex <= toIndex && toIndex <= getSize()); Assertions.checkArgument(fromIndex >= 0 && fromIndex <= toIndex && toIndex <= getSize());
this.shuffleOrder = shuffleOrder; this.shuffleOrder = shuffleOrder;
removeMediaSourcesInternal(fromIndex, toIndex); removeMediaSourcesInternal(fromIndex, toIndex);
@ -185,7 +201,7 @@ import java.util.Set;
* @throws IllegalArgumentException When an index is invalid, i.e. {@code currentIndex} &lt; 0, * @throws IllegalArgumentException When an index is invalid, i.e. {@code currentIndex} &lt; 0,
* {@code currentIndex} &gt;= {@link #getSize()}, {@code newIndex} &lt; 0 * {@code currentIndex} &gt;= {@link #getSize()}, {@code newIndex} &lt; 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); return moveMediaSourceRange(currentIndex, currentIndex + 1, newIndex, shuffleOrder);
} }
@ -228,39 +244,28 @@ import java.util.Set;
} }
/** Clears the playlist. */ /** Clears the playlist. */
public final Timeline clear(@Nullable ShuffleOrder shuffleOrder) { public Timeline clear(@Nullable ShuffleOrder shuffleOrder) {
this.shuffleOrder = shuffleOrder != null ? shuffleOrder : this.shuffleOrder.cloneAndClear(); this.shuffleOrder = shuffleOrder != null ? shuffleOrder : this.shuffleOrder.cloneAndClear();
removeMediaSourcesInternal(/* fromIndex= */ 0, /* toIndex= */ getSize()); removeMediaSourcesInternal(/* fromIndex= */ 0, /* toIndex= */ getSize());
return createTimeline(); return createTimeline();
} }
/** Whether the playlist is prepared. */ /** Whether the playlist is prepared. */
public final boolean isPrepared() { public boolean isPrepared() {
return isPrepared; return isPrepared;
} }
/** Returns the number of media sources in the playlist. */ /** Returns the number of media sources in the playlist. */
public final int getSize() { public int getSize() {
return mediaSourceHolders.size(); 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. * Sets a new shuffle order to use when shuffling the child media sources.
* *
* @param shuffleOrder A {@link ShuffleOrder}. * @param shuffleOrder A {@link ShuffleOrder}.
*/ */
public final Timeline setShuffleOrder(ShuffleOrder shuffleOrder) { public Timeline setShuffleOrder(ShuffleOrder shuffleOrder) {
int size = getSize(); int size = getSize();
if (shuffleOrder.getLength() != size) { if (shuffleOrder.getLength() != size) {
shuffleOrder = shuffleOrder =
@ -273,7 +278,7 @@ import java.util.Set;
} }
/** Prepares the playlist. */ /** Prepares the playlist. */
public final void prepare(@Nullable TransferListener mediaTransferListener) { public void prepare(@Nullable TransferListener mediaTransferListener) {
Assertions.checkState(!isPrepared); Assertions.checkState(!isPrepared);
this.mediaTransferListener = mediaTransferListener; this.mediaTransferListener = mediaTransferListener;
for (int i = 0; i < mediaSourceHolders.size(); i++) { for (int i = 0; i < mediaSourceHolders.size(); i++) {
@ -312,7 +317,7 @@ import java.util.Set;
* *
* @param mediaPeriod The period to release. * @param mediaPeriod The period to release.
*/ */
public final void releasePeriod(MediaPeriod mediaPeriod) { public void releasePeriod(MediaPeriod mediaPeriod) {
MediaSourceHolder holder = MediaSourceHolder holder =
Assertions.checkNotNull(mediaSourceByMediaPeriod.remove(mediaPeriod)); Assertions.checkNotNull(mediaSourceByMediaPeriod.remove(mediaPeriod));
holder.mediaSource.releasePeriod(mediaPeriod); holder.mediaSource.releasePeriod(mediaPeriod);
@ -324,7 +329,7 @@ import java.util.Set;
} }
/** Releases the playlist. */ /** Releases the playlist. */
public final void release() { public void release() {
for (MediaSourceAndListener childSource : childSources.values()) { for (MediaSourceAndListener childSource : childSources.values()) {
try { try {
childSource.mediaSource.releaseSource(childSource.caller); childSource.mediaSource.releaseSource(childSource.caller);
@ -340,14 +345,14 @@ import java.util.Set;
} }
/** Throws any pending error encountered while loading or refreshing. */ /** 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()) { for (MediaSourceAndListener childSource : childSources.values()) {
childSource.mediaSource.maybeThrowSourceInfoRefreshError(); childSource.mediaSource.maybeThrowSourceInfoRefreshError();
} }
} }
/** Creates a timeline reflecting the current state of the playlist. */ /** Creates a timeline reflecting the current state of the playlist. */
public final Timeline createTimeline() { public Timeline createTimeline() {
if (mediaSourceHolders.isEmpty()) { if (mediaSourceHolders.isEmpty()) {
return Timeline.EMPTY; return Timeline.EMPTY;
} }

View File

@ -23,18 +23,19 @@ import static org.robolectric.annotation.LooperMode.Mode.LEGACY;
import android.net.Uri; import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; 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.SinglePeriodTimeline;
import com.google.android.exoplayer2.source.ads.AdPlaybackState; import com.google.android.exoplayer2.source.ads.AdPlaybackState;
import com.google.android.exoplayer2.source.ads.SinglePeriodAdTimeline; import com.google.android.exoplayer2.source.ads.SinglePeriodAdTimeline;
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.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition; import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectorResult; import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
import com.google.android.exoplayer2.upstream.Allocator; 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.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -70,12 +71,15 @@ public final class MediaPeriodQueueTest {
private Allocator allocator; private Allocator allocator;
private MediaSourceList mediaSourceList; private MediaSourceList mediaSourceList;
private FakeMediaSource fakeMediaSource; private FakeMediaSource fakeMediaSource;
private MediaSourceList.MediaSourceHolder mediaSourceHolder;
@Before @Before
public void setUp() { public void setUp() {
mediaPeriodQueue = new MediaPeriodQueue(); mediaPeriodQueue = new MediaPeriodQueue();
mediaSourceList = mock(MediaSourceList.class); mediaSourceList =
new MediaSourceList(
mock(MediaSourceList.MediaSourceListInfoRefreshListener.class),
/* analyticsCollector= */ null,
Util.createHandlerForCurrentOrMainLooper());
rendererCapabilities = new RendererCapabilities[0]; rendererCapabilities = new RendererCapabilities[0];
trackSelector = mock(TrackSelector.class); trackSelector = mock(TrackSelector.class);
allocator = mock(Allocator.class); allocator = mock(Allocator.class);
@ -408,10 +412,13 @@ public final class MediaPeriodQueueTest {
private void setupTimeline(Timeline timeline) { private void setupTimeline(Timeline timeline) {
fakeMediaSource = new FakeMediaSource(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); mediaSourceHolder.mediaSource.prepareSourceInternal(/* mediaTransferListener */ null);
Timeline playlistTimeline = createPlaylistTimeline(); Timeline playlistTimeline = mediaSourceList.createTimeline();
firstPeriodUid = playlistTimeline.getUidOfPeriod(/* periodIndex= */ 0); firstPeriodUid = playlistTimeline.getUidOfPeriod(/* periodIndex= */ 0);
playbackInfo = playbackInfo =
@ -443,13 +450,7 @@ public final class MediaPeriodQueueTest {
SinglePeriodAdTimeline adTimeline = SinglePeriodAdTimeline adTimeline =
new SinglePeriodAdTimeline(CONTENT_TIMELINE, adPlaybackState); new SinglePeriodAdTimeline(CONTENT_TIMELINE, adPlaybackState);
fakeMediaSource.setNewSourceInfo(adTimeline); fakeMediaSource.setNewSourceInfo(adTimeline);
playbackInfo = playbackInfo.copyWithTimeline(createPlaylistTimeline()); playbackInfo = playbackInfo.copyWithTimeline(mediaSourceList.createTimeline());
}
private MediaSourceList.PlaylistTimeline createPlaylistTimeline() {
return new MediaSourceList.PlaylistTimeline(
Collections.singleton(mediaSourceHolder),
new ShuffleOrder.DefaultShuffleOrder(/* length= */ 1));
} }
private void advance() { private void advance() {

View File

@ -31,6 +31,7 @@ import com.google.android.exoplayer2.source.ShuffleOrder;
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;
import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -51,7 +52,10 @@ public class MediaSourceListTest {
@Before @Before
public void setUp() { public void setUp() {
mediaSourceList = mediaSourceList =
new MediaSourceList(mock(MediaSourceList.MediaSourceListInfoRefreshListener.class)); new MediaSourceList(
mock(MediaSourceList.MediaSourceListInfoRefreshListener.class),
/* analyticsCollector= */ null,
Util.createHandlerForCurrentOrMainLooper());
} }
@Test @Test