Skip to content

Commit bf94233

Browse files
tianyifcopybara-github
authored andcommitted
Allow setting a DataSource.Factory in DefaultPreloadManager.Builder
This change enables users to provide a custom `DataSource.Factory`, which will be used as the upstream data source for caching and for creating `MediaSource`s within the `DefaultPreloadManager`. PiperOrigin-RevId: 847280760
1 parent a2cdbfc commit bf94233

File tree

4 files changed

+156
-25
lines changed

4 files changed

+156
-25
lines changed

RELEASENOTES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
* Fix bug where loading continues after playback ended when removing the
2727
currently playing item from a playlist
2828
([#2873](https://github.com/androidx/media/issues/2873)).
29+
* Allow setting a custom `DataSource.Factory` in
30+
`DefaultPreloadManager.Builder`.
2931
* CompositionPlayer:
3032
* Add support for `EditedMediaItem.removeVideo`.
3133
* Transformer:

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/BasePreloadManager.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import androidx.media3.common.util.Util;
3131
import androidx.media3.exoplayer.source.MediaSource;
3232
import com.google.common.base.Predicate;
33-
import com.google.common.base.Supplier;
3433
import java.util.ArrayList;
3534
import java.util.Collection;
3635
import java.util.Collections;
@@ -50,12 +49,12 @@ public abstract class BasePreloadManager<T, PreloadStatusT> {
5049
protected abstract static class BuilderBase<T, PreloadStatusT> {
5150
protected final TargetPreloadStatusControl<T, PreloadStatusT> targetPreloadStatusControl;
5251
protected RankingDataComparator<T> rankingDataComparator;
53-
protected Supplier<MediaSource.Factory> mediaSourceFactorySupplier;
52+
protected MediaSourceFactorySupplier mediaSourceFactorySupplier;
5453

5554
public BuilderBase(
5655
RankingDataComparator<T> rankingDataComparator,
5756
TargetPreloadStatusControl<T, PreloadStatusT> targetPreloadStatusControl,
58-
Supplier<MediaSource.Factory> mediaSourceFactorySupplier) {
57+
MediaSourceFactorySupplier mediaSourceFactorySupplier) {
5958
this.rankingDataComparator = rankingDataComparator;
6059
this.targetPreloadStatusControl = targetPreloadStatusControl;
6160
this.mediaSourceFactorySupplier = mediaSourceFactorySupplier;

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManager.java

Lines changed: 106 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import androidx.media3.common.util.HandlerWrapper;
3535
import androidx.media3.common.util.UnstableApi;
3636
import androidx.media3.common.util.Util;
37+
import androidx.media3.datasource.DataSource;
3738
import androidx.media3.datasource.DefaultDataSource;
3839
import androidx.media3.datasource.cache.Cache;
3940
import androidx.media3.datasource.cache.CacheDataSource;
@@ -75,6 +76,7 @@ public final class DefaultPreloadManager
7576
public static final class Builder extends BuilderBase<Integer, PreloadStatus> {
7677

7778
private final Context context;
79+
@Nullable private DataSource.Factory dataSourceFactory;
7880
private PlaybackLooperProvider preloadLooperProvider;
7981
private TrackSelector.Factory trackSelectorFactory;
8082
private Supplier<BandwidthMeter> bandwidthMeterSupplier;
@@ -98,7 +100,7 @@ public Builder(
98100
super(
99101
new SimpleRankingDataComparator(),
100102
targetPreloadStatusControl,
101-
new MediaSourceFactorySupplier(context));
103+
new DefaultMediaSourceFactorySupplier(context));
102104
this.context = context;
103105
this.preloadLooperProvider = new PlaybackLooperProvider();
104106
this.trackSelectorFactory = DefaultTrackSelector::new;
@@ -110,21 +112,82 @@ public Builder(
110112
}
111113

112114
/**
113-
* Sets the {@link MediaSource.Factory} that will be used by the built {@link
115+
* @deprecated Use {@link #setMediaSourceFactorySupplier(MediaSourceFactorySupplier)} instead.
116+
* If an app still uses this method, it should be aware that the set {@link
117+
* MediaSource.Factory} may not have the {@link #setCache(Cache) set Cache} and {@link
118+
* #setDataSourceFactory(DataSource.Factory) set DataSource.Factory} injected.
119+
*/
120+
@Deprecated
121+
@CanIgnoreReturnValue
122+
public Builder setMediaSourceFactory(MediaSource.Factory mediaSourceFactory) {
123+
checkState(!buildCalled && !buildExoPlayerCalled);
124+
this.mediaSourceFactorySupplier =
125+
new MediaSourceFactorySupplier() {
126+
@Override
127+
@CanIgnoreReturnValue
128+
public MediaSourceFactorySupplier setCache(@Nullable Cache cache) {
129+
return this;
130+
}
131+
132+
@Override
133+
@CanIgnoreReturnValue
134+
public MediaSourceFactorySupplier setDataSourceFactory(
135+
@Nullable DataSource.Factory dataSourceFactory) {
136+
return this;
137+
}
138+
139+
@Override
140+
public MediaSource.Factory get() {
141+
return mediaSourceFactory;
142+
}
143+
};
144+
return this;
145+
}
146+
147+
/**
148+
* Sets the {@link MediaSourceFactorySupplier} which supplies the {@link MediaSource.Factory}
149+
* that will be used by the built {@link DefaultPreloadManager} and {@link ExoPlayer}.
150+
*
151+
* <p>The default is a {@link MediaSourceFactorySupplier} that creates a {@link
152+
* DefaultMediaSourceFactory} with the {@linkplain #setCache(Cache) set cache} and the
153+
* {@linkplain #setDataSourceFactory(DataSource.Factory) set DataSource.Factory}.
154+
*
155+
* @param mediaSourceFactorySupplier A {@link MediaSourceFactorySupplier}.
156+
* @return This builder.
157+
* @throws IllegalStateException If {@link #build()}, {@link #buildExoPlayer()} or {@link
158+
* #buildExoPlayer(ExoPlayer.Builder)} has already been called.
159+
*/
160+
@CanIgnoreReturnValue
161+
public Builder setMediaSourceFactorySupplier(
162+
MediaSourceFactorySupplier mediaSourceFactorySupplier) {
163+
checkState(!buildCalled && !buildExoPlayerCalled);
164+
this.mediaSourceFactorySupplier =
165+
mediaSourceFactorySupplier.setCache(cache).setDataSourceFactory(dataSourceFactory);
166+
return this;
167+
}
168+
169+
/**
170+
* Sets the {@link DataSource.Factory} that will be used by the built {@link
114171
* DefaultPreloadManager} and {@link ExoPlayer}.
115172
*
116-
* <p>The default is a {@link DefaultMediaSourceFactory}.
173+
* <p>The {@link DataSource.Factory} will be used as the upstream {@link DataSource.Factory} for
174+
* caching the media items, and propagated to the {@link MediaSource.Factory} used by {@link
175+
* DefaultPreloadManager} and {@link ExoPlayer} for media source creation. Once set, the {@link
176+
* DataSource.Factory} will be passed into the existing {@linkplain
177+
* MediaSourceFactorySupplier#setDataSourceFactory(DataSource.Factory)
178+
* MediaSourceFactorySupplier} on this builder, and the {@link MediaSourceFactorySupplier} later
179+
* set to this builder.
117180
*
118-
* @param mediaSourceFactory A {@link MediaSource.Factory}
181+
* @param dataSourceFactory A {@link DataSource.Factory}.
119182
* @return This builder.
120183
* @throws IllegalStateException If {@link #build()}, {@link #buildExoPlayer()} or {@link
121184
* #buildExoPlayer(ExoPlayer.Builder)} has already been called.
122185
*/
123186
@CanIgnoreReturnValue
124-
public Builder setMediaSourceFactory(MediaSource.Factory mediaSourceFactory) {
187+
public Builder setDataSourceFactory(DataSource.Factory dataSourceFactory) {
125188
checkState(!buildCalled && !buildExoPlayerCalled);
126-
((MediaSourceFactorySupplier) this.mediaSourceFactorySupplier)
127-
.setCustomMediaSourceFactory(mediaSourceFactory);
189+
this.dataSourceFactory = dataSourceFactory;
190+
this.mediaSourceFactorySupplier.setDataSourceFactory(dataSourceFactory);
128191
return this;
129192
}
130193

@@ -223,7 +286,14 @@ public Builder setPreloadLooper(Looper preloadLooper) {
223286
}
224287

225288
/**
226-
* Sets the {@link Cache} that will be used for caching the media items.
289+
* Sets the {@link Cache} that will be used by the built {@link DefaultPreloadManager} and
290+
* {@link ExoPlayer}.
291+
*
292+
* <p>The {@link Cache} will be used for caching the media items, and propagated to the {@link
293+
* MediaSource.Factory} used by {@link DefaultPreloadManager} and {@link ExoPlayer} for media
294+
* source creation. Once set, the {@link Cache} will be passed into the existing {@linkplain
295+
* MediaSourceFactorySupplier#setCache(Cache) MediaSourceFactorySupplier} on this builder, and
296+
* the {@link MediaSourceFactorySupplier} later set to this builder.
227297
*
228298
* <p>The default is {@code null}. If an app will return {@link
229299
* PreloadStatus#specifiedRangeCached(long, long)} or {@link
@@ -239,7 +309,7 @@ public Builder setPreloadLooper(Looper preloadLooper) {
239309
public Builder setCache(@Nullable Cache cache) {
240310
checkState(!buildCalled && !buildExoPlayerCalled);
241311
this.cache = cache;
242-
((MediaSourceFactorySupplier) this.mediaSourceFactorySupplier).setCache(cache);
312+
this.mediaSourceFactorySupplier.setCache(cache);
243313
return this;
244314
}
245315

@@ -297,7 +367,7 @@ public ExoPlayer buildExoPlayer() {
297367
* builder:
298368
*
299369
* <ul>
300-
* <li>{@link #setMediaSourceFactory(MediaSource.Factory) MediaSource.Factory}
370+
* <li>{@link #setMediaSourceFactorySupplier(MediaSourceFactorySupplier)} MediaSource.Factory}
301371
* <li>{@link #setRenderersFactory(RenderersFactory) RenderersFactory}
302372
* <li>{@link #setTrackSelectorFactory(TrackSelector.Factory) TrackSelector.Factory}
303373
* <li>{@link #setLoadControl(LoadControl) LoadControl}
@@ -522,8 +592,13 @@ private DefaultPreloadManager(Builder builder) {
522592
if (cache != null) {
523593
preCacheThread = new HandlerThread("DefaultPreloadManager:PreCacheHelper");
524594
preCacheThread.start();
595+
DataSource.Factory upstreamDataSourceFactory =
596+
builder.dataSourceFactory != null
597+
? builder.dataSourceFactory
598+
: new DefaultDataSource.Factory(builder.context);
525599
preCacheHelperFactory =
526-
new PreCacheHelper.Factory(builder.context, cache, preCacheThread.getLooper())
600+
new PreCacheHelper.Factory(
601+
builder.context, cache, upstreamDataSourceFactory, preCacheThread.getLooper())
527602
.setDownloadExecutor(builder.cachingExecutor)
528603
.setListener(new PreCacheHelperListener());
529604
} else {
@@ -818,42 +893,51 @@ private boolean continueOrCompletePreloading(
818893
}
819894
}
820895

821-
private static class MediaSourceFactorySupplier implements Supplier<MediaSource.Factory> {
896+
private static class DefaultMediaSourceFactorySupplier implements MediaSourceFactorySupplier {
822897

823898
private final Context context;
824899
private final Supplier<DefaultMediaSourceFactory> defaultMediaSourceFactorySupplier;
825-
826-
@Nullable private MediaSource.Factory customMediaSourceFactory;
827900
@Nullable private Cache cache;
901+
@Nullable private DataSource.Factory dataSourceFactory;
828902

829-
public MediaSourceFactorySupplier(Context context) {
903+
private DefaultMediaSourceFactorySupplier(Context context) {
830904
this.context = context;
831905
defaultMediaSourceFactorySupplier =
832906
Suppliers.memoize(() -> new DefaultMediaSourceFactory(context));
833907
}
834908

835-
public void setCache(@Nullable Cache cache) {
909+
@Override
910+
@CanIgnoreReturnValue
911+
public DefaultMediaSourceFactorySupplier setCache(@Nullable Cache cache) {
836912
this.cache = cache;
913+
return this;
837914
}
838915

839-
public void setCustomMediaSourceFactory(@Nullable MediaSource.Factory mediaSourceFactory) {
840-
this.customMediaSourceFactory = mediaSourceFactory;
916+
@Override
917+
@CanIgnoreReturnValue
918+
public DefaultMediaSourceFactorySupplier setDataSourceFactory(
919+
@Nullable DataSource.Factory dataSourceFactory) {
920+
this.dataSourceFactory = dataSourceFactory;
921+
return this;
841922
}
842923

843924
@Override
844925
public MediaSource.Factory get() {
845-
if (customMediaSourceFactory != null) {
846-
return customMediaSourceFactory;
847-
}
848926
DefaultMediaSourceFactory defaultMediaSourceFactory = defaultMediaSourceFactorySupplier.get();
927+
DataSource.Factory dataSourceFactory =
928+
this.dataSourceFactory != null
929+
? this.dataSourceFactory
930+
: new DefaultDataSource.Factory(context);
849931
@Nullable Cache cache = this.cache;
850932
if (cache != null) {
851933
CacheDataSource.Factory cacheDataSourceFactory =
852934
new CacheDataSource.Factory()
853-
.setUpstreamDataSourceFactory(new DefaultDataSource.Factory(context))
935+
.setUpstreamDataSourceFactory(dataSourceFactory)
854936
.setCache(cache)
855937
.setCacheWriteDataSinkFactory(null);
856938
defaultMediaSourceFactory.setDataSourceFactory(cacheDataSourceFactory);
939+
} else {
940+
defaultMediaSourceFactory.setDataSourceFactory(dataSourceFactory);
857941
}
858942
return defaultMediaSourceFactory;
859943
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package androidx.media3.exoplayer.source.preload;
17+
18+
import androidx.annotation.Nullable;
19+
import androidx.media3.common.util.UnstableApi;
20+
import androidx.media3.datasource.DataSource;
21+
import androidx.media3.datasource.cache.Cache;
22+
import androidx.media3.exoplayer.source.MediaSource;
23+
import com.google.common.base.Supplier;
24+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
25+
26+
/** Supplies a {@link MediaSource.Factory}. */
27+
@UnstableApi
28+
public interface MediaSourceFactorySupplier extends Supplier<MediaSource.Factory> {
29+
30+
/**
31+
* Sets the {@link Cache} that will be used by the supplied {@link MediaSource.Factory}.
32+
*
33+
* @return This supplier, for convenience.
34+
*/
35+
@CanIgnoreReturnValue
36+
MediaSourceFactorySupplier setCache(@Nullable Cache cache);
37+
38+
/**
39+
* Sets the {@link DataSource.Factory} that will be used by the supplied {@link
40+
* MediaSource.Factory}.
41+
*
42+
* @return This supplier, for convenience.
43+
*/
44+
@CanIgnoreReturnValue
45+
MediaSourceFactorySupplier setDataSourceFactory(@Nullable DataSource.Factory dataSourceFactory);
46+
}

0 commit comments

Comments
 (0)