如何在ExoPlayer中预缓存HLS自适应流



我正在尝试将HLS视频预缓存/预缓冲到我的应用程序中。我使用CacheWriter来缓存(.mp4(文件,但它无法缓存HLS视频片段。基本上,我只有主播放列表文件的URL,它有不同质量的媒体播放列表,每个媒体播放列表都有片段(.ts(。

因此,我必须缓存主播放列表和任何一个媒体播放列表然后缓存一些片段,并将缓存的媒体播放到Exoplayer。我如何缓存这些?我还参观了https://github.com/google/ExoPlayer/issues/9337但这并没有任何这样的例子

这就是我通过CacheWriter 缓存.mp4的方式

CacheWriter cacheWriter = new CacheWriter( mCacheDataSource,
dataSpec,
null,
progressListener);
cacheWriter.cache();

我正在回答我自己的问题,以供更多的用户使用。我们可以使用ExoPlayer提供的HlsDownloader在ExoPlayer中预缓存预缓存HLS自适应流。

将这个Kotlin类添加到您的项目ExoPlayerModule.kt中。

//SitaRam
package com.example.youtpackagename
import android.content.Context
import android.util.Log
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
import com.google.android.exoplayer2.source.hls.HlsMediaSource
import com.google.android.exoplayer2.source.hls.offline.HlsDownloader
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import com.google.android.exoplayer2.upstream.FileDataSource
import com.google.android.exoplayer2.upstream.cache.CacheDataSource
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
import com.google.android.exoplayer2.upstream.cache.SimpleCache
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.util.concurrent.CancellationException
//bytes to be downloaded
private const val PRE_CACHE_AMOUNT = 2 * 1048576L
class ExoPlayerModule(context: Context) {

private var cronetDataSourceFactory =  DefaultHttpDataSource.Factory()
//StaticMember is class which contains cookie in my case, you can skip cookies and use DefaultHttpDataSource.Factory().
/*val Cookie = mapOf("Cookie" to StaticMember.getCookie())
private var cronetDataSourceFactory = if (StaticMember.getCookie() != null) {
DefaultHttpDataSource.Factory().setDefaultRequestProperties(Cookie)
}else {
DefaultHttpDataSource.Factory()
}*/
private val cacheReadDataSourceFactory = FileDataSource.Factory()
private var cache = simpleCache.SimpleCache(context)
private var cacheDataSourceFactory = CacheDataSource.Factory()
.setCache(cache)
//        .setCacheWriteDataSinkFactory(cacheSink)
.setCacheReadDataSourceFactory(cacheReadDataSourceFactory)
.setUpstreamDataSourceFactory(cronetDataSourceFactory)
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
fun isUriCached(uri: String, position: Long = 0): Boolean {
return cache.isCached(uri, position, PRE_CACHE_AMOUNT)
}

//updating cookies (if you are using cookies).
/* fun updateDataSourceFactory(){
val Cookie = mapOf("Cookie" to StaticMember.getCookie())
cronetDataSourceFactory = if (StaticMember.getCookie() != null) {
DefaultHttpDataSource.Factory().setDefaultRequestProperties(Cookie)
}else {
DefaultHttpDataSource.Factory()
}
cacheDataSourceFactory = CacheDataSource.Factory()
.setCache(cache)
//        .setCacheWriteDataSinkFactory(cacheSink)
.setCacheReadDataSourceFactory(cacheReadDataSourceFactory)
.setUpstreamDataSourceFactory(cronetDataSourceFactory)
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
}*/
// TODO add the same for mp4. Also they might be a much better option, since they only have
// single track, so no matter what connection you have - loading can't happen twice
fun getHlsMediaSource(mediaItem: MediaItem): HlsMediaSource {
return HlsMediaSource.Factory(cacheDataSourceFactory)
.setAllowChunklessPreparation(true)
.createMediaSource(mediaItem)
}
fun releaseCache() = cache.release()
suspend fun preCacheUri(mediaItem: MediaItem) {
val downloader = HlsDownloader(mediaItem, cacheDataSourceFactory)
withContext(Dispatchers.IO) {
try {
downloader.download { _, bytesDownloaded, _ ->
if (MainActivity.nextUrl==mediaItem){
//  Log.e("bytesCaching", "while: same $mediaItem same")
}else {
//  Log.e("bytesCaching", "while: $mediaItem")
downloader.cancel()
}
if (bytesDownloaded >= PRE_CACHE_AMOUNT) {
//                        log("video precached at $percent%")
downloader.cancel()
}
}
} catch (e: Exception) {
if (e !is CancellationException) log("precache exception $e")
}
}
}
private fun log(s: String) {
TODO("Not yet implemented")
}
}

初始化ExoPlayerModule

ExoPlayerModule PlayerModuleO = new ExoPlayerModule(MainActivity.this);

用于预加载。

String previousUrl = "";
public void preLoad(String url) {
if (previousUrl.equals(url)) {
return;
}
previousUrl = url;
MediaItem mediaItem =MediaItem.fromUri(Uri.parse(url));
PlayerModuleO.preCacheUri(mediaItem, new Continuation<>() {
@NonNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NonNull Object o) {
}
});
}

播放缓存或非缓存媒体。

MediaItem mediaItem = MediaItem.fromUri(Uri.parse(url));
exoPlayer.setMediaSource(PlayerModuleO.getHlsMediaSource(mediaItem));
exoPlayer.prepare();
exoPlayer.play();

释放缓存

PlayerModuleO.releaseCache();

如果你有任何问题,请随时询问。

最新更新