Use buffered duration from start position to control preload progress
`PreloadMediaSource` allows to have a `startPositionUs` passed when `preload` is called, then in `PreloadControl.onContinueLoadingRequested`, it can be more intuitive to see the buffered duration rather than the absolute buffered position as the preload progress. Similar in `DefaultPreloadManager`, we haven't allowed the apps to set a custom start position for individual sources though, once we add this support, using the "duration from the start position" than the absolute position will be less error-prone, otherwise, it can run into a case that the position that the apps set is smaller than the start position. PiperOrigin-RevId: 674251362
This commit is contained in:
parent
023fd32cb1
commit
72ae454f67
@ -10,6 +10,13 @@
|
||||
* Deprecated `MediaCodecUtil.getCodecProfileAndLevel`. Use
|
||||
`androidx.media3.common.util.CodecSpecificDataUtil.getCodecProfileAndLevel`
|
||||
instead.
|
||||
* Pass `bufferedDurationUs` instead of `bufferedPositionUs` with
|
||||
`PreloadMediaSource.PreloadControl.onContinueLoadingRequested()`. Also
|
||||
changes `DefaultPreloadManager.Status.STAGE_LOADED_TO_POSITION_MS` to
|
||||
`DefaultPreloadManager.Status.STAGE_LOADED_FOR_DURATION_MS`, apps then
|
||||
need to pass a value representing a specific duration from the default
|
||||
start position for which the corresponding media source has to be
|
||||
preloaded with this IntDef, instead of a position.
|
||||
* Transformer:
|
||||
* Track Selection:
|
||||
* Extractors:
|
||||
|
@ -33,7 +33,7 @@ import androidx.media3.exoplayer.DefaultRendererCapabilitiesList
|
||||
import androidx.media3.exoplayer.DefaultRenderersFactory
|
||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
||||
import androidx.media3.exoplayer.source.preload.DefaultPreloadManager
|
||||
import androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STAGE_LOADED_TO_POSITION_MS
|
||||
import androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STAGE_LOADED_FOR_DURATION_MS
|
||||
import androidx.media3.exoplayer.source.preload.TargetPreloadStatusControl
|
||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
|
||||
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter
|
||||
@ -200,9 +200,9 @@ class ViewPagerMediaAdapter(
|
||||
inner class DefaultPreloadControl : TargetPreloadStatusControl<Int> {
|
||||
override fun getTargetPreloadStatus(rankingData: Int): DefaultPreloadManager.Status? {
|
||||
if (abs(rankingData - currentPlayingIndex) == 2) {
|
||||
return DefaultPreloadManager.Status(STAGE_LOADED_TO_POSITION_MS, 500L)
|
||||
return DefaultPreloadManager.Status(STAGE_LOADED_FOR_DURATION_MS, 500L)
|
||||
} else if (abs(rankingData - currentPlayingIndex) == 1) {
|
||||
return DefaultPreloadManager.Status(STAGE_LOADED_TO_POSITION_MS, 1000L)
|
||||
return DefaultPreloadManager.Status(STAGE_LOADED_FOR_DURATION_MS, 1000L)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
||||
|
||||
/**
|
||||
* Stages for the preload status. One of {@link #STAGE_SOURCE_PREPARED}, {@link
|
||||
* #STAGE_TRACKS_SELECTED} or {@link #STAGE_LOADED_TO_POSITION_MS}.
|
||||
* #STAGE_TRACKS_SELECTED} or {@link #STAGE_LOADED_FOR_DURATION_MS}.
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@ -66,7 +66,7 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
||||
value = {
|
||||
STAGE_SOURCE_PREPARED,
|
||||
STAGE_TRACKS_SELECTED,
|
||||
STAGE_LOADED_TO_POSITION_MS,
|
||||
STAGE_LOADED_FOR_DURATION_MS,
|
||||
})
|
||||
public @interface Stage {}
|
||||
|
||||
@ -76,8 +76,11 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
||||
/** The {@link PreloadMediaSource} has tracks selected. */
|
||||
public static final int STAGE_TRACKS_SELECTED = 1;
|
||||
|
||||
/** The {@link PreloadMediaSource} is loaded to a specific position in microseconds. */
|
||||
public static final int STAGE_LOADED_TO_POSITION_MS = 2;
|
||||
/**
|
||||
* The {@link PreloadMediaSource} is loaded for a specific duration from the default start
|
||||
* position, in milliseconds.
|
||||
*/
|
||||
public static final int STAGE_LOADED_FOR_DURATION_MS = 2;
|
||||
|
||||
private final @Stage int stage;
|
||||
private final long value;
|
||||
@ -124,7 +127,7 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
||||
* @param allocator The {@link Allocator}. It should be the same allocator of the {@link
|
||||
* ExoPlayer} that will play the managed {@link PreloadMediaSource}.
|
||||
* @param preloadLooper The {@link Looper} that will be used for preloading. It should be the same
|
||||
* playback looper of the {@link ExoPlayer} that will play the manager {@link
|
||||
* playback looper of the {@link ExoPlayer} that will play the managed {@link
|
||||
* PreloadMediaSource}.
|
||||
*/
|
||||
public DefaultPreloadManager(
|
||||
@ -228,14 +231,14 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
||||
|
||||
@Override
|
||||
public boolean onContinueLoadingRequested(
|
||||
PreloadMediaSource mediaSource, long bufferedPositionUs) {
|
||||
PreloadMediaSource mediaSource, long bufferedDurationUs) {
|
||||
// Set `clearExceededDataFromTargetPreloadStatus` to `false` as clearing the exceeded data
|
||||
// from the status STAGE_LOADED_TO_POSITION_MS is not supported.
|
||||
// from the status STAGE_LOADED_FOR_DURATION_MS is not supported.
|
||||
return continueOrCompletePreloading(
|
||||
mediaSource,
|
||||
/* continueLoadingPredicate= */ status ->
|
||||
status.getStage() == Status.STAGE_LOADED_TO_POSITION_MS
|
||||
&& status.getValue() > Util.usToMs(bufferedPositionUs),
|
||||
status.getStage() == Status.STAGE_LOADED_FOR_DURATION_MS
|
||||
&& status.getValue() > Util.usToMs(bufferedDurationUs),
|
||||
/* clearExceededDataFromTargetPreloadStatus= */ false);
|
||||
}
|
||||
|
||||
|
@ -87,10 +87,10 @@ public final class PreloadMediaSource extends WrappingMediaSource {
|
||||
* 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.
|
||||
* @param bufferedDurationUs An estimate of the duration from the start position for which data
|
||||
* is buffered, in microseconds.
|
||||
*/
|
||||
boolean onContinueLoadingRequested(PreloadMediaSource mediaSource, long bufferedPositionUs);
|
||||
boolean onContinueLoadingRequested(PreloadMediaSource mediaSource, long bufferedDurationUs);
|
||||
|
||||
/**
|
||||
* Called from {@link PreloadMediaSource} when the player starts using this source.
|
||||
@ -502,14 +502,15 @@ public final class PreloadMediaSource extends WrappingMediaSource {
|
||||
return;
|
||||
}
|
||||
PreloadMediaPeriod preloadMediaPeriod = (PreloadMediaPeriod) mediaPeriod;
|
||||
if (prepared && mediaPeriod.getBufferedPositionUs() == C.TIME_END_OF_SOURCE) {
|
||||
long bufferedPositionUs = mediaPeriod.getBufferedPositionUs();
|
||||
if (prepared && bufferedPositionUs == C.TIME_END_OF_SOURCE) {
|
||||
preloadControl.onLoadedToTheEndOfSource(PreloadMediaSource.this);
|
||||
stopPreloading();
|
||||
return;
|
||||
}
|
||||
if (prepared
|
||||
&& !preloadControl.onContinueLoadingRequested(
|
||||
PreloadMediaSource.this, preloadMediaPeriod.getBufferedPositionUs())) {
|
||||
PreloadMediaSource.this, bufferedPositionUs - periodStartPositionUs)) {
|
||||
stopPreloading();
|
||||
return;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
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_LOADED_FOR_DURATION_MS;
|
||||
import static androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STAGE_SOURCE_PREPARED;
|
||||
import static androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STAGE_TRACKS_SELECTED;
|
||||
import static androidx.media3.test.utils.FakeMediaSourceFactory.DEFAULT_WINDOW_UID;
|
||||
@ -195,7 +195,7 @@ public class DefaultPreloadManagerTest {
|
||||
rankingData -> {
|
||||
targetPreloadStatusControlCallStates.add(rankingData);
|
||||
if (abs(rankingData - currentPlayingItemIndex.get()) == 1) {
|
||||
return new DefaultPreloadManager.Status(STAGE_LOADED_TO_POSITION_MS, 100L);
|
||||
return new DefaultPreloadManager.Status(STAGE_LOADED_FOR_DURATION_MS, 100L);
|
||||
} else {
|
||||
return new DefaultPreloadManager.Status(STAGE_SOURCE_PREPARED);
|
||||
}
|
||||
@ -256,7 +256,7 @@ public class DefaultPreloadManagerTest {
|
||||
rankingData -> {
|
||||
targetPreloadStatusControlCallStates.add(rankingData);
|
||||
if (abs(rankingData - currentPlayingItemIndex.get()) == 1) {
|
||||
return new DefaultPreloadManager.Status(STAGE_LOADED_TO_POSITION_MS, 100L);
|
||||
return new DefaultPreloadManager.Status(STAGE_LOADED_FOR_DURATION_MS, 100L);
|
||||
} else {
|
||||
return new DefaultPreloadManager.Status(STAGE_SOURCE_PREPARED);
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ public class PreloadAndPlaybackCoordinationTest {
|
||||
|
||||
@Override
|
||||
public boolean onContinueLoadingRequested(
|
||||
PreloadMediaSource mediaSource, long bufferedPositionUs) {
|
||||
PreloadMediaSource mediaSource, long bufferedDurationUs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ import org.junit.runner.RunWith;
|
||||
public final class PreloadMediaSourceTest {
|
||||
|
||||
private static final int LOADING_CHECK_INTERVAL_BYTES = 10 * 1024;
|
||||
private static final int TARGET_PRELOAD_POSITION_US = 10000;
|
||||
private static final int TARGET_PRELOAD_DURATION_US = 10000;
|
||||
|
||||
private Allocator allocator;
|
||||
private BandwidthMeter bandwidthMeter;
|
||||
@ -121,9 +121,9 @@ public final class PreloadMediaSourceTest {
|
||||
new TestPreloadControl() {
|
||||
@Override
|
||||
public boolean onContinueLoadingRequested(
|
||||
PreloadMediaSource mediaSource, long bufferedPositionUs) {
|
||||
PreloadMediaSource mediaSource, long bufferedDurationUs) {
|
||||
onContinueLoadingRequestedCalled = true;
|
||||
if (bufferedPositionUs >= TARGET_PRELOAD_POSITION_US) {
|
||||
if (bufferedDurationUs >= TARGET_PRELOAD_DURATION_US) {
|
||||
preloadMediaSourceReference.set(mediaSource);
|
||||
return false;
|
||||
}
|
||||
@ -1186,7 +1186,7 @@ public final class PreloadMediaSourceTest {
|
||||
|
||||
@Override
|
||||
public boolean onContinueLoadingRequested(
|
||||
PreloadMediaSource mediaSource, long bufferedPositionUs) {
|
||||
PreloadMediaSource mediaSource, long bufferedDurationUs) {
|
||||
onContinueLoadingRequestedCalled = true;
|
||||
return true;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user