在ExoPlayer中使用缓存

时间:2015-02-24 15:56:15

标签: android caching media exoplayer

我正在寻找在ExoPlayer中实现缓存的任何示例。

ExoPlayer在其库中有关于缓存的不同类,Google在此video中解释我们可以使用CacheDataSource类实现它,但Google并没有提供任何演示。不幸的是,这似乎相当复杂,所以我目前正在寻找示例(在Google上没有成功)。

有没有人成功或有任何有用的信息?感谢。

10 个答案:

答案 0 :(得分:27)

以下是ExoPlayer 2的解决方案。+

创建自定义缓存数据源工厂

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);
    }
}

玩家

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);

效果很好。

答案 1 :(得分:6)

默认情况下,ExoPlayer不缓存媒体(视频,音频等)。例如,如果您要播放在线视频文件,则ExoPlayer每次打开连接时,先读取数据然后播放。

幸运的是,它为我们提供了一些接口和实现类,以支持我们应用程序中的缓存媒体。

您可以编写自己的缓存,以实现ExoPlayer中的给定接口。为简单起见,我将指导您如何使用实现类启用缓存。

步骤1:指定一个包含媒体文件的文件夹,在Android中,对于较小的缓存文件夹(小于1MB),您应该使用getCacheDir,否则可以指定自己喜欢的缓存文件夹,例如getFileDir

步骤2:指定缓存文件夹的大小,并指定达到该大小时的策略。有2个API

  • NoOpCacheEvictor永远不会逐出/删除缓存文件。根据缓存文件夹的位置(如果它位于内部存储中),当用户清除应用程序数据或卸载应用程序时,该文件夹将被删除。
  • LeastRecentlyUsedCacheEvictor,它将首先逐出/删除最近最少使用的缓存文件。例如,如果您的缓存大小为10MB,则当达到该大小时,它将自动查找并删除最近最少使用的文件。

放在一起

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)

答案 2 :(得分:5)

这是一个用OkHttp替换演示数据源的示例,默认是没有缓存 https://github.com/b95505017/ExoPlayer/commit/ebfdda8e7848a2e2e275f5c0525f614b56ef43a6 https://github.com/b95505017/ExoPlayer/tree/okhttp_http_data_source 因此,您只需要正确配置OkHttp缓存并缓存请求。

答案 3 :(得分:2)

我在渲染器构建器

中实现了这个
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());

答案 4 :(得分:0)

Exoplayer的文档列表为class DashDownloader,并具有用于该类型源的一些示例代码。 (单击[框架]返回文档导航。我必须将其删除才能获得深层链接。)

答案 5 :(得分:0)

除了Bao Le的答案之外,这里还准备使用Kotlin版本的CacheDataSourceFactory,该版本保留SimpleCache的一个实例,以解决多个Cache对象写入同一目录的问题。

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)
    }
}

答案 6 :(得分:0)

这是我在Kotlin的样本(项目here可用):

completed: Bool

答案 7 :(得分:0)

要解决多个视频或多个进程尝试访问同一缓存的问题,您需要一个真正的Singleton。一种可靠的方法是这样:

object VideoCache {
    private var sDownloadCache: SimpleCache? = null
    private const val maxCacheSize: Long = 100 * 1024 * 1024

    fun getInstance(context: Context): SimpleCache {
        val evictor = LeastRecentlyUsedCacheEvictor(maxCacheSize)
        if (sDownloadCache == null) sDownloadCache = SimpleCache(File(context.cacheDir, "koko-media"), evictor)
        return sDownloadCache as SimpleCache
    }
}

您现在可以使用:

private val simpleCache: SimpleCache by lazy {
        VideoCache.getInstance(context)
    }

答案 8 :(得分:0)

我在这里回答了类似的问题:https://stackoverflow.com/a/58678192/2029134

基本上,我使用这个库:https://github.com/danikula/AndroidVideoCache 从URL缓存文件 然后将其放入ExoPlayer。

这是示例代码:

String mediaURL = "https://my_cool_vid.com/vi.mp4";
SimpleExoPlayer exoPlayer = ExoPlayerFactory.newSimpleInstance(getContext());
HttpProxyCacheServer proxyServer = HttpProxyCacheServer.Builder(getContext()).maxCacheSize(1024 * 1024 * 1024).build();

String proxyURL = proxyServer.getProxyUrl(mediaURL);


DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(getContext(),
                Util.getUserAgent(getContext(), getActivity().getApplicationContext().getPackageName()));


exoPlayer.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory)
                .createMediaSource(Uri.parse(proxyURL)););

希望有帮助。

答案 9 :(得分:-1)

SimpleCache simpleCache = new SimpleCache(new File(context.getCacheDir(), "media/"+id), evictor);

此处,id必须是唯一的。