google / ExoPlayer

This project is deprecated and stale. The latest ExoPlayer code is available in https://github.com/androidx/media
https://developer.android.com/media/media3/exoplayer
Apache License 2.0
21.72k stars 6.03k forks source link

Play ClearKey DRM encrypted video from local storage #8237

Closed ruthwikkk closed 3 years ago

ruthwikkk commented 3 years ago

[REQUIRED] Searched documentation and issues

[REQUIRED] Question

I have implemented video playback using ClearKey DRM in my app. The keys required are available offline so I'm using LocalMediaDrmCallback for creating DRM callback and the online playback working fine. For download and offline playback, I am aware of ExoPlayer's caching mechanism but I'm using a third party download manager which downloads the encrypted video from server as it is. When I tried to play the downloaded video from local storage it throws IllegalStateException.

This is how I created DrmSessionManager

 fun buildDrmSessionManager(uuid: UUID, multiSession: Boolean, id:String, value:String): DefaultDrmSessionManager<FrameworkMediaCrypto?> {
        val drmCallback = LocalMediaDrmCallback("{\"keys\":[{\"kty\":\"oct\",\"k\":\"${value}\",\"kid\":\"${id}\"}],\"type\":\"temporary\"}".toByteArray())
        val mediaDrm = FrameworkMediaDrm.newInstance(uuid)
        return DefaultDrmSessionManager(uuid, mediaDrm, drmCallback, null, multiSession)
    }

Code for creating MediaSource from local storage

val dataSourceFactory: DataSource.Factory = FileDataSource.Factory()
val drmSessionManager = buildDrmSessionManager(UUID, false, "key_id", "key")
val videoSource: MediaSource = ProgressiveMediaSource.Factory(dataSourceFactory)
                    .setDrmSessionManager(drmSessionManager)
                    .createMediaSource(videoUriLocalStorage)

Is this approach correct ? Is it possible to play the DRM videos from local storage ?

icbaker commented 3 years ago

There isn't enough information here to help you.

When I tried to play the downloaded video from local storage it throws IllegalStateException.

It would be useful to see a full stack trace to understand where this is coming from. We also need to know what version of ExoPlayer you're using.

Is it possible to play the DRM videos from local storage ?

Yes it's possible.

The fastest way for us to help is if you can provide a minimal reproducible example that demonstrates the problem in a way that we can build locally.

This could be an Android Studio project on GitHub, or zipped up and sent to dev.exoplayer@gmail.com using a subject in the format "Issue #1234", where "#1234" should be replaced with your issue number. Please also update this issue to indicate you’ve done this.

ruthwikkk commented 3 years ago

Thanks for your reply @icbaker I have created a sample repo for the issue : https://github.com/ruthwikkk/DRMOffline. Please check

icbaker commented 3 years ago

Thanks for the sample project, I can reproduce the error you're seeing.

I don't know for sure, but I think the keys you're passing might be incorrect? I see the same error when I replace the key and key ID in your project with nonsense values.

How did you create your encrypted MP4? Maybe try encrypting a new video from scratch using shaka?

ruthwikkk commented 3 years ago

Thanks for your reply @icbaker

The same file when steaming from server working fine. I used the same key_id and key for online playback.

I encrypted the Mp4 using Mp4Box.

MP4Box -crypt drm_file.xml input_video.mp4 -out enc_video.mp4

the XML file used

<GPACDRM type="CENC AES-CTR">
<CrypTrack IsEncrypted="1" constant_IV_size="16" constant_IV="0x0a610676cb88f302d10ac8bc66e039ed" saiSavedBox="senc">
    <key KID="0x5846270325df6d8756b397fbb53c07db" value="0xa08524c412bed0841246a7879922863e"/>
  </CrypTrack>
</GPACDRM>
icbaker commented 3 years ago

I've dug a bit more into this and found a couple of things:

Firstly, your media file doesn't have a pssh box which is where the encryption info should be - this means that when it's played standalone (i.e. without a DASH/HLS manifest, which can carry the pssh info separately), ExoPlayer doesn't know that it's encrypted (and also doesn't know what key ID to try and use to decrypt it). This results in ExoPlayer trying to play the media without using the DrmSessionManager you're providing - causing the failure you see.

Secondly there was a bug in ProgressiveMediaPeriod in version 2.11.4 meaning it didn't set some DRM-related fields on Format correctly. This was fixed in https://github.com/google/ExoPlayer/commit/4736a102f875c4a8b7ac53e7c9d75fe85032d7e7 which is available from 2.11.5 onwards.

So I think you need to do two things:

Once you've done both those things, it'll hopefully work.


My instructions above about using shaka packager to create a file don't work btw - I'll remove them from the comment. I didn't realise it was automatically adding 5s of clear lead and I wasn't playing the video past 5s before concluding it worked. I only spotted this when accidentally changing the keys and seeing it still play - sorry for misleading you there.

ruthwikkk commented 3 years ago

Yes it's working now. Thanks for your suggestions @icbaker

I have updated ExoPlayer to 2.11.5, then changed the video generation script. I'm posting the changed script for reference.

<GPACDRM type="CENC AES-CTR">
  <DRMInfo type="pssh" version="1">
    <BS ID128="1077efecc0b24d02ace33c1e52e2fb4b"/>
    <BS bits="32" value="1"/>
    <BS ID128="cd7eb9ff88f34caeb06185b00024e4c2"/>
  </DRMInfo>
  <CrypTrack IV_size="8" first_IV="0xbb5738fe08f11341" isEncrypted="1" saiSavedBox="senc" trackID="1">
    <key KID="0xcd7eb9ff88f34caeb06185b00024e4c2" value="0x63cb5f7184dd4b689a5c5ff11ee6a328"/>
  </CrypTrack>
</GPACDRM>