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,在 AesCipherDataSource
、AesCipherDataSink
、SimpleCache
等之间有点让人不知所措。我没能找到一个简单的播放视频的方法。
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()
}
}
所以问题:
- 如何实现本地播放这段加密视频?
- 通过 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()
}
}
在 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,在 AesCipherDataSource
、AesCipherDataSink
、SimpleCache
等之间有点让人不知所措。我没能找到一个简单的播放视频的方法。
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()
}
}
所以问题:
- 如何实现本地播放这段加密视频?
- 通过 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()
}
}