mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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
|
* Deprecated `MediaCodecUtil.getCodecProfileAndLevel`. Use
|
||||||
`androidx.media3.common.util.CodecSpecificDataUtil.getCodecProfileAndLevel`
|
`androidx.media3.common.util.CodecSpecificDataUtil.getCodecProfileAndLevel`
|
||||||
instead.
|
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:
|
* Transformer:
|
||||||
* Track Selection:
|
* Track Selection:
|
||||||
* Extractors:
|
* Extractors:
|
||||||
|
@ -33,7 +33,7 @@ import androidx.media3.exoplayer.DefaultRendererCapabilitiesList
|
|||||||
import androidx.media3.exoplayer.DefaultRenderersFactory
|
import androidx.media3.exoplayer.DefaultRenderersFactory
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
||||||
import androidx.media3.exoplayer.source.preload.DefaultPreloadManager
|
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.source.preload.TargetPreloadStatusControl
|
||||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
|
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
|
||||||
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter
|
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter
|
||||||
@ -200,9 +200,9 @@ class ViewPagerMediaAdapter(
|
|||||||
inner class DefaultPreloadControl : TargetPreloadStatusControl<Int> {
|
inner class DefaultPreloadControl : TargetPreloadStatusControl<Int> {
|
||||||
override fun getTargetPreloadStatus(rankingData: Int): DefaultPreloadManager.Status? {
|
override fun getTargetPreloadStatus(rankingData: Int): DefaultPreloadManager.Status? {
|
||||||
if (abs(rankingData - currentPlayingIndex) == 2) {
|
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) {
|
} 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
|
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
|
* 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
|
@Documented
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@ -66,7 +66,7 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||||||
value = {
|
value = {
|
||||||
STAGE_SOURCE_PREPARED,
|
STAGE_SOURCE_PREPARED,
|
||||||
STAGE_TRACKS_SELECTED,
|
STAGE_TRACKS_SELECTED,
|
||||||
STAGE_LOADED_TO_POSITION_MS,
|
STAGE_LOADED_FOR_DURATION_MS,
|
||||||
})
|
})
|
||||||
public @interface Stage {}
|
public @interface Stage {}
|
||||||
|
|
||||||
@ -76,8 +76,11 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||||||
/** The {@link PreloadMediaSource} has tracks selected. */
|
/** The {@link PreloadMediaSource} has tracks selected. */
|
||||||
public static final int STAGE_TRACKS_SELECTED = 1;
|
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 @Stage int stage;
|
||||||
private final long value;
|
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
|
* @param allocator The {@link Allocator}. It should be the same allocator of the {@link
|
||||||
* ExoPlayer} that will play the managed {@link PreloadMediaSource}.
|
* ExoPlayer} that will play the managed {@link PreloadMediaSource}.
|
||||||
* @param preloadLooper The {@link Looper} that will be used for preloading. It should be the same
|
* @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}.
|
* PreloadMediaSource}.
|
||||||
*/
|
*/
|
||||||
public DefaultPreloadManager(
|
public DefaultPreloadManager(
|
||||||
@ -228,14 +231,14 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onContinueLoadingRequested(
|
public boolean onContinueLoadingRequested(
|
||||||
PreloadMediaSource mediaSource, long bufferedPositionUs) {
|
PreloadMediaSource mediaSource, long bufferedDurationUs) {
|
||||||
// Set `clearExceededDataFromTargetPreloadStatus` to `false` as clearing the exceeded data
|
// 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(
|
return continueOrCompletePreloading(
|
||||||
mediaSource,
|
mediaSource,
|
||||||
/* continueLoadingPredicate= */ status ->
|
/* continueLoadingPredicate= */ status ->
|
||||||
status.getStage() == Status.STAGE_LOADED_TO_POSITION_MS
|
status.getStage() == Status.STAGE_LOADED_FOR_DURATION_MS
|
||||||
&& status.getValue() > Util.usToMs(bufferedPositionUs),
|
&& status.getValue() > Util.usToMs(bufferedDurationUs),
|
||||||
/* clearExceededDataFromTargetPreloadStatus= */ false);
|
/* clearExceededDataFromTargetPreloadStatus= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,10 +87,10 @@ public final class PreloadMediaSource extends WrappingMediaSource {
|
|||||||
* instead.
|
* instead.
|
||||||
*
|
*
|
||||||
* @param mediaSource The {@link PreloadMediaSource} that requests to continue loading.
|
* @param mediaSource The {@link PreloadMediaSource} that requests to continue loading.
|
||||||
* @param bufferedPositionUs An estimate of the absolute position in microseconds up to which
|
* @param bufferedDurationUs An estimate of the duration from the start position for which data
|
||||||
* data is buffered.
|
* 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.
|
* Called from {@link PreloadMediaSource} when the player starts using this source.
|
||||||
@ -502,14 +502,15 @@ public final class PreloadMediaSource extends WrappingMediaSource {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PreloadMediaPeriod preloadMediaPeriod = (PreloadMediaPeriod) mediaPeriod;
|
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);
|
preloadControl.onLoadedToTheEndOfSource(PreloadMediaSource.this);
|
||||||
stopPreloading();
|
stopPreloading();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (prepared
|
if (prepared
|
||||||
&& !preloadControl.onContinueLoadingRequested(
|
&& !preloadControl.onContinueLoadingRequested(
|
||||||
PreloadMediaSource.this, preloadMediaPeriod.getBufferedPositionUs())) {
|
PreloadMediaSource.this, bufferedPositionUs - periodStartPositionUs)) {
|
||||||
stopPreloading();
|
stopPreloading();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.source.preload;
|
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_SOURCE_PREPARED;
|
||||||
import static androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STAGE_TRACKS_SELECTED;
|
import static androidx.media3.exoplayer.source.preload.DefaultPreloadManager.Status.STAGE_TRACKS_SELECTED;
|
||||||
import static androidx.media3.test.utils.FakeMediaSourceFactory.DEFAULT_WINDOW_UID;
|
import static androidx.media3.test.utils.FakeMediaSourceFactory.DEFAULT_WINDOW_UID;
|
||||||
@ -195,7 +195,7 @@ public class DefaultPreloadManagerTest {
|
|||||||
rankingData -> {
|
rankingData -> {
|
||||||
targetPreloadStatusControlCallStates.add(rankingData);
|
targetPreloadStatusControlCallStates.add(rankingData);
|
||||||
if (abs(rankingData - currentPlayingItemIndex.get()) == 1) {
|
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 {
|
} else {
|
||||||
return new DefaultPreloadManager.Status(STAGE_SOURCE_PREPARED);
|
return new DefaultPreloadManager.Status(STAGE_SOURCE_PREPARED);
|
||||||
}
|
}
|
||||||
@ -256,7 +256,7 @@ public class DefaultPreloadManagerTest {
|
|||||||
rankingData -> {
|
rankingData -> {
|
||||||
targetPreloadStatusControlCallStates.add(rankingData);
|
targetPreloadStatusControlCallStates.add(rankingData);
|
||||||
if (abs(rankingData - currentPlayingItemIndex.get()) == 1) {
|
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 {
|
} else {
|
||||||
return new DefaultPreloadManager.Status(STAGE_SOURCE_PREPARED);
|
return new DefaultPreloadManager.Status(STAGE_SOURCE_PREPARED);
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ public class PreloadAndPlaybackCoordinationTest {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onContinueLoadingRequested(
|
public boolean onContinueLoadingRequested(
|
||||||
PreloadMediaSource mediaSource, long bufferedPositionUs) {
|
PreloadMediaSource mediaSource, long bufferedDurationUs) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ import org.junit.runner.RunWith;
|
|||||||
public final class PreloadMediaSourceTest {
|
public final class PreloadMediaSourceTest {
|
||||||
|
|
||||||
private static final int LOADING_CHECK_INTERVAL_BYTES = 10 * 1024;
|
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 Allocator allocator;
|
||||||
private BandwidthMeter bandwidthMeter;
|
private BandwidthMeter bandwidthMeter;
|
||||||
@ -121,9 +121,9 @@ public final class PreloadMediaSourceTest {
|
|||||||
new TestPreloadControl() {
|
new TestPreloadControl() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onContinueLoadingRequested(
|
public boolean onContinueLoadingRequested(
|
||||||
PreloadMediaSource mediaSource, long bufferedPositionUs) {
|
PreloadMediaSource mediaSource, long bufferedDurationUs) {
|
||||||
onContinueLoadingRequestedCalled = true;
|
onContinueLoadingRequestedCalled = true;
|
||||||
if (bufferedPositionUs >= TARGET_PRELOAD_POSITION_US) {
|
if (bufferedDurationUs >= TARGET_PRELOAD_DURATION_US) {
|
||||||
preloadMediaSourceReference.set(mediaSource);
|
preloadMediaSourceReference.set(mediaSource);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1186,7 +1186,7 @@ public final class PreloadMediaSourceTest {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onContinueLoadingRequested(
|
public boolean onContinueLoadingRequested(
|
||||||
PreloadMediaSource mediaSource, long bufferedPositionUs) {
|
PreloadMediaSource mediaSource, long bufferedDurationUs) {
|
||||||
onContinueLoadingRequestedCalled = true;
|
onContinueLoadingRequestedCalled = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user