首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >安卓-如何使用ExoPlayer播放AES/GCM/NoPadding加密视频?

安卓-如何使用ExoPlayer播放AES/GCM/NoPadding加密视频?
EN

Stack Overflow用户
提问于 2021-06-15 08:26:46
回答 1查看 217关注 0票数 0

them /GCM/NoPadding在内部应用程序存储中加密视频,我想使用ExoPlayer播放它们。

对我来说什么都不管用。我试过:

  • AesCipherDataSource与FileDataSource ->无抽取器可读取流
  • 自定义DataSources ->无效NAL长度

不同版本的。

我是不是遗漏了什么?

下面是一些代码:

我的PlayerSetup

代码语言:javascript
运行
复制
    fun setupPlayer(photoId: Int) = viewModelScope.launch(Dispatchers.IO) {
        val photo = photoRepository.get(photoId)

        player = SimpleExoPlayer.Builder(app)
            .setMediaSourceFactory(createMediaSourceFactory())
            .build()
        player!!.apply {
            onMain {
                setMediaItem(createMediaItem(photo))
                prepare()
                playWhenReady = true
            }
        }
    }

    private fun createMediaSourceFactory(): MediaSourceFactory {
        val aesDataSource = AesCipherDataSource(encryptionManager.encodedKey, FileDataSource())

        val factory = DataSource.Factory {
            aesDataSource
        }

        return ProgressiveMediaSource.Factory(factory)
    }

    private fun createMediaItem(photo: Photo): MediaItem {
        val uri = Uri.fromFile(app.getFileStreamPath(photo.internalFileName).canonicalFile)

        return MediaItem.Builder()
            .setMimeType(photo.type.mimeType)
            .setUri(uri)
            .build()
    }

并且我的自定义DataSource尝试(不在上面的代码中使用):

代码语言:javascript
运行
复制
class AesGCMDataSource(
    private val upstream: DataSource,
    private val encryptionManager: EncryptionManager
) : DataSource {

    private var cipherInputStream: CipherInputStream? = null

    override fun addTransferListener(transferListener: TransferListener) {
        upstream.addTransferListener(transferListener)
    }

    override fun open(dataSpec: DataSpec): Long {
        val inputStream = DataSourceInputStream(upstream, dataSpec)
        cipherInputStream = encryptionManager.createCipherInputStream(inputStream)

        inputStream.open()
        return C.LENGTH_UNSET.toLong()
    }

    override fun read(target: ByteArray, offset: Int, length: Int): Int {
        Assertions.checkNotNull<Any>(cipherInputStream)

        val read = cipherInputStream!!.read(target, offset, length)

        return if (read < 0) {
            C.RESULT_END_OF_INPUT
        } else {
            read
        }
    }

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

    override fun getUri(): Uri? = upstream.uri

    override fun close() = upstream.close()
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-07-17 12:05:23

我找到了一个解决办法:

创建这个DataSource:

代码语言:javascript
运行
复制
class AesDataSource(
    private val cipher: Cipher
) : DataSource {

    private var inputStream: CipherInputStream? = null
    private lateinit var uri: Uri

    override fun open(dataSpec: DataSpec): Long {
        uri = dataSpec.uri
        uri.path ?: return 0

        val file = File(uri.path!!).canonicalFile
        inputStream = CipherInputStream(file.inputStream(), cipher)
        if (dataSpec.position != 0L) {
            inputStream?.forceSkip(dataSpec.position) // Needed for skipping
        }

        return dataSpec.length
    }

    @Throws(IOException::class)
    override fun read(target: ByteArray, offset: Int, length: Int): Int =
        if (length == 0) {
            0
        } else {
            inputStream?.read(target, offset, length) ?: 0
        }

    override fun addTransferListener(transferListener: TransferListener) {}

    override fun getUri(): Uri = uri

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

DataSource使用这个扩展函数:

代码语言:javascript
运行
复制
/**
 * Skip bytes by reading them to a specific point.
 * This is needed in GCM because the Authorisation Tag wont match when bytes are really skipped.
 */
fun CipherInputStream.forceSkip(bytesToSkip: Long): Long {
    var processedBytes = 0L
    while (processedBytes < bytesToSkip) {
        read()
        processedBytes++
    }

    return processedBytes
}

像这样使用它:

代码语言:javascript
运行
复制
fun setupPlayer(file: Uri) {
        player = SimpleExoPlayer.Builder(app)
            .setMediaSourceFactory(createMediaSourceFactory())
            .build()
            .apply {
                setMediaItem(createMediaItem(file))
                prepare()
                playWhenReady = true
            }
}

private fun createMediaSourceFactory(): MediaSourceFactory {
    val aesDataSource = AesDataSource(yourCipher) // Use your Cipher instance

    val factory = DataSource.Factory {
        aesDataSource
    }

    return ProgressiveMediaSource.Factory(factory)
}

private fun createMediaItem(file: Uri): MediaItem {
    return MediaItem.fromUri(uri)
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67982671

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档