Offline HLS playback with encryption
See original GitHub issueSearched documentation and issues
https://github.com/google/ExoPlayer/issues/5193 https://github.com/google/ExoPlayer/issues/5424 https://github.com/google/ExoPlayer/issues/6122 https://github.com/google/ExoPlayer/issues/4821
Question
We want to make it possible to locally store adaptive HLS streams in an encrypted form in order to play this content later.
To describe the problem:
Offline playback without any explicit encryption works perfectly fine, same as playing a local encrypted mp3
file with use of Exoplayer’s Aes
module. The problem occurs when we try to make use of Exoplayer’s Aes
module along with the Exoplayer’s Downloader. In short, we construct a DownloadManager
as in #5193 (with some differences caused by changes in the DownloadManager
api) and it looks like the content is being properly downloaded. After the content is downloaded, we use AesCipherDataSource
to wrap our cache data source and pass it as before to the HlsMediaSourceFactory
, but it looks like the encryption / decryption fails. In logcat we can find the following error: (Input does not start with the #EXTM3U header
).
So the question is: is there possibility to update documentation for this area or could you provide some informations / guide regarding proper way of implementing such functionality in the current Exoplayer version (2.10.3)?
Device(s) and version(s) of Android being used
Samsung Galaxy S8, Samsung SM-T813, Emulators with Android 6+
Issue Analytics
- State:
- Created 4 years ago
- Comments:6 (4 by maintainers)
Top GitHub Comments
The problem is that you have an asymmetry between where you’re encrypting during download and where you’re decrypting during playback. During downloading, the flow of data looks like this. Note the encryption is happening when writing individual files into the cache:
During playback, the flow of data looks like this. Note that decryption is happening on the continuous stream of data from the
CacheDataSource
, rather than the individual files. This is an important difference, and will lead to incorrect decryption. Note also that if the content is partially cached and some of it needs to be fetched from the network, this configuration will attempt to decrypt content that has never been encrypted.What you should be doing, is building a chain that looks like this, so that the decryption happens in an equivalent place as where the encryption is happening.
One other minor problem with your code is that you’re using the same
CacheDataSink
instance for everyAesCipherDataSink
, where-as you should be creating a new one each time.There’s a minimal patch here that shows how to get cache encryption working in the ExoPlayer demo app. You can also just checkout the branch.
Yes, when the playlist is provided directly from the network (or cache) it does work as expected. It also works correctly when we do not try to encrypt the downloaded content, issue occurs only when we make use of
Aes
Module. Please, look at the snippet below:The way we create
DownloadManager
in theDownloadService
:The way we create our
HlsMediaSource
:When we don’t use encryption, we simply make use of
cachedDataSourceFactory
(without wrapping it inAesCipherDataSource
) asHlsMediaSourceFactory
’s parameter and it works as expected - we have fully functional offline player.