1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2024-12-23 08:30:44 +00:00

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 android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSource; 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.FileDataSource;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.upstream.cache.CacheDataSink; import com.google.android.exoplayer2.upstream.cache.CacheDataSink;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource; import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import org.schabi.newpipe.player.datasource.YoutubeHttpDataSource; final class CacheFactory implements DataSource.Factory {
/* package-private */ final class CacheFactory implements DataSource.Factory {
private static final int CACHE_FLAGS = CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR; private static final int CACHE_FLAGS = CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR;
private final long maxFileSize;
private final Context context; private final Context context;
private final String userAgent;
private final TransferListener transferListener; private final TransferListener transferListener;
private final DataSource.Factory upstreamDataSourceFactory; private final DataSource.Factory upstreamDataSourceFactory;
private final SimpleCache simpleCache; private final SimpleCache cache;
public static class Builder { CacheFactory(final Context context,
private final Context context; final TransferListener transferListener,
private final String userAgent; final SimpleCache cache,
private final TransferListener transferListener; final DataSource.Factory upstreamDataSourceFactory) {
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) {
this.context = context; this.context = context;
this.userAgent = userAgent;
this.transferListener = transferListener; this.transferListener = transferListener;
this.simpleCache = simpleCache; this.cache = cache;
this.upstreamDataSourceFactory = upstreamDataSourceFactory; this.upstreamDataSourceFactory = upstreamDataSourceFactory;
maxFileSize = PlayerHelper.getPreferredFileSize();
} }
@NonNull @NonNull
@Override @Override
public DataSource createDataSource() { 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, final DefaultDataSource dataSource = new DefaultDataSource.Factory(context,
upstreamDataSourceFactoryToUse) upstreamDataSourceFactory)
.setTransferListener(transferListener) .setTransferListener(transferListener)
.createDataSource(); .createDataSource();
final FileDataSource fileSource = new FileDataSource(); final FileDataSource fileSource = new FileDataSource();
final CacheDataSink dataSink = new CacheDataSink(simpleCache, maxFileSize); final CacheDataSink dataSink
return new CacheDataSource(simpleCache, dataSource, fileSource, dataSink, CACHE_FLAGS, = new CacheDataSink(cache, PlayerHelper.getPreferredFileSize());
null); 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.content.Context;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider; import com.google.android.exoplayer2.database.StandaloneDatabaseProvider;
@ -31,6 +30,7 @@ import org.schabi.newpipe.player.datasource.YoutubeHttpDataSource;
import java.io.File; import java.io.File;
public class PlayerDataSource { public class PlayerDataSource {
public static final String TAG = PlayerDataSource.class.getSimpleName();
public static final int LIVE_STREAM_EDGE_GAP_MILLIS = 10000; public static final int LIVE_STREAM_EDGE_GAP_MILLIS = 10000;
@ -47,7 +47,7 @@ public class PlayerDataSource {
* {@link YoutubeProgressiveDashManifestCreator}, {@link YoutubeOtfDashManifestCreator} and * {@link YoutubeProgressiveDashManifestCreator}, {@link YoutubeOtfDashManifestCreator} and
* {@link YoutubePostLiveStreamDvrDashManifestCreator}. * {@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. * The folder name in which the ExoPlayer cache will be written.
@ -61,44 +61,53 @@ public class PlayerDataSource {
*/ */
private static SimpleCache cache; 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 DataSource.Factory cachelessDataSourceFactory;
private final CacheFactory cacheDataSourceFactory;
public PlayerDataSource(@NonNull final Context context, // YouTube-specific Data Source Factories (with cache)
@NonNull final String userAgent, // They use YoutubeHttpDataSource.Factory, with different parameters each
@NonNull final TransferListener transferListener) { private final CacheFactory ytHlsCacheDataSourceFactory;
continueLoadingCheckIntervalBytes = PlayerHelper.getProgressiveLoadIntervalBytes(context); private final CacheFactory ytDashCacheDataSourceFactory;
final File cacheDir = new File(context.getExternalCacheDir(), CACHE_FOLDER_NAME); private final CacheFactory ytProgressiveDashCacheDataSourceFactory;
if (!cacheDir.exists()) {
//noinspection ResultOfMethodCallIgnored
cacheDir.mkdir();
}
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, public PlayerDataSource(final Context context,
transferListener); final String userAgent,
cacheDataSourceFactoryBuilder.setSimpleCache(cache); 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, cachelessDataSourceFactory = new DefaultDataSource.Factory(context,
new DefaultHttpDataSource.Factory().setUserAgent(userAgent)) new DefaultHttpDataSource.Factory().setUserAgent(userAgent))
.setTransferListener(transferListener); .setTransferListener(transferListener);
cacheDataSourceFactory = new CacheFactory(context, transferListener, cache,
new DefaultHttpDataSource.Factory().setUserAgent(userAgent));
YoutubeProgressiveDashManifestCreator.getCache().setMaximumSize( // YouTube-specific data source factories use getYoutubeHttpDataSourceFactory()
MAXIMUM_SIZE_CACHED_GENERATED_MANIFESTS_PER_CACHE); ytHlsCacheDataSourceFactory = new CacheFactory(context, transferListener, cache,
YoutubeOtfDashManifestCreator.getCache().setMaximumSize( getYoutubeHttpDataSourceFactory(false, false, userAgent));
MAXIMUM_SIZE_CACHED_GENERATED_MANIFESTS_PER_CACHE); 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( YoutubePostLiveStreamDvrDashManifestCreator.getCache().setMaximumSize(
MAXIMUM_SIZE_CACHED_GENERATED_MANIFESTS_PER_CACHE); MAX_MANIFEST_CACHE_SIZE);
} }
//region Live media source factories
public SsMediaSource.Factory getLiveSsMediaSourceFactory() { public SsMediaSource.Factory getLiveSsMediaSourceFactory() {
return getSSMediaSourceFactory().setLivePresentationDelayMs(LIVE_STREAM_EDGE_GAP_MILLIS); return getSSMediaSourceFactory().setLivePresentationDelayMs(LIVE_STREAM_EDGE_GAP_MILLIS);
} }
@ -118,26 +127,26 @@ public class PlayerDataSource {
getDefaultDashChunkSourceFactory(cachelessDataSourceFactory), getDefaultDashChunkSourceFactory(cachelessDataSourceFactory),
cachelessDataSourceFactory); cachelessDataSourceFactory);
} }
//endregion
//region Generic media source factories
public HlsMediaSource.Factory getHlsMediaSourceFactory( public HlsMediaSource.Factory getHlsMediaSourceFactory(
@Nullable final HlsPlaylistParserFactory hlsPlaylistParserFactory) { @Nullable final HlsPlaylistParserFactory hlsPlaylistParserFactory) {
final HlsMediaSource.Factory factory = new HlsMediaSource.Factory( final HlsMediaSource.Factory factory = new HlsMediaSource.Factory(cacheDataSourceFactory);
cacheDataSourceFactoryBuilder.build()); factory.setPlaylistParserFactory(hlsPlaylistParserFactory);
if (hlsPlaylistParserFactory != null) {
factory.setPlaylistParserFactory(hlsPlaylistParserFactory);
}
return factory; return factory;
} }
public DashMediaSource.Factory getDashMediaSourceFactory() { public DashMediaSource.Factory getDashMediaSourceFactory() {
return new DashMediaSource.Factory( return new DashMediaSource.Factory(
getDefaultDashChunkSourceFactory(cacheDataSourceFactoryBuilder.build()), getDefaultDashChunkSourceFactory(cacheDataSourceFactory),
cacheDataSourceFactoryBuilder.build()); cacheDataSourceFactory);
} }
public ProgressiveMediaSource.Factory getProgressiveMediaSourceFactory() { public ProgressiveMediaSource.Factory getProgressiveMediaSourceFactory() {
return new ProgressiveMediaSource.Factory(cacheDataSourceFactoryBuilder.build()) return new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.setContinueLoadingCheckIntervalBytes(continueLoadingCheckIntervalBytes); .setContinueLoadingCheckIntervalBytes(progressiveLoadIntervalBytes);
} }
public SsMediaSource.Factory getSSMediaSourceFactory() { public SsMediaSource.Factory getSSMediaSourceFactory() {
@ -147,42 +156,57 @@ public class PlayerDataSource {
} }
public SingleSampleMediaSource.Factory getSingleSampleMediaSourceFactory() { 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() { public DashMediaSource.Factory getYoutubeDashMediaSourceFactory() {
cacheDataSourceFactoryBuilder.setUpstreamDataSourceFactory(
getYoutubeHttpDataSourceFactory(true, true));
return new DashMediaSource.Factory( return new DashMediaSource.Factory(
getDefaultDashChunkSourceFactory(cacheDataSourceFactoryBuilder.build()), getDefaultDashChunkSourceFactory(ytDashCacheDataSourceFactory),
cacheDataSourceFactoryBuilder.build()); ytDashCacheDataSourceFactory);
}
public HlsMediaSource.Factory getYoutubeHlsMediaSourceFactory() {
cacheDataSourceFactoryBuilder.setUpstreamDataSourceFactory(
getYoutubeHttpDataSourceFactory(false, false));
return new HlsMediaSource.Factory(cacheDataSourceFactoryBuilder.build());
} }
public ProgressiveMediaSource.Factory getYoutubeProgressiveMediaSourceFactory() { public ProgressiveMediaSource.Factory getYoutubeProgressiveMediaSourceFactory() {
cacheDataSourceFactoryBuilder.setUpstreamDataSourceFactory( return new ProgressiveMediaSource.Factory(ytProgressiveDashCacheDataSourceFactory)
getYoutubeHttpDataSourceFactory(false, true)); .setContinueLoadingCheckIntervalBytes(progressiveLoadIntervalBytes);
return new ProgressiveMediaSource.Factory(cacheDataSourceFactoryBuilder.build())
.setContinueLoadingCheckIntervalBytes(continueLoadingCheckIntervalBytes);
} }
//endregion
@NonNull
private DefaultDashChunkSource.Factory getDefaultDashChunkSourceFactory( //region Static methods
private static DefaultDashChunkSource.Factory getDefaultDashChunkSourceFactory(
final DataSource.Factory dataSourceFactory) { final DataSource.Factory dataSourceFactory) {
return new DefaultDashChunkSource.Factory(dataSourceFactory); return new DefaultDashChunkSource.Factory(dataSourceFactory);
} }
@NonNull private static YoutubeHttpDataSource.Factory getYoutubeHttpDataSourceFactory(
private YoutubeHttpDataSource.Factory getYoutubeHttpDataSourceFactory(
final boolean rangeParameterEnabled, final boolean rangeParameterEnabled,
final boolean rnParameterEnabled) { final boolean rnParameterEnabled,
final String userAgent) {
return new YoutubeHttpDataSource.Factory() return new YoutubeHttpDataSource.Factory()
.setRangeParameterEnabled(rangeParameterEnabled) .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
} }