mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add Listener to BasePreloadManager to propagate preload events to apps
PiperOrigin-RevId: 651421044
This commit is contained in:
parent
735e0cf8a1
commit
7aa70a5f2f
@ -21,6 +21,7 @@
|
|||||||
error occurs.
|
error occurs.
|
||||||
* `MediaCodecVideoRenderer` avoids decoding samples that are neither
|
* `MediaCodecVideoRenderer` avoids decoding samples that are neither
|
||||||
rendered nor used as reference by other samples.
|
rendered nor used as reference by other samples.
|
||||||
|
* Add `BasePreloadManager.Listener` to propagate preload events to apps.
|
||||||
* Transformer:
|
* Transformer:
|
||||||
* Add `SurfaceAssetLoader`, which supports queueing video data to
|
* Add `SurfaceAssetLoader`, which supports queueing video data to
|
||||||
Transformer via a `Surface`.
|
Transformer via a `Surface`.
|
||||||
|
@ -18,10 +18,13 @@ package androidx.media3.exoplayer.source.preload;
|
|||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import androidx.annotation.GuardedBy;
|
import androidx.annotation.GuardedBy;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.util.Clock;
|
||||||
|
import androidx.media3.common.util.ListenerSet;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
@ -58,10 +61,22 @@ public abstract class BasePreloadManager<T> {
|
|||||||
public abstract BasePreloadManager<T> build();
|
public abstract BasePreloadManager<T> build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Listener for events in a preload manager. */
|
||||||
|
public interface Listener {
|
||||||
|
|
||||||
|
/** Called when the given {@link MediaItem} has completed preloading. */
|
||||||
|
void onCompleted(MediaItem mediaItem);
|
||||||
|
|
||||||
|
/** Called when an {@linkplain PreloadException error} occurs. */
|
||||||
|
void onError(PreloadException exception);
|
||||||
|
}
|
||||||
|
|
||||||
private final Object lock;
|
private final Object lock;
|
||||||
|
private final Looper looper;
|
||||||
protected final Comparator<T> rankingDataComparator;
|
protected final Comparator<T> rankingDataComparator;
|
||||||
private final TargetPreloadStatusControl<T> targetPreloadStatusControl;
|
private final TargetPreloadStatusControl<T> targetPreloadStatusControl;
|
||||||
private final MediaSource.Factory mediaSourceFactory;
|
private final MediaSource.Factory mediaSourceFactory;
|
||||||
|
private final ListenerSet<Listener> listeners;
|
||||||
private final Map<MediaItem, MediaSourceHolder> mediaItemMediaSourceHolderMap;
|
private final Map<MediaItem, MediaSourceHolder> mediaItemMediaSourceHolderMap;
|
||||||
private final Handler startPreloadingHandler;
|
private final Handler startPreloadingHandler;
|
||||||
|
|
||||||
@ -77,14 +92,45 @@ public abstract class BasePreloadManager<T> {
|
|||||||
TargetPreloadStatusControl<T> targetPreloadStatusControl,
|
TargetPreloadStatusControl<T> targetPreloadStatusControl,
|
||||||
MediaSource.Factory mediaSourceFactory) {
|
MediaSource.Factory mediaSourceFactory) {
|
||||||
lock = new Object();
|
lock = new Object();
|
||||||
|
looper = Util.getCurrentOrMainLooper();
|
||||||
this.rankingDataComparator = rankingDataComparator;
|
this.rankingDataComparator = rankingDataComparator;
|
||||||
this.targetPreloadStatusControl = targetPreloadStatusControl;
|
this.targetPreloadStatusControl = targetPreloadStatusControl;
|
||||||
this.mediaSourceFactory = mediaSourceFactory;
|
this.mediaSourceFactory = mediaSourceFactory;
|
||||||
|
listeners = new ListenerSet<>(looper, Clock.DEFAULT, (listener, flags) -> {});
|
||||||
mediaItemMediaSourceHolderMap = new HashMap<>();
|
mediaItemMediaSourceHolderMap = new HashMap<>();
|
||||||
startPreloadingHandler = Util.createHandlerForCurrentOrMainLooper();
|
startPreloadingHandler = Util.createHandlerForCurrentOrMainLooper();
|
||||||
sourceHolderPriorityQueue = new PriorityQueue<>();
|
sourceHolderPriorityQueue = new PriorityQueue<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a {@link Listener} to listen to the preload events.
|
||||||
|
*
|
||||||
|
* <p>This method can be called from any thread.
|
||||||
|
*/
|
||||||
|
public void addListener(Listener listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a {@link Listener}.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException If this method is called from the wrong thread.
|
||||||
|
*/
|
||||||
|
public void removeListener(Listener listener) {
|
||||||
|
verifyApplicationThread();
|
||||||
|
listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all the {@linkplain Listener listeners}.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException If this method is called from the wrong thread.
|
||||||
|
*/
|
||||||
|
public void clearListeners() {
|
||||||
|
verifyApplicationThread();
|
||||||
|
listeners.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the count of the {@linkplain MediaSource media sources} currently being managed by the
|
* Gets the count of the {@linkplain MediaSource media sources} currently being managed by the
|
||||||
* preload manager.
|
* preload manager.
|
||||||
@ -206,15 +252,33 @@ public abstract class BasePreloadManager<T> {
|
|||||||
public final void release() {
|
public final void release() {
|
||||||
reset();
|
reset();
|
||||||
releaseInternal();
|
releaseInternal();
|
||||||
|
clearListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called when the given {@link MediaSource} completes to preload. */
|
/** Called when the given {@link MediaSource} completes preloading. */
|
||||||
protected final void onPreloadCompleted(MediaSource source) {
|
protected final void onPreloadCompleted(MediaSource source) {
|
||||||
|
listeners.sendEvent(
|
||||||
|
/* eventFlag= */ C.INDEX_UNSET, listener -> listener.onCompleted(source.getMediaItem()));
|
||||||
|
maybeAdvanceToNextSource(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called when an error occurs. */
|
||||||
|
protected final void onPreloadError(PreloadException error, MediaSource source) {
|
||||||
|
listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, listener -> listener.onError(error));
|
||||||
|
maybeAdvanceToNextSource(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called when the given {@link MediaSource} has been skipped before completing preloading. */
|
||||||
|
protected final void onPreloadSkipped(MediaSource source) {
|
||||||
|
maybeAdvanceToNextSource(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeAdvanceToNextSource(MediaSource preloadingSource) {
|
||||||
startPreloadingHandler.post(
|
startPreloadingHandler.post(
|
||||||
() -> {
|
() -> {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
if (sourceHolderPriorityQueue.isEmpty()
|
if (sourceHolderPriorityQueue.isEmpty()
|
||||||
|| checkNotNull(sourceHolderPriorityQueue.peek()).mediaSource != source) {
|
|| checkNotNull(sourceHolderPriorityQueue.peek()).mediaSource != preloadingSource) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
@ -307,6 +371,12 @@ public abstract class BasePreloadManager<T> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void verifyApplicationThread() {
|
||||||
|
if (Looper.myLooper() != looper) {
|
||||||
|
throw new IllegalStateException("Preload manager is accessed on the wrong thread.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** A holder for information for preloading a single media source. */
|
/** A holder for information for preloading a single media source. */
|
||||||
private final class MediaSourceHolder implements Comparable<MediaSourceHolder> {
|
private final class MediaSourceHolder implements Comparable<MediaSourceHolder> {
|
||||||
|
|
||||||
|
@ -241,17 +241,17 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUsedByPlayer(PreloadMediaSource mediaSource) {
|
public void onUsedByPlayer(PreloadMediaSource mediaSource) {
|
||||||
onPreloadCompleted(mediaSource);
|
DefaultPreloadManager.this.onPreloadSkipped(mediaSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadedToTheEndOfSource(PreloadMediaSource mediaSource) {
|
public void onLoadedToTheEndOfSource(PreloadMediaSource mediaSource) {
|
||||||
onPreloadCompleted(mediaSource);
|
DefaultPreloadManager.this.onPreloadCompleted(mediaSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPreloadError(PreloadException error, PreloadMediaSource mediaSource) {
|
public void onPreloadError(PreloadException error, PreloadMediaSource mediaSource) {
|
||||||
onPreloadCompleted(mediaSource);
|
DefaultPreloadManager.this.onPreloadError(error, mediaSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean continueOrCompletePreloading(
|
private boolean continueOrCompletePreloading(
|
||||||
@ -269,8 +269,10 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||||||
if (clearExceededDataFromTargetPreloadStatus) {
|
if (clearExceededDataFromTargetPreloadStatus) {
|
||||||
clearSourceInternal(mediaSource);
|
clearSourceInternal(mediaSource);
|
||||||
}
|
}
|
||||||
|
DefaultPreloadManager.this.onPreloadCompleted(mediaSource);
|
||||||
|
} else {
|
||||||
|
DefaultPreloadManager.this.onPreloadSkipped(mediaSource);
|
||||||
}
|
}
|
||||||
onPreloadCompleted(mediaSource);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ import androidx.media3.exoplayer.RenderersFactory;
|
|||||||
import androidx.media3.exoplayer.analytics.PlayerId;
|
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||||
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
||||||
import androidx.media3.exoplayer.drm.DrmSessionManager;
|
import androidx.media3.exoplayer.drm.DrmSessionManager;
|
||||||
|
import androidx.media3.exoplayer.drm.DrmSessionManagerProvider;
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
import androidx.media3.exoplayer.source.MediaPeriod;
|
import androidx.media3.exoplayer.source.MediaPeriod;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
@ -57,6 +58,7 @@ import androidx.media3.exoplayer.upstream.Allocator;
|
|||||||
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
||||||
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
||||||
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
|
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
|
||||||
|
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||||
import androidx.media3.test.utils.FakeAudioRenderer;
|
import androidx.media3.test.utils.FakeAudioRenderer;
|
||||||
import androidx.media3.test.utils.FakeMediaPeriod;
|
import androidx.media3.test.utils.FakeMediaPeriod;
|
||||||
import androidx.media3.test.utils.FakeMediaSource;
|
import androidx.media3.test.utils.FakeMediaSource;
|
||||||
@ -67,6 +69,8 @@ import androidx.media3.test.utils.FakeVideoRenderer;
|
|||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@ -207,6 +211,8 @@ public class DefaultPreloadManagerTest {
|
|||||||
rendererCapabilitiesListFactory,
|
rendererCapabilitiesListFactory,
|
||||||
allocator,
|
allocator,
|
||||||
Util.getCurrentOrMainLooper());
|
Util.getCurrentOrMainLooper());
|
||||||
|
TestPreloadManagerListener preloadManagerListener = new TestPreloadManagerListener();
|
||||||
|
preloadManager.addListener(preloadManagerListener);
|
||||||
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
||||||
MediaItem mediaItem0 =
|
MediaItem mediaItem0 =
|
||||||
mediaItemBuilder
|
mediaItemBuilder
|
||||||
@ -228,9 +234,12 @@ public class DefaultPreloadManagerTest {
|
|||||||
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
||||||
|
|
||||||
preloadManager.invalidate();
|
preloadManager.invalidate();
|
||||||
runMainLooperUntil(() -> targetPreloadStatusControlCallStates.size() == 3);
|
runMainLooperUntil(() -> preloadManagerListener.onCompletedMediaItemRecords.size() == 3);
|
||||||
|
|
||||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1, 2).inOrder();
|
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1, 2).inOrder();
|
||||||
|
assertThat(preloadManagerListener.onCompletedMediaItemRecords)
|
||||||
|
.containsExactly(mediaItem0, mediaItem1, mediaItem2)
|
||||||
|
.inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -259,6 +268,8 @@ public class DefaultPreloadManagerTest {
|
|||||||
rendererCapabilitiesListFactory,
|
rendererCapabilitiesListFactory,
|
||||||
allocator,
|
allocator,
|
||||||
Util.getCurrentOrMainLooper());
|
Util.getCurrentOrMainLooper());
|
||||||
|
TestPreloadManagerListener preloadManagerListener = new TestPreloadManagerListener();
|
||||||
|
preloadManager.addListener(preloadManagerListener);
|
||||||
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
||||||
MediaItem mediaItem0 =
|
MediaItem mediaItem0 =
|
||||||
mediaItemBuilder
|
mediaItemBuilder
|
||||||
@ -278,17 +289,16 @@ public class DefaultPreloadManagerTest {
|
|||||||
preloadManager.add(mediaItem0, /* rankingData= */ 0);
|
preloadManager.add(mediaItem0, /* rankingData= */ 0);
|
||||||
preloadManager.add(mediaItem1, /* rankingData= */ 1);
|
preloadManager.add(mediaItem1, /* rankingData= */ 1);
|
||||||
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
||||||
PreloadMediaSource preloadMediaSource2 =
|
|
||||||
(PreloadMediaSource) preloadManager.getMediaSource(mediaItem2);
|
|
||||||
preloadMediaSource2.prepareSource(
|
|
||||||
(source, timeline) -> {}, bandwidthMeter.getTransferListener(), PlayerId.UNSET);
|
|
||||||
preloadManager.setCurrentPlayingIndex(2);
|
preloadManager.setCurrentPlayingIndex(2);
|
||||||
currentPlayingItemIndex.set(2);
|
currentPlayingItemIndex.set(2);
|
||||||
|
|
||||||
preloadManager.invalidate();
|
preloadManager.invalidate();
|
||||||
runMainLooperUntil(() -> targetPreloadStatusControlCallStates.size() == 3);
|
runMainLooperUntil(() -> preloadManagerListener.onCompletedMediaItemRecords.size() == 3);
|
||||||
|
|
||||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1, 0).inOrder();
|
assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1, 0).inOrder();
|
||||||
|
assertThat(preloadManagerListener.onCompletedMediaItemRecords)
|
||||||
|
.containsExactly(mediaItem2, mediaItem1, mediaItem0)
|
||||||
|
.inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -309,6 +319,8 @@ public class DefaultPreloadManagerTest {
|
|||||||
rendererCapabilitiesListFactory,
|
rendererCapabilitiesListFactory,
|
||||||
allocator,
|
allocator,
|
||||||
Util.getCurrentOrMainLooper());
|
Util.getCurrentOrMainLooper());
|
||||||
|
TestPreloadManagerListener preloadManagerListener = new TestPreloadManagerListener();
|
||||||
|
preloadManager.addListener(preloadManagerListener);
|
||||||
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
||||||
MediaItem mediaItem0 =
|
MediaItem mediaItem0 =
|
||||||
mediaItemBuilder.setMediaId("mediaId0").setUri("http://exoplayer.dev/video0").build();
|
mediaItemBuilder.setMediaId("mediaId0").setUri("http://exoplayer.dev/video0").build();
|
||||||
@ -327,11 +339,12 @@ public class DefaultPreloadManagerTest {
|
|||||||
(PreloadMediaSource) preloadManager.getMediaSource(mediaItem0);
|
(PreloadMediaSource) preloadManager.getMediaSource(mediaItem0);
|
||||||
preloadMediaSource0.prepareSource(
|
preloadMediaSource0.prepareSource(
|
||||||
(source, timeline) -> {}, bandwidthMeter.getTransferListener(), PlayerId.UNSET);
|
(source, timeline) -> {}, bandwidthMeter.getTransferListener(), PlayerId.UNSET);
|
||||||
|
wrappedMediaSource0.setAllowPreparation(true);
|
||||||
|
wrappedMediaSource1.setAllowPreparation(true);
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
// The preload of mediaItem0 should complete and the preload manager continues to preload
|
|
||||||
// mediaItem1, even when the preloadMediaSource0 hasn't finished preparation.
|
|
||||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1).inOrder();
|
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1).inOrder();
|
||||||
|
assertThat(preloadManagerListener.onCompletedMediaItemRecords).containsExactly(mediaItem1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -352,6 +365,8 @@ public class DefaultPreloadManagerTest {
|
|||||||
rendererCapabilitiesListFactory,
|
rendererCapabilitiesListFactory,
|
||||||
allocator,
|
allocator,
|
||||||
Util.getCurrentOrMainLooper());
|
Util.getCurrentOrMainLooper());
|
||||||
|
TestPreloadManagerListener preloadManagerListener = new TestPreloadManagerListener();
|
||||||
|
preloadManager.addListener(preloadManagerListener);
|
||||||
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
||||||
MediaItem mediaItem0 =
|
MediaItem mediaItem0 =
|
||||||
mediaItemBuilder.setMediaId("mediaId0").setUri("http://exoplayer.dev/video0").build();
|
mediaItemBuilder.setMediaId("mediaId0").setUri("http://exoplayer.dev/video0").build();
|
||||||
@ -368,35 +383,31 @@ public class DefaultPreloadManagerTest {
|
|||||||
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
||||||
FakeMediaSource wrappedMediaSource2 = fakeMediaSourceFactory.getLastCreatedSource();
|
FakeMediaSource wrappedMediaSource2 = fakeMediaSourceFactory.getLastCreatedSource();
|
||||||
wrappedMediaSource2.setAllowPreparation(false);
|
wrappedMediaSource2.setAllowPreparation(false);
|
||||||
MediaSource.MediaSourceCaller externalCaller = (source, timeline) -> {};
|
|
||||||
PreloadMediaSource preloadMediaSource0 =
|
|
||||||
(PreloadMediaSource) preloadManager.getMediaSource(mediaItem0);
|
|
||||||
preloadMediaSource0.prepareSource(
|
|
||||||
externalCaller, bandwidthMeter.getTransferListener(), PlayerId.UNSET);
|
|
||||||
preloadManager.setCurrentPlayingIndex(0);
|
preloadManager.setCurrentPlayingIndex(0);
|
||||||
|
|
||||||
preloadManager.invalidate();
|
preloadManager.invalidate();
|
||||||
|
wrappedMediaSource0.setAllowPreparation(true);
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1).inOrder();
|
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1).inOrder();
|
||||||
|
assertThat(preloadManagerListener.onCompletedMediaItemRecords).containsExactly(mediaItem0);
|
||||||
|
|
||||||
targetPreloadStatusControlCallStates.clear();
|
targetPreloadStatusControlCallStates.clear();
|
||||||
preloadMediaSource0.releaseSource(externalCaller);
|
preloadManagerListener.reset();
|
||||||
PreloadMediaSource preloadMediaSource2 =
|
|
||||||
(PreloadMediaSource) preloadManager.getMediaSource(mediaItem2);
|
|
||||||
preloadMediaSource2.prepareSource(
|
|
||||||
externalCaller, bandwidthMeter.getTransferListener(), PlayerId.UNSET);
|
|
||||||
preloadManager.setCurrentPlayingIndex(2);
|
preloadManager.setCurrentPlayingIndex(2);
|
||||||
preloadManager.invalidate();
|
preloadManager.invalidate();
|
||||||
|
|
||||||
// Simulate the delay of the preparation of wrappedMediaSource0, which was triggered at the
|
// Simulate the delay of the preparation of wrappedMediaSource1, which was triggered at the
|
||||||
// first call of invalidate(). This is expected to result in nothing, as the whole flow of
|
// first call of invalidate(). This is expected to result in nothing, as the whole flow of
|
||||||
// preloading should respect the priority order triggered by the latest call of invalidate().
|
// preloading should respect the priority order triggered by the latest call of invalidate().
|
||||||
wrappedMediaSource0.setAllowPreparation(true);
|
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
|
||||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1).inOrder();
|
|
||||||
wrappedMediaSource1.setAllowPreparation(true);
|
wrappedMediaSource1.setAllowPreparation(true);
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
assertThat(preloadManagerListener.onCompletedMediaItemRecords).isEmpty();
|
||||||
|
wrappedMediaSource2.setAllowPreparation(true);
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1, 0).inOrder();
|
assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1, 0).inOrder();
|
||||||
|
assertThat(preloadManagerListener.onCompletedMediaItemRecords)
|
||||||
|
.containsExactly(mediaItem2, mediaItem1, mediaItem0)
|
||||||
|
.inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -419,6 +430,8 @@ public class DefaultPreloadManagerTest {
|
|||||||
rendererCapabilitiesListFactory,
|
rendererCapabilitiesListFactory,
|
||||||
allocator,
|
allocator,
|
||||||
Util.getCurrentOrMainLooper());
|
Util.getCurrentOrMainLooper());
|
||||||
|
TestPreloadManagerListener preloadManagerListener = new TestPreloadManagerListener();
|
||||||
|
preloadManager.addListener(preloadManagerListener);
|
||||||
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
||||||
MediaItem mediaItem0 =
|
MediaItem mediaItem0 =
|
||||||
mediaItemBuilder
|
mediaItemBuilder
|
||||||
@ -443,6 +456,91 @@ public class DefaultPreloadManagerTest {
|
|||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1, 2);
|
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1, 2);
|
||||||
|
assertThat(preloadManagerListener.onCompletedMediaItemRecords).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void invalidate_sourceHasPreloadException_continuesPreloadingNextSource() {
|
||||||
|
ArrayList<Integer> targetPreloadStatusControlCallStates = new ArrayList<>();
|
||||||
|
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||||
|
rankingData -> {
|
||||||
|
targetPreloadStatusControlCallStates.add(rankingData);
|
||||||
|
return new DefaultPreloadManager.Status(STAGE_SOURCE_PREPARED);
|
||||||
|
};
|
||||||
|
IOException causeException = new IOException("Failed to refresh source info");
|
||||||
|
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
||||||
|
MediaItem mediaItem0 =
|
||||||
|
mediaItemBuilder.setMediaId("mediaId0").setUri("http://exoplayer.dev/video0").build();
|
||||||
|
MediaItem mediaItem1 =
|
||||||
|
mediaItemBuilder.setMediaId("mediaId1").setUri("http://exoplayer.dev/video1").build();
|
||||||
|
MediaSource.Factory mediaSourceFactory =
|
||||||
|
new MediaSource.Factory() {
|
||||||
|
@Override
|
||||||
|
public MediaSource.Factory setDrmSessionManagerProvider(
|
||||||
|
DrmSessionManagerProvider drmSessionManagerProvider) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaSource.Factory setLoadErrorHandlingPolicy(
|
||||||
|
LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @C.ContentType int[] getSupportedTypes() {
|
||||||
|
return new int[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaSource createMediaSource(MediaItem mediaItem) {
|
||||||
|
FakeMediaSource mediaSource =
|
||||||
|
new FakeMediaSource() {
|
||||||
|
@Override
|
||||||
|
public MediaItem getMediaItem() {
|
||||||
|
return mediaItem;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (mediaItem.equals(mediaItem0)) {
|
||||||
|
mediaSource =
|
||||||
|
new FakeMediaSource() {
|
||||||
|
@Override
|
||||||
|
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
||||||
|
throw causeException;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaItem getMediaItem() {
|
||||||
|
return mediaItem;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mediaSource.setAllowPreparation(false);
|
||||||
|
}
|
||||||
|
return mediaSource;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
DefaultPreloadManager preloadManager =
|
||||||
|
new DefaultPreloadManager(
|
||||||
|
targetPreloadStatusControl,
|
||||||
|
mediaSourceFactory,
|
||||||
|
trackSelector,
|
||||||
|
bandwidthMeter,
|
||||||
|
rendererCapabilitiesListFactory,
|
||||||
|
allocator,
|
||||||
|
Util.getCurrentOrMainLooper());
|
||||||
|
TestPreloadManagerListener preloadManagerListener = new TestPreloadManagerListener();
|
||||||
|
preloadManager.addListener(preloadManagerListener);
|
||||||
|
preloadManager.add(mediaItem0, /* rankingData= */ 0);
|
||||||
|
preloadManager.add(mediaItem1, /* rankingData= */ 1);
|
||||||
|
|
||||||
|
preloadManager.invalidate();
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
|
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1).inOrder();
|
||||||
|
assertThat(Iterables.getOnlyElement(preloadManagerListener.onErrorPreloadExceptionRecords))
|
||||||
|
.hasCauseThat()
|
||||||
|
.isEqualTo(causeException);
|
||||||
|
assertThat(preloadManagerListener.onCompletedMediaItemRecords).containsExactly(mediaItem1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -820,4 +918,30 @@ public class DefaultPreloadManagerTest {
|
|||||||
assertThat(renderer.isReleased).isTrue();
|
assertThat(renderer.isReleased).isTrue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class TestPreloadManagerListener implements BasePreloadManager.Listener {
|
||||||
|
|
||||||
|
public final List<MediaItem> onCompletedMediaItemRecords;
|
||||||
|
public final List<PreloadException> onErrorPreloadExceptionRecords;
|
||||||
|
|
||||||
|
public TestPreloadManagerListener() {
|
||||||
|
onCompletedMediaItemRecords = new ArrayList<>();
|
||||||
|
onErrorPreloadExceptionRecords = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCompleted(MediaItem mediaItem) {
|
||||||
|
onCompletedMediaItemRecords.add(mediaItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(PreloadException exception) {
|
||||||
|
onErrorPreloadExceptionRecords.add(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
onCompletedMediaItemRecords.clear();
|
||||||
|
onErrorPreloadExceptionRecords.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user