You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ExoPlayer使用CacheDataSource时ClippingConfiguration不生效问题咨询

ExoPlayer ClippingConfiguration 结合缓存不生效问题解决

问题描述

在ExoPlayer中为MediaItem配置了ClippingConfiguration,但使用自定义缓存的ProgressiveMediaSource时裁剪不生效,改用DefaultMediaSourceFactory创建媒体源则正常工作。相关代码如下:

配置MediaItem的代码

val mediaItem: MediaItem = MediaItem.Builder()
        .setUri(recitationUri)
        .setMediaId("$surah:${ayah}")
        .setClippingConfiguration(
            ClippingConfiguration.Builder()
                .setStartPositionMs(ayahTiming.start)
                .setEndPositionMs(ayahTiming.end)
                .build()
        ).build()

// 裁剪不生效的创建方式
val mediaSourceFactory = ProgressiveMediaSource.Factory(MediaCache.getInstance(this@PlaybackService).cacheFactory)
val source = mediaSourceFactory.createMediaSource(mediaItem)

// 裁剪生效的创建方式
val source = DefaultMediaSourceFactory(this).createMediaSource(mediaItem)

自定义MediaCache代码

const val CACHE_DIR_NAME = "cached_audio"
const val MAX_CACHE_SIZE = 256 * 1024 * 1024L//256MB

class MediaCache private constructor(context: Context) {

val cacheFactory: CacheDataSource.Factory

init {
    cacheFactory = setupExoPlayerCache(context)
}

companion object {
    @Volatile
    private lateinit var instance: MediaCache

    fun getInstance(context: Context): MediaCache {
        synchronized(this) {
            if (!::instance.isInitialized) {
                instance = MediaCache(context)
            }
            return instance
        }
    }
}

@SuppressLint("UnsafeOptInUsageError")
private fun setupExoPlayerCache(context: Context): CacheDataSource.Factory {
    val cacheEvictor = LeastRecentlyUsedCacheEvictor(MAX_CACHE_SIZE)
    val databaseProvider = StandaloneDatabaseProvider(context)
    val cache = SimpleCache(
        File(context.cacheDir, CACHE_DIR_NAME),
        cacheEvictor, databaseProvider
    )
    val upstreamFactory = DefaultDataSource.Factory(context)
    return CacheDataSource.Factory().apply {
        setCache(cache)
        setUpstreamDataSourceFactory(upstreamFactory)
        setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
    }
}

原因分析

  • ProgressiveMediaSource.Factory仅负责创建对应格式的原始媒体源,不会解析MediaItem中的ClippingConfiguration,也不会自动包装ClippingMediaSource,因此裁剪配置无法生效。
  • DefaultMediaSourceFactory内部会自动检查MediaItem的裁剪配置,若存在则自动用ClippingMediaSource包装原始媒体源,同时处理DRM、广告等其他逻辑,所以裁剪功能正常工作。

解决办法

方法一:手动包装ClippingMediaSource

创建ProgressiveMediaSource后,手动用ClippingMediaSource包装,主动应用MediaItem中的裁剪配置:

val mediaSourceFactory = ProgressiveMediaSource.Factory(MediaCache.getInstance(this@PlaybackService).cacheFactory)
val originalSource = mediaSourceFactory.createMediaSource(mediaItem)
// 确保clippingConfiguration非空后应用裁剪
val clippedSource = mediaItem.clippingConfiguration?.let {
    ClippingMediaSource(
        originalSource,
        it.startPositionMs,
        it.endPositionMs
    )
} ?: originalSource

方法二:改用DefaultMediaSourceFactory并配置缓存数据源

保留DefaultMediaSourceFactory的自动处理逻辑,同时将缓存数据源工厂注入,既支持缓存又自动应用裁剪,更符合ExoPlayer设计规范:

val cacheDataSourceFactory = MediaCache.getInstance(this@PlaybackService).cacheFactory
val mediaSourceFactory = DefaultMediaSourceFactory(this)
    .setDataSourceFactory(cacheDataSourceFactory)
val source = mediaSourceFactory.createMediaSource(mediaItem)

内容的提问来源于stack exchange,提问作者Hadi Ahmadi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:45:19