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

如何为ExoPlayer配置多类型DataSource.Factory适配多种媒体?

解决ExoPlayer多类型媒体兼容问题

要同时适配流式MP4、本地混淆MP4、流式/本地HLS/DASH,核心是做一个数据源工厂分发器,根据媒体的具体特征(类型、存储方式)自动选择对应的DataSource.Factory,下面是具体实现方案:

步骤1:实现本地混淆MP4的解密数据源工厂

先针对本地混淆MP4实现自定义的解密数据源工厂,假设你已经有了处理解密逻辑的EncryptedFileDataSource:

class EncryptedFileDataSourceFactory : DataSource.Factory {
    override fun createDataSource(): DataSource {
        return EncryptedFileDataSource() // 你的自定义解密数据源,负责读取并解密混淆MP4
    }
}

步骤2:实现数据源工厂分发器

这个分发器会根据请求的Uri特征,判断该用哪种数据源工厂:

class MediaDataSourceFactory(
    private val cacheFactory: DataSource.Factory,
    private val encryptedFactory: DataSource.Factory
) : DataSource.Factory {

    override fun createDataSource(): DataSource {
        return object : DataSource {
            private var delegateDataSource: DataSource? = null

            override fun open(dataSpec: DataSpec): Long {
                val uri = dataSpec.uri
                // 根据Uri特征判断是否为本地混淆MP4,可根据实际业务调整判断逻辑
                val isEncryptedLocalMp4 = when {
                    // 示例1:通过自定义scheme识别,比如uri为"encrypted:///sdcard/xxx.enc.mp4"
                    uri.scheme == "encrypted" -> true
                    // 示例2:通过本地文件后缀识别,比如".enc.mp4"结尾的本地文件
                    uri.scheme == "file" && uri.path?.endsWith(".enc.mp4") == true -> true
                    else -> false
                }

                // 选择对应的数据源
                delegateDataSource = if (isEncryptedLocalMp4) {
                    encryptedFactory.createDataSource()
                } else {
                    cacheFactory.createDataSource()
                }

                return delegateDataSource!!.open(dataSpec)
            }

            override fun read(buffer: ByteArray, offset: Int, length: Int): Int {
                return delegateDataSource!!.read(buffer, offset, length)
            }

            override fun close() {
                delegateDataSource?.close()
            }

            override fun getUri(): Uri? {
                return delegateDataSource?.uri
            }

            override fun getResponseHeaders(): MutableMap<String, MutableList<String>> {
                return delegateDataSource?.responseHeaders ?: mutableMapOf()
            }
        }
    }
}

步骤3:重构ExoPlayer初始化代码

将分发器作为统一的数据源工厂传入ExoPlayer:

val cacheFactory = CacheDataSource.Factory()
    .setCache(cache)
    .setUpstreamDataSourceFactory(httpFactory)
    .setCacheWriteDataSinkFactory(null)
    .setCacheReadDataSourceFactory(FileDataSource.Factory())
    .setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)

// 初始化自定义解密数据源工厂
val encryptedFactory = EncryptedFileDataSourceFactory()

// 初始化数据源分发器
val mediaDataSourceFactory = MediaDataSourceFactory(cacheFactory, encryptedFactory)

val audioAttributes = AudioAttributes.Builder()
    .setUsage(C.USAGE_MEDIA)
    .setContentType(C.AUDIO_CONTENT_TYPE_SPEECH)
    .build()

return ExoPlayer.Builder(context, DefaultRenderersFactory(context))
    .setMediaSourceFactory(
        DefaultMediaSourceFactory(context)
            .setDrmSessionManagerProvider { drmSessionManager }
            .setDataSourceFactory(mediaDataSourceFactory) // 设置分发器作为统一数据源
    )
    .setAudioAttributes(audioAttributes, true)
    .build()

关键细节说明

  • 判断逻辑灵活调整:你可以根据业务需求修改isEncryptedLocalMp4的判断规则,比如用文件路径前缀、Uri参数、业务标记等方式识别本地混淆MP4。
  • CacheDataSource的复用:原有的cacheFactory已经能覆盖流式MP4、流式HLS/DASH(缓存未命中时自动用OkHttpDataSource拉取)、本地缓存的HLS/DASH(读取缓存文件),无需额外修改。
  • 自定义DataSource注意事项:确保EncryptedFileDataSource正确实现ExoPlayer的DataSource接口,处理好文件读取、解密逻辑及异常捕获。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 12:16:23