mirror of
https://github.com/androidx/media.git
synced 2025-04-29 22:36:54 +08:00
parent
4cd8d64446
commit
872d8f078b
@ -437,6 +437,31 @@ public interface ExoPlayer extends Player {
|
||||
default void onOffloadedPlayback(boolean isOffloadedPlayback) {}
|
||||
}
|
||||
|
||||
/** Configuration options for preloading playlist items. */
|
||||
@UnstableApi
|
||||
class PreloadConfiguration {
|
||||
|
||||
/** Default preload configuration that disables playlist preloading. */
|
||||
public static final PreloadConfiguration DEFAULT =
|
||||
new PreloadConfiguration(/* targetPreloadDurationUs= */ C.TIME_UNSET);
|
||||
|
||||
/**
|
||||
* The target duration to buffer when preloading, in microseconds or {@link C#TIME_UNSET} to
|
||||
* disable preloading.
|
||||
*/
|
||||
public final long targetPreloadDurationUs;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*
|
||||
* @param targetPreloadDurationUs The target duration to preload, in microseconds or {@link
|
||||
* C#TIME_UNSET} to disable preloading.
|
||||
*/
|
||||
public PreloadConfiguration(long targetPreloadDurationUs) {
|
||||
this.targetPreloadDurationUs = targetPreloadDurationUs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for {@link ExoPlayer} instances.
|
||||
*
|
||||
@ -1556,6 +1581,19 @@ public interface ExoPlayer extends Player {
|
||||
@UnstableApi
|
||||
void setShuffleOrder(ShuffleOrder shuffleOrder);
|
||||
|
||||
/**
|
||||
* Sets the {@linkplain PreloadConfiguration preload configuration} to configure playlist
|
||||
* preloading.
|
||||
*
|
||||
* @param preloadConfiguration The preload configuration.
|
||||
*/
|
||||
@UnstableApi
|
||||
void setPreloadConfiguration(PreloadConfiguration preloadConfiguration);
|
||||
|
||||
/** Returns the {@linkplain PreloadConfiguration preload configuration}. */
|
||||
@UnstableApi
|
||||
PreloadConfiguration getPreloadConfiguration();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -141,30 +141,6 @@ import java.util.concurrent.TimeoutException;
|
||||
MediaLibraryInfo.registerModule("media3.exoplayer");
|
||||
}
|
||||
|
||||
/** Configuration options for preloading playlist items. */
|
||||
public static final class PreloadConfiguration {
|
||||
|
||||
/** Default preload configuration that disables playlist preloading. */
|
||||
public static final PreloadConfiguration DEFAULT =
|
||||
new PreloadConfiguration(/* targetPreloadDurationUs= */ C.TIME_UNSET);
|
||||
|
||||
/**
|
||||
* The target duration to buffer when preloading, in microseconds or {@link C#TIME_UNSET} to
|
||||
* disable preloading.
|
||||
*/
|
||||
public final long targetPreloadDurationUs;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
*
|
||||
* @param targetPreloadDurationUs The target duration to preload, in microseconds or {@link
|
||||
* C#TIME_UNSET} to disable preloading.
|
||||
*/
|
||||
public PreloadConfiguration(long targetPreloadDurationUs) {
|
||||
this.targetPreloadDurationUs = targetPreloadDurationUs;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TAG = "ExoPlayerImpl";
|
||||
|
||||
/**
|
||||
@ -909,6 +885,7 @@ import java.util.concurrent.TimeoutException;
|
||||
return shuffleModeEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreloadConfiguration(PreloadConfiguration preloadConfiguration) {
|
||||
verifyApplicationThread();
|
||||
if (this.preloadConfiguration.equals(preloadConfiguration)) {
|
||||
@ -918,6 +895,7 @@ import java.util.concurrent.TimeoutException;
|
||||
internalPlayer.setPreloadConfiguration(preloadConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreloadConfiguration getPreloadConfiguration() {
|
||||
return preloadConfiguration;
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ import androidx.media3.common.util.TraceUtil;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.datasource.DataSourceException;
|
||||
import androidx.media3.exoplayer.DefaultMediaClock.PlaybackParametersListener;
|
||||
import androidx.media3.exoplayer.ExoPlayer.PreloadConfiguration;
|
||||
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
|
||||
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||
import androidx.media3.exoplayer.drm.DrmSession;
|
||||
@ -241,7 +242,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@Nullable private ExoPlaybackException pendingRecoverableRendererError;
|
||||
private long setForegroundModeTimeoutMs;
|
||||
private long playbackMaybeBecameStuckAtMs;
|
||||
private ExoPlayerImpl.PreloadConfiguration preloadConfiguration;
|
||||
private PreloadConfiguration preloadConfiguration;
|
||||
private Timeline lastPreloadPoolInvalidationTimeline;
|
||||
|
||||
public ExoPlayerImplInternal(
|
||||
@ -263,7 +264,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
PlaybackInfoUpdateListener playbackInfoUpdateListener,
|
||||
PlayerId playerId,
|
||||
Looper playbackLooper,
|
||||
ExoPlayerImpl.PreloadConfiguration preloadConfiguration) {
|
||||
PreloadConfiguration preloadConfiguration) {
|
||||
this.playbackInfoUpdateListener = playbackInfoUpdateListener;
|
||||
this.renderers = renderers;
|
||||
this.trackSelector = trackSelector;
|
||||
@ -372,7 +373,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
handler.obtainMessage(MSG_SET_SHUFFLE_ENABLED, shuffleModeEnabled ? 1 : 0, 0).sendToTarget();
|
||||
}
|
||||
|
||||
public void setPreloadConfiguration(ExoPlayerImpl.PreloadConfiguration preloadConfiguration) {
|
||||
public void setPreloadConfiguration(PreloadConfiguration preloadConfiguration) {
|
||||
handler.obtainMessage(MSG_SET_PRELOAD_CONFIGURATION, preloadConfiguration).sendToTarget();
|
||||
}
|
||||
|
||||
@ -561,7 +562,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
setShuffleModeEnabledInternal(msg.arg1 != 0);
|
||||
break;
|
||||
case MSG_SET_PRELOAD_CONFIGURATION:
|
||||
setPreloadConfigurationInternal((ExoPlayerImpl.PreloadConfiguration) msg.obj);
|
||||
setPreloadConfigurationInternal((PreloadConfiguration) msg.obj);
|
||||
break;
|
||||
case MSG_DO_SOME_WORK:
|
||||
doSomeWork();
|
||||
@ -950,8 +951,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
handleLoadingMediaPeriodChanged(/* loadingTrackSelectionChanged= */ false);
|
||||
}
|
||||
|
||||
private void setPreloadConfigurationInternal(
|
||||
ExoPlayerImpl.PreloadConfiguration preloadConfiguration) {
|
||||
private void setPreloadConfigurationInternal(PreloadConfiguration preloadConfiguration) {
|
||||
this.preloadConfiguration = preloadConfiguration;
|
||||
queue.updatePreloadConfiguration(playbackInfo.timeline, preloadConfiguration);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import androidx.media3.common.Player.RepeatMode;
|
||||
import androidx.media3.common.Timeline;
|
||||
import androidx.media3.common.util.Assertions;
|
||||
import androidx.media3.common.util.HandlerWrapper;
|
||||
import androidx.media3.exoplayer.ExoPlayer.PreloadConfiguration;
|
||||
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
|
||||
import androidx.media3.exoplayer.source.MediaPeriod;
|
||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||
@ -84,7 +85,7 @@ import java.util.List;
|
||||
private int length;
|
||||
@Nullable private Object oldFrontPeriodUid;
|
||||
private long oldFrontPeriodWindowSequenceNumber;
|
||||
private ExoPlayerImpl.PreloadConfiguration preloadConfiguration;
|
||||
private PreloadConfiguration preloadConfiguration;
|
||||
private List<MediaPeriodHolder> preloadPriorityList;
|
||||
|
||||
/**
|
||||
@ -99,7 +100,7 @@ import java.util.List;
|
||||
AnalyticsCollector analyticsCollector,
|
||||
HandlerWrapper analyticsCollectorHandler,
|
||||
MediaPeriodHolder.Factory mediaPeriodHolderFactory,
|
||||
ExoPlayerImpl.PreloadConfiguration preloadConfiguration) {
|
||||
PreloadConfiguration preloadConfiguration) {
|
||||
this.analyticsCollector = analyticsCollector;
|
||||
this.analyticsCollectorHandler = analyticsCollectorHandler;
|
||||
this.mediaPeriodHolderFactory = mediaPeriodHolderFactory;
|
||||
@ -142,7 +143,7 @@ import java.util.List;
|
||||
* @param preloadConfiguration The new preload configuration.
|
||||
*/
|
||||
public void updatePreloadConfiguration(
|
||||
Timeline timeline, ExoPlayerImpl.PreloadConfiguration preloadConfiguration) {
|
||||
Timeline timeline, PreloadConfiguration preloadConfiguration) {
|
||||
this.preloadConfiguration = preloadConfiguration;
|
||||
invalidatePreloadPool(timeline);
|
||||
}
|
||||
|
@ -992,6 +992,18 @@ public class SimpleExoPlayer extends BasePlayer
|
||||
player.setRepeatMode(repeatMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreloadConfiguration(PreloadConfiguration preloadConfiguration) {
|
||||
blockUntilConstructorFinished();
|
||||
player.setPreloadConfiguration(preloadConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreloadConfiguration getPreloadConfiguration() {
|
||||
blockUntilConstructorFinished();
|
||||
return player.getPreloadConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShuffleModeEnabled(boolean shuffleModeEnabled) {
|
||||
blockUntilConstructorFinished();
|
||||
|
@ -274,14 +274,16 @@ public class ExoPlayerTest {
|
||||
}
|
||||
|
||||
private TestExoPlayerBuilder parameterizeTestExoPlayerBuilder(TestExoPlayerBuilder builder) {
|
||||
// TODO: set PreloadConfiguration when enabled again after the release.
|
||||
return builder;
|
||||
return builder.setPreloadConfiguration(createPreloadConfiguration());
|
||||
}
|
||||
|
||||
private ExoPlayerTestRunner.Builder parameterizeExoPlayerTestRunnerBuilder(
|
||||
ExoPlayerTestRunner.Builder builder) {
|
||||
// TODO: set PreloadConfiguration when enabled again after the release.
|
||||
return builder;
|
||||
return builder.setPreloadConfiguration(createPreloadConfiguration());
|
||||
}
|
||||
|
||||
private ExoPlayer.PreloadConfiguration createPreloadConfiguration() {
|
||||
return new ExoPlayer.PreloadConfiguration(getTargetPreloadDurationUs());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -6806,6 +6808,80 @@ public class ExoPlayerTest {
|
||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE /* source prepared */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prepare_preloadingEnabled_nextWindowPeriodCreatedForPreloading() throws Exception {
|
||||
FakeMediaSource mediaSource1 =
|
||||
new FakeMediaSource(
|
||||
new FakeTimeline(
|
||||
new TimelineWindowDefinition(
|
||||
/* isSeekable= */ true,
|
||||
/* isDynamic= */ false,
|
||||
/* durationUs= */ DefaultLoadControl.DEFAULT_MAX_BUFFER_MS * 2)));
|
||||
List<MediaPeriodId> createdMediaPeriodIds = new ArrayList<>();
|
||||
FakeMediaSource mediaSource2 =
|
||||
new FakeMediaSource() {
|
||||
@Override
|
||||
public MediaPeriod createPeriod(
|
||||
MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
createdMediaPeriodIds.add(id);
|
||||
return super.createPeriod(id, allocator, startPositionUs);
|
||||
}
|
||||
};
|
||||
ExoPlayer player =
|
||||
// Intentionally not using `parameterizeTestExoPlayerBuilder()` for preload specific test.
|
||||
new TestExoPlayerBuilder(context)
|
||||
.setPreloadConfiguration(
|
||||
new ExoPlayer.PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L))
|
||||
.build();
|
||||
player.setMediaSources(ImmutableList.of(mediaSource1, mediaSource2));
|
||||
|
||||
player.prepare();
|
||||
run(player).untilPendingCommandsAreFullyHandled();
|
||||
|
||||
assertThat(createdMediaPeriodIds).hasSize(1);
|
||||
play(player).untilState(Player.STATE_ENDED);
|
||||
assertThat(createdMediaPeriodIds).hasSize(1);
|
||||
player.release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prepare_preloadingEnabledRepeatModeOne_sameWindowPeriodCreatedForPreloading()
|
||||
throws Exception {
|
||||
FakeTimeline timeline =
|
||||
new FakeTimeline(
|
||||
new TimelineWindowDefinition(
|
||||
/* isSeekable= */ true,
|
||||
/* isDynamic= */ false,
|
||||
/* durationUs= */ DefaultLoadControl.DEFAULT_MAX_BUFFER_MS * 2));
|
||||
List<MediaPeriodId> createdMediaPeriodIds = new ArrayList<>();
|
||||
FakeMediaSource mediaSource =
|
||||
new FakeMediaSource(timeline) {
|
||||
@Override
|
||||
public MediaPeriod createPeriod(
|
||||
MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
createdMediaPeriodIds.add(id);
|
||||
return super.createPeriod(id, allocator, startPositionUs);
|
||||
}
|
||||
};
|
||||
ExoPlayer player =
|
||||
// Intentionally not using `parameterizeTestExoPlayerBuilder()` for preload specific test.
|
||||
new TestExoPlayerBuilder(context)
|
||||
.setPreloadConfiguration(
|
||||
new ExoPlayer.PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L))
|
||||
.build();
|
||||
player.setRepeatMode(Player.REPEAT_MODE_ONE);
|
||||
player.setMediaSource(mediaSource);
|
||||
|
||||
player.prepare();
|
||||
run(player).untilPendingCommandsAreFullyHandled();
|
||||
|
||||
assertThat(createdMediaPeriodIds).hasSize(2);
|
||||
player.setRepeatMode(Player.REPEAT_MODE_OFF);
|
||||
play(player).untilState(Player.STATE_ENDED);
|
||||
assertThat(createdMediaPeriodIds).hasSize(2);
|
||||
player.release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void seekToIndexLargerThanNumberOfPlaylistItems() throws Exception {
|
||||
Timeline fakeTimeline =
|
||||
@ -14507,6 +14583,7 @@ public class ExoPlayerTest {
|
||||
new DefaultRenderersFactory(context).setAllowedVideoJoiningTimeMs(0))
|
||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||
.build();
|
||||
player.setPreloadConfiguration(createPreloadConfiguration());
|
||||
player.setPauseAtEndOfMediaItems(true);
|
||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0));
|
||||
player.setVideoSurface(surface);
|
||||
|
@ -40,7 +40,7 @@ import androidx.media3.common.Timeline;
|
||||
import androidx.media3.common.Tracks;
|
||||
import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.common.util.HandlerWrapper;
|
||||
import androidx.media3.exoplayer.ExoPlayerImpl.PreloadConfiguration;
|
||||
import androidx.media3.exoplayer.ExoPlayer.PreloadConfiguration;
|
||||
import androidx.media3.exoplayer.analytics.AnalyticsCollector;
|
||||
import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector;
|
||||
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||
|
@ -284,6 +284,16 @@ public final class ExoPlayerTestRunner implements Player.Listener, ActionSchedul
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ExoPlayer#setPreloadConfiguration(ExoPlayer.PreloadConfiguration)
|
||||
* @return This builder.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setPreloadConfiguration(ExoPlayer.PreloadConfiguration preloadConfiguration) {
|
||||
testPlayerBuilder.setPreloadConfiguration(preloadConfiguration);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an {@link ActionSchedule} to be run by the test runner. The first action will be
|
||||
* executed immediately before {@link ExoPlayer#prepare()}.
|
||||
|
@ -154,6 +154,16 @@ public class StubExoPlayer extends StubPlayer implements ExoPlayer {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreloadConfiguration(PreloadConfiguration preloadConfiguration) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreloadConfiguration getPreloadConfiguration() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaSource(MediaSource mediaSource) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -56,6 +56,7 @@ public class TestExoPlayerBuilder {
|
||||
private long seekForwardIncrementMs;
|
||||
private boolean deviceVolumeControlEnabled;
|
||||
private boolean suppressPlaybackWhenUnsuitableOutput;
|
||||
@Nullable private ExoPlayer.PreloadConfiguration preloadConfiguration;
|
||||
private boolean dynamicSchedulingEnabled;
|
||||
|
||||
public TestExoPlayerBuilder(Context context) {
|
||||
@ -161,6 +162,18 @@ public class TestExoPlayerBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the preload configuration.
|
||||
*
|
||||
* @see ExoPlayer#setPreloadConfiguration(ExoPlayer.PreloadConfiguration)
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public TestExoPlayerBuilder setPreloadConfiguration(
|
||||
ExoPlayer.PreloadConfiguration preloadConfiguration) {
|
||||
this.preloadConfiguration = preloadConfiguration;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Renderer Renderers} that have been set with {@link #setRenderers} or null if
|
||||
* no {@link Renderer Renderers} have been explicitly set. Note that these renderers may not be
|
||||
@ -373,6 +386,9 @@ public class TestExoPlayerBuilder {
|
||||
builder.setMediaSourceFactory(mediaSourceFactory);
|
||||
}
|
||||
ExoPlayer exoPlayer = builder.build();
|
||||
if (preloadConfiguration != null) {
|
||||
exoPlayer.setPreloadConfiguration(preloadConfiguration);
|
||||
}
|
||||
return exoPlayer;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user