Improve CacheFactory and PlayerDataSource code

This commit is contained in:
Stypox 2022-05-21 11:12:37 +02:00 committed by AudricV
parent ef20d9b91a
commit 7ce2250d85
No known key found for this signature in database
GPG Key ID: DA92EC7905614198
2 changed files with 93 additions and 132 deletions

View File

@ -3,107 +3,44 @@ package org.schabi.newpipe.player.helper;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.FileDataSource;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.upstream.cache.CacheDataSink;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import org.schabi.newpipe.player.datasource.YoutubeHttpDataSource;
/* package-private */ final class CacheFactory implements DataSource.Factory {
final class CacheFactory implements DataSource.Factory {
private static final int CACHE_FLAGS = CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR;
private final long maxFileSize;
private final Context context;
private final String userAgent;
private final TransferListener transferListener;
private final DataSource.Factory upstreamDataSourceFactory;
private final SimpleCache simpleCache;
private final SimpleCache cache;
public static class Builder {
private final Context context;
private final String userAgent;
private final TransferListener transferListener;
private DataSource.Factory upstreamDataSourceFactory;
private SimpleCache simpleCache;
Builder(@NonNull final Context context,
@NonNull final String userAgent,
@NonNull final TransferListener transferListener) {
this.context = context;
this.userAgent = userAgent;
this.transferListener = transferListener;
}
public void setUpstreamDataSourceFactory(
@Nullable final DataSource.Factory upstreamDataSourceFactory) {
this.upstreamDataSourceFactory = upstreamDataSourceFactory;
}
public void setSimpleCache(@NonNull final SimpleCache simpleCache) {
this.simpleCache = simpleCache;
}
public CacheFactory build() {
if (simpleCache == null) {
throw new IllegalStateException("No SimpleCache instance has been specified. "
+ "Please specify one with setSimpleCache");
}
return new CacheFactory(context, userAgent, transferListener, simpleCache,
upstreamDataSourceFactory);
}
}
private CacheFactory(@NonNull final Context context,
@NonNull final String userAgent,
@NonNull final TransferListener transferListener,
@NonNull final SimpleCache simpleCache,
@Nullable final DataSource.Factory upstreamDataSourceFactory) {
CacheFactory(final Context context,
final TransferListener transferListener,
final SimpleCache cache,
final DataSource.Factory upstreamDataSourceFactory) {
this.context = context;
this.userAgent = userAgent;
this.transferListener = transferListener;
this.simpleCache = simpleCache;
this.cache = cache;
this.upstreamDataSourceFactory = upstreamDataSourceFactory;
maxFileSize = PlayerHelper.getPreferredFileSize();
}
@NonNull
@Override
public DataSource createDataSource() {
final DataSource.Factory upstreamDataSourceFactoryToUse;
if (upstreamDataSourceFactory == null) {
upstreamDataSourceFactoryToUse = new DefaultHttpDataSource.Factory()
.setUserAgent(userAgent);
} else {
if (upstreamDataSourceFactory instanceof DefaultHttpDataSource.Factory) {
upstreamDataSourceFactoryToUse =
((DefaultHttpDataSource.Factory) upstreamDataSourceFactory)
.setUserAgent(userAgent);
} else if (upstreamDataSourceFactory instanceof YoutubeHttpDataSource.Factory) {
upstreamDataSourceFactoryToUse =
((YoutubeHttpDataSource.Factory) upstreamDataSourceFactory)
.setUserAgentForNonMobileStreams(userAgent);
} else {
upstreamDataSourceFactoryToUse = upstreamDataSourceFactory;
}
}
final DefaultDataSource dataSource = new DefaultDataSource.Factory(context,
upstreamDataSourceFactoryToUse)
upstreamDataSourceFactory)
.setTransferListener(transferListener)
.createDataSource();
final FileDataSource fileSource = new FileDataSource();
final CacheDataSink dataSink = new CacheDataSink(simpleCache, maxFileSize);
return new CacheDataSource(simpleCache, dataSource, fileSource, dataSink, CACHE_FLAGS,
null);
final CacheDataSink dataSink
= new CacheDataSink(cache, PlayerHelper.getPreferredFileSize());
return new CacheDataSource(cache, dataSource, fileSource, dataSink, CACHE_FLAGS, null);
}
}

View File

@ -3,7 +3,6 @@ package org.schabi.newpipe.player.helper;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider;
@ -31,6 +30,7 @@ import org.schabi.newpipe.player.datasource.YoutubeHttpDataSource;
import java.io.File;
public class PlayerDataSource {
public static final String TAG = PlayerDataSource.class.getSimpleName();
public static final int LIVE_STREAM_EDGE_GAP_MILLIS = 10000;
@ -47,7 +47,7 @@ public class PlayerDataSource {
* {@link YoutubeProgressiveDashManifestCreator}, {@link YoutubeOtfDashManifestCreator} and
* {@link YoutubePostLiveStreamDvrDashManifestCreator}.
*/
private static final int MAXIMUM_SIZE_CACHED_GENERATED_MANIFESTS_PER_CACHE = 500;
private static final int MAX_MANIFEST_CACHE_SIZE = 500;
/**
* The folder name in which the ExoPlayer cache will be written.
@ -61,44 +61,53 @@ public class PlayerDataSource {
*/
private static SimpleCache cache;
private final int continueLoadingCheckIntervalBytes;
private final CacheFactory.Builder cacheDataSourceFactoryBuilder;
private final int progressiveLoadIntervalBytes;
// Generic Data Source Factories (without or with cache)
private final DataSource.Factory cachelessDataSourceFactory;
private final CacheFactory cacheDataSourceFactory;
public PlayerDataSource(@NonNull final Context context,
@NonNull final String userAgent,
@NonNull final TransferListener transferListener) {
continueLoadingCheckIntervalBytes = PlayerHelper.getProgressiveLoadIntervalBytes(context);
final File cacheDir = new File(context.getExternalCacheDir(), CACHE_FOLDER_NAME);
if (!cacheDir.exists()) {
//noinspection ResultOfMethodCallIgnored
cacheDir.mkdir();
}
// YouTube-specific Data Source Factories (with cache)
// They use YoutubeHttpDataSource.Factory, with different parameters each
private final CacheFactory ytHlsCacheDataSourceFactory;
private final CacheFactory ytDashCacheDataSourceFactory;
private final CacheFactory ytProgressiveDashCacheDataSourceFactory;
if (cache == null) {
final LeastRecentlyUsedCacheEvictor evictor
= new LeastRecentlyUsedCacheEvictor(PlayerHelper.getPreferredCacheSize());
cache = new SimpleCache(cacheDir, evictor, new StandaloneDatabaseProvider(context));
Log.d(PlayerDataSource.class.getSimpleName(), "initExoPlayerCache: cacheDir = "
+ cacheDir.getAbsolutePath());
}
cacheDataSourceFactoryBuilder = new CacheFactory.Builder(context, userAgent,
transferListener);
cacheDataSourceFactoryBuilder.setSimpleCache(cache);
public PlayerDataSource(final Context context,
final String userAgent,
final TransferListener transferListener) {
progressiveLoadIntervalBytes = PlayerHelper.getProgressiveLoadIntervalBytes(context);
// make sure the static cache was created: needed by CacheFactories below
instantiateCacheIfNeeded(context);
// generic data source factories use DefaultHttpDataSource.Factory
cachelessDataSourceFactory = new DefaultDataSource.Factory(context,
new DefaultHttpDataSource.Factory().setUserAgent(userAgent))
.setTransferListener(transferListener);
cacheDataSourceFactory = new CacheFactory(context, transferListener, cache,
new DefaultHttpDataSource.Factory().setUserAgent(userAgent));
YoutubeProgressiveDashManifestCreator.getCache().setMaximumSize(
MAXIMUM_SIZE_CACHED_GENERATED_MANIFESTS_PER_CACHE);
YoutubeOtfDashManifestCreator.getCache().setMaximumSize(
MAXIMUM_SIZE_CACHED_GENERATED_MANIFESTS_PER_CACHE);
// YouTube-specific data source factories use getYoutubeHttpDataSourceFactory()
ytHlsCacheDataSourceFactory = new CacheFactory(context, transferListener, cache,
getYoutubeHttpDataSourceFactory(false, false, userAgent));
ytDashCacheDataSourceFactory = new CacheFactory(context, transferListener, cache,
getYoutubeHttpDataSourceFactory(true, true, userAgent));
ytProgressiveDashCacheDataSourceFactory = new CacheFactory(context, transferListener, cache,
getYoutubeHttpDataSourceFactory(false, true, userAgent));
// set the maximum size to manifest creators
YoutubeProgressiveDashManifestCreator.getCache().setMaximumSize(MAX_MANIFEST_CACHE_SIZE);
YoutubeOtfDashManifestCreator.getCache().setMaximumSize(MAX_MANIFEST_CACHE_SIZE);
YoutubePostLiveStreamDvrDashManifestCreator.getCache().setMaximumSize(
MAXIMUM_SIZE_CACHED_GENERATED_MANIFESTS_PER_CACHE);
MAX_MANIFEST_CACHE_SIZE);
}
//region Live media source factories
public SsMediaSource.Factory getLiveSsMediaSourceFactory() {
return getSSMediaSourceFactory().setLivePresentationDelayMs(LIVE_STREAM_EDGE_GAP_MILLIS);
}
@ -118,26 +127,26 @@ public class PlayerDataSource {
getDefaultDashChunkSourceFactory(cachelessDataSourceFactory),
cachelessDataSourceFactory);
}
//endregion
//region Generic media source factories
public HlsMediaSource.Factory getHlsMediaSourceFactory(
@Nullable final HlsPlaylistParserFactory hlsPlaylistParserFactory) {
final HlsMediaSource.Factory factory = new HlsMediaSource.Factory(
cacheDataSourceFactoryBuilder.build());
if (hlsPlaylistParserFactory != null) {
factory.setPlaylistParserFactory(hlsPlaylistParserFactory);
}
final HlsMediaSource.Factory factory = new HlsMediaSource.Factory(cacheDataSourceFactory);
factory.setPlaylistParserFactory(hlsPlaylistParserFactory);
return factory;
}
public DashMediaSource.Factory getDashMediaSourceFactory() {
return new DashMediaSource.Factory(
getDefaultDashChunkSourceFactory(cacheDataSourceFactoryBuilder.build()),
cacheDataSourceFactoryBuilder.build());
getDefaultDashChunkSourceFactory(cacheDataSourceFactory),
cacheDataSourceFactory);
}
public ProgressiveMediaSource.Factory getProgressiveMediaSourceFactory() {
return new ProgressiveMediaSource.Factory(cacheDataSourceFactoryBuilder.build())
.setContinueLoadingCheckIntervalBytes(continueLoadingCheckIntervalBytes);
return new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.setContinueLoadingCheckIntervalBytes(progressiveLoadIntervalBytes);
}
public SsMediaSource.Factory getSSMediaSourceFactory() {
@ -147,42 +156,57 @@ public class PlayerDataSource {
}
public SingleSampleMediaSource.Factory getSingleSampleMediaSourceFactory() {
return new SingleSampleMediaSource.Factory(cacheDataSourceFactoryBuilder.build());
return new SingleSampleMediaSource.Factory(cacheDataSourceFactory);
}
//endregion
//region YouTube media source factories
public HlsMediaSource.Factory getYoutubeHlsMediaSourceFactory() {
return new HlsMediaSource.Factory(ytHlsCacheDataSourceFactory);
}
public DashMediaSource.Factory getYoutubeDashMediaSourceFactory() {
cacheDataSourceFactoryBuilder.setUpstreamDataSourceFactory(
getYoutubeHttpDataSourceFactory(true, true));
return new DashMediaSource.Factory(
getDefaultDashChunkSourceFactory(cacheDataSourceFactoryBuilder.build()),
cacheDataSourceFactoryBuilder.build());
}
public HlsMediaSource.Factory getYoutubeHlsMediaSourceFactory() {
cacheDataSourceFactoryBuilder.setUpstreamDataSourceFactory(
getYoutubeHttpDataSourceFactory(false, false));
return new HlsMediaSource.Factory(cacheDataSourceFactoryBuilder.build());
getDefaultDashChunkSourceFactory(ytDashCacheDataSourceFactory),
ytDashCacheDataSourceFactory);
}
public ProgressiveMediaSource.Factory getYoutubeProgressiveMediaSourceFactory() {
cacheDataSourceFactoryBuilder.setUpstreamDataSourceFactory(
getYoutubeHttpDataSourceFactory(false, true));
return new ProgressiveMediaSource.Factory(cacheDataSourceFactoryBuilder.build())
.setContinueLoadingCheckIntervalBytes(continueLoadingCheckIntervalBytes);
return new ProgressiveMediaSource.Factory(ytProgressiveDashCacheDataSourceFactory)
.setContinueLoadingCheckIntervalBytes(progressiveLoadIntervalBytes);
}
//endregion
@NonNull
private DefaultDashChunkSource.Factory getDefaultDashChunkSourceFactory(
//region Static methods
private static DefaultDashChunkSource.Factory getDefaultDashChunkSourceFactory(
final DataSource.Factory dataSourceFactory) {
return new DefaultDashChunkSource.Factory(dataSourceFactory);
}
@NonNull
private YoutubeHttpDataSource.Factory getYoutubeHttpDataSourceFactory(
private static YoutubeHttpDataSource.Factory getYoutubeHttpDataSourceFactory(
final boolean rangeParameterEnabled,
final boolean rnParameterEnabled) {
final boolean rnParameterEnabled,
final String userAgent) {
return new YoutubeHttpDataSource.Factory()
.setRangeParameterEnabled(rangeParameterEnabled)
.setRnParameterEnabled(rnParameterEnabled);
.setRnParameterEnabled(rnParameterEnabled)
.setUserAgentForNonMobileStreams(userAgent);
}
private static void instantiateCacheIfNeeded(final Context context) {
if (cache == null) {
final File cacheDir = new File(context.getExternalCacheDir(), CACHE_FOLDER_NAME);
Log.d(TAG, "instantiateCacheIfNeeded: cacheDir = " + cacheDir.getAbsolutePath());
if (!cacheDir.exists() && !cacheDir.mkdir()) {
Log.w(TAG, "instantiateCacheIfNeeded: could not create cache dir");
}
final LeastRecentlyUsedCacheEvictor evictor
= new LeastRecentlyUsedCacheEvictor(PlayerHelper.getPreferredCacheSize());
cache = new SimpleCache(cacheDir, evictor, new StandaloneDatabaseProvider(context));
}
}
//endregion
}