从 ExoPlayer 缓存中获取所有内容

Posted

技术标签:

【中文标题】从 ExoPlayer 缓存中获取所有内容【英文标题】:Get everything in from ExoPlayer Cache 【发布时间】:2018-06-13 17:45:43 【问题描述】:

使用https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/upstream/cache/CacheDataSourceFactory.html 有没有办法获取所有已缓存的MediaSource?

【问题讨论】:

【参考方案1】:

缓存没有提供方便的 API 来获取所有完全缓存的 URI 或类似的。

如果您创建自己的CacheEvictor(例如通过包装LeastRecentlyUsedCacheEvictor),您可以在添加或删除跨度时自己记账,然后委托给LRUCacheEvictor。这样您就可以维护一个缓存 URL 列表。

您可以检查给定 uri 的哪些部分被缓存:

// create the data spec of a given media file
Uri uri = Uri.parse("http://cool.stuff.com/song-123.mp3")
DataSpec dataSpec = new DataSpec(uri);
// get information about what is cached for the given data spec
CacheUtil.CachingCounters counters = new CacheUtil.CachingCounters();
CacheUtil.getCached(dataSpec, cache, counters);
if (counters.contentLength == counters.totalCachedBytes()) 
  // all bytes cached
 else if (counters.totalCachedBytes() == 0)
  // not cached at all
 else 
  // partially cached

如果给定 uri 的数据仅被部分缓存,您可以检查哪些 span 可用,如下所示:

NavigableSet<CacheSpan> cachedSpans =
   cache.getCachedSpans(CacheUtil.generateKey(uri));

【讨论】:

这非常有用,谢谢!我肯定会使用它,但它并没有直接回答我最初试图问的问题(我可能应该进一步澄清它)。至于缓存中的每个 uri,我是否需要自己维护一个 URI 列表作为进入和离开缓存,或者有没有办法只获取缓存中的每个可用 URI?如果我需要自己维护它,那么最好的方法是什么。再次感谢您的洞察力! 您尝试实现的用例是什么?实际上,为什么您需要知道缓存中的内容?如果您尝试以受控方式实现离线行为,我建议您使用下载器:github.com/google/ExoPlayer/blob/release-v2/library/core/src/… 首要目标是我的纯音频播放器将拉下新曲目,并在播放过程中缓存它们,如果由于某种原因它无法拉下新曲目(连接不良或服务器问题),它应该从缓存中抓取一个曲目以继续无缝播放,同时继续此过程(如果可能,从服务器获取新曲目,如果没有,则移动到下一个缓存曲目)。抓取缓存时,它应该抓取最旧的轨道(以防止重复),然后将其标记为最近使用(因此下次它不会抓取相同的轨道)。这一切都有意义吗? 我明白了。是的,我认为您需要自己维护 uris/keys 的地图。如果您使用的是 SimpleCache,您可能想要创建自己的 CacheEvictor。这可以基于例如。 LeastRecentlyCacheEvictor 还跟踪哪些 url 被完全缓存(在 onSpanAdded 中),如果是,则将它们添加到列表中。删除此 uri/key 的跨度后,您需要再次从该列表中删除该 uri。如果网络丢失,您可以向 evictor 询问 uris 列表。 这对我来说很有意义。我也在更多地研究这个 ProgressiveDownloader,因为它可能对我的用例有用。查看 ExoPlayer 存储库,它似乎只用于测试,你是否碰巧知道它是否有任何资源或实际用途,或者你可以指出我的任何其他下载器?非常感谢您的帮助。【参考方案2】:

CacheUtil 不再存在,https://github.com/google/ExoPlayer/blob/2a88f0fb295ff5b56e6fbcbe7e91bdf922cbae13/RELEASENOTES.md#2120-2020-09-11

还有另一种检查缓存内容的方法:

import com.google.android.exoplayer2.upstream.DataSpec
import com.google.android.exoplayer2.upstream.cache.CacheDataSource
import com.google.android.exoplayer2.upstream.cache.ContentMetadata
import com.google.android.exoplayer2.upstream.cache.Cache

/* the same instance, used in player build pipeline */
lateinit var cacheDataSourceFactory: CacheDataSource.Factory
/* usually this is SimpleCache instance, also used in player build pipeline */
lateinit var cacheImpl: Cache

fun isCompletelyCached(urL :String) :Boolean 
  val uri = Uri.parse(urL)
  // factory which is used to generate "content key" for uri.
  // content keys are not always equal to urL
  // in complex cases the factory may be different from default implementation
  val cacheKeyFactory = cacheDataSourceFactory.cacheKeyFactory
  // content key used to retrieve metadata for cache entry
  val contentKey = cacheKeyFactory.buildCacheKey(DataSpec(uri))
  val contentMetadata = cache.getContentMetadata(contentKey)
  val contentLength = ContentMetadata.getContentLength(contentMetadata)
  if(contentLength < 0)
    // this happens when player has never queried this urL over network 
    // or has no info about size of the source
    return false
  
  // this is summary for all cache spans, cached by exoplayer, which belongs to given urL.
  // each span is a chunk of content, which may be randomly downloaded
  val cachedLength = cache.getCachedBytes(contentKey, 0L, contentLength)

  return contentLength == cachedLength

【讨论】:

以上是关于从 ExoPlayer 缓存中获取所有内容的主要内容,如果未能解决你的问题,请参考以下文章

启用 Exoplayer Cache 后滑动缓慢加载

ExoPlayer Talk 01 缓存策略分析与优化

如何使用 Exoplayer 播放从 Mediastore 获取的视频

使用 ExoPlayer 的 Android MediaPlayer 持久缓存

DummyDataSource 无法打开:Android ExoPlayer

从 ExoPlayer 获取 403 响应代码错误