-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Proposal: improved protected content workflow #1367
Comments
@sandersaares Wow, fantastic job on capturing this info! Thank you so much for taking this on. |
@sandersaares - firstly - great article that is clearly presented. I'd definitely encourage a discussion of this approach and the changes it would require to the current code base with other developers who have contributed to, or have an interest in, the content protection performance of dash.js. The bulk of the original development was done by @greg80303 who has moved on to another position within CableLabs but still monitors these lists. Also @bwidtmann from Maxdome and @colde from XStream have worked on protection in the past and Michael Zinn on Slack channel is interested now. Are you able to make the face-to-face meeting in Munich in May? That would be the best time to discuss this. If not, it can be done via email and issue threads, or perhaps we can arrange some special calls as talking is easier than typing. I have a question upfront:
Cheers |
I fear I will not be present at the F2F meeting and will have to try to take part in the discussion by electronic channels instead. Will there be a call-in possibility to take part in F2F discussions in a limited capacity?
I cannot immediately think of any better behavior to suggest nor can I think of any significant issues this behavior might cause. Sounds like it would be sufficient, yes.
I would expect dash.js to determine the set of available CDMs, yes. In fact, based on the log entries I see in the console when dash.js is starting up, I believe it already does so and reports what it finds in the log. The However, perhaps a developer with more familiarity can correct me on this, as I am not overly familiar with the actual codebase. |
@sandersaares - Nice writeup, you cover a lot of area well, and it is clear that there is a lot of though behind your suggestion. It is rare that i see things presented this well for projects. That being said, i think what you are proposing is slightly too limiting of the DRM support in dash.js. Right now it handles quite a few cases of different types of content with different packages and does so pretty well. What you are suggesting would certainly result in a lot of content no longer being supported for encrypted playback. I will try and go through the most problematic ones from my point of view here. I am not saying it is not the right way to go, it might very well be, but it will remove some of dash.js's status as the swiss army knife of MPEG-DASH playback.
In general, it seems you have been looking a lot at PlayReady as the primary DRM system. I think you should probably validate your assumptions with at least Widevine as well, as some things (e.g. key rotation) is implemented wildly different between those two systems. I do also sort of disagree with the idea that being able to play all DASH-IF IOP compatible protected content is unfeasible. In fact, i think that is exactly what our target should be. |
Thanks for the criticism, @colde! Can you outline in more detail the potential difficulties you see with regard to different DRM systems? For example, the case of Widevine key rotation that you mentioned - what is the issue you foresee there? |
Some thoughts from my developer perspective:
|
@wilaw,
|
@pjdicke - that scenario was described by @bwidtmann and not myself. He is closer to the DRM issue than I am and may have an answer for you. |
@pjdicke what do you mean with "getting initData for Playready"? In the concept of MPEG Common Encryption it is totally valid because at content creation time you do not know which DRM is implemented by the client (browser). It is up to the client (dash.js) to determine the correct DRM at run time. This is done by try-and-error. |
@bwidtmann, I'm trying to isolate Widevine for testing but it looks like it's choosing Playready while I'm testing in Chrome. So I guess I don't have a way to do that?? |
@pjdicke can you please open a separate issue following our guidelines (public available link to your manifest, browser log, ...). Then I can help you better with your concrete problem. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
This issue has been automatically closed because no further activity occurred. If you think this issue is still relevant please reopen it. Thank you for your contributions. |
Some recent discussion in Slack and GitHub (#1346) hinted to me that the logic initializing playback of protected content in dash.js could perhaps use a review. I investigated the situation and I propose here a design for initializing playback of protected content in a way that overcomes some difficulties currently present, defines behavior for some currently undefined/unsupported/unexplored scenarios and overall leads us towards a more interoperable state that is better aligned with DASH-IF IOP.
The approach described here integrates elements not only from DASH-IF IOP but also several topics that are still evolving (and which may influence future versions of DASH-IF IOP to narrow the gap). The focus here is on defining for dash.js a very well defined mechanism for initiating playback of protected content in a highly interoperable way that is capable of functioning in different business scenarios and integrating with different DRMs.
To accomplish this, various constraints on the DASH presentations are placed and relevant dash.js behavior is defined. All statements below describe the proposed state, not the current state, even if they are written in present tense.
Basic concepts
This section defines the "goal state" that the proposed scheme aims to satisfy and the concepts to be implemented.
Let's start by stating the obvious. Dash.js is capable of playing back encrypted content. To do this, it provides content keys to a suitable CDM that is connected to a MediaSource, to which dash.js provides media segments. Multiple CDMs may be made available to dash.js by the user agent, in which case it is up to dash.js to select which one to use.
There may be one or more content keys required at the same time to play back protected content (different adaptation sets) and the set of content keys may change between (but not within) periods. The presence or absence of encryption may also change between (but not within) periods.
The presence of encryption in an adaptation is signaled by the presence of a
ContentProtection
element whoseschemeIdUri
andvalue
attributes match the values in the sample above (IOP 7.7.2). The required content key is identified by thedefault_KID
attribute.Media segments are opaque blobs to dash.js and neither segments nor any data extracted from them by the user agent are processed by dash.js (at least in scope of protected content workflows - this proposal makes no comment on media sample playback workflows).
The encryption of media data within media segments is not necessarily performed using content keys - the only relevant constraint here is that a content key is associated with the media segments (via manifest reference) and providing this content key to the CDM must be sufficient to decrypt the samples in the media segment. On a lower level content keys may instead be combined with custom data embedded within the media segments, deriving hierarchical media keys which in turn are used to encrypt samples and thus enabling scalable re-authorization scenarios (see IOP 7.5). All of this is opaque to dash.js which only deals with the content keys that are provided to CDMs.
All CDMs require initialization data for each content key in order to become capable of decrypting content using that content key. This initialization data is passed to the CDM via the
generateRequest()
method defined by EME.DASH presentations may provide default CDM initialization data in
ContentProtection/cenc:pssh
elements, as illustrated above (IOP 7.7.2). Alternatively, client code may provide the initialization data to dash.js. The initialization data in the manifest is ignored if it is provided by client code. Initialization data is NOT loaded from DASH segments in any circumstances.EME defines the
encrypted
event that notifies dash.js when DRM system specific data is detected in the media segments. This data is treated as an opaque blob and passed directly to the active CDM (viaupdate()
) without further processing by dash.js. Use of this data for CDM initialization is forbidden, although CDMs may use it for other purposes (notably, this is the mechanism that passes media key derivation input data to the CDM, in situations where derived keys are used).Content keys are provided to CDMs within licenses that are opaque to dash.js. Dash.js mediates the flow of license requests from the CDM to a license server and back, potentially enhancing the flow with supplementary information about application/business layer logic (e.g. the identity of the logged in user), in the form of HTTP request headers or similar metadata.
CDMs may supply dash.js with a default URL of the license server (either deliberately or by including it in the license request data itself). Alternatively, client code may provide this URL, or override the one provided by the CDM.
Client code is the ultimate authority on the behavior of dash.js and may override any defaults provided by the DASH manifest or the CDM.
Client code may express an order of preference of CDMs.
Implementation
This section defines the mechanisms to implement in order to reach the goal state and to make the described concepts a reality. Not all aspects of the implementation are covered here, as many are likely to be trivial.
Detecting encrypted content
Dash.js shall use the presence of a
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc" ... />
element in the manifest to determine whether an adaptation set is encrypted or not.The EME
encrypted
event shall not be used for the purpose of detecting encryption and any data supplied byencrypted
is simply passed to the active CDM'supdate()
method without inspection.Selecting the CDM
When encrypted content is detected, dash.js shall use the algorithm described here to select the CDM to be used decrypt it. A single CDM shall be used to decrypt all data from the same DASH presentation - this algorithm is only executed once per DASH presentation.
The CDM selection algorithm shall only be run against a single content key even in the presence of multiple content keys signaled by the manifest. It is assumed that the result of the algorithm does not depend on which content key is selected, so dash.js may pick any one of them.
The algorithm description follows.
All CDMs provided by the user agent shall initially be considered valid candidates.
Next, dash.js shall exclude CDMs for which neither the DASH manifest nor client code (via
protData
) has provided initialization data.Next, any CDMs explicitly disabled by client code shall be excluded. This is signalled via the
protData
structure by setting the "enabled" property to false, as shown above.Then the remaining CDMs are sorted according to an order of preference defined by client code. This order is signaled via the
protData
structure by assining a numeric identifier to thepriority
property, as shown above. The default value is 0 and higher values indicate greater preference.Finally, the CDM with the highest
priority
value shall be selected from among the remaining candidates.CDM initialization
Upon encountering a content key for the first time, dash.js shall initialize the selected CDM to decrypt that content key, by creating an EME key session and supplying the initialization data to the
generateRequest()
method. This may cause a license request to be triggered by the CDM if it requires the content key to be retrieved from an external source.The initialization data shall be obtained from the DASH manifest unless overridden by client code via
protData
, in a CDM-specific map of key ID to initialization data. See above for an example.Initialization data generation by dash.js
There are CDMs for which initialization data is trivial to generate from scratch (e.g. clear key). Dash.js shall not generate initialization data unless explicitly configured by client code to do so (via some appropriate entry in
protData
). If dash.js is configured to generate initialization data, it shall be considered as having initialization data by the CDM selection algorithm.This matches current behavior and is perhaps stating the obvious but deserves emphasis anyway because it is important to ensure that the CDM selection algorithm is predictable and to differentiate between the following scenarios:
This is perhaps most relevant with the clear key scenario, though not exclusively so (in many real-world cases, initialization data serves to simply pass the
default_KID
to the CDM, so initialization data generation might be a likely future enhancement to keep in mind).EME key session handling
There is a 1:1 relationship between each unique content key (differentiated by key ID supplied in
default_KID
) and an EME key session. Key sessions are created when the first adaptation set using a particular content key is detected and destroyed when no active adaptation sets use that key anymore.Implications on content and capabilities
From the above, we can draw some potentially relevant conclusions and see how content is affected and what capabilities are created:
Sample scenarios
This section provides some brief examples of dash.js usage in various common scenarios and describes how this model of protected content processing applies.
Sample 1: Single-DRM single-key presentation using PlayReady
This is a very basic scenario where a single content key protects all the data in a video and that we wish to play back using PlayReady. First, let us observe the case where all the data is provided using the default values specified in the DASH manifest.
In the manifest, we require two ContenProtection elements in this scenario - one to signal the
default_KID
and another to provide the initialization data for the PlayReady CDM.In this scenario, client code provides only the presentation URL to dash.js.
When playback is started, the following notable events occur:
ContentProtection
element and concludes that the presentation includes encrypted content.Naturally, the CDM selection step (and playback) will fail if the user agent does not actually provide the PlayReady CDM.
Sample 2: Single-DRM single-key presentation using clear key and configuring CDM via client code
In this scenario, the DASH presentation is encrypted but contains no information to configure a CDM. Instead, client code provides all such data.
In the manifest, we require only a single ContenProtection element in this scenario, in order to signal the
default_KID
and mark the content as encrypted.In this scenario, client code supplies the CDM initialization data:
When playback is started, the following notable events occur:
ContentProtection
element and concludes that the presentation includes encrypted content.Note: the method of providing the content key itself to the clear key CDM is intentionally not described here and considered out of scope of this proposal.
Sample 3: Multi-drm presentation with CDM preference
Here we have a DASH presentation that supports both PlayReady and Widevine and provides the initialization data in the manifest. Client code simply expresses a preference for which CDM to use if both are available.
Three
ContentProtection
elements are present in the manifest, one fordefault_KID
and two for providing initialization data for the different CDMs.Client code simply expresses a preference for one above the other:
When playback is started, the following notable events occur:
ContentProtection
elements and concludes that the presentation includes encrypted content.Sample 4: Overriding the manifest
Here we have a manifest that defines CDM initialization data that client code wants to ignore.
In the manifest, we have
ContentProtection
elements for thedefault_KID
and for two CDMs.Client code, however, wants to ensure that Widevine is never used and to provide custom initialization data for PlayReady.
When playback is started, the following notable events occur:
ContentProtection
elements and concludes that the presentation includes encrypted content.Compatibility notes
All DASH presentations that this approach can play are compatible with DASH-IF IOP but not all presentations compatible with DASH-IF IOP can be played by this approach. This is intentional. The latter is not really a feasible target in any case, as there remains quite a lot of flexibility even in DASH-IF IOP.
Future work
It might be desirable for a DASH presentation to supply default license server URLs (per-CDM) in a form easily usable by dash.js, via the presentation manifest. At present, this data is present in the initData of some DRM systems but this requires parsing and is not very interoperable. I seem to recall that some standardization work on this topic has been discussed but I cannot find any specific references.
Footnote
My knowledge of dash.js internals is minimal and I mainly approach it from a high-level architectural perspective, assuming nothing about the existing implementation. Please excuse my use of any terminology that differs from existing dash.js practices.
Many thanks to @kilroyh for helping me understand the background and logic behind many of these topics!
The text was updated successfully, but these errors were encountered: