Je cherche un exemple d'implémentation de cache dans ExoPlayer.
ExoPlayer a dans sa bibliothèque différentes classes concernant le cache et Google explique dans cette video que nous pouvons l’implémenter avec la classe CacheDataSource, mais Google ne fournit aucune démonstration. Malheureusement, cela semble assez compliqué à utiliser, je recherche donc actuellement des exemples (aucun succès sur Google).
Est-ce que quelqu'un réussit ou a des informations qui pourraient aider? Merci.
Voici la solution pour ExoPlayer 2. +
Créer une fabrique de source de données de cache personnalisée
class CacheDataSourceFactory implements DataSource.Factory {
private final Context context;
private final DefaultDataSourceFactory defaultDatasourceFactory;
private final long maxFileSize, maxCacheSize;
CacheDataSourceFactory(Context context, long maxCacheSize, long maxFileSize) {
super();
this.context = context;
this.maxCacheSize = maxCacheSize;
this.maxFileSize = maxFileSize;
String userAgent = Util.getUserAgent(context, context.getString(R.string.app_name));
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
defaultDatasourceFactory = new DefaultDataSourceFactory(this.context,
bandwidthMeter,
new DefaultHttpDataSourceFactory(userAgent, bandwidthMeter));
}
@Override
public DataSource createDataSource() {
LeastRecentlyUsedCacheEvictor evictor = new LeastRecentlyUsedCacheEvictor(maxCacheSize);
SimpleCache simpleCache = new SimpleCache(new File(context.getCacheDir(), "media"), evictor);
return new CacheDataSource(simpleCache, defaultDatasourceFactory.createDataSource(),
new FileDataSource(), new CacheDataSink(simpleCache, maxFileSize),
CacheDataSource.FLAG_BLOCK_ON_CACHE | CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR, null);
}
}
Et le joueur
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
SimpleExoPlayer exoPlayer = ExoPlayerFactory.newSimpleInstance(this, trackSelector);
MediaSource audioSource = new ExtractorMediaSource(Uri.parse(url),
new CacheDataSourceFactory(context, 100 * 1024 * 1024, 5 * 1024 * 1024), new DefaultExtractorsFactory(), null, null);
exoPlayer.setPlayWhenReady(true);
exoPlayer.prepare(audioSource);
Ça marche plutôt bien.
Je l'ai implémenté comme ceci dans le générateur de rendu
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
private static final int BUFFER_SEGMENT_COUNT = 160;
final String userAgent = Util.getUserAgent(mContext, appName);
final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
final Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE);*
Cache cache = new SimpleCache(context.getCacheDir(), new LeastRecentlyUsedCacheEvictor(1024 * 1024 * 10));
DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
CacheDataSource cacheDataSource = new CacheDataSource(cache, dataSource, false, false);
ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri
, cacheDataSource
, allocator
, BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE
, new Mp4Extractor());
La liste de documentation d'Exoplayer est la classe DashDownloader et contient un exemple de code pour ce type de source. (Cliquez sur [Images] pour revenir à la navigation dans la documentation. J'ai dû la supprimer pour obtenir le lien profond.)
Par défaut, ExoPlayer ne met pas en cache les supports (vidéo, audio, etc.). Par exemple, si vous souhaitez lire un fichier vidéo en ligne, chaque fois qu'ExoPlayer ouvre une connexion, lit les données puis les lit.
Heureusement, il nous fournit des interfaces et des classes d'implémentation pour prendre en charge la mise en cache des médias dans notre application.
Vous pouvez écrire votre propre cache qui implémente des interfaces données à partir d'ExoPlayer. Pour simplifier les choses, je vais vous expliquer comment activer le cache en utilisant des classes d'implémentation.
Étape 1: Spécifiez un dossier contenant vos fichiers multimédia. Sous Android, vous devez utiliser getCacheDir pour les dossiers de cache plus petits (moins de 1 Mo). Sinon, vous pouvez spécifier votre dossier de cache préféré, getFileDir par exemple.
Étape 2: Spécifiez une taille pour le dossier de cache et une stratégie lorsque la taille est atteinte. Il y a 2 API
Mettre ensemble
val renderersFactory = DefaultRenderersFactory(context.applicationContext)
val trackSelector = DefaultTrackSelector()
val loadControl = DefaultLoadControl()
val player = ExoPlayerFactory.newSimpleInstance(context, renderersFactory, trackSelector, loadControl)
player.addListener(this)
// Specify cache folder, my cache folder named media which is inside getCacheDir.
val cacheFolder = File(context.cacheDir, "media")
// Specify cache size and removing policies
val cacheEvictor = LeastRecentlyUsedCacheEvictor(1 * 1024 * 1024) // My cache size will be 1MB and it will automatically remove least recently used files if the size is reached out.
// Build cache
val cache = SimpleCache(cacheFolder, cacheEvictor)
// Build data source factory with cache enabled, if data is available in cache it will return immediately, otherwise it will open a new connection to get the data.
val cacheDataSourceFactory = CacheDataSourceFactory(cache, DefaultHttpDataSourceFactory("ExoplayerDemo"))
val uri = Uri.parse("Put your media url here")
val mediaSource = ExtractorMediaSource.Factory(cacheDataSourceFactory).createMediaSource(uri)
player.prepare(mediaSource)
Outre la réponse de Bao Le, voici la version Kotlin prête à l'emploi de CacheDataSourceFactory
qui conserve une instance de SimpleCache
pour résoudre le problème de l'écriture de plusieurs objets Cache dans le même répertoire.
class CacheDataSourceFactory(private val context: Context,
private val maxCacheSize: Long,
private val maxFileSize: Long) : DataSource.Factory {
private val defaultDatasourceFactory: DefaultDataSourceFactory
private val simpleCache: SimpleCache by lazy {
val evictor = LeastRecentlyUsedCacheEvictor(maxCacheSize)
SimpleCache(File(context.cacheDir, "media"), evictor)
}
init {
val userAgent = Util.getUserAgent(context, context.packageName)
val bandwidthMeter = DefaultBandwidthMeter()
defaultDatasourceFactory = DefaultDataSourceFactory(context,
bandwidthMeter,
DefaultHttpDataSourceFactory(userAgent, bandwidthMeter))
}
override fun createDataSource(): DataSource {
return CacheDataSource(simpleCache,
defaultDatasourceFactory.createDataSource(),
FileDataSource(),
CacheDataSink(simpleCache, maxFileSize),
CacheDataSource.FLAG_BLOCK_ON_CACHE or CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR,
null)
}
}
Voici mon exemple à Kotlin (projet disponible ici ):
class MainActivity : AppCompatActivity() {
private var player: SimpleExoPlayer? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (cache == null) {
cache = SimpleCache(File(cacheDir, "media"), LeastRecentlyUsedCacheEvictor(MAX_PREVIEW_CACHE_SIZE_IN_BYTES))
}
setContentView(R.layout.activity_main)
}
override fun onStart() {
super.onStart()
playVideo()
}
private fun playVideo() {
player = ExoPlayerFactory.newSimpleInstance(this@MainActivity, DefaultTrackSelector())
playerView.player = player
player!!.volume = 1f
player!!.playWhenReady = true
player!!.repeatMode = Player.REPEAT_MODE_ALL
player!!.playVideoFromUrl(this, "https://sample-videos.com/video123/mkv/240/big_buck_bunny_240p_20mb.mkv", cache!!)
// player!!.playVideoFromUrl(this, "https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_1mb.mkv", cache!!)
// player!!.playVideoFromUrl(this, "https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_1mb.mkv")
// player!!.playRawVideo(this,R.raw.videoplayback)
}
override fun onStop() {
super.onStop()
playerView.player = null
player!!.release()
player = null
}
companion object {
const val MAX_PREVIEW_CACHE_SIZE_IN_BYTES = 20L * 1024L * 1024L
var cache: com.google.Android.exoplayer2.upstream.cache.Cache? = null
@JvmStatic
fun getUserAgent(context: Context): String {
val packageManager = context.packageManager
val info = packageManager.getPackageInfo(context.packageName, 0)
val appName = info.applicationInfo.loadLabel(packageManager).toString()
return Util.getUserAgent(context, appName)
}
}
fun SimpleExoPlayer.playRawVideo(context: Context, @RawRes rawVideoRes: Int) {
val dataSpec = DataSpec(RawResourceDataSource.buildRawResourceUri(rawVideoRes))
val rawResourceDataSource = RawResourceDataSource(context)
rawResourceDataSource.open(dataSpec)
val factory: DataSource.Factory = DataSource.Factory { rawResourceDataSource }
prepare(LoopingMediaSource(ExtractorMediaSource.Factory(factory).createMediaSource(rawResourceDataSource.uri)))
}
fun SimpleExoPlayer.playVideoFromUrl(context: Context, url: String, cache: Cache? = null) = playVideoFromUri(context, Uri.parse(url), cache)
fun SimpleExoPlayer.playVideoFile(context: Context, file: File) = playVideoFromUri(context, Uri.fromFile(file))
fun SimpleExoPlayer.playVideoFromUri(context: Context, uri: Uri, cache: Cache? = null) {
val factory = if (cache != null)
CacheDataSourceFactory(cache, DefaultHttpDataSourceFactory(getUserAgent(context)))
else
DefaultDataSourceFactory(context, MainActivity.getUserAgent(context))
val mediaSource = ExtractorMediaSource.Factory(factory).createMediaSource(uri)
prepare(mediaSource)
}
}