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: Refactor most DRM-related code (mostly: RxJS-based -> Promise-based) #1042

Merged
merged 7 commits into from
Mar 17, 2022

Conversation

peaBerberian
Copy link
Collaborator

@peaBerberian peaBerberian commented Jan 6, 2022

This is a pretty huge refacto on most EME-related code, whose main goal is to improve its readability and maintainability:

  • Most of that code made heavy usage of RxJS concepts due to its mainly asynchronous nature.
    Promises are now used instead, as they are generally easier to reason about for that kind of code (especially: it is not lazy, with explicit cancellation, it is always ""shared"") and better understood, as the idiomatic JS way to deal with async code.
    Plus, most async EME APIs are already Promise-based anyway.

  • The EMEManager module, which was a function returning an RxJS observable, is now called the ContentDecryptor and is a class.
    It now resides in the src/core/decrypt directory (name may change). It was before in the src/core/eme directory. Its logging namespace also went from EME: to DRM:, for clarity.

    Its external API (to the Init module) also changed, with the goal of being both clear and also more complete when it comes to DRM-handling.
    For example, the disableMediaKeysAttachmentLock keySystems option was previously handled by the Init after the EMEManager communicated it.
    It is now completely handled by the ContentDecryptor, which only externalizes a concept of a "current state" to the Init.

For the moment, this is still a work-in-progress as I would like to better document it in the architecture documentation and also want to continue testing it (I already performed a lot of tests and rewrote its "global tests", which are tests on the whole module in isolation of the rest of the code).

Also, the session_events_listener.ts file remained mostly unchanged for now, as it was readable enough.
We may still want to port it in the future if we want to continue de-RxJSing the RxPlayer code.

As discussed with @alaamoum for another part of the code, files can also still be hard to navigate in the decrypt directory for a newcomer, due to its sheer number and the very flat file architecture. A better solution may also be found on that subject.

@peaBerberian peaBerberian added DRM Relative to DRM (EncryptedMediaExtensions) work-in-progress This Pull Request or issue is not finished yet labels Jan 6, 2022
@peaBerberian peaBerberian force-pushed the misc/eme-no-rxjs branch 2 times, most recently from 9194e45 to 1a07cb1 Compare January 6, 2022 18:00
@peaBerberian peaBerberian added the Refacto This Pull Request changes a lot of RxPlayer's code and/or logic label Jan 6, 2022
@peaBerberian peaBerberian force-pushed the misc/eme-no-rxjs branch 4 times, most recently from f885608 to 27b400a Compare January 10, 2022 16:49
peaBerberian added a commit that referenced this pull request Jan 21, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
peaBerberian added a commit that referenced this pull request Jan 21, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
peaBerberian added a commit that referenced this pull request Jan 21, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
peaBerberian added a commit that referenced this pull request Jan 21, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
peaBerberian added a commit that referenced this pull request Jan 21, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
peaBerberian added a commit that referenced this pull request Jan 24, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
peaBerberian added a commit that referenced this pull request Jan 24, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
peaBerberian added a commit that referenced this pull request Jan 24, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
peaBerberian added a commit that referenced this pull request Jan 28, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
peaBerberian added a commit that referenced this pull request Jan 28, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
@peaBerberian peaBerberian added Priority: 1 (High) This issue or PR has a high priority. and removed work-in-progress This Pull Request or issue is not finished yet labels Feb 14, 2022
@peaBerberian
Copy link
Collaborator Author

Added high priority because it includes the singleLicensePer content fixes (#1056)

peaBerberian added a commit that referenced this pull request Feb 17, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
@peaBerberian peaBerberian changed the base branch from master to next February 17, 2022 13:52
@peaBerberian peaBerberian added the skip-performance-checks Performance tests are not run on this PR label Feb 18, 2022
@peaBerberian peaBerberian added this to the 3.27.0 milestone Mar 16, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
}
const message = err instanceof Error ?
err.message :
"Unknown error made it impossible to close the session";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not really related to this PR, but maybe we can add an enum/object for error messages to make editing them easier ?

@peaBerberian peaBerberian merged commit 3d1aa34 into next Mar 17, 2022
peaBerberian added a commit that referenced this pull request Mar 17, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
peaBerberian added a commit that referenced this pull request Mar 17, 2022
DRM: Refactor most DRM-related code (mostly: RxJS-based -> Promise-based)
@peaBerberian peaBerberian mentioned this pull request Mar 17, 2022
peaBerberian added a commit that referenced this pull request Mar 18, 2022
This update is build on the DRM refacto started in #1042 and fixes some
of (complex, yet relatively minor) performance issues that could be
encountered in a `singleLicensePer: "content"` mode.
It also paves the way for the future `singleLicensePer: "periods"` mode.

In particular, it does the following things:

Optimization on the in-memory MediaKeySession cache when in
`singleLicensePer: "content"` mode
-----------------------------------------------------------

The in-memory cache of MediaKeySessions presented by the
`LoadedSessionsStore` - which basically allows faster loading of
already-loaded content by re-using a recently-created MediaKeySession
before relied only on the initialization data for identification
purposes.

This means that false negatives can occur (in that: cache miss where it
should have been a hit) in the `singleLicensePer: "content"` mode
(which implies that a single license request will actually retrieve all
keys linked to the content), if when re-loading the content a different
initialization data is initially encountered.

This would be a false negative here because even if this is different
initial initialization data than before, the already-created
MediaKeySession still should have received the key to decrypt that other
quality.

This is now fixed by linking a MediaKeySession in that cache both to the
initialization data linked to it and to all key ids that are explicitely
(i.e. in the fetched license) AND implicitely (i.e. in the Manifest file
when in a `singleLicensePer: "content"` mode yet not in the license)
linked to it.

This is done through the declaration of a new structure, the
`KeySessionRecord`, which can be "associated" to supplementary key ids
at any time, and which contains an `isCompatibleWith` method to quickly
check if newly encountered initialization data and/or key id is actually
compatible to an already-created MediaKeySession.

MediaKeySessions loaded (persisted ones) AND retrieved from our memory
cache now are not subject to the `singleLicensePer` option.
----------------------------------------------------------------------

All persisted MediaKeySessions as well as all those kept in our
in-memory cache were before subject to the same `singleLicensePer`
rules than the current content that is being loaded.

This means that technically, a license fetched for a previous content in
a `singleLicensePer: "init-data"` mode (the default), could be wrongly
inferred to be a `singleLicensePer: "content"` one if a new content
compatible with it was loaded in that mode. This would result in the
impossibility to use more than the one key stored with that
MediaKeySession.

This issue is described by the problem 1 of the #1031 issue (note that
problem 2 is not yet fixed, though it is a work-in-progress).

To fix this, the RxPlayer does now two things:
  1. A cached or persisted (still in WIP for that second one)
     MediaKeySession will now be linked in the corresponding cache to
     all known key id that are linked to it either implicit or explicit
     (description of both term higher in this message).

  2. Only MediaKeySessions receiving fetched license will now follow the
     `singleLicensePer` rule, all other type of sessions (loaded or
     retrieved from cache) will only be linked to the key id known
     at the time the caching has been done.

     This could mean relatively rare false negatives when a re-loaded
     content in a `singleLicensePer: "content"` mode contains key ids
     previously unheard of, but it is still the safest solution.

Write skeleton for the future `singleLicensePer: "periods"`
-----------------------------------------------------------

One of the goal of that big implementation was also to pave the way for
more complex `singleLicensePer` modes, as we plan to do for the v3.27.0
(#1028).
The main differences are that the `ContentDecryptor` (the new
`EMEManager`) now also receives Manifest-related structures in the
initialization data emitted to it and perform all the blacklisting by
itself.

This allows it to e.g. know about implicit key ids (keys that should
have been in the license according to the `singleLicensePer` mode but
which aren't) without necessitating a round-trip with the Init module.

Remaining issues
----------------

One unfortunate side-effect of the current implementation, is the
creation of a lock to only allow one initialization data at a time until
either the right MediaKeySession is obtained (either created, loaded, or
retrieved from cache) in `singleLicensePer: "init-data"` mode or until the
license's keys are available in singleLicensePer: "content"` mode.
This could mean unnecessary waiting when a persistent MediaKeySession is
being loaded, and even more when we have troubles loading it - which is
a frequent occurence on some set-top-box.

I'm sure that we could write a less strict lock though, I just didn't
take the time to do it.

Another remaining issue is that I did not finish working on persisted
MediaKeySession here, most notably to fix the problem 2 exposed by
the #1031 issue.
peaBerberian added a commit that referenced this pull request Mar 18, 2022
DRM: Refactor most DRM-related code (mostly: RxJS-based -> Promise-based)
@peaBerberian peaBerberian deleted the misc/eme-no-rxjs branch June 3, 2022 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DRM Relative to DRM (EncryptedMediaExtensions) Priority: 1 (High) This issue or PR has a high priority. Refacto This Pull Request changes a lot of RxPlayer's code and/or logic skip-performance-checks Performance tests are not run on this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants