Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What to customize to support DRM key reusability? #7286

Closed
killmyrene opened this issue Apr 24, 2020 · 3 comments
Closed

What to customize to support DRM key reusability? #7286

killmyrene opened this issue Apr 24, 2020 · 3 comments
Assignees
Labels

Comments

@killmyrene
Copy link

killmyrene commented Apr 24, 2020

Searched documentation and issues

https://medium.com/google-exoplayer/improved-decoder-reuse-in-exoplayer-ef4c6d99591d

#949

What to customize to support DRM key reusability?

I'm looking for ways to customize exoplayer to allow drm key (my case its Widevine) to be reused in case that the player retries/replays a streaming content to reduce startup/rebuffering time. This can either be from retrying playback from a failure, replay by user input, or in a case where the user switches content from content A -> content B -> content A.

Basically, the MediaDrmCallback.executeKeyRequest event shouldn't be called upon retrying and the streaming drm content should be playable.

I tried a couple of things to achieve this but to no avail.

  • Tried caching the byte[] license data so that it'll be used immediately upon calling MediaDrmCallback.executeKeyRequest, but it throws a generic drm error
  • Tried using DefaultDrmSessionManager.setMode() to save license data under MediaDrmCallback.executeKeyRequest, but it throws an error upon setting it
  • Tried using one DrmSessionManager instance on two playback sessions with same content url according to my colleagues' suggestion, but license is issued the 2nd time upon retry

Next step would be to look into the CodecRenderers, but I'm having trouble looking and which APIs are to be overwritten to achieve it

Apple's ContentKeySession in some way were able to achieve this. So I hope exoplayer has the capability to do the same thing

@ojw28
Copy link
Contributor

ojw28 commented Apr 30, 2020

Tried caching the byte[] license data so that it'll be used immediately upon calling MediaDrmCallback.executeKeyRequest, but it throws a generic drm error

This is not a valid thing to do. Key requests and responses have a 1:1 correspondence. It is not valid to provide a license response generated in response to one key request as the response to another different key request.

There are only two ways of doing what you're trying to do:

  1. Ensure that the DRM session is held open throughout, so there is only one key request. I think you would most likely have to fork and customize DefaultDrmSessionManager to do this.
  2. Use proper offline licenses, rather than streaming licenses. Although this is really only intended for download use cases, rather than the ones you describe.

@killmyrene
Copy link
Author

killmyrene commented Apr 30, 2020

I loosely customize the DefaultDrmSessionManager as you describe, but I got exceptions thrown out for illegal state. I suppose I have to customize the drmSession as well?

public class CustomDrmSessionManager<T extends ExoMediaCrypto> extends DefaultDrmSessionManager {

    @Override
    public DrmSession<T> acquireSession(Looper playbackLooper, DrmInitData drmInitData) {
        DrmSession<T> session = null;

        if (existingDrmSession != null) {
            session = existingDrmSession;
        } else {
            session = super.acquireSession(playbackLooper, drmInitData);
        }
        existingDrmSession = session;

        return session;
    }
}

Exception after 1 retry

> 2020-04-30 17:34:41.881 18242-18506/tv.youi.clientapp E/ExoPlayerImplInternal: Internal runtime error.
>     java.lang.IllegalStateException
>         at com.google.android.exoplayer2.util.Assertions.checkState(Assertions.java:81)
>         at com.google.android.exoplayer2.drm.DefaultDrmSession.acquire(DefaultDrmSession.java:266)
>         at com.google.android.exoplayer2.drm.DrmSession$-CC.replaceSession(DrmSession.java:44)
>         at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.setSourceDrmSession(MediaCodecRenderer.java:1020)
>         at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.onInputFormatChanged(MediaCodecRenderer.java:1232)
>         at com.google.android.exoplayer2.audio.MediaCodecAudioRenderer.onInputFormatChanged(MediaCodecAudioRenderer.java:551)
>         at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.readToFlagsOnlyBuffer(MediaCodecRenderer.java:801)
>         at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:700)
>         at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:599)
>         at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:329)
>         at android.os.Handler.dispatchMessage(Handler.java:101)
>         at android.os.Looper.loop(Looper.java:164)
>         at android.os.HandlerThread.run(HandlerThread.java:65)
> 2020-04-30 17:34:41.887 18242-18506/tv.youi.clientapp E/MediaPeriodHolder: Period release failed.
>     java.lang.NullPointerException: Attempt to invoke virtual method 'void com.google.android.exoplayer2.drm.DefaultDrmSession$RequestHandler.removeCallbacksAndMessages(java.lang.Object)' on a null object reference
>         at com.google.android.exoplayer2.drm.DefaultDrmSession.release(DefaultDrmSession.java:282)
>         at com.google.android.exoplayer2.source.SampleMetadataQueue.releaseDrmSessionReferences(SampleMetadataQueue.java:170)
>         at com.google.android.exoplayer2.source.SampleQueue.preRelease(SampleQueue.java:278)
>         at com.google.android.exoplayer2.source.chunk.ChunkSampleStream.release(ChunkSampleStream.java:337)
>         at com.google.android.exoplayer2.source.dash.DashMediaPeriod.release(DashMediaPeriod.java:172)
>         at com.google.android.exoplayer2.source.dash.DashMediaSource.releasePeriod(DashMediaSource.java:704)
>         at com.google.android.exoplayer2.MediaPeriodHolder.releaseMediaPeriod(MediaPeriodHolder.java:425)
>         at com.google.android.exoplayer2.MediaPeriodHolder.release(MediaPeriodHolder.java:308)
>         at com.google.android.exoplayer2.MediaPeriodQueue.clear(MediaPeriodQueue.java:273)
>         at com.google.android.exoplayer2.ExoPlayerImplInternal.resetInternal(ExoPlayerImplInternal.java:923)
>         at com.google.android.exoplayer2.ExoPlayerImplInternal.stopInternal(ExoPlayerImplInternal.java:850)
>         at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:402)
>         at android.os.Handler.dispatchMessage(Handler.java:101)
>         at android.os.Looper.loop(Looper.java:164)
>         at android.os.HandlerThread.run(HandlerThread.java:65)
> 2020-04-30 17:34:41.889 18242-18242/tv.youi.clientapp E/ExoPlayerListener: Player Error: com.google.android.exoplayer2.ExoPlaybackException: java.lang.IllegalStateException

@ojw28
Copy link
Contributor

ojw28 commented May 3, 2020

What you're doing is not sufficient to actually keep the session open. DrmSession instances are reference counted using DrmSession.acquire and DrmSession.release.

If you want the session to stay open, you'll need to ensure that its reference count doesn't drop to 0. So, when you decide to hold on to the session, you should increment its reference count by calling DrmSession.acquire. When you decide you no longer need it to be held open, call DrmSession.release.

@ojw28 ojw28 closed this as completed May 18, 2020
@google google locked and limited conversation to collaborators Jul 18, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants