在ExoPlayer中使用缓存

ila*_*sas 34 media android caching exoplayer

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

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

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

Bao*_* Le 32

这是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);
    }
}
Run Code Online (Sandbox Code Playgroud)

和玩家

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);
Run Code Online (Sandbox Code Playgroud)

它工作得很好.

  • 解决问题的方法:保留SimpleCache的共享实例,而不是在createDataSource中创建它.否则多个Cache对象将写入相同的文件导致麻烦 (12认同)
  • @Bao Le,这个实现的预期行为应该是缓存流的视频播放也应该在离线状态下发生,对吧?但是当网络断开时我无法播放,尽管它​​是缓存流。视频播放明显只能在线播放吗?或者我在这里错过了什么? (2认同)

Nhấ*_*ang 13

默认情况下,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)
Run Code Online (Sandbox Code Playgroud)


cod*_*uss 10

我在这里回答了这个类似的问题: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)););
Run Code Online (Sandbox Code Playgroud)

希望有帮助。


Row*_*ezi 7

要解决多个视频或进程尝试访问同一个缓存的问题,您需要一个真正的 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
    }
}
Run Code Online (Sandbox Code Playgroud)

您现在可以使用:

private val simpleCache: SimpleCache by lazy {
        VideoCache.getInstance(context)
    }
Run Code Online (Sandbox Code Playgroud)


Jac*_*cki 5

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

  • 我从exoplayer 2.2.0演示应用程序获得了OkHttpDataSource.您可以分享一些用于配置OkHttp缓存的链接. (2认同)

小智 3

我在渲染器构建器中像这样实现了它

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());
Run Code Online (Sandbox Code Playgroud)

  • 该代码编译并运行,但似乎没有在指定的缓存文件夹中写入任何视频。它对你有用吗?是否可以在没有互联网连接的情况下从缓存中播放?更深入的信息将不胜感激。谢谢 (3认同)
  • 根据 https://github.com/google/ExoPlayer/issues/420,此答案仅对 DASH 流有效。对于 MP4 文件,OkHttpDataSource 似乎产生了良好的结果(根据该线程上的人员的说法)。 (3认同)