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

feat: allow reuse of persistent license sessions #4461

Merged

Conversation

valotvince
Copy link
Contributor

@valotvince valotvince commented Aug 31, 2022

Add capability to re-use persistent license sessions across sessions.

DrmEngine will now always:

  • try to start stored persistent sessions before trying to fetch a license, as-to be able to check if all needed keys are already loaded.
  • ask for a new license when the persistent session doesn't have the needed keys for playback,

Given the flag persistentSessionOnlinePlayback is true, DrmEngine:

  • won't remove the persistent session from the device at the end of the playback,
  • won't throw an error when the persistent session isn't found on the device,

For now, it needs Shaka's users to persist session information by themselves (localStorage, IndexDB, ...) before giving it back for the next session. Still, it lays foundation to develop the feature to fully handling it on Shaka's side.

TBD:

  • Rename all offlineSessionIds_ in DrmEngine to storedPersistentSessionIds_ because we now use those in two different contexts, online / offline (and in comments too) (after full review to avoid noise over the feature)
  • Rename loadOfflineSession_ in DrmEngine to loadStoredPersistentSession_ (after full review to avoid noise over the feature)
  • Add a public API to retrieve the session (ids and expiration) from DRM Engine if not already available
  • Refactor / Set up another way to give persistentSessionIds to also give associated initData with it
  • Test full offline to ensure no regressions
  • Document feature usage

Related to #1956

lib/media/drm_engine.js Outdated Show resolved Hide resolved
}

// TODO: We should get a key status change event. Remove once Chrome CDM
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Worked like a charm on Chrome, I might need to test the full offline capabilities

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Downloaded Widevine DRM content worked great with Chrome ;)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens in old TV browsers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't tested yet the persistent licenses on TV but will do

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tizen 6.5

It correctly starts the persistent-license session at first launch but fails on the session load at second launch, so my guess is that this isn't supported.
The sessionId is always the same initialdrmsessionid so I don't think it could work at all.

Shaka Error DRM.FAILED_TO_CREATE_SESSION (Failed to execute 'load' on 'MediaKeySession': Loading session failed. . (2))

webOS 5.0

It fails to request a media key system access when asking for a persistent-license with the error REQUESTED_KEY_SYSTEM_CONFIG_UNAVAILABLE, so it seems it doesn't support persistent license at all


So my overall conclusion is persistent license isn't supported on TV devices, so I don't think we need to go further into making this feature working on those devices.


Error Recoverability

It pointed me out on an issue with my code where fails to load a persistent license will stop the player, rather than trying to fetch a new one (intended behaviour).

I have two options for that, let me know what you prefer (my pref is option 1):

  • Option 1: Send a RECOVERABLE error
this.onError_(new shaka.util.Error(
    this.config_.persistentSessionOnlinePlayback ?
      shaka.util.Error.Severity.RECOVERABLE : shaka.util.Error.Severity.CRITICAL;
    shaka.util.Error.Category.DRM,
    shaka.util.Error.Code.FAILED_TO_CREATE_SESSION,
    error.message));
  • Option 2: Bypass the error, only log something:
if (this.config_.persistentSessionOnlinePlayback) {
  shaka.log.warning('Persistent session', sessionId, 'not available');
} else {
  this.onError_(new shaka.util.Error(
      shaka.util.Error.Severity.CRITICAL,
      shaka.util.Error.Category.DRM,
      shaka.util.Error.Code.OFFLINE_SESSION_REMOVED));

  return Promise.resolve();
}

test/media/drm_engine_unit.js Outdated Show resolved Hide resolved
@avelad avelad added type: enhancement New feature or request component: EME The issue involves the Encrypted Media Extensions web API labels Aug 31, 2022
@avelad avelad requested review from joeyparrish, theodab and avelad and removed request for joeyparrish August 31, 2022 16:53
@github-actions
Copy link
Contributor

github-actions bot commented Sep 1, 2022

Incremental code coverage: 90.85%

@valotvince valotvince marked this pull request as ready for review September 1, 2022 09:54
@avelad
Copy link
Collaborator

avelad commented Sep 1, 2022

Related to #1956

@@ -617,23 +636,40 @@ shaka.media.DrmEngine = class {
*
* @return {!Promise}
*/
createOrLoad() {
// Create temp sessions.
async createOrLoad() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that the attach(video) function calls createOrLoad() but does not wait for the returned promise to resolve. Should it wait?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was done on purpose so that creating / loading sessions could be done in parallel of fetching manifest information, segments and more.

Copy link
Collaborator

@avelad avelad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, LGTM, but I think we should give the possibility (player configuration) that ShakaPlayer itself stores this information in localStorage for example.

externs/shaka/manifest.js Outdated Show resolved Hide resolved
@avelad avelad added the priority: P2 Smaller impact or easy workaround label Sep 26, 2022
@avelad
Copy link
Collaborator

avelad commented Oct 9, 2022

@valotvince it seems that there are some conflict with the main branch, can you rebase? Thanks!

@valotvince
Copy link
Contributor Author

valotvince commented Nov 14, 2022

👋 @avelad @joeyparrish I've rebased on main & added documentation about the feature, could you review it again ? Thanks :)

I think we should give the possibility (player configuration) that ShakaPlayer itself stores this information in localStorage for example.

If that's possible I'd like it to be done in another PR, as localStorage (or Indexeddb) is not necessarily the best on some devices. It might take more reflexion on the whole storage capabilities of Shaka, like allowing to give a StorageManager like the ABRManager which would handle the storage, without Shaka's knowing what the storage is.

@valotvince
Copy link
Contributor Author

Hi @avelad @joeyparrish, any comments to share on this PR ? Thanks !

docs/tutorials/drm-config.md Outdated Show resolved Hide resolved
externs/shaka/player.js Outdated Show resolved Hide resolved
* }}
*
* @description
* DRM Session Metadata for as session
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSDoc for this is the same as DrmSessionMetadata, but the two represent different types of data.

From what I gather, PersistentSessionMetadata is the type that the user passes in, representing an existing persistent session. Meanwhile, DrmSessionMetadata represents an active session, which may have come from this playback or have been loaded from a persistent session.

Correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct ! :)

this.onError_(new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
severity,
shaka.util.Error.Category.DRM,
shaka.util.Error.Code.OFFLINE_SESSION_REMOVED));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSDoc for OFFLINE_SESSION_REMOVED should probably be updated. Right now it specifically says "The content is not playable", which is no longer going to be true if this error will sometimes be recoverable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change the description of the error to:

A required offline session was removed. The content might not be playable depending of the playback context.

metadata.loaded = true;
if (this.areAllSessionsLoaded_()) {
this.allSessionsLoaded_.resolve();
metadata.loaded = true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to set metadata.loaded to true here?
All that value does is determine when areAllSessionsLoaded_ returns true. So if we are manually setting this to true for each stream we call loadOfflineSession_ on, and we call createOrLoad on each session at once inside createOrLoad, doesn't that mean that we will just immediately resolve this.allSessionsLoaded_ when the last session begins to load? That seems like it defeats the purpose of waiting for the key status change event, if we're still resolving before the event fires.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only set metadata.loaded when the session is not present anymore (we will never receive a keystatuschange event), and send a OFFLINE_SESSION_REMOVED event.

If other sessions are present & correctly loaded, we wait for the keystatuschange to set the session as loaded.

expect(session2.generateRequest).not.toHaveBeenCalled();
});

it(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would fit into one line. No need to do the weird indenting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole line

it('tries persistent session ids before requesting a license', async () => {

is 81 characters long 😞

@valotvince valotvince requested review from theodab and removed request for joeyparrish January 23, 2023 10:00
Copy link
Collaborator

@avelad avelad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, can you rebase and resolve the conflicts? Thanks!

@valotvince valotvince force-pushed the drm-online-persistent-license branch from f0e9fec to 5f8bf6f Compare April 13, 2023 08:53
@valotvince
Copy link
Contributor Author

@avelad 👋 Rebase done !

});
```

NB: Shaka doesn't provide a out-of-the-box storage mechanism for the sessions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is such a mechanism in the offline/ codebase. We could discuss a follow-up PR to use that mechanism if/when offline support is included.

@joeyparrish
Copy link
Member

Failed tests look like unrelated flakiness, but I'm re-running them just to be sure.

@joeyparrish joeyparrish merged commit cc97da1 into shaka-project:main Apr 19, 2023
29 checks passed
@valotvince valotvince deleted the drm-online-persistent-license branch June 23, 2023 14:04
@github-actions github-actions bot added the status: archived Archived and locked; will not be updated label Jul 25, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
component: EME The issue involves the Encrypted Media Extensions web API priority: P2 Smaller impact or easy workaround status: archived Archived and locked; will not be updated type: enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants