From 72ae454f6727f71f834ef9f155757cfb9ff876ee Mon Sep 17 00:00:00 2001 From: tianyifeng Date: Fri, 13 Sep 2024 05:01:54 -0700 Subject: [PATCH] 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 --- RELEASENOTES.md | 7 +++++++ .../viewpager/ViewPagerMediaAdapter.kt | 6 +++--- .../source/preload/DefaultPreloadManager.java | 21 +++++++++++-------- .../source/preload/PreloadMediaSource.java | 11 +++++----- .../preload/DefaultPreloadManagerTest.java | 6 +++--- .../PreloadAndPlaybackCoordinationTest.java | 2 +- .../preload/PreloadMediaSourceTest.java | 8 +++---- 7 files changed, 36 insertions(+), 25 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4a1a9cf4a9..f5b21fbacc 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -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: diff --git a/demos/shortform/src/main/java/androidx/media3/demo/shortform/viewpager/ViewPagerMediaAdapter.kt b/demos/shortform/src/main/java/androidx/media3/demo/shortform/viewpager/ViewPagerMediaAdapter.kt index ed18fe75b9..a78709fce4 100644 --- a/demos/shortform/src/main/java/androidx/media3/demo/shortform/viewpager/ViewPagerMediaAdapter.kt +++ b/demos/shortform/src/main/java/androidx/media3/demo/shortform/viewpager/ViewPagerMediaAdapter.kt @@ -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 { 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 } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManager.java index 0dee797a79..d3740991ce 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManager.java @@ -57,7 +57,7 @@ public final class DefaultPreloadManager extends BasePreloadManager { /** * 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 { 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 { /** 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 { * @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 { @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); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/PreloadMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/PreloadMediaSource.java index 41e0cad8be..d2a313672e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/PreloadMediaSource.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/PreloadMediaSource.java @@ -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; } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java index ddeab4e781..e5a9299de0 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java @@ -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); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/PreloadAndPlaybackCoordinationTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/PreloadAndPlaybackCoordinationTest.java index 35d61d1654..925e78a37f 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/PreloadAndPlaybackCoordinationTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/PreloadAndPlaybackCoordinationTest.java @@ -116,7 +116,7 @@ public class PreloadAndPlaybackCoordinationTest { @Override public boolean onContinueLoadingRequested( - PreloadMediaSource mediaSource, long bufferedPositionUs) { + PreloadMediaSource mediaSource, long bufferedDurationUs) { return true; } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/PreloadMediaSourceTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/PreloadMediaSourceTest.java index 415ec2ecda..5e6db67123 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/PreloadMediaSourceTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/PreloadMediaSourceTest.java @@ -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; }