Allow target preload status to be null to indicate not to preload
PiperOrigin-RevId: 619246259
This commit is contained in:
parent
98dac54816
commit
8f2f3bb7e4
@ -30,6 +30,9 @@
|
||||
should be shown. Custom `SimpleDecoder` implementations can check
|
||||
`isAtLeastOutputStartTimeUs` if needed or mark other buffers with
|
||||
`DecoderOutputBuffer.shouldBeSkipped` to skip them.
|
||||
* Allow a null value to be returned by
|
||||
`TargetPreloadStatusControl.getTargetPreloadStatus(T)` to indicate not
|
||||
to preload a `MediaSource` with the given `rankingData`.
|
||||
* Transformer:
|
||||
* Add `audioConversionProcess` and `videoConversionProcess` to
|
||||
`ExportResult` indicating how the respective track in the output file
|
||||
|
@ -126,7 +126,9 @@ public abstract class BasePreloadManager<T> {
|
||||
synchronized (lock) {
|
||||
sourceHolderPriorityQueue.clear();
|
||||
sourceHolderPriorityQueue.addAll(mediaItemMediaSourceHolderMap.values());
|
||||
maybeStartPreloadNextSource();
|
||||
while (!sourceHolderPriorityQueue.isEmpty() && !maybeStartPreloadNextSource()) {
|
||||
sourceHolderPriorityQueue.poll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,8 +182,9 @@ public abstract class BasePreloadManager<T> {
|
||||
|| checkNotNull(sourceHolderPriorityQueue.peek()).mediaSource != source) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
sourceHolderPriorityQueue.poll();
|
||||
maybeStartPreloadNextSource();
|
||||
} while (!sourceHolderPriorityQueue.isEmpty() && !maybeStartPreloadNextSource());
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -237,15 +240,27 @@ public abstract class BasePreloadManager<T> {
|
||||
/** Releases the preload manager, see {@link #release()}. */
|
||||
protected void releaseInternal() {}
|
||||
|
||||
/**
|
||||
* Starts to preload the {@link MediaSource} at the head of the priority queue, if the {@linkplain
|
||||
* TargetPreloadStatusControl.PreloadStatus target preload status} for that source is not null.
|
||||
*
|
||||
* @return {@code true} if the {@link MediaSource} at the head of the priority queue starts to
|
||||
* preload, otherwise {@code false}.
|
||||
* @throws NullPointerException if the priority queue is empty.
|
||||
*/
|
||||
@GuardedBy("lock")
|
||||
private void maybeStartPreloadNextSource() {
|
||||
if (!sourceHolderPriorityQueue.isEmpty() && shouldStartPreloadingNextSource()) {
|
||||
private boolean maybeStartPreloadNextSource() {
|
||||
if (shouldStartPreloadingNextSource()) {
|
||||
MediaSourceHolder preloadingHolder = checkNotNull(sourceHolderPriorityQueue.peek());
|
||||
this.targetPreloadStatusOfCurrentPreloadingSource =
|
||||
targetPreloadStatusControl.getTargetPreloadStatus(preloadingHolder.rankingData);
|
||||
if (targetPreloadStatusOfCurrentPreloadingSource != null) {
|
||||
preloadSourceInternal(preloadingHolder.mediaSource, preloadingHolder.startPositionUs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** A holder for information for preloading a single media source. */
|
||||
private final class MediaSourceHolder implements Comparable<MediaSourceHolder> {
|
||||
|
@ -17,7 +17,6 @@ package androidx.media3.exoplayer.source.preload;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
import static java.lang.Math.abs;
|
||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||
|
||||
@ -233,12 +232,13 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
||||
@Nullable
|
||||
TargetPreloadStatusControl.PreloadStatus targetPreloadStatus =
|
||||
getTargetPreloadStatus(mediaSource);
|
||||
checkState(targetPreloadStatus instanceof Status);
|
||||
if (targetPreloadStatus != null) {
|
||||
Status status = (Status) targetPreloadStatus;
|
||||
if (continueLoadingPredicate.apply(checkNotNull(status))) {
|
||||
return true;
|
||||
}
|
||||
onPreloadCompleted(mediaSource);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -15,16 +15,22 @@
|
||||
*/
|
||||
package androidx.media3.exoplayer.source.preload;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.exoplayer.source.MediaSource;
|
||||
|
||||
/** Controls the target preload status. */
|
||||
@UnstableApi
|
||||
public interface TargetPreloadStatusControl<T> {
|
||||
|
||||
/** Returns the target preload status for a source with the given {@code rankingData}. */
|
||||
/**
|
||||
* Returns the target preload status for a source with the given {@code rankingData}. May be null
|
||||
* if a {@link MediaSource} with the given {@code rankingData} should not be preloaded.
|
||||
*/
|
||||
@Nullable
|
||||
PreloadStatus getTargetPreloadStatus(T rankingData);
|
||||
|
||||
/** Defines the status of the preloading for a source. */
|
||||
/** Defines the status of the preloading for a {@link MediaSource}. */
|
||||
interface PreloadStatus {
|
||||
|
||||
/** The stage of the preloading. */
|
||||
|
@ -166,10 +166,10 @@ public class DefaultPreloadManagerTest {
|
||||
@Test
|
||||
public void
|
||||
invalidate_withoutSettingCurrentPlayingIndex_sourcesPreloadedToTargetStatusesInOrder() {
|
||||
ArrayList<Integer> targetPreloadStatusControlCallReference = new ArrayList<>();
|
||||
ArrayList<Integer> targetPreloadStatusControlCallStates = new ArrayList<>();
|
||||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData -> {
|
||||
targetPreloadStatusControlCallReference.add(rankingData);
|
||||
targetPreloadStatusControlCallStates.add(rankingData);
|
||||
return new DefaultPreloadManager.Status(
|
||||
DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED);
|
||||
};
|
||||
@ -190,7 +190,6 @@ public class DefaultPreloadManagerTest {
|
||||
mediaItemBuilder.setMediaId("mediaId1").setUri("http://exoplayer.dev/video1").build();
|
||||
MediaItem mediaItem2 =
|
||||
mediaItemBuilder.setMediaId("mediaId2").setUri("http://exoplayer.dev/video2").build();
|
||||
|
||||
preloadManager.add(mediaItem0, /* rankingData= */ 0);
|
||||
FakeMediaSource wrappedMediaSource0 = fakeMediaSourceFactory.getLastCreatedSource();
|
||||
wrappedMediaSource0.setAllowPreparation(false);
|
||||
@ -200,21 +199,22 @@ public class DefaultPreloadManagerTest {
|
||||
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
||||
FakeMediaSource wrappedMediaSource2 = fakeMediaSourceFactory.getLastCreatedSource();
|
||||
wrappedMediaSource2.setAllowPreparation(false);
|
||||
|
||||
preloadManager.invalidate();
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
assertThat(targetPreloadStatusControlCallReference).containsExactly(0);
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0);
|
||||
wrappedMediaSource0.setAllowPreparation(true);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
assertThat(targetPreloadStatusControlCallReference).containsExactly(0, 1).inOrder();
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidate_withSettingCurrentPlayingIndex_sourcesPreloadedToTargetStatusesInOrder() {
|
||||
ArrayList<Integer> targetPreloadStatusControlCallReference = new ArrayList<>();
|
||||
ArrayList<Integer> targetPreloadStatusControlCallStates = new ArrayList<>();
|
||||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData -> {
|
||||
targetPreloadStatusControlCallReference.add(rankingData);
|
||||
targetPreloadStatusControlCallStates.add(rankingData);
|
||||
return new DefaultPreloadManager.Status(
|
||||
DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED);
|
||||
};
|
||||
@ -235,7 +235,6 @@ public class DefaultPreloadManagerTest {
|
||||
mediaItemBuilder.setMediaId("mediaId1").setUri("http://exoplayer.dev/video1").build();
|
||||
MediaItem mediaItem2 =
|
||||
mediaItemBuilder.setMediaId("mediaId2").setUri("http://exoplayer.dev/video2").build();
|
||||
|
||||
preloadManager.add(mediaItem0, /* rankingData= */ 0);
|
||||
FakeMediaSource wrappedMediaSource0 = fakeMediaSourceFactory.getLastCreatedSource();
|
||||
wrappedMediaSource0.setAllowPreparation(false);
|
||||
@ -245,27 +244,27 @@ public class DefaultPreloadManagerTest {
|
||||
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
||||
FakeMediaSource wrappedMediaSource2 = fakeMediaSourceFactory.getLastCreatedSource();
|
||||
wrappedMediaSource2.setAllowPreparation(false);
|
||||
|
||||
PreloadMediaSource preloadMediaSource2 =
|
||||
(PreloadMediaSource) preloadManager.getMediaSource(mediaItem2);
|
||||
preloadMediaSource2.prepareSource(
|
||||
(source, timeline) -> {}, bandwidthMeter.getTransferListener(), PlayerId.UNSET);
|
||||
preloadManager.setCurrentPlayingIndex(2);
|
||||
|
||||
preloadManager.invalidate();
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
assertThat(targetPreloadStatusControlCallReference).containsExactly(2, 1);
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1);
|
||||
wrappedMediaSource1.setAllowPreparation(true);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
assertThat(targetPreloadStatusControlCallReference).containsExactly(2, 1, 0).inOrder();
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1, 0).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidate_sourceHandedOverToPlayerDuringPreloading_continuesPreloadingNextSource() {
|
||||
ArrayList<Integer> targetPreloadStatusControlCallReference = new ArrayList<>();
|
||||
ArrayList<Integer> targetPreloadStatusControlCallStates = new ArrayList<>();
|
||||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData -> {
|
||||
targetPreloadStatusControlCallReference.add(rankingData);
|
||||
targetPreloadStatusControlCallStates.add(rankingData);
|
||||
return new DefaultPreloadManager.Status(
|
||||
DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED);
|
||||
};
|
||||
@ -284,7 +283,6 @@ public class DefaultPreloadManagerTest {
|
||||
mediaItemBuilder.setMediaId("mediaId0").setUri("http://exoplayer.dev/video0").build();
|
||||
MediaItem mediaItem1 =
|
||||
mediaItemBuilder.setMediaId("mediaId1").setUri("http://exoplayer.dev/video1").build();
|
||||
|
||||
preloadManager.add(mediaItem0, /* rankingData= */ 0);
|
||||
FakeMediaSource wrappedMediaSource0 = fakeMediaSourceFactory.getLastCreatedSource();
|
||||
wrappedMediaSource0.setAllowPreparation(false);
|
||||
@ -292,7 +290,7 @@ public class DefaultPreloadManagerTest {
|
||||
FakeMediaSource wrappedMediaSource1 = fakeMediaSourceFactory.getLastCreatedSource();
|
||||
wrappedMediaSource1.setAllowPreparation(false);
|
||||
preloadManager.invalidate();
|
||||
assertThat(targetPreloadStatusControlCallReference).containsExactly(0);
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0);
|
||||
|
||||
PreloadMediaSource preloadMediaSource0 =
|
||||
(PreloadMediaSource) preloadManager.getMediaSource(mediaItem0);
|
||||
@ -302,15 +300,15 @@ public class DefaultPreloadManagerTest {
|
||||
|
||||
// The preload of mediaItem0 should complete and the preload manager continues to preload
|
||||
// mediaItem1, even when the preloadMediaSource0 hasn't finished preparation.
|
||||
assertThat(targetPreloadStatusControlCallReference).containsExactly(0, 1).inOrder();
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidate_beforePreloadCompletedForLastInvalidate_preloadRespectsToLatestOrder() {
|
||||
ArrayList<Integer> targetPreloadStatusControlCallReference = new ArrayList<>();
|
||||
ArrayList<Integer> targetPreloadStatusControlCallStates = new ArrayList<>();
|
||||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData -> {
|
||||
targetPreloadStatusControlCallReference.add(rankingData);
|
||||
targetPreloadStatusControlCallStates.add(rankingData);
|
||||
return new DefaultPreloadManager.Status(
|
||||
DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED);
|
||||
};
|
||||
@ -340,18 +338,18 @@ public class DefaultPreloadManagerTest {
|
||||
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
||||
FakeMediaSource wrappedMediaSource2 = fakeMediaSourceFactory.getLastCreatedSource();
|
||||
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.invalidate();
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
assertThat(targetPreloadStatusControlCallReference).containsExactly(0, 1).inOrder();
|
||||
targetPreloadStatusControlCallReference.clear();
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1).inOrder();
|
||||
|
||||
targetPreloadStatusControlCallStates.clear();
|
||||
preloadMediaSource0.releaseSource(externalCaller);
|
||||
PreloadMediaSource preloadMediaSource2 =
|
||||
(PreloadMediaSource) preloadManager.getMediaSource(mediaItem2);
|
||||
@ -359,11 +357,57 @@ public class DefaultPreloadManagerTest {
|
||||
externalCaller, bandwidthMeter.getTransferListener(), PlayerId.UNSET);
|
||||
preloadManager.setCurrentPlayingIndex(2);
|
||||
preloadManager.invalidate();
|
||||
|
||||
// Simulate the delay of the preparation of wrappedMediaSource0, which was triggered at the
|
||||
// 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().
|
||||
wrappedMediaSource0.setAllowPreparation(true);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
assertThat(targetPreloadStatusControlCallReference).containsExactly(2, 1).inOrder();
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1).inOrder();
|
||||
wrappedMediaSource1.setAllowPreparation(true);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
assertThat(targetPreloadStatusControlCallReference).containsExactly(2, 1, 0).inOrder();
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1, 0).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidate_provideNullTargetPreloadStatus_sourcesSkippedForPreload() {
|
||||
ArrayList<Integer> targetPreloadStatusControlCallStates = new ArrayList<>();
|
||||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData -> {
|
||||
targetPreloadStatusControlCallStates.add(rankingData);
|
||||
return null;
|
||||
};
|
||||
FakeMediaSourceFactory fakeMediaSourceFactory = new FakeMediaSourceFactory();
|
||||
DefaultPreloadManager preloadManager =
|
||||
new DefaultPreloadManager(
|
||||
targetPreloadStatusControl,
|
||||
fakeMediaSourceFactory,
|
||||
trackSelector,
|
||||
bandwidthMeter,
|
||||
rendererCapabilitiesListFactory,
|
||||
allocator,
|
||||
Util.getCurrentOrMainLooper());
|
||||
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();
|
||||
MediaItem mediaItem2 =
|
||||
mediaItemBuilder.setMediaId("mediaId2").setUri("http://exoplayer.dev/video2").build();
|
||||
preloadManager.add(mediaItem0, /* rankingData= */ 0);
|
||||
FakeMediaSource wrappedMediaSource0 = fakeMediaSourceFactory.getLastCreatedSource();
|
||||
wrappedMediaSource0.setAllowPreparation(false);
|
||||
preloadManager.add(mediaItem1, /* rankingData= */ 1);
|
||||
FakeMediaSource wrappedMediaSource1 = fakeMediaSourceFactory.getLastCreatedSource();
|
||||
wrappedMediaSource1.setAllowPreparation(false);
|
||||
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
||||
FakeMediaSource wrappedMediaSource2 = fakeMediaSourceFactory.getLastCreatedSource();
|
||||
wrappedMediaSource2.setAllowPreparation(false);
|
||||
|
||||
preloadManager.invalidate();
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -404,11 +448,11 @@ public class DefaultPreloadManagerTest {
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
preloadManager.add(mediaItem1, /* rankingData= */ 1);
|
||||
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
||||
preloadManager.invalidate();
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
preloadManager.remove(mediaItem1);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
@ -477,11 +521,11 @@ public class DefaultPreloadManagerTest {
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
preloadManager.add(mediaItem1, /* rankingData= */ 1);
|
||||
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
||||
preloadManager.invalidate();
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
preloadManager.release();
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user