mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Complete preloading when the period has loaded to the end of the source
When the period has loaded to the end of the source, the `period.getBufferedPositionUs` will be set to `C.TIME_END_OF_SOURCE`, which is a negative value. Thus, the original `continueLoadingPredicate` will never turn to `false`, as the `bufferedPositionUs` is definitely less than the target preload position that is expected to be positive. In this change, we added `PreloadMediaSource.PreloadControl.onLoadedToTheEndOfSource(PreloadMediaSource)` to indicate that the source has loaded to the end. This allows the `DefaultPreloadManager` and the custom `PreloadMediaSource.PreloadControl` implementations to preload the next source or take other actions. This bug was not revealed by the the `DefaultPreloadManagerTest` because the related tests were all using the `FakeMediaSource` and only setting the preload target to `STAGE_TIMELINE_REFRESHED`. Thus, the tests for testing the `invalidate()` behaviors were modified to use the real progressive media whenever possible, unless we have to use `FakeMediaSource` to squeeze a chance to do more operations between the preloading of sources to test some special scenarios. PiperOrigin-RevId: 631776442
This commit is contained in:
parent
9ece3932e8
commit
1a5f57e9eb
5 changed files with 192 additions and 68 deletions
|
|
@ -18,6 +18,12 @@
|
|||
* Fix issue with updating the last rebuffer time which resulted in
|
||||
incorrect `bs` (buffer starvation) key in CMCD
|
||||
([#1124](https://github.com/androidx/media/issues/1124)).
|
||||
* Add
|
||||
`PreloadMediaSource.PreloadControl.onLoadedToTheEndOfSource(PreloadMediaSource)`
|
||||
to indicate that the source has loaded to the end. This allows the
|
||||
`DefaultPreloadManager` and the custom
|
||||
`PreloadMediaSource.PreloadControl` implementations to preload the next
|
||||
source or take other actions.
|
||||
* Transformer:
|
||||
* Work around a decoder bug where the number of audio channels was capped
|
||||
at stereo when handling PCM input.
|
||||
|
|
|
|||
|
|
@ -203,13 +203,17 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||
@Override
|
||||
public boolean onTimelineRefreshed(PreloadMediaSource mediaSource) {
|
||||
return continueOrCompletePreloading(
|
||||
mediaSource, status -> status.getStage() > Status.STAGE_TIMELINE_REFRESHED);
|
||||
mediaSource,
|
||||
/* continueLoadingPredicate= */ status ->
|
||||
status.getStage() > Status.STAGE_TIMELINE_REFRESHED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepared(PreloadMediaSource mediaSource) {
|
||||
return continueOrCompletePreloading(
|
||||
mediaSource, status -> status.getStage() > Status.STAGE_SOURCE_PREPARED);
|
||||
mediaSource,
|
||||
/* continueLoadingPredicate= */ status ->
|
||||
status.getStage() > Status.STAGE_SOURCE_PREPARED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -217,9 +221,9 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||
PreloadMediaSource mediaSource, long bufferedPositionUs) {
|
||||
return continueOrCompletePreloading(
|
||||
mediaSource,
|
||||
status ->
|
||||
(status.getStage() == Status.STAGE_LOADED_TO_POSITION_MS
|
||||
&& status.getValue() > Util.usToMs(bufferedPositionUs)));
|
||||
/* continueLoadingPredicate= */ status ->
|
||||
status.getStage() == Status.STAGE_LOADED_TO_POSITION_MS
|
||||
&& status.getValue() > Util.usToMs(bufferedPositionUs));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -227,6 +231,11 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||
onPreloadCompleted(mediaSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadedToTheEndOfSource(PreloadMediaSource mediaSource) {
|
||||
onPreloadCompleted(mediaSource);
|
||||
}
|
||||
|
||||
private boolean continueOrCompletePreloading(
|
||||
MediaSource mediaSource, Predicate<Status> continueLoadingPredicate) {
|
||||
@Nullable
|
||||
|
|
|
|||
|
|
@ -81,9 +81,12 @@ public final class PreloadMediaSource extends WrappingMediaSource {
|
|||
/**
|
||||
* Called from {@link PreloadMediaSource} when it requests to continue loading.
|
||||
*
|
||||
* <p>If fully loaded, then {@link #onLoadedToTheEndOfSource(PreloadMediaSource)} will be called
|
||||
* instead.
|
||||
*
|
||||
* @param mediaSource The {@link PreloadMediaSource} that requests to continue loading.
|
||||
* @param bufferedPositionUs An estimate of the absolute position in microseconds up to which
|
||||
* data is buffered, or {@link C#TIME_END_OF_SOURCE} if the track is fully buffered.
|
||||
* data is buffered.
|
||||
*/
|
||||
boolean onContinueLoadingRequested(PreloadMediaSource mediaSource, long bufferedPositionUs);
|
||||
|
||||
|
|
@ -93,6 +96,15 @@ public final class PreloadMediaSource extends WrappingMediaSource {
|
|||
* @param mediaSource The {@link PreloadMediaSource} that the player starts using.
|
||||
*/
|
||||
void onUsedByPlayer(PreloadMediaSource mediaSource);
|
||||
|
||||
/**
|
||||
* Called from {@link PreloadMediaSource} when it has loaded to the end of source.
|
||||
*
|
||||
* <p>The default implementation is a no-op.
|
||||
*
|
||||
* @param mediaSource The {@link PreloadMediaSource} that has loaded to the end of source.
|
||||
*/
|
||||
default void onLoadedToTheEndOfSource(PreloadMediaSource mediaSource) {}
|
||||
}
|
||||
|
||||
/** Factory for {@link PreloadMediaSource}. */
|
||||
|
|
@ -402,7 +414,9 @@ public final class PreloadMediaSource extends WrappingMediaSource {
|
|||
return;
|
||||
}
|
||||
PreloadMediaPeriod preloadMediaPeriod = (PreloadMediaPeriod) mediaPeriod;
|
||||
if (!prepared
|
||||
if (prepared && mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE) {
|
||||
preloadControl.onLoadedToTheEndOfSource(PreloadMediaSource.this);
|
||||
} else if (!prepared
|
||||
|| preloadControl.onContinueLoadingRequested(
|
||||
PreloadMediaSource.this, preloadMediaPeriod.getBufferedPositionUs())) {
|
||||
preloadMediaPeriod.continueLoading(
|
||||
|
|
|
|||
|
|
@ -15,19 +15,25 @@
|
|||
*/
|
||||
package androidx.media3.exoplayer.source.preload;
|
||||
|
||||
import static androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STAGE_LOADED_TO_POSITION_MS;
|
||||
import static androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED;
|
||||
import static androidx.media3.test.utils.robolectric.RobolectricUtil.runMainLooperUntil;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.lang.Math.abs;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Looper;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.util.SystemClock;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.datasource.DefaultDataSource;
|
||||
import androidx.media3.exoplayer.DefaultRendererCapabilitiesList;
|
||||
import androidx.media3.exoplayer.Renderer;
|
||||
import androidx.media3.exoplayer.RendererCapabilitiesList;
|
||||
|
|
@ -35,6 +41,7 @@ import androidx.media3.exoplayer.RenderersFactory;
|
|||
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||
import androidx.media3.exoplayer.source.MediaSource;
|
||||
import androidx.media3.exoplayer.source.ProgressiveMediaSource;
|
||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
|
||||
import androidx.media3.exoplayer.trackselection.TrackSelector;
|
||||
import androidx.media3.exoplayer.upstream.Allocator;
|
||||
|
|
@ -50,6 +57,7 @@ import androidx.test.core.app.ApplicationProvider;
|
|||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
|
@ -161,20 +169,26 @@ public class DefaultPreloadManagerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
invalidate_withoutSettingCurrentPlayingIndex_sourcesPreloadedToTargetStatusesInOrder() {
|
||||
public void invalidate_withoutSettingCurrentPlayingIndex_sourcesPreloadedToTargetStatusesInOrder()
|
||||
throws Exception {
|
||||
ArrayList<Integer> targetPreloadStatusControlCallStates = new ArrayList<>();
|
||||
AtomicInteger currentPlayingItemIndex = new AtomicInteger();
|
||||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData -> {
|
||||
targetPreloadStatusControlCallStates.add(rankingData);
|
||||
return new DefaultPreloadManager.Status(
|
||||
DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED);
|
||||
if (abs(rankingData - currentPlayingItemIndex.get()) == 1) {
|
||||
return new DefaultPreloadManager.Status(STAGE_LOADED_TO_POSITION_MS, 100L);
|
||||
} else {
|
||||
return new DefaultPreloadManager.Status(STAGE_TIMELINE_REFRESHED);
|
||||
}
|
||||
};
|
||||
FakeMediaSourceFactory fakeMediaSourceFactory = new FakeMediaSourceFactory();
|
||||
ProgressiveMediaSource.Factory mediaSourceFactory =
|
||||
new ProgressiveMediaSource.Factory(
|
||||
new DefaultDataSource.Factory(ApplicationProvider.getApplicationContext()));
|
||||
DefaultPreloadManager preloadManager =
|
||||
new DefaultPreloadManager(
|
||||
targetPreloadStatusControl,
|
||||
fakeMediaSourceFactory,
|
||||
mediaSourceFactory,
|
||||
trackSelector,
|
||||
bandwidthMeter,
|
||||
rendererCapabilitiesListFactory,
|
||||
|
|
@ -182,44 +196,51 @@ public class DefaultPreloadManagerTest {
|
|||
Util.getCurrentOrMainLooper());
|
||||
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
||||
MediaItem mediaItem0 =
|
||||
mediaItemBuilder.setMediaId("mediaId0").setUri("http://exoplayer.dev/video0").build();
|
||||
mediaItemBuilder
|
||||
.setMediaId("mediaId0")
|
||||
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
|
||||
.build();
|
||||
MediaItem mediaItem1 =
|
||||
mediaItemBuilder.setMediaId("mediaId1").setUri("http://exoplayer.dev/video1").build();
|
||||
mediaItemBuilder
|
||||
.setMediaId("mediaId1")
|
||||
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
|
||||
.build();
|
||||
MediaItem mediaItem2 =
|
||||
mediaItemBuilder.setMediaId("mediaId2").setUri("http://exoplayer.dev/video2").build();
|
||||
mediaItemBuilder
|
||||
.setMediaId("mediaId2")
|
||||
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
|
||||
.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();
|
||||
runMainLooperUntil(() -> targetPreloadStatusControlCallStates.size() == 3);
|
||||
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0);
|
||||
wrappedMediaSource0.setAllowPreparation(true);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1).inOrder();
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1, 2).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidate_withSettingCurrentPlayingIndex_sourcesPreloadedToTargetStatusesInOrder() {
|
||||
public void invalidate_withSettingCurrentPlayingIndex_sourcesPreloadedToTargetStatusesInOrder()
|
||||
throws Exception {
|
||||
ArrayList<Integer> targetPreloadStatusControlCallStates = new ArrayList<>();
|
||||
AtomicInteger currentPlayingItemIndex = new AtomicInteger();
|
||||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData -> {
|
||||
targetPreloadStatusControlCallStates.add(rankingData);
|
||||
return new DefaultPreloadManager.Status(
|
||||
DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED);
|
||||
if (abs(rankingData - currentPlayingItemIndex.get()) == 1) {
|
||||
return new DefaultPreloadManager.Status(STAGE_LOADED_TO_POSITION_MS, 100L);
|
||||
} else {
|
||||
return new DefaultPreloadManager.Status(STAGE_TIMELINE_REFRESHED);
|
||||
}
|
||||
};
|
||||
FakeMediaSourceFactory fakeMediaSourceFactory = new FakeMediaSourceFactory();
|
||||
ProgressiveMediaSource.Factory mediaSourceFactory =
|
||||
new ProgressiveMediaSource.Factory(
|
||||
new DefaultDataSource.Factory(ApplicationProvider.getApplicationContext()));
|
||||
DefaultPreloadManager preloadManager =
|
||||
new DefaultPreloadManager(
|
||||
targetPreloadStatusControl,
|
||||
fakeMediaSourceFactory,
|
||||
mediaSourceFactory,
|
||||
trackSelector,
|
||||
bandwidthMeter,
|
||||
rendererCapabilitiesListFactory,
|
||||
|
|
@ -227,32 +248,33 @@ public class DefaultPreloadManagerTest {
|
|||
Util.getCurrentOrMainLooper());
|
||||
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
||||
MediaItem mediaItem0 =
|
||||
mediaItemBuilder.setMediaId("mediaId0").setUri("http://exoplayer.dev/video0").build();
|
||||
mediaItemBuilder
|
||||
.setMediaId("mediaId0")
|
||||
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
|
||||
.build();
|
||||
MediaItem mediaItem1 =
|
||||
mediaItemBuilder.setMediaId("mediaId1").setUri("http://exoplayer.dev/video1").build();
|
||||
mediaItemBuilder
|
||||
.setMediaId("mediaId1")
|
||||
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
|
||||
.build();
|
||||
MediaItem mediaItem2 =
|
||||
mediaItemBuilder.setMediaId("mediaId2").setUri("http://exoplayer.dev/video2").build();
|
||||
mediaItemBuilder
|
||||
.setMediaId("mediaId2")
|
||||
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
|
||||
.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);
|
||||
PreloadMediaSource preloadMediaSource2 =
|
||||
(PreloadMediaSource) preloadManager.getMediaSource(mediaItem2);
|
||||
preloadMediaSource2.prepareSource(
|
||||
(source, timeline) -> {}, bandwidthMeter.getTransferListener(), PlayerId.UNSET);
|
||||
preloadManager.setCurrentPlayingIndex(2);
|
||||
currentPlayingItemIndex.set(2);
|
||||
|
||||
preloadManager.invalidate();
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
runMainLooperUntil(() -> targetPreloadStatusControlCallStates.size() == 3);
|
||||
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1);
|
||||
wrappedMediaSource1.setAllowPreparation(true);
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1, 0).inOrder();
|
||||
}
|
||||
|
||||
|
|
@ -262,8 +284,7 @@ public class DefaultPreloadManagerTest {
|
|||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData -> {
|
||||
targetPreloadStatusControlCallStates.add(rankingData);
|
||||
return new DefaultPreloadManager.Status(
|
||||
DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED);
|
||||
return new DefaultPreloadManager.Status(STAGE_TIMELINE_REFRESHED);
|
||||
};
|
||||
FakeMediaSourceFactory fakeMediaSourceFactory = new FakeMediaSourceFactory();
|
||||
DefaultPreloadManager preloadManager =
|
||||
|
|
@ -306,8 +327,7 @@ public class DefaultPreloadManagerTest {
|
|||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData -> {
|
||||
targetPreloadStatusControlCallStates.add(rankingData);
|
||||
return new DefaultPreloadManager.Status(
|
||||
DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED);
|
||||
return new DefaultPreloadManager.Status(STAGE_TIMELINE_REFRESHED);
|
||||
};
|
||||
FakeMediaSourceFactory fakeMediaSourceFactory = new FakeMediaSourceFactory();
|
||||
DefaultPreloadManager preloadManager =
|
||||
|
|
@ -374,11 +394,13 @@ public class DefaultPreloadManagerTest {
|
|||
targetPreloadStatusControlCallStates.add(rankingData);
|
||||
return null;
|
||||
};
|
||||
FakeMediaSourceFactory fakeMediaSourceFactory = new FakeMediaSourceFactory();
|
||||
ProgressiveMediaSource.Factory mediaSourceFactory =
|
||||
new ProgressiveMediaSource.Factory(
|
||||
new DefaultDataSource.Factory(ApplicationProvider.getApplicationContext()));
|
||||
DefaultPreloadManager preloadManager =
|
||||
new DefaultPreloadManager(
|
||||
targetPreloadStatusControl,
|
||||
fakeMediaSourceFactory,
|
||||
mediaSourceFactory,
|
||||
trackSelector,
|
||||
bandwidthMeter,
|
||||
rendererCapabilitiesListFactory,
|
||||
|
|
@ -386,20 +408,23 @@ public class DefaultPreloadManagerTest {
|
|||
Util.getCurrentOrMainLooper());
|
||||
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
||||
MediaItem mediaItem0 =
|
||||
mediaItemBuilder.setMediaId("mediaId0").setUri("http://exoplayer.dev/video0").build();
|
||||
mediaItemBuilder
|
||||
.setMediaId("mediaId0")
|
||||
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
|
||||
.build();
|
||||
MediaItem mediaItem1 =
|
||||
mediaItemBuilder.setMediaId("mediaId1").setUri("http://exoplayer.dev/video1").build();
|
||||
mediaItemBuilder
|
||||
.setMediaId("mediaId1")
|
||||
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
|
||||
.build();
|
||||
MediaItem mediaItem2 =
|
||||
mediaItemBuilder.setMediaId("mediaId2").setUri("http://exoplayer.dev/video2").build();
|
||||
mediaItemBuilder
|
||||
.setMediaId("mediaId2")
|
||||
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
|
||||
.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();
|
||||
|
|
@ -410,8 +435,7 @@ public class DefaultPreloadManagerTest {
|
|||
@Test
|
||||
public void removeByMediaItems_correspondingHeldSourceRemovedAndReleased() {
|
||||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData ->
|
||||
new DefaultPreloadManager.Status(DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED);
|
||||
rankingData -> new DefaultPreloadManager.Status(STAGE_TIMELINE_REFRESHED);
|
||||
MediaSource.Factory mockMediaSourceFactory = mock(MediaSource.Factory.class);
|
||||
DefaultPreloadManager preloadManager =
|
||||
new DefaultPreloadManager(
|
||||
|
|
@ -462,8 +486,7 @@ public class DefaultPreloadManagerTest {
|
|||
@Test
|
||||
public void removeByMediaSources_heldSourceRemovedAndReleased() {
|
||||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData ->
|
||||
new DefaultPreloadManager.Status(DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED);
|
||||
rankingData -> new DefaultPreloadManager.Status(STAGE_TIMELINE_REFRESHED);
|
||||
MediaSource.Factory mockMediaSourceFactory = mock(MediaSource.Factory.class);
|
||||
DefaultPreloadManager preloadManager =
|
||||
new DefaultPreloadManager(
|
||||
|
|
@ -521,8 +544,7 @@ public class DefaultPreloadManagerTest {
|
|||
@Test
|
||||
public void reset_returnZeroCount_sourcesButNotRendererCapabilitiesListReleased() {
|
||||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData ->
|
||||
new DefaultPreloadManager.Status(DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED);
|
||||
rankingData -> new DefaultPreloadManager.Status(STAGE_TIMELINE_REFRESHED);
|
||||
MediaSource.Factory mockMediaSourceFactory = mock(MediaSource.Factory.class);
|
||||
List<FakeRenderer> underlyingRenderers = new ArrayList<>();
|
||||
RenderersFactory renderersFactory =
|
||||
|
|
@ -593,8 +615,7 @@ public class DefaultPreloadManagerTest {
|
|||
@Test
|
||||
public void release_returnZeroCount_sourcesAndRendererCapabilitiesListReleased() {
|
||||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||
rankingData ->
|
||||
new DefaultPreloadManager.Status(DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED);
|
||||
rankingData -> new DefaultPreloadManager.Status(STAGE_TIMELINE_REFRESHED);
|
||||
MediaSource.Factory mockMediaSourceFactory = mock(MediaSource.Factory.class);
|
||||
List<FakeRenderer> underlyingRenderers = new ArrayList<>();
|
||||
RenderersFactory renderersFactory =
|
||||
|
|
|
|||
|
|
@ -362,6 +362,80 @@ public final class PreloadMediaSourceTest {
|
|||
assertThat(onUsedByPlayerCalled.get()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preload_loadToTheEndOfSource() throws Exception {
|
||||
AtomicBoolean onTimelineRefreshedCalled = new AtomicBoolean();
|
||||
AtomicBoolean onPreparedCalled = new AtomicBoolean();
|
||||
AtomicBoolean onContinueLoadingRequestedCalled = new AtomicBoolean();
|
||||
AtomicBoolean onLoadedToTheEndOfSourceCalled = new AtomicBoolean();
|
||||
AtomicBoolean onUsedByPlayerCalled = new AtomicBoolean();
|
||||
PreloadMediaSource.PreloadControl preloadControl =
|
||||
new PreloadMediaSource.PreloadControl() {
|
||||
@Override
|
||||
public boolean onTimelineRefreshed(PreloadMediaSource mediaSource) {
|
||||
onTimelineRefreshedCalled.set(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepared(PreloadMediaSource mediaSource) {
|
||||
onPreparedCalled.set(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContinueLoadingRequested(
|
||||
PreloadMediaSource mediaSource, long bufferedPositionUs) {
|
||||
// In fact, this method is not necessarily to be called if the
|
||||
// LOADING_CHECK_INTERVAL_BYTES set for the ProgressiveMediaSource.Factory is large
|
||||
// enough to have the media load to the end in one round. However, since we explicitly
|
||||
// set with a small value below, we will still expect this method to be called for at
|
||||
// least once.
|
||||
onContinueLoadingRequestedCalled.set(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUsedByPlayer(PreloadMediaSource mediaSource) {
|
||||
onUsedByPlayerCalled.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadedToTheEndOfSource(PreloadMediaSource mediaSource) {
|
||||
onLoadedToTheEndOfSourceCalled.set(true);
|
||||
}
|
||||
};
|
||||
ProgressiveMediaSource.Factory mediaSourceFactory =
|
||||
new ProgressiveMediaSource.Factory(
|
||||
new DefaultDataSource.Factory(ApplicationProvider.getApplicationContext()));
|
||||
mediaSourceFactory.setContinueLoadingCheckIntervalBytes(LOADING_CHECK_INTERVAL_BYTES);
|
||||
TrackSelector trackSelector =
|
||||
new DefaultTrackSelector(ApplicationProvider.getApplicationContext());
|
||||
trackSelector.init(() -> {}, bandwidthMeter);
|
||||
PreloadMediaSource.Factory preloadMediaSourceFactory =
|
||||
new PreloadMediaSource.Factory(
|
||||
mediaSourceFactory,
|
||||
preloadControl,
|
||||
trackSelector,
|
||||
bandwidthMeter,
|
||||
getRendererCapabilities(renderersFactory),
|
||||
allocator,
|
||||
Util.getCurrentOrMainLooper());
|
||||
PreloadMediaSource preloadMediaSource =
|
||||
preloadMediaSourceFactory.createMediaSource(
|
||||
new MediaItem.Builder()
|
||||
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
|
||||
.build());
|
||||
|
||||
preloadMediaSource.preload(/* startPositionUs= */ 0L);
|
||||
runMainLooperUntil(onLoadedToTheEndOfSourceCalled::get);
|
||||
|
||||
assertThat(onTimelineRefreshedCalled.get()).isTrue();
|
||||
assertThat(onPreparedCalled.get()).isTrue();
|
||||
assertThat(onContinueLoadingRequestedCalled.get()).isTrue();
|
||||
assertThat(onUsedByPlayerCalled.get()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
prepareSource_beforeSourceInfoRefreshedForPreloading_onlyInvokeExternalCallerOnSourceInfoRefreshed() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue