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

DRM with key rotation: Black flicker observed when keys rotate #3561

Closed
ojw28 opened this issue Dec 7, 2017 · 35 comments

Comments

Projects
None yet
8 participants
@ojw28
Copy link
Contributor

commented Dec 7, 2017

Spun out from #1298. When playing a stream with key rotation, the display flickers black at the points when keys rotate. Every three minutes in this sample:

{
  "name": "Sintel - 3 minutes key rotation",
  "uri": "https://storage.googleapis.com/playerinfra/wv/sintel_3min_rotate.mpd",
  "drm_scheme": "widevine",
  "drm_multi_session": true,
  "drm_license_url": "https://widevine-proxy.appspot.com/proxy"
}

This is because the secure video decoder is (incorrectly) released and re-instantiated.

There also appears to be an audio underrun on some devices, although this may be more of a performance problem with some devices as opposed to an ExoPlayer issue.

@ojw28 ojw28 added the bug label Dec 7, 2017

@ojw28 ojw28 self-assigned this Dec 7, 2017

@ojw28 ojw28 changed the title Black flicker when playing DRM protected streams with key rotation DRM with key rotation: Black flicker observed when keys rotate Dec 7, 2017

@ueno-yuhei

This comment has been minimized.

Copy link

commented Dec 8, 2017

Thank you for the new issue.
If you have found something here I will send comments and actual code to this issue.

@jboisjo

This comment has been minimized.

Copy link

commented Jan 11, 2018

any updates for this issue?

@ojw28

This comment has been minimized.

Copy link
Contributor Author

commented Jan 12, 2018

We will provide an update when there is one (lack of update == there is no update). Thanks.

@kvillnv

This comment has been minimized.

Copy link

commented Jan 15, 2018

Same issue here, looking forward for an update as well. Thanks

@cizarco

This comment has been minimized.

Copy link

commented Jan 16, 2018

After reading the document about Key Rotation on Android (https://storage.googleapis.com/wvdocs/Widevine_DRM_Android_Using_Key_Rotation.pdf). to the best of my knowledge, the bug is related to the moment of releasing the initial sessionId1 which lead to tearing down the MediaCodec and creating new one. So, I think somewehere in Exoplayer library, you destruct the initial session.

@linhai326

This comment has been minimized.

Copy link

commented Mar 14, 2018

Originally, i thought @cizarco was right: obviously, the current implementation closes the primary/first session when opening a new one, which doesn't match the document's design. But even if I don't release any session, this issue still happens. So it seems the cause was somewhere else.

@ojw28

This comment has been minimized.

Copy link
Contributor Author

commented Mar 14, 2018

@cizarco is right. Just commenting out releasing of sessions isn't sufficient to fix it. More in depth changes are required (e.g. to ensure MediaCodecRenderer doesn't tear down the MediaCodec).

ojw28 added a commit that referenced this issue Jan 21, 2019

@ojw28

This comment has been minimized.

Copy link
Contributor Author

commented Jan 21, 2019

Fixed in dev-v2 for API level 23 and above. There's still a little pause when key rotation occurs, however.

@ojw28

This comment has been minimized.

Copy link
Contributor Author

commented Jan 21, 2019

The pause is tracked by #4133, so closing this one.

@ojw28 ojw28 closed this Jan 21, 2019

@rajeshghosh1978

This comment was marked as off-topic.

Copy link

commented Mar 6, 2019

After @ojw28 fix, still the black flicker is happening for me. Then I gone through DefaultDrmSessionManager and found one possible reason in acquireSession function. In that function, there is a comparison between old and new List and every time there is a bitrate change between SD and HD it is returning as false, since we are using different PSSH for SD and HD. Since the comparison is in the object level, so any changes in the data it returns false. On that case you are creating the new DefaultDrmSession.

if (Util.areEqual(existingSession.schemeDatas, schemeDatas)) {
session = existingSession;
break;
}

So i have added another flag to check. If we are switching to a known PSSH box then don't create new DefaultDrmSession.
Looks like

if (Util.areEqual(existingSession.schemeDatas, schemeDatas) || causePreviousSession) {
session = existingSession;
break;
}

This is working perfectly with key rotation. Please let me know if I have done anything wrong here.

@ojw28

This comment was marked as off-topic.

Copy link
Contributor Author

commented Mar 6, 2019

still the black flicker is happening for me

Are you using the dev-v2 branch? The fix isn't in a tagged release yet.

@rajeshghosh1978

This comment was marked as off-topic.

Copy link

commented Mar 6, 2019

Yes I am using the dev-v2 only, where I can see all the changes for 14eb561 commit

@ojw28

This comment was marked as off-topic.

Copy link
Contributor Author

commented Mar 6, 2019

I'm think the change you've made (assuming causePreviousSession is true) is equivalent to ignoring the fact that the keys rotate (which should result in playback failure if your stream and license server are rotating keys correctly). The black flicker is gone for me on dev-v2 for API level 23 and above.

@rajeshghosh1978

This comment was marked as off-topic.

Copy link

commented Mar 6, 2019

I have checked with key rotation as well, which is working with my changes. Whenever there is a key rotation schemeDatas contains a new PSSH, so causePreviousSession is false so a new session is getting created.

If you can check your code in LTE network rather than Wifi you can see the flicker. In LTE mode, playback trying to switch between SD and HD frequently (Almost in every 3 to 5 seconds), but in Wifi mode, the same playback happens with the same bitrate almost all the time.

@ojw28

This comment was marked as off-topic.

Copy link
Contributor Author

commented Mar 6, 2019

Whenever there is a key rotation schemeDatas contains a new PSSH, so causePreviousSession is false so a new session is getting created.

So in the key rotation case, the code that executes is identical to what it was without your change. I don't understand how you can not be getting the black flicker in that case, but getting the black flicker in the SD -> HD case without your change. The code paths are identical in both cases, and there's no particular difference between a change due to SD -> HD switching and a change due to key rotation.

Are you sure it's specifically black flicker you're seeing, as opposed to short pauses (but not flashing black)?

@rajeshghosh1978

This comment was marked as off-topic.

Copy link

commented Mar 6, 2019

No this is not a pause. With my change, I am getting a black screen for two times. First when bitrate switch from SD to HD (Because we found a new PSSH) and the second time when there is a key rotation (Got another new PSSH).

Here is the modified acquireSession function, I have renamed the variables

ArrayList<byte[]> pssh = new ArrayList<>();

  @Override
  public DrmSession<T> acquireSession(Looper playbackLooper, DrmInitData drmInitData) {
    Assertions.checkState(this.playbackLooper == null || this.playbackLooper == playbackLooper);
    if (sessions.isEmpty()) {
      this.playbackLooper = playbackLooper;
      if (mediaDrmHandler == null) {
        mediaDrmHandler = new MediaDrmHandler(playbackLooper);
      }
    }

    List<SchemeData> schemeDatas = null;
    if (offlineLicenseKeySetId == null) {
      schemeDatas = getSchemeDatas(drmInitData, uuid, false);
      if (schemeDatas.isEmpty()) {
        final MissingSchemeDataException error = new MissingSchemeDataException(uuid);
        eventDispatcher.dispatch(listener -> listener.onDrmSessionManagerError(error));
        return new ErrorStateDrmSession<>(new DrmSessionException(error));
      }
    }

    boolean shouldNotDestroySession = false;
    DefaultDrmSession<T> session;
    if (!multiSession) {
      session = sessions.isEmpty() ? null : sessions.get(0);
    } else {
      // Only use an existing session if it has matching init data.
      session = null;

        for (DrmInitData.SchemeData temp : schemeDatas) {
            if(!pssh.contains(temp.data))
            {
                pssh.add(temp.data);
            }
            else
            {
                shouldNotDestroySession = true;
            }
        }

      for (DefaultDrmSession<T> existingSession : sessions) {
        if (Util.areEqual(existingSession.schemeDatas, schemeDatas) || shouldNotDestroySession) {
          session = existingSession;
          break;
        }
      }
    }

    if (session == null) {
      // Create a new session.
      session =
          new DefaultDrmSession<>(
              uuid,
              mediaDrm,
              this,
              schemeDatas,
              mode,
              offlineLicenseKeySetId,
              optionalKeyRequestParameters,
              callback,
              playbackLooper,
              eventDispatcher,
              initialDrmRequestRetryCount);
      sessions.add(session);
    }
    session.acquire();
    return session;
  }
@ojw28

This comment was marked as off-topic.

Copy link
Contributor Author

commented Mar 7, 2019

Your code doesn't look valid to me. It seems to ignore the new PSSH and continue using the same session, which has keys that were requested using the old PSSH. This is equivalent to ignoring the fact that key rotation has occurred in the stream, and if this were a valid thing to do, there would be no use case for key rotation to exist in the first place. If your content is able to play successfully with this change, I suspect there is something wrong with the way you're rotating keys, which is allowing the keys obtained using the first PSSH to play content after key rotation has occurred, which is not how key rotation is supposed to work. Or perhaps you didn't leave the stream running long enough to encounter the playback failure that should occur.

For the SD -> HD case it is valid to ignore the new PSSH if your license server returns both SD and HD keys when a key request is made using the PSSH from one stream or the other. This is what ExoPlayer assumes by default when multiSession = false. Key rotation is different, since the equivalent would be for the license server to return all keys required for playback as the keys rotate, which would defeat the purpose of rotating the keys in the first place.

@rajeshghosh1978

This comment was marked as off-topic.

Copy link

commented Mar 7, 2019

I have tested this with a channel where key rotates after each program change which is after every 30 minutes. Let me explain my changes step by step.

I have also renamed the variable to shouldNotDestroySession and move ArrayList entry outside (No need to keep this inside Session for loop ).

  1. We got the first Adaption set with PSSH#111111
    schemeDatas = getSchemeDatas(drmInitData, uuid, false);

The below block will add the PSSH in the ArrayList keeping shouldNotDestroySession as false, so new session will be created

  for (DrmInitData.SchemeData temp : schemeDatas) {
        if(!pssh.contains(temp.data))
        {
            pssh.add(temp.data);
        }
        else
        {
            shouldNotDestroySession = true;
        }
    }
    
      
  for (DefaultDrmSession<T> existingSession : sessions) {
    if (Util.areEqual(existingSession.schemeDatas, schemeDatas) || shouldNotDestroySession) {
      session = existingSession;
      break;
    }
  }
  1. We got second Adaption set with PSSH#22222 for HD
    Same flow like above.

  2. Due to bitrate change, we got PSSH#111111 again. So the above block won't add anything to ArrayList
    but shouldNotDestroySession will be true and we will use the existing session.

  3. Due to key rotation we got Adaption set with PSSH#333333. Again we got a new PSSH so the above block will add that
    into the array list keeping shouldDestroySession as false. Hence a new session will get created.

That's why key rotation is also working for me, without frequent flickering in LTE mode.

@ojw28

This comment was marked as off-topic.

Copy link
Contributor Author

commented Mar 7, 2019

(4) Due to bitrate change, we got PSSH#111111 again. So the above block won't add anything to ArrayList but shouldNotDestroySession will be true and we will use the existing session.

This makes an assumption that requesting keys for HD returns keys for both HD and SD, which may or may not be fine depending on how your license server works, as I mentioned above. Note that under the assumption that it is fine, the new session that your code opens in (3) is probably unnecessary, as you'll most likely have the HD key already from (1).

I think what you're really trying to do is not open a new session for SD/HD switching, but open a new session when key rotation occurs. However, since there's no reliable way for you to disambiguate between these two cases, you're using the pssh list as a safe way of under-approximating when you can avoid opening a new session.

So yes, I think I understand this now. I don't think it's necessarily the best way to do things though; there's probably a better way that avoids the approximation. I also don't think there should be a black flicker even when you don't do this. If you can provide good reproduction steps, please file a new issue that provides complete information about this.

@rajeshghosh1978

This comment was marked as off-topic.

Copy link

commented Mar 7, 2019

I have added a log in the for loop, each time I am getting a single PSSH key, so it seems like our server returns single key, please correct me if I am wrong.

for (DrmInitData.SchemeData temp : schemeDatas) {
               Log.d("PSSH",new String(temp.data));
           }
@ojw28

This comment was marked as off-topic.

Copy link
Contributor Author

commented Mar 7, 2019

The actual keys are in the license response, not the media (which is where the scheme data comes from). So you're looking in the wrong place. What you're doing would not work correctly unless your license server returns both keys. In particular playback would fail at (4) because the current session, which was obtained using the PSSH from the HD stream in (3), would not contain the SD key. The SD key that was loaded in (1) does not remain loaded and usable after (3).

@rajeshghosh1978

This comment was marked as off-topic.

Copy link

commented Mar 8, 2019

Yes, I know that the DrmInit data passed in the request body with the license URL to get all the keys. That's why I got confused about why you are creating a new session even when bitrate changes happen between SD and HD. I have replicated two different MPD just like we are using in our server. The first one is the initial MPD to start the playback and the other one is while key rotation happens. Using the similar kind of structure if you are destroying session during switching from SD to HD , it means our server returns single key on each license request, correct me if I am wrong.

We are using the same key for Audio and SD and a separate key for HD.

  1. Initial MPD
<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT888.0640258789062S">
  <Period id="0">
    <AdaptationSet id="0" contentType="video" width="1024" height="436" frameRate="24/1" subsegmentAlignment="true" par="40:17">
      <Representation id="0" bandwidth="723722" codecs="avc1.64001f" mimeType="video/mp4" sar="1:1">
        <ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="00000000-0000-0000-0000-000000000000">
				<cenc:pssh>PSSH#111111</cenc:pssh>
				<ms:laurl licenseUrl="https://licenseurl11111111111111" />
		</ContentProtection>
        <BaseURL>sintel_3min_rotate_video.mp4</BaseURL>
        <SegmentBase indexRange="1029-1960" timescale="24">
          <Initialization range="0-1028"/>
        </SegmentBase>
      </Representation>
    </AdaptationSet>
    <AdaptationSet id="1" contentType="audio" lang="en" subsegmentAlignment="true">
      <Representation id="1" bandwidth="193424" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="48000">
        <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
        <ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="00000000-0000-0000-0000-000000000000">
        <cenc:pssh>PSSH#111111</cenc:pssh>
				<ms:laurl licenseUrl="https://licenseurl11111111111111" />
		</ContentProtection>
        <BaseURL>sintel_3min_rotate_audio.mp4</BaseURL>
        <SegmentBase indexRange="897-1996" timescale="48000">
          <Initialization range="0-896"/>
        </SegmentBase>
      </Representation>
    </AdaptationSet>
    <AdaptationSet id="2" contentType="video"  maxHeight="720" maxWidth="1280" frameRate="24/1" subsegmentAlignment="true" par="40:17">
      <Representation id="0" bandwidth="1023722" codecs="avc1.64001f" mimeType="video/mp4" sar="1:1">
        <ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="00000000-0000-0000-0000-000000000000">
        <ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="00000000-0000-0000-0000-000000000000">
				<cenc:pssh>PSSH#2222222</cenc:pssh>
				<ms:laurl licenseUrl="https://licenseurl22222222222222222" />
		</ContentProtection>
        <BaseURL>sintel_3min_rotate_video.mp4</BaseURL>
        <SegmentBase indexRange="1029-1960" timescale="24">
          <Initialization range="0-1028"/>
        </SegmentBase>
      </Representation>
    </AdaptationSet>
  </Period>
</MPD>

  1. When key Rotates
<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT888.0640258789062S">
  <Period id="0">
    <AdaptationSet id="0" contentType="video" width="1024" height="436" frameRate="24/1" subsegmentAlignment="true" par="40:17">
      <Representation id="0" bandwidth="723722" codecs="avc1.64001f" mimeType="video/mp4" sar="1:1">
        <ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="00000000-0000-0000-0000-000000000000">
				<cenc:pssh>PSSH#33333333</cenc:pssh>
				<ms:laurl licenseUrl="https://licenseurl333333333333" />
		</ContentProtection>
        <BaseURL>sintel_3min_rotate_video.mp4</BaseURL>
        <SegmentBase indexRange="1029-1960" timescale="24">
          <Initialization range="0-1028"/>
        </SegmentBase>
      </Representation>
    </AdaptationSet>
    <AdaptationSet id="1" contentType="audio" lang="en" subsegmentAlignment="true">
      <Representation id="1" bandwidth="193424" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="48000">
        <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
        <ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="00000000-0000-0000-0000-000000000000">
        <cenc:pssh>PSSH#33333333</cenc:pssh>
				<ms:laurl licenseUrl="https://licenseurl333333333333" />
		</ContentProtection>
        <BaseURL>sintel_3min_rotate_audio.mp4</BaseURL>
        <SegmentBase indexRange="897-1996" timescale="48000">
          <Initialization range="0-896"/>
        </SegmentBase>
      </Representation>
    </AdaptationSet>
    <AdaptationSet id="2" contentType="video"  maxHeight="720" maxWidth="1280" frameRate="24/1" subsegmentAlignment="true" par="40:17">
      <Representation id="0" bandwidth="1023722" codecs="avc1.64001f" mimeType="video/mp4" sar="1:1">
        <ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="00000000-0000-0000-0000-000000000000">
        <ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="00000000-0000-0000-0000-000000000000">
				<cenc:pssh>PSSH#4444444444</cenc:pssh>
				<ms:laurl licenseUrl="https://licenseurl444444444444444" />
		</ContentProtection>
        <BaseURL>sintel_3min_rotate_video.mp4</BaseURL>
        <SegmentBase indexRange="1029-1960" timescale="24">
          <Initialization range="0-1028"/>
        </SegmentBase>
      </Representation>
    </AdaptationSet>
  </Period>
</MPD>
@AchibanaUkyo

This comment was marked as off-topic.

Copy link

commented Mar 13, 2019

If you are using multiSession=true then your modified code is same with the code before. You don't need add extra check for pssh as it's in the existingSession.schemeDatas already. So first time from SD->HD a new session will be created. while back from HD to SD an existing session will be returned.
For key rotation as it's a new PSSH so a new session will be created.

@rajeshghosh1978

This comment was marked as off-topic.

Copy link

commented Mar 13, 2019

@AchibanaUkyo : That was also my assumption. But that is not the current behavior. I am using multiSession=true, still, it was recreating session each time SD->HD change before my fix.

@AchibanaUkyo

This comment was marked as off-topic.

Copy link

commented Mar 13, 2019

@rajeshghosh1978 I can't see the difference as if the previous session is destroyed how you can return the existingSession? Unless the switch from SD->HD happens along with the key rotation then a new session is created as the PSSH changed.

@rajeshghosh1978

This comment was marked as off-topic.

Copy link

commented Mar 14, 2019

@AchibanaUkyo : Previous session only gets destroyed if DrmSessionManager.releaseSession get called. But with my changes that's not called if I can use existingSession. Here is the usecase

  1. Playback starts with SD, so SD session is added in sessions list.
  2. Playback switch to HD, another new session is stored in the session list.
  3. Playback again switch to SD so we can use existingSession (Doesn't matter SD or HD, as our server returns all keys)
  4. When key rotates, playback switch to new SD, so new session will get added in sessions list.
  5. Playback again switches to HD, which needs to add again as key rotation.
  6. Playback continues without creating any new session until next key rotation.
@AchibanaUkyo

This comment was marked as off-topic.

Copy link

commented Mar 15, 2019

@rajeshghosh1978 I understand the sequence, my point is this should work automatically if you use multiSession=true without your changes. In your code the shouldNotDestroySession is only used for returning existingSession, it won't prevent the call of DrmSessionManager.releaseSession.
Do you have test stream for this multi-key + key rotation?

@rajeshghosh1978

This comment was marked as off-topic.

Copy link

commented Mar 15, 2019

@AchibanaUkyo : With my changes, DrmSessionManager.releaseSession is not even called multiple time. I am working on a very secure environment, so can't share any test link.

@AchibanaUkyo

This comment was marked as off-topic.

Copy link

commented Mar 18, 2019

@rajeshghosh1978 in your sample mpd the SD HD are in different AdaptionSet, I don't think you can switch from them automatically unless you use urn:mpeg💨adaptation-set-switching:2016 in DASH IF IOP Section 3.8. check #4933

@rajeshghosh1978

This comment was marked as off-topic.

Copy link

commented Mar 18, 2019

@AchibanaUkyo : As I mentioned earlier, the provided structure was just an example. I have taken this sample from the first comment of this thread and copy pasted the same block to create multiple adaption sets.

@AchibanaUkyo

This comment was marked as off-topic.

Copy link

commented Mar 20, 2019

@rajeshghosh1978 I would suggest you try the multiSession=false and remove the modification you did for the acquireSession then you will get the same behaviour. I get the same feeling with @ojw28 that your license server will return multi-key(SD and HD). As you shared one key for SD and Audio so you won't see the license request for SD when switching from HD->SD as Audio is still active. Then if you don't request license for HD with you changes you are using the Audio session too for HD.

@ojw28

This comment was marked as off-topic.

Copy link
Contributor Author

commented Mar 20, 2019

To perhaps clarify the discussion here:

  • Key rotation requires multiSession=true, else playback will fail when rotation occurs
  • If the licenser server returns both SD and HD keys when a request is made for either of them, it's best to have multiSession=false because doing so avoids opening a new DRM session.

The use case @rajeshghosh1978 has involves both of the above. His change is a fairly hacky way to set multiSession=true whilst mostly avoiding opening a new DRM session each time there's an adaptive switch between SD and HD. The reason it only "mostly" avoids doing this is because a new DRM session will still be opened for the first switch between SD and HD after each key rotation.

@AchibanaUkyo

This comment was marked as off-topic.

Copy link

commented Mar 20, 2019

The point is that why each time an adaptive switch between SD and HD will open a new DRM session while there is no session released. Even with multiSession=true this shouldn't happen as acquireSesssion will return existingSession if the switch happened once. This may need the SD and HD in same AdaptationSet. If the SD and HD are in different AdaptationSet then switch between SD and HD the session will be released first so each time switch will need request license again, then the change from @rajeshghosh1978 will work but both SD and HD use the Audio's session actually(this can explain why only from SD->HD need license as SD and audio uses same pssh)

@AchibanaUkyo

This comment was marked as off-topic.

Copy link

commented Mar 23, 2019

@ojw28 will the releaseDrmSessionIfUnused be configurable? this option can be used for this case. So adaptive bitrate won't trigger license request again if the session kept. This can be also used for the key rotation with time shift so seek across different keys won't need request again.

@ojw28

This comment has been minimized.

Copy link
Contributor Author

commented Apr 21, 2019

As per my comment above, this is fixed in dev-v2 for API level 23 and above. The issue is tricky to fix in a general way on earlier API levels, and we will likely not be investing time into doing so.

Note there's still a small pause when key rotation occurs. This is tracked by #4133.


Aside: I've marked the extended discussion that occurred in this issue after it was closed as off topic, so that it's easy to understand for those reading it.

@google google locked and limited conversation to collaborators Apr 21, 2019

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
You can’t perform that action at this time.