question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Offline HLS playback with encryption

See original GitHub issue

Searched 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

https://exoplayer.dev/

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:closed
  • Created 4 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
ojw28commented, Jun 9, 2020

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:

Screenshot 2020-06-09 at 13 52 31

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.

Screenshot 2020-06-09 at 13 55 25

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.

Screenshot 2020-06-09 at 13 55 29

One other minor problem with your code is that you’re using the same CacheDataSink instance for every AesCipherDataSink, 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.

0reactions
emilsobcommented, Aug 13, 2019

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 the DownloadService:

val cacheSink = CacheDataSink(cache, -1)
val downloaderConstructorHelper = DownloaderConstructorHelper(
    cache,
    OkHttpDataSourceFactory(httpClient, USER_AGENT)
    object: DataSource.Factory {
        override fun createDataSource(): DataSource {
        return AesCipherDataSource(secretKey, FileDataSource())
    },
    object: DataSink.Factory {
        override fun createDataSink(): DataSink {
            return AesCipherDataSink(secretKey, cacheSink, ByteArray(10 * 1024))
        }
    },
    null // priorityTaskManager
)
 
return DownloadManager(
    appContext,
    DefaultDownloadIndex(dbProvider),
    DefaultDownloadFactory(downloaderConstructorHelper)
)

The way we create our HlsMediaSource:

val dataFactory = DataSource.Factory {
    AesCipherDataSource(secretKey, cachedDataSourceFactory.createDataSource())
}
val hlsMediaSourceFactory = HlsMediaSource.Factory(dataFactory)
    .setAllowChunklessPreparation(true)

When we don’t use encryption, we simply make use of cachedDataSourceFactory (without wrapping it in AesCipherDataSource) as HlsMediaSourceFactory’s parameter and it works as expected - we have fully functional offline player.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to do offline Playback with AES-128 Encrypted Streams ...
This how-to guide describes how to implement Offline playback of downloadable AES-128 content-protected MPEG-DASH or HLS using the THEOplayer's Android SDK.
Read more >
How to Play Encrypted HTTP Live Streams Offline with ...
The video playback will work with the same method as the unencrypted HLS and the player will automatically access the key URL and...
Read more >
Protecting Videos with HLS Encryption
HLS encrypted videos are available for play on desktop and mobile devices when the first rendition of a video is uploaded and encrypted....
Read more >
Playing Offline HLS with AES-128 encryption iOS
I have an encrypted HLS with simple AES-128 and it doesn't want to play in offline mode, I was trying to integrate AVAssetResourceLoaderDelegate ......
Read more >
Playing Offline HLS with AES-128 encryption iOS - YouTube
iOS : Playing Offline HLS with AES-128 encryption iOS [ Beautify Your Computer : https://www.hows.tech/p/recommended.html ] iOS : Playing ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found