Android + exoplayer: 在本地播放AES加密视频

Android + exoplayer: play AES encrypted videos, locally

在 linux 盒子上,我有一个用 openssl 加密的 MP4 视频:

openssl enc -aes-128-ecb -a -in video.mp4 -out video.enc -K `cat aes.key`

请注意,这是一个练习,算法的强度无关紧要。

该文件已发送到 Android 应用程序,我正在尝试使用 ExoPlayer 播放它。

我事先对文本文件做了一些测试,以确保解密工作正常

fun decrypt(key: ByteArray, data: ByteArray): ByteArray {
    val spec = SecretKeySpec(key, "AES")
    val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
    cipher.init(Cipher.DECRYPT_MODE, spec)
    globalCipher.init(Cipher.DECRYPT_MODE, spec)
    return cipher.doFinal(data)
}

关于 ExoPlayer,在 AesCipherDataSourceAesCipherDataSinkSimpleCache 等之间有点让人不知所措。我没能找到一个简单的播放视频的方法。

fun playVideo() {
    val player = SimpleExoPlayer.Builder(this).build()
    playerView.player = player

    val dataSourceFactory = DefaultDataSourceFactory? // <-- what's the factory?
    val dataSource = AesCipherDataSource(globalCipher, ?) // <-- what's the data source?
    val extractorsFactory: ExtractorsFactory = DefaultExtractorsFactory()
    try {
        val uri = Uri.fromFile(File(path, "video.enc"))
        val videoSource =
                ExtractorMediaSource(uri, dataSourceFactory, extractorsFactory, null, null)
        player.prepare(videoSource)
        player.playWhenReady = true
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

所以问题:

  1. 如何实现本地播放这段加密视频?
  2. 通过 HTTP 提供该视频后需要更改什么? (需要添加清单?headers?)

这是解决方案。可能需要一些调整来处理跳帧、快进等,但这会播放 AES/ECB/PKCS5Padding 加密视频

class EncryptedDataSourceFactory(
    private val key: String
) : DataSource.Factory {
    override fun createDataSource(): EncryptedDataSource =
        EncryptedDataSource(key)
}
class EncryptedDataSource(private val key: String) : DataSource {
    private var inputStream: CipherInputStream? = null
    private lateinit var uri: Uri

    override fun addTransferListener(transferListener: TransferListener) {}

    override fun open(dataSpec: DataSpec): Long {
        uri = dataSpec.uri
        try {
            val file = File(uri.path)
            val skeySpec = SecretKeySpec(key.toByteArray(), KeyProperties.KEY_ALGORITHM_AES)
            val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
            cipher.init(Cipher.DECRYPT_MODE, skeySpec)
            inputStream = CipherInputStream(file.inputStream(), cipher)
        } catch (e: Exception) {
            
        }
        return dataSpec.length
    }

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

    override fun getUri(): Uri? =
        uri

    @Throws(IOException::class)
    override fun close() {
        inputStream?.close()
    }
}
    private fun playVideo(key: String) {
        val player = SimpleExoPlayer.Builder(this).build()
        playerView.player = player

        val dataSourceFactory: DataSource.Factory = EncryptedDataSourceFactory(key)
        val extractorsFactory: ExtractorsFactory = DefaultExtractorsFactory()
        try {
            val uri = Uri.fromFile(video)
            val videoSource: MediaSource = ExtractorMediaSource(uri, dataSourceFactory, extractorsFactory, null, null)
            player.prepare(videoSource)
            player.playWhenReady = true
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }