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



在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 提供视频后需要更改哪些内容?(需要添加清单吗?标头?

这是解决方案。可能需要一些调整来处理跳帧、快进等,但这会播放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()
}
}

最新更新