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

Allow playback with LICENSE_REQUEST_FAILED #3051

Closed
sarge opened this issue Dec 15, 2020 · 7 comments · Fixed by #6457
Closed

Allow playback with LICENSE_REQUEST_FAILED #3051

sarge opened this issue Dec 15, 2020 · 7 comments · Fixed by #6457
Assignees
Labels
priority: P1 Big impact or workaround impractical; resolve before feature release status: archived Archived and locked; will not be updated type: bug Something isn't working correctly
Milestone

Comments

@sarge
Copy link

sarge commented Dec 15, 2020

Have you read the Tutorials?
Yes

Have you read the FAQ and checked for duplicate open issues?
Yes

What version of Shaka Player are you using?
v3.0.5

Please ask your question

How should the PlayReady license server allow playback of SD bitrates only?

I have packaged content with widevine and playready headers using shakapacker using a different content key per bitrate. I can successfully playback content using both key systems.

Widevine is packaged using multiple KIDS in the PSSH across all bitrates
Playready is packaged with a single KID in the PSSH
Very similar to
https://shaka-player-demo.appspot.com/demo/#audiolang=en-US;textlang=en-US;uilang=en-US;asset=https://media.axprod.net/TestVectors/v7-MultiDRM-MultiKey/Manifest.mpd;panel=ALL_CONTENT;panelData=drm:PLAYREADY;build=uncompiled

All content plays across all bitrates.

Widevine

  • I can successfully return a subset to keys and playback will be limited to just those tracks.

Playready

  • due to the single KID per PSSH - multiple requests are made (this is expected, browsers do not appear to support 4.2.0.0, with multiple KIDs)
  • rejecting any request 403 will prevent any content from starting DRM.LICENSE_REQUEST_FAILED
  • returning a valid but unplayable license response (via expired date) prevents content from starting - no errors
  • returning a valid but incorrect ContentKey (content plays but does not decrypt correctly for those bitrates)

How are others solving this?

Cheers

@sarge sarge added the type: question A question from the community label Dec 15, 2020
@joeyparrish
Copy link
Member

Widevine

  • I can successfully return a subset to keys and playback will be limited to just those tracks.

This is exactly what we recommend at Widevine. I am not familiar with the details of PlayReady licenses or policies, and I had assumed something similar was possible.

Playready

  • due to the single KID per PSSH - multiple requests are made (this is expected, browsers do not appear to support 4.2.0.0, with multiple KIDs)
  • rejecting any request 403 will prevent any content from starting DRM.LICENSE_REQUEST_FAILED

This error shouldn't stop playback in and of itself. It gets dispatched from DrmEngine through Player and to the application, but it doesn't stop streaming, for example. If other keys are available through other requests that succeeded, the application should be able to ignore it.

You can look more deeply at such an error to match LICENSE_REQUEST_FAILED specifically due to a 403, if that's helpful. The contents of the data array of shaka.util.Error differs from one error code to another, but LICENSE_REQUEST_FAILED has the underlying error object for the failure in data[0]. Then if that's a BAD_HTTP_STATUS error, you can get the HTTP status code from its data array. The details of data are all documented with the individual error codes.

function isLicenseRequest403(error) {
  return error.code == shaka.util.Error.Code.LICENSE_REQUEST_FAILED &&
      error.data[0].code == shaka.util.Error.Code.BAD_HTTP_STATUS &&
      error.data[0].data[1] == 403;
}
  • returning a valid but unplayable license response (via expired date) prevents content from starting - no errors

I'm not sure if ABR will react correctly to having all the keys, but having some of them be expired. (If not, we should probably file it as a separate bug and fix it. But ideally you wouldn't have to rely on this technique anyway.)

  • returning a valid but incorrect ContentKey (content plays but does not decrypt correctly for those bitrates)

You should definitely not do this, in my opinion.

One detail you should note is that before we get keys, we will assume that at least the lowest res keys will be available to us later, and we will start streaming on that assumption. If the subset you return excludes these particular keys, playback could break.

@sarge
Copy link
Author

sarge commented Dec 16, 2020

Thanks @joeyparrish

So a couple of observations, and if you have time could validate my plan.

When the DRM request fails - https://github.com/google/shaka-player/blob/master/lib/media/drm_engine.js#L1252
onError_ is called we reject all active sessions - https://github.com/google/shaka-player/blob/master/lib/media/drm_engine.js#L80
Which I think is really about assisting with cleaning up, perhaps not what we want to do in this case, where we expect a subset of license requests to fail.

A second state issue is the session.loaded bool. By moving through the error case, the session.loaded | metadata.loaded does not get marked as true and hence areAllSessionsLoaded_ never returns true so we don't tell the player we are ready. A possible solution here is to remove the failed session attempt by making a call to this.activeSessions_.delete(session);. This seems to satisfy the areAllSessionsLoaded call (https://github.com/google/shaka-player/blob/master/lib/media/drm_engine.js#L1787)

Roughly implementing both of the above changes still does not allow playback. I note that seeking will still attempt to make license requests for the failed license requests. So I am wondering if I need to remove 'drmInfo' from various bitrates or even just remove the bitrates altogether as we do not want bitrates that we cannot play in any bitrate selectors.

Thoughts?

@joeyparrish
Copy link
Member

Oh, I see. I overlooked allSessionsLoaded_.reject(err) in DrmEngine.

Which I think is really about assisting with cleaning up, perhaps not what we want to do in this case, where we expect a subset of license requests to fail.

Looking more closely, there appear to be two purposes to allSessionsLoaded_:

  1. to gate key status updates that might otherwise be premature and cause us to remove the wrong tracks in Player
  2. returned from createOrLoad(), where the Promise is only directly used in offline Storage (a failure of the Promise would result in a failure to store the content)

I think you're on the right track, though, by deleting the failed session.

When we dispatch key status updates, the Player should look to see which keys we have and mark the other streams as unplayable. You shouldn't have to do anything else after that, so long as the key status update fires after the fate of each license is settled. It's possible that in your rough edit, you removed the failed sessions, but didn't check afterward if the key status should be dispatched. Something like this, which duplicates the end of the key status event handler:

  delete(session);
  if (areAllSessionsLoaded_()) {
    this.allSessionsLoaded_.resolve();
    this.keyStatusTimer_.tickAfter( ... );
  }

That way, if the successful sessions completed earlier, and you just removed the last failed session, the key statuses are sent to the Player to trigger a final filtering based on key IDs. If you don't do this, you would only see filtering work if the last license request to finish was a successful one.

There is one more edge case, though. If we had no sessions to begin with, we should consider "all sessions loaded" to be true. This would be needed for storing clear content offline. But if all sessions failed (even if we only had one to start with), we should consider "all sessions loaded" to be false. So maybe something like this on failure:

  if (this.activeSessions_.size == 1) {
    this.allSessionsLoaded_.reject(err);
  } else {
    this.activeSessions_.delete(session);
    if (areAllSessionsLoaded_()) {
      this.allSessionsLoaded_.resolve();
      this.keyStatusTimer_.tickAfter( ... );
    }
  }

I'm still a little nervous that maybe this will cause us to assume any failure is okay so long as something else worked, which would not always be true. But I think we could probably come up with a solution to that, too.

Does this help?

@sarge
Copy link
Author

sarge commented Dec 17, 2020

Thanks @joeyparrish

Here is what I have done so far, #3052

@sarge
Copy link
Author

sarge commented Dec 18, 2020

Hi @joeyparrish I am pretty sure that because of how the variants work, I have focused my attention on cleaning up the media sessions. I have attempted to close, remove and the mediakey session.

Anticipating a need to reproduce this without a license server I have pushed a simple way to inject failure. #3052

I would grateful for a nudge in a new / better direction.

@TheModMaker TheModMaker added flag: seeking PR We are actively seeking PRs for this; we do not currently expect the core team will resolve this type: enhancement New feature or request and removed type: question A question from the community labels Mar 22, 2021
@TheModMaker TheModMaker added this to the Backlog milestone Mar 22, 2021
@TheModMaker TheModMaker changed the title How should license servers respond to allow only some bitrates to play? Allow playback with LICENSE_REQUEST_FAILED Mar 22, 2021
@KarlGallagher
Copy link

@TheModMaker @joeyparrish just commenting to say that we see the same/similar issues with our solution for PlayReady.

Can I also comment on the priority of this task?

Given that it seems very difficult to deploy content with seperate 4K adaptation sets since license agreements mean we must omit keys /reject license requests from clients not supporting the defined robustness requirments (SL3000).

This issue means we have no fallback/compatibility recourse when using ShakaPlayer e.g. restrict playback to SD (or SD/HD) only since even when we manually configure bitrate and/or max height settings the content wont play back on SL2000 based clients since the drm session is stuck waiting on the omitted key

Hopefully, you can reconsider the level of attention this issue is receiving...

Thanks.

@joeyparrish
Copy link
Member

I'm setting this to P1 (top non-emergency priority).

@joeyparrish joeyparrish added priority: P1 Big impact or workaround impractical; resolve before feature release type: bug Something isn't working correctly and removed type: enhancement New feature or request labels Oct 4, 2021
@joeyparrish joeyparrish modified the milestones: Backlog, v3.3 Oct 4, 2021
@avelad avelad modified the milestones: v3.3, v4.1 May 4, 2022
@avelad avelad modified the milestones: v4.1, v4.2 Jun 3, 2022
@avelad avelad modified the milestones: v4.2, v4.3 Aug 17, 2022
@avelad avelad modified the milestones: v4.3, v4.4 Nov 11, 2022
@avelad avelad modified the milestones: v4.4, v4.5 Aug 31, 2023
@avelad avelad removed this from the v4.5 milestone Oct 5, 2023
@avelad avelad added this to the v4.6 milestone Oct 5, 2023
@avelad avelad modified the milestones: v4.6, v5.0 Nov 16, 2023
@avelad avelad modified the milestones: v4.7, v5.0 Dec 4, 2023
@avelad avelad self-assigned this Apr 19, 2024
@avelad avelad removed the flag: seeking PR We are actively seeking PRs for this; we do not currently expect the core team will resolve this label Apr 19, 2024
@shaka-bot shaka-bot added the status: archived Archived and locked; will not be updated label Jun 22, 2024
@shaka-project shaka-project locked as resolved and limited conversation to collaborators Jun 22, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
priority: P1 Big impact or workaround impractical; resolve before feature release status: archived Archived and locked; will not be updated type: bug Something isn't working correctly
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants