Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved. W3C liability, trademark and document use rules apply.
This proposal extends HTMLMediaElement providing APIs to control playback of protected content.
The API supports use cases ranging from simple clear key decryption to high value video (given an appropriate user agent implementation). License/key exchange is controlled by the application, facilitating the development of robust playback applications supporting a range of content decryption and protection technologies.
This specification does not define a content protection or Digital Rights Management system. Rather, it defines a common API that may be used to discover, select and interact with such systems as well as with simpler content encryption systems. Implementation of Digital Rights Management is not required for compliance with this specification: only the simple clear key system is required to be implemented as a common baseline.
The common API supports a simple set of content encryption capabilities, leaving application functions such as authentication and authorization to page authors. This is achieved by requiring content protection system-specific messaging to be mediated by the page rather than assuming out-of-band communication between the encryption system and a license or other server.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.
Implementers should be aware that this specification is not stable. Implementers who are not taking part in the discussions are likely to find the specification changing out from under them in incompatible ways. Vendors interested in implementing this specification before it eventually reaches the Candidate Recommendation stage should join the mailing list mentioned below and take part in the discussions.
This document was published by the HTML working group as a Working Draft. Please submit comments regarding this document by using the W3C's (public bug database) with the product set to HTML WG and the component set to Encrypted Media Extensions. If you cannot access the bug database, submit comments to<EMAIL_ADDRESS>(subscribe, archives) and arrangements will be made to transpose the comments to the bug database. All feedback is welcome.
Publication as a Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
Note: It is an open issue whether and how the specification should do more to encourage/ensure CDM-level interoperability. See Bug 20944.
Note: This specification contains sections for describing security and privacy considerations. These sections are not final and review is welcome.
This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
This section is non-normative.
This proposal allows JavaScript to select content protection mechanisms, control license/key exchange, and implement custom license management algorithms. It supports a wide range of use cases without requiring client-side modifications in each user agent for each use case. This also enables content providers to develop a single application solution for all devices. A generic stack implemented using the proposed APIs is shown below. This diagram shows an example flow: other combinations of API calls and events are possible.
Text in this font and color is non-normative.
The Content Decryption Module (CDM) is a generic term for the client component that provides the functionality, including decryption, for one or more Key Systems.
Implementations may or may not separate the implementations of CDMs or treat them as separate from the user agent. This is transparent to the API and application. A user agent may support one or more CDMs.
A Key System is a generic term for a decryption mechanism and/or content protection provider. Key System strings provide unique identification of a Key System. They are used by the user agent to select the Content Decryption Modules and identify the source of a key-related event. Simple Decryption Key Systems are supported by all user agents. User agents may also provide additional CDMs with corresponding Key System strings.
A Key System string is always a reverse domain name. For example, "com.example.somesystem". Key System strings are compared using case-sensitive matching. It is recommended that CDMs use simple lower-case ASCII key system strings.
Within a given system ("somesystem" in the example), subsystems may be defined as determined by the key system provider. For example, "com.example.somesystem.1" and "com.example.somesystem.1_5". Key System providers should keep in mind that these will be used for comparison and discovery, so they should be easy to compare and the structure should remain reasonably simple.
A Key Session, or simply Session, provides a context for message exchange with the CDM as a result of which key(s) are made available to be CDM.
Sessions are embodied as MediaKeySession
objects.
Each Key session is associated with a single instance of Initialization Data provided in the createSession()
call.
Each Key Session is associated with a single MediaKeys
object, and only media elements associated with that object may access key(s) associated with the session.
Other MediaKeys
objects, CDM instances, and media elements may not access the key session or use its key(s).
Key sessions and the keys they contain are no longer usable by the CDM for decryption when the session is closed, including when the MediaKeySession
object is destroyed.
A Session ID is a unique string identifier generated by the user agent or CDM that can be used by the application to identify MediaKeySession
objects.
(The underlying content protection client or server do not necessarily need to support Session IDs.)
A new Session ID is generated each time the user agent and CDM successfully create a new session.
Each Session ID shall be unique within the browsing context in which it was created. (Note: Some use cases may require that Session IDs be unique within the origin over time, including across browsing sessions.)
Unless otherwise stated, key refers to a decryption key that can be used to decrypt blocks within media data.
Each such key is uniquely identified by a key ID.
A key is associated with the session used to provide it to the CDM. (The same key may be present in multiple sessions.)
Such keys may only be provided to the CDM via an update()
call. (They may later be loaded by loadSession()
as part of the stored session data.)
A key is considered usable if the CDM is certain the key is currently usable for decryption. For example, a key is not usable if its license has expired.
A key is associated with a key ID, which uniquely identifies a key. The container specifies the ID of the key that can decrypt a block or set of blocks within the media data. Initialization Data may contain key ID(s) to identify the keys that are needed to decrypt the media data. However, there is no requirement that Initialization Data contain any or all key IDs used in the media data or media resource. Licenses provided to the CDM associate each key with a key ID so the CDM can select the appropriate key when decrypting an encrypted block of media data.
A license is key system-specific state information that includes one or more key(s) - each associated with a key ID - and potentially other information about key usage.
Key Systems usually require a block of initialization data containing information about the stream to be decrypted before they can construct a license request message. This block could be a simple key or content ID or a more complex structure containing such information. It should always allow unique identification of the key(s) needed to decrypt the content. This initialization information may be obtained in some application-specific way or provided with the media data.
Initialization Data is a generic term for container-specific data that is used by CDMs to generate a license request.
Initialization data found with the media data is provided to the application in the initData
attribute of the encrypted
event.
The format of the initialization data depends upon the type of container, and containers may support more than one format of initialization data. The initialization data type is a string that indicates what format the initialization data is provided in. Initialization data type strings are always matched case-sensitively. It is recommended that initialization data type strings are lower-case ASCII strings.
The Encrypted Media Extensions Stream Format and Initialization Data Format Registry provides the mapping from initialization data type string to the specification for each format.
During playback, embedded media data is exposed to script in the embedding origin. In order for the API to fire encrypted
and message
events, media data must be CORS-same-origin with the embedding page.
If media data is cross-origin with the embedding document, authors should use the crossorigin attribute
on the media element and CORS headers on the media data response to make it CORS-same-origin.
We extend media element to allow decryption key acquisition to be handled by the application.
enum MediaWaitingFor { "none", "data", "key" }; partial interface HTMLMediaElement { // Encrypted Media readonly attribute MediaKeys? mediaKeys; Promise<void> setMediaKeys(MediaKeys? mediaKeys); attribute EventHandler onencrypted; readonly attribute MediaWaitingFor waitingFor; }; enum IsTypeSupportedResult { "" /* empty string */, "maybe", "probably" }; enum SessionType { "temporary", "persistent" }; interface MediaKeys { readonly attribute DOMString keySystem; Promise<MediaKeySession> createSession(DOMString initDataType, (ArrayBuffer or ArrayBufferView) initData, optional SessionType sessionType = "temporary"); Promise<MediaKeySession> loadSession(DOMString sessionId); Promise<void> setServerCertificate((ArrayBuffer or ArrayBufferView) serverCertificate); static Promise<MediaKeys> create(DOMString keySystem); static IsTypeSupportedResult isTypeSupported(DOMstring keySystem, optional DOMString initDataType, optional DOMString contentType, optional DOMString capability); }; interface MediaKeySession : EventTarget { // session properties readonly attribute DOMString sessionId; readonly attribute unrestricted double expiration; readonly attribute Promise<void> closed; // session operations Promise<void> update((ArrayBuffer or ArrayBufferView) response); Promise<void> close(); Promise<void> remove(); Promise<sequence<ArrayBuffer>> getUsableKeyIds(); };
All errors are reported asynchronously by rejecting the returned Promise. This includes WebIDL type mapping errors.
The steps of an algorithm are always aborted when resolving or rejecting a promise.
The mediaKeys
attribute is the MediaKeys
being used when decrypting encrypted media data for this media element.
The setMediaKeys(mediaKeys
) method provides the MediaKeys
to use when decrypting media data during playback. It must run the following steps:
If mediaKeys and the mediaKeys
attribute are the same object, return a resolved promise.
Let promise be a new promise.
Run the following steps asynchronously:
If mediaKeys is not null, it is already in use by another media element, and the user agent is unable to use it with this element, reject promise with a new DOMException
whose name is "QuotaExceededError"
.
If the mediaKeys
attribute is not null, run the following steps:
If the user agent or CDM do not support removing the association, return a promise rejected with a new DOMException
whose name is "NotSupportedError"
.
If the association cannot currently be removed (i.e. during playback), return a promise rejected with a new DOMException
whose name is "InvalidStateError"
.
Stop using the CDM instance represented by the mediaKeys
attribute to decrypt media data and remove the association with the media element.
If the preceding step failed, reject promise with a new DOMException
whose name is the appropriate error name and that has an appropriate message.
If mediaKeys is not null, run the following steps:
Associate the CDM instance represented by mediaKeys with the media element for decrypting media data.
If the preceding step failed, run the following steps:
Set the mediaKeys
attribute to null.
Reject promise with a new DOMException
whose name is the appropriate error name and that has an appropriate message.
Queue a task to run the Attempt to Resume Playback If Necessary algorithm on the media element.
The user agent may choose to skip this step if it knows resuming will fail (i.e. mediaKeys has no sessions).
Set the mediaKeys
attribute to mediaKeys.
Resolve promise.
Return promise.
Note: Support for clearing or replacing the associated MediaKeys
object during playback is a quality of implementation issue. In many cases it will result in a bad user experience or rejected promise.
Note: As a best practice, applications should create a MediaKeys object and call setMediaKeys()
before providing media data (for example, setting the src attribute of the media element). This avoids potential delays in some implementations.
Note: In some implementations, MediaKeySession
objects created by createSession()
may not fire any events until the MediaKeys
object is associated with a media element using setMediaKeys()
.
The onencrypted
event handler for the encrypted
event must be supported by all HTMLMediaElements as both a content attribute and an IDL attribute.
The waitingFor
attribute indicates what the media element is waiting for, if anything (indicated by the waiting
and canplay
events). This is described in the Encrypted Block Encountered algorithm.
The create(keySystem)
method creates a new MediaKeys
object for keySystem. It must run the following steps:
If keySystem is an empty string, return a promise rejected with a new DOMException
whose name is "InvalidAccessError"
.
If keySystem is not one of the Key Systems supported by the user agent, return a promise rejected with a new DOMException
whose name is "NotSupportedError"
. String comparison is case-sensitive.
If keySystem is not supported or not allowed on the origin of the Document
, return a promise rejected with a new DOMException
whose name is "NotSupportedError"
.
Let promise be a new promise.
Run the following steps asynchronously:
Let cdm be the content decryption module corresponding to keySystem.
Load and initialize the cdm if necessary.
If cdm fails to load or initialize, reject promise with a new DOMException
whose name is the appropriate error name and that has an appropriate message.
Let media keys be a new MediaKeys
object, and initialize it as follows:
Set the keySystem
attribute to keySystem.
Resolve promise with media keys.
Return promise.
The keySystem
attribute identifies the Key System being used.
The createSession(initDataType, initData, sessionType)
method creates a new MediaKeySession
object for the initData. It must run the following steps:
Bug 26575 - MediaKeySession creation may be separated from generating a license request.
The contents of initData are container-specific Initialization Data. initDataType is the initialization data type that indicates how to interpret initData.
Note: User agents and CDMs should not treat sessions created with audio data differently than those created with video data. That is, there is no such thing as an "audio session" or a "video session" - all sessions are used for all media streams processed by cdm.
If initDataType is an empty string, return a promise rejected with a new DOMException
whose name is "InvalidAccessError"
.
If initData is an empty array, return a promise rejected with a new DOMException
whose name is"InvalidAccessError"
.
If initDataType is not an initialization data type supported by the content decryption module corresponding to the keySystem
, return a promise rejected with a new DOMException
whose name is "NotSupportedError"
. String comparison is case-sensitive.
If sessionType is not supported by the content decryption module corresponding to the keySystem
, return a promise rejected with a new DOMException
whose name is "NotSupportedError"
.
Let init data be a copy of the contents of the initData parameter.
Let promise be a new promise.
Run the following steps asynchronously:
Let request be null.
Let cdm be the cdm loaded in create()
.
Use the cdm to execute the following steps:
If the init data is not valid for initDataType, reject promise with a new DOMException
whose name is "InvalidAccessError"
.
If the init data is not supported by the cdm, reject promise with a new DOMException
whose name is "NotSupportedError"
.
Let request be a request (e.g. a license request) generated based on the init data, which is interpreteted per initDataType, and sessionType.
If sessionType is "temporary
", the request is for a temporary non-persisted license.
If sessionType is "persistent
", the request is for a persistable session, possibly with a persistable license.
If sessionType is not "persistent
", the CDM must not store a record of or data related to the session at any point.
Note: The license server may reject the requested type, but it should not issue a different type.
cdm must not use any stream-specific data, including media data, not provided via the init data.
Let the session ID be a unique Session ID string. It may be obtained from cdm.
Let session be a new MediaKeySession
object, and initialize it as follows:
Set the sessionId
attribute to session ID.
Let expiration
be NaN
.
Let closed
be a new promise.
Let the session type be sessionType.
If any of the preceding steps failed, reject promise with a new DOMException
whose name is the appropriate error name and that has an appropriate message.
Add an entry for the value of the sessionId
attribute to the list of active session IDs for this object.
Run the Queue a "message" Event algorithm on the session, providing request and null
.
Resolve promise with session.
Return promise.
The loadSession(sessionId)
method creates a new MediaKeySession
object containing the data stored for the sessionId. It must run the following steps:
Bug 26575 - MediaKeySession creation may be separated from loading the session.
If the content decryption module corresponding to the keySystem
attribute does not support loading previous sessions, return a promise rejected with a new DOMException
whose name is "NotSupportedError"
.
If sessionId is an empty string, return a promise rejected with a new DOMException
whose name is "InvalidAccessError"
.
Let promise be a new promise.
Run the following steps asynchronously:
If the list of active session IDs for this object includes an entry for sessionId, resolve promise with null
.
In other words, do not create a session if a non-closed session already exists for this sessionId.
Let expiration time be NaN
.
Let request be null.
Let destination URL be null.
Let origin be the origin of the MediaKeys
object's Document
.
Let cdm be the cdm loaded in create()
.
Use the cdm to execute the following steps:
If there is no data stored for the sessionId in the origin, resolve promise with undefined
.
Let session data be the data stored for the sessionId in the origin. This must not include data from other origin(s) or that is not associated with an origin.
Load the session data.
If the session data indicates an expiration time for the session, let expiration time be the expiration time in milliseconds since 01 January 1970 UTC.
If a message exchange is required:
Let request be a request generated by the CDM based on the session data.
If the session data indicates a destination URL for the request, let destination URL be that URL. The URL may be validated and/or normalized.
Let session be a new MediaKeySession
object, and initialize it as follows:
Set the sessionId
attribute to sessionId.
Let expiration
be NaN
.
Let closed
be a new promise.
Let the session type be "persistent
".
If any of the preceding steps failed, reject promise with a new DOMException
whose name is the appropriate error name and that has an appropriate message.
Add an entry for the value of the sessionId
attribute to the list of active session IDs for this object.
If the loaded session contains usable keys, run the Usable Keys Changed algorithm on the session.
The algorithm may also be run later should additional processing be necessary to determine with certainty whether one or more keys is usable.
Run the Update Expiration algorithm on the session, providing expiration time.
If request is not null, run the Queue a "message" Event algorithm on the session, providing request and destination URL.
Resolve promise with session.
Return promise.
The setServerCertificate(serverCertificate)
method provides a server certificate to be used to encrypt messages to the license server. It must run the following steps:
The contents of serverCertificate are Key System-specific.
Key systems that use such certificates should also support requesting the certificate from the server via an additional round trip. This simplifies interoperability for applications that are not designed to provide certificates to specific key systems.
If serverCertificate is an empty array, return a promise rejected with a new DOMException
whose name is "InvalidAccessError"
.
If the keySystem
does not support server certificates, return a promise rejected with a new DOMException
whose name is "NotSupportedError"
.
Let certificate be a copy of the contents of the serverCertificate parameter.
Let promise be a new promise.
Run the following steps asynchronously:
Let cdm be the cdm loaded in create()
.
Use the cdm to process certificate.
If any of the preceding steps failed, reject promise with a new DOMException
whose name is the appropriate error name and that has an appropriate message.
Resolve promise.
Return promise.
The isTypeSupported(keySystem, initDataType, contentType, capability)
method returns whether keySystem is supported with the initDataType, container and codec(s) specified by contentType, and capability.
Bug 25923 - isTypeSupported() may be made asynchronous.
The following list shows some examples.
MediaKeys.isTypeSupported("com.example.somesystem")
MediaKeys.isTypeSupported("com.example.somesystem", "keyids")
MediaKeys.isTypeSupported("com.example.somesystem", initDataType, mimeType, "foo")
"probably" == MediaKeys.isTypeSupported("org.w3.clearkey", initDataType, mimeType)
It must run the following steps:
If keySystem is an empty string or contains an unrecognized or unsupported Key System, return the empty string and abort these steps. String comparison is case-sensitive.
If the keySystem implementation is not available and usable, return the empty string and abort these steps.
Follow the steps for the first matching condition from the following list:
If initDataType was not provided, follow the steps for the first matching condition from the following list and abort these steps:
maybe
".If initDataType is an empty string or contains an unrecognized or unsupported initialization data type, return the empty string and abort these steps. String comparison is case-sensitive.
If initDataType is not an initialization data type supported by the keySystem implementation, return the empty string and abort these steps.
If the keySystem implementation supporting initDataType is not available and usable, return the empty string and abort these steps.
If contentType was not provided, follow the steps for the first matching condition from the following list and abort these steps:
maybe
".If contentType is an empty string or contains an invalid or unrecognized MIME type, return the empty string and abort these steps.
Let container be the container type specified by contentType.
Let parameters be the RFC 6381 parameters, if any, specified by contentType.
Let media types be the set of media types specified by parameters. (For example, all of the codecs.) It may be empty. The case-sensitivity of string comparisons is determined by the appropriate RFC or other specification. (Case-sensitive string comparison is recommended because RFC 6381 says, "Values are case sensitive" for some formats.)
If the user agent does not support container, return the empty string and abort these steps. The case-sensitivity of string comparisons is determined by the appropriate RFC. (Per RFC 6838, "Both top-level type and subtype names are case-insensitive.")
If the user agent and keySystem implementation do not support playback of encrypted media data for all media types, return the empty string and abort these steps.
If capability was not provided, follow the steps for the first matching condition from the following list and abort these steps:
maybe
".maybe
".If capability is an empty string, return the empty string and abort these steps.
If the keySystem implementation does not support capability, return the empty string and abort these steps. String comparison is case-sensitive.
Follow the steps for the first matching condition from the following list:
maybe
".maybe
".The sessionId
attribute is the Session ID for this object and the associated key(s) or license(s).
The expiration
attribute is the time, in milliseconds since since 01 January, 1970 UTC, after which the key(s) in the session will no longer be usable to decrypt media data, or NaN
if no such time exists, as determined by the CDM.
The closed
attribute signals when object becomes closed as a result of the Session Close algorithm being run.
This promise can only be fulfilled and is never rejected.
The update(response)
method provides messages, including licenses, to the CDM. It must run the following steps:
The contents of response are keySystem-specific.
If response is an empty array, return a promise rejected with a new DOMException
whose name is "InvalidAccessError"
.
Let message be a copy of the contents of the response parameter.
Let promise be a new promise.
Run the following steps asynchronously:
Let cdm be the cdm loaded in create()
.
Let request be null.
Let destination URL be null.
Use the cdm to execute the following steps:
If the format of message is invalid in any way, reject promise with a new DOMException
whose name is "InvalidAccessError"
.
Process message, following the stipulation for the first matching condition from the following list:
temporary
" and message does not specify that session data, including any license, key(s), or similar session data it contains, should be storedpersistent
"DOMException
whose name is "InvalidAccessError"
.Note: When message contains key(s) and/or related data, cdm will likely cache the key and related data indexed by key ID.
Note: The replacement algorithm within a session is Key System-dependent.
Note: Keys from different sessions should be cached independently such that closing one session does not affect keys in other sessions, even if they have overlapping key IDs.
Note: It is recommended that CDMs support a standard and reasonably high minimum number of keys per MediaKeySession
object, including a standard replacement algorithm, and a standard and reasonably high minimum number of MediaKeySession
objects.
This enables a reasonable number of key rotation algorithms to be implemented across user agents and may reduce the likelihood of playback interruptions in use cases that involve various streams in the same element (i.e. adaptive streams, various audio and video tracks) using different keys.
If the set of usable keys changed, run the Usable Keys Changed algorithm on the session.
The algorithm may also be run later should additional processing be necessary to determine with certainty whether one or more keys is usable.
If the expiration time for the session changed, run the Update Expiration algorithm on the session, providing the new expiration time.
If another message needs to be sent to the server, execute the following steps:
Let request be that message.
If there is a specific destination URL for the message, let destination URL be that URL. The URL may be validated and/or normalized.
If any of the preceding steps failed, reject promise with a new DOMException
whose name is the appropriate error name and that has an appropriate message.
If request is not null, run the Queue a "message" Event algorithm on the session, providing request and destination URL.
Resolve promise.
Return promise.
The close()
method allows an application to indicate that it no longer needs the session and the CDM should release any resources associated with this object and close it.
The returned promise is resolved when the request has been processed, and the closed
attribute promise is resolved when the session is closed.
It must run the following steps:
If the Session Close algorithm has been run on this object, return a resolved promise.
Let promise be a new promise.
Run the following steps asynchronously:
Let cdm be the cdm loaded in create()
.
Use the cdm to execute the following steps:
Process the close request.
Do not remove stored session data.
If the previous step caused the session to be closed, run the Session Close algorithm on this object.
Resolve promise.
Return promise.
The remove()
method allows an application to remove stored session data associated with this object.
It must run the following steps:
If the session type is not "persistent
", return a promise rejected with a new DOMException
whose name is "InvalidAccessError"
.
If the Session Close algorithm has been run on this object, return a promise rejected with a new DOMException
whose name is "InvalidStateError"
.
Let promise be a new promise.
Run the following steps asynchronously:
Let cdm be the cdm loaded in create()
.
Use the cdm to execute the following steps:
Process the remove request.
This may involve exchanging message(s) with the application.
Unless this step fails, the CDM must have cleared all stored session data associated with this object, including the sessionId
, before proceeding to the next step.
(A subsequent call to loadSession()
with sessionId
would fail because there is no data stored for the sessionId.)
Run the following steps asynchronously once the above step has completed:
If any of the preceding steps failed, reject promise with a new DOMException
whose name is the appropriate error name and that has an appropriate message.
Run the Session Close algorithm on this object.
Resolve promise.
Return promise.
The getUsableKeyIds()
method allows an application to get the key IDs for keys in the session that the CDM knows are currently usable to decrypt media data. It must run the following steps:
Let promise be a new promise.
Run the following steps asynchronously:
Let cdm be the cdm loaded in create()
.
Use the cdm to execute the following steps:
Let usable key ids be a list of the key IDs for keys in the session that the CDM knows are currently usable to decrypt media data. usable key ids must not contain IDs for keys that may not currently be usable. Each element must be unique.
If the preceding step failed, reject promise with a new DOMException
whose name is the appropriate error name and that has an appropriate message.
Resolve promise with a new Sequence object, each element of which contains one element of usable key ids.
The methods report errors by rejecting the returned promise with a DOMException
.
The following DOMException names from DOM4 are used in the algorithms.
Causes specified specified in the algorithms are listed alongside each name, though these names may be used for other reasons as well.
| Name | Possible Causes (non-exhaustive) |
|---|---|
NotSupportedError |
The existing MediaKeys object cannot be removed. The key system is not supported. The key system is not supported on insecure origins. The initialization data type is not supported by the key system. The session type is not supported by the key system. The initialization data is not supported by the key system. The operation is not supported by the key system. |
InvalidStateError |
The existing MediaKeys object cannot be removed at this time. The session is closed. |
InvalidAccessError |
The parameter is empty. Invalid initialization data. The operation is not supported on sessions of this type. Invalid response format. A persistent license was provided for a "temporary" session. |
QuotaExceededError |
The MediaKeys object cannot be used with additional HTMLMediaElements. |
This section is non-normative.
Media data processed by a CDM may not be available through Javascript APIs in the usual way (for example using the CanvasRenderingContext2D drawImage() method and the AudioContext MediaElementAudioSourceNode). This specification does not define conditions for such non-availability of media data, however, if media data is not available to Javascript APIs then these APIs may behave as if no media data was present at all.
Where media rendering is not performed by the UA, for example in the case of a hardware protected media pipeline, then the full set of HTML rendering capabilities, for example CSS Transforms, may not be available. One likely restriction is that video media may be constrained to appear only in rectangular regions with sides parallel to the edges of the window and with normal orientation.
[Constructor(DOMString type, optional MediaEncryptedEventInit eventInitDict)] interface MediaEncryptedEvent : Event { readonly attribute DOMString initDataType; readonly attribute ArrayBuffer? initData; }; dictionary MediaEncryptedEventInit : EventInit { DOMString initDataType = ""; ArrayBuffer? initData = null; };
[Constructor(DOMString type, optional MediaKeyMessageEventInit eventInitDict)] interface MediaKeyMessageEvent : Event { readonly attribute ArrayBuffer message; readonly attribute DOMString? destinationURL; }; dictionary MediaKeyMessageEventInit : EventInit { ArrayBuffer message = ArrayBuffer(); DOMString? destinationURL = null; };
initDataType
Returns a string indicating the initialization data type of the Initialization Data related to the event.
initData
Returns the Initialization Data related to the event.
message
Returns the message (i.e. license request) to send.
destinationURL
Returns the URL to which the message
should be sent.
The initDataType
attribute contains a string indicating the initialization data type specific to the event. The format of the initData
will vary according to the initDataType
.
The initData
attribute contains Initialization Data specific to the event.
The message
attribute contains a message from the CDM. Messages are Key System-specific. In most cases, it should be sent to a key server.
The destinationURL
is the URL to which the message
should be sent.
It may be null.
An application may choose not to send the message to this URL.
Implementations may validate and/or normalize the URL, which could result in a value that is different from the original or null. Applications should not assume that the value of this attribute will be identical across implementations.
Events are constructed as defined in Constructing events, in [DOM4].
This section is non-normative.
The following event is fired at HTMLMediaElement
.
| Event name | Interface | Dispatched when... | Preconditions |
|---|---|---|---|
encrypted |
MediaEncryptedEvent |
The user agent encounters Initialization Data in the media data. |
readyState is equal to HAVE_METADATA or greater.
It is possible that the element is playing or has played.
|
The following events are fired at MediaKeySession
.
| Event name | Interface | Dispatched when... |
|---|---|---|
keyschange |
Event |
There has been a change in usable keys. |
message |
MediaKeyMessageEvent |
A message has been generated (and likely needs to be sent to a server).
For example, a license request has been generated as the result of a createSession() call or another message must be sent in response to an update() call.
|
The following steps are run when the media element encounters Initialization Data in the media data during the resource fetch algorithm:
Let initDataType be the empty string.
Let initData be null.
If the media data is CORS-same-origin, run the following steps:
Let initDataType be the string representing the initialization data type of that initialization data.
Let initData be that initialization data.
Queue a task to fire a simple event named encrypted
at the media element.
The event is of type MediaEncryptedEvent
and has:
initDataType
= initDataTypeinitData
= initData
Note that readyState
is not changed and no algorithms are aborted. This event merely provides information.
Note that if the media is not CORS-same-origin then the initData will be null. This allows applications that can retrieve initData from an alternative source to continue. Applications with no way to retrieve initData may wish to consider aborting playback in this case.
Continue Normal Flow: Continue with the existing media element's resource fetch algorithm.
The following steps are run when the media element encounters a block (i.e. frame) of encrypted media data during the resource fetch algorithm:
If the media element's mediaKeys
attribute is not null, run the following steps:
Let media keys be the MediaKeys
object referenced by that atribute.
Let cdm be the cdm loaded during the initialization of the media keys.
If there is at least one MediaKeySession
created by the media keys on which the Session Close algorithm has not been run, run the following steps:
This check ensures the cdm has finished loading and is a prequisite for a matching key being available.
Let the block key ID be the key ID of the current block (as specified by the container).
Use the cdm to execute the following steps:
Let available keys be the union of keys in sessions that were created by the media keys.
Follow the steps for the first matching condition from the following list:
Let block key be the matching key.
Note: If multiple sessions contain a usable key for the block key ID, which key to use is Key System-dependent.
Use the cdm to decrypt the block using block key.
Follow the steps for the first matching condition from the following list:
MEDIA_ERR_DECODE
error, and abort these steps.If the waitingFor
attribute on the media element is "key
", set the waitingFor
attribute on the media element to "none
".
Abort these steps and process the decrypted block as normal. (Decode the block.)
Note: Not all decryption problems (i.e. using the wrong key) will result in a decryption failure. In such cases, no error is fired here but one may be fired during decode.
Let session be the MediaKeySession
object associated with that session.
Run the Queue an "error" Event algorithm on the session, providing the appropriate error name.
Abort these steps.
There is no usable key for the block. Run the following steps:
Run the Queue a "waiting" Event algorithm on the media element.
Wait for a signal to resume playback.
For frame-based encryption, this may be implemented as follows when the media element attempts to decode a frame as part of the resource fetch algorithm:
Let encrypted be false.
Detect whether the frame is encrypted.
Decode the frame.
Provide the frame for rendering.
The following steps are added to readyState change algorithms in Playing the media resource:
waiting
event, the user agent must also set the waitingFor
attribute on the Media Element to "data
".canplay
event, the user agent must also set the waitingFor
attribute on the Media Element to "none
".The Queue a "message" Event algorithm is run when the CDM needs to queue a message event to a MediaKeySession
object.
Requests to run this algorithm include a target MediaKeySession
object, a request, and a destination URL.
The following steps are run:
Let the session be the specified MediaKeySession
object.
Queue a task to fire a simple event named message
at the session.
The event is of type MediaKeyMessageEvent
and has:
message
= the specified requestdestinationURL
= the specified destination URL
Bug 26372 - There is currently no mechanism for reporting errors and other events not related to a specific method call.
The Usable Keys Changed algorithm is run when the CDM changes the set of keys in the session that may be used for decryption.
This can happen as the result of an update()
call or some other event.
Requests to run this algorithm include a target MediaKeySession
object.
The following steps are run:
Let the session be the associated MediaKeySession
object.
Queue a task to fire a simple event named keyschange
at the session.
Queue a task to run the Attempt to Resume Playback If Necessary algorithm on each of the media element(s) whose mediaKeys
attribute is the MediaKeys object that created the session.
The user agent may choose to skip this step if it knows resuming will fail (i.e. no additional keys became available).
The Update Expiration algorithm is run when the CDM changes the expiration time of a session.
This can happen as the result of an update()
call or some other event.
Requests to run this algorithm include a target MediaKeySession
object and the new expiration time, which may be NaN
.
The following steps are run:
Let the session be the associated MediaKeySession
object.
Let expiration time be NaN
.
If the new expiration time is not NaN
, let expiration time be the new expiration time in milliseconds since 01 January 1970 UTC.
Set the session's expiration
attribute to expiration time.
The Session Close algorithm is run when the CDM closes the session associated with a MediaKeySession
object.
The CDM may close a session at any point, such as in response to a close()
call, when the session is no longer needed, or when system resources are lost.
Keys in other sessions should be unaffected, even if they have overlapping key IDs.
The following steps are run:
Let the session be the associated MediaKeySession
object.
Remove the entry for the value of the session's sessionId
attribute from the list of active session IDs for the MediaKeys object that created the session.
Let promise be the closed
attribute of the session.
Resolve promise.
The Queue a "waiting" Event algorithm is run when the CDM needs to queue a waiting event to a HTMLMediaElement
object.
Requests to run this algorithm include a target HTMLMediaElement
object.
The following steps are run:
Let the media element be the specified HTMLMediaElement
object.
If the waitingFor
attribute on the media element is not "key
", run the following steps:
Set the waitingFor
attribute on the media element to "key
".
Queue a task to fire a simple event named waiting
at the media element.
Suspend playback.
The Attempt to Resume Playback If Necessary algorithm is run when one or more keys becomes available.
If playback is blocked waiting for a key, it resumes playback if a necessary key has been provided.
Requests to run this algorithm include a target HTMLMediaElement
object.
The following steps are run:
Let the media element be the specified HTMLMediaElement
object.
If the waitingFor
attribute on the media element is not "key
", abort these steps.
Attempt to resume the resource fetch algorithm by running the Encrypted Block Encountered algorithm.
If the user agent can advance the current playback position
in the direction of playback
, run the following steps:
Queue a task to fire a simple event named canplay
at the media element.
If the paused
attribute on the media element is false, queue a task to fire a simple event named playing
at the media element.
Otherwise, the waitingFor
attribute on the media element must not be "none
".
All user agents must support the simple decryption capabilities described in this section regardless of whether they support a more advanced CDM. This ensures that there is a common baseline level of protection that is guaranteed to be supported in all user agents, including those that are entirely open source. Thus, content providers that need only basic protection can build simple applications that will work on all platforms without needing to work with any content protection providers.
The "org.w3.clearkey" Key System uses plain-text clear (unencrypted) key(s) to decrypt the source. No additional client-side content protection is required. This Key System is described below.
The following describe how Clear Key supports key system-specific capabilities:
The setMediaKeys()
method: Implementations may support associating the MediaKeys
object with more than one HTMLMediaElement
.
The setServerCertificate()
method: Not supported.
The isTypeSupported()
method: There are no supported capability values.
The "persistent
" SessionType
, including the loadSession()
and remove()
methods: Implementations may support these.
The following describe how Clear Key implements key system-specific behaviors:
In the createSession()
algorithm:
The generated request is a JSON object encoded in UTF-8 as described in License Request Format.
The request is generated by extracting the key IDs from the init data.
The "type" member value is the value of the sessionType parameter.
The sessionId
attribute is a numerical value representable by a 32-bit integer.
The expiration
attribute is always NaN
.
In the update()
algorithm:
The response parameter is a JWK Set as described in License Format.
message is considered invalid if it is not a valid JWK Set with at least one valid JWK key of a valid length for the media type.
The getUsableKeyIds()
method always returns all key IDs that have been provided via update()
.
Initialization Data: Implementations may support any combination of registered and Initialization Data types. Implementations should support the "keyids" type and other types appropriate for content types supported by the user agent.
This section describes the format of the license request provided to the application via the message
attribute of the message event.
The format is a JSON object containing the following members:
SessionType
When contained in the ArrayBuffer message
attribute of a MediaKeyMessageEvent
object, the JSON string is encoded in UTF-8 as specified in the Encoding specification.
Applications may decode the contents of the ArrayBuffer to a JSON string using the TextDecoder interface.
This section is non-normative.
The following example is a license request for a temporary license for two key IDs. (Line breaks are for readability only.)
{ "kids": [ "67ef0gd8pvfd0", "77ef0gd8pvfd0" ], "type":"temporary" }
This section describes the format of the license to be provided via the response parameter of the update()
method.
The format is a JSON Web Key (JWK) Set containing representation of the symmetric key to be used for decryption, as defined in the IETF Internet-draft JSON Web Key (JWK) specification.
For each JWK in the set, the parameter values are as follows:
The JSON object may have an optional "type" member value, which may be any of the SessionType
values.
If not specified, the default value of "temporary
" is used.
The update()
algorithm compares this value to the sessionType.
When passed to the update()
method as the ArrayBuffer response parameter, the JSON string must be encoded in UTF-8 as specified in the Encoding specification.
Applications may encode the JSON string using the TextEncoder interface.
This section is non-normative.
The following example is a JWK Set containing a single symmetric key. (Line breaks are for readability only.)
{ "keys": [{ "kty":"oct", "alg":"A128KW", "k":"GawgguFyGrWKav7AX4VKUg" "kid":"67ef0gd8pvfd0", }], "type":"temporary" }
This section is non-normative.
For more information on base64url and working with it, see the Base64url Encoding entry in the Terminology section and Notes on implementing base64url encoding without padding of the JSON Web Signature (JWS) specification. Specifically, there is no '=' padding, and the characters '-' and '_' must be used instead of '+' and '/', respectively.
User Agent and Key System implementations must consider media data, Initialization Data, responses (i.e. data passed to update()
), licenses, key data, and all other data provided by the application as untrusted content and potential attack vectors.
They must use appropriate safeguards to mitigate any associated threats and take care to safely parse, decrypt, etc. such data.
User Agents may want to validate data before passing it to the CDM, especially if the CDM does not run in the same (sandboxed) context as the DOM (i.e. rendering).
Implementations should not return active content or passive content that affects program control flow to the application.
For example, it is not safe to expose URLs or other information that may have come from media data, such as is the case for the Initialization Data passed to createSession()
.
An exception is made for information extracted from a license or other messages from the license server, which is assumed to be trusted by the application.
(This requires that the application be responsible for determining the location of the license or the URL of the license server.)
For example, a license may include a destination URL to receive renewal requests.
Applications should validate any URLs or other "instructions" provided by the user agent (or CDM).
Specifically, applications should validate that any destinationURL
value represents a trusted and expected origin and path.
A restrictive whitelist, possibly using strict patterns, is recommended.
Applications should reject URLs that do not have a secure origin (e.g. HTTPS).
User Agents are responsible for providing users with a secure way to browse the web. Since User Agents may integrate with third party CDM implementations, CDM implementers must provide sufficient information and controls to user agent implementers to enable them to properly asses the security implications of integrating with the Key System.
Note: Unsandboxed CDMs (or CDMs that use platform features) and UAs that use them must be especially careful in all areas of security, including parsing of key and media data, etc. due to the potential for compromises to provide access to OS/platform features, interact with or run as root, access drivers, kernel, firmware, hardware, etc., all of which may not be written to be robust against hostile software or web-based attacks. Additionally, CDMs may not be updated with security fixes as frequently, especially when part of the OS, platform or hardware.
User agents may choose to only support the EME APIs and/or specific Key Systems (i.e. based on privacy and security risks) on secure origins. This is especially important if a user agent chooses to support a Key System implementation that cannot be sufficiently sandboxed or otherwise secured. Such user agents should also properly handle Mixed Content to avoid potential exposure to insecure content. See also Use Secure Origin and Transport.
If a user agent chooses to support a Key System implementation that cannot be sufficiently sandboxed or otherwise secured, the user agent should ensure that users are fully informed and/or give explicit consent before loading or invoking it. See also User Alerts / Prompts.
The presence or use of Key Systems on a user's device raises a number of privacy issues, falling into two categories: (a) user-specific information that may be disclosed by the EME interface itself, or within messages from Key Systems and (b) user-specific information that may be persistently stored on the users device.
User Agents should take responsibility for providing users with adequate control over their own privacy. Since User Agents may integrate with third party CDM implementations, CDM implementers must provide sufficient information and controls to user agent implementers to enable them to implement appropriate techniques to ensure users have control over their privacy, including but not limited to the techniques described below.
Concerns regarding information disclosed by EME and Key Systems fall into two categories, concerns about non-specific information that may nevertheless contribute to the possibility of fingerprinting a user agent or device and user-specific information that may be used directly for user tracking.
Malicious applications may be able to fingerprint users or user agents by detecting or enumerating the list of Key Systems that are supported and related information. If proper origin protections are not provided this could include detection of sites that have been visited and information stored for those sites. In particular, Key Systems should not share key or other data between sites that are not CORS-same-origin.
CDMs, especially those implemented outside the user agent, may not have the same fundamental isolations as the web platform. It is important that steps be taken to avoid information leakage, especially across origins. This includes both in-memory and stored data. Failure to do so could lead to information leakage to/from Incognito/Private Browsing sessions, across profiles, and even across different operating system user accounts.
To avoid such issues, user agent and CDM implementations should ensure that:
mediaKeys
attribute is not the MediaKeys object.User-specific information may be obtained over the EME API in two ways: through detection of stored keys and through Key System messages.
Key Systems may access or create persistent or semi-persistent identifiers for a device or user of a device. In some cases these identifiers may be bound to a specific device in a secure manner. If these identifiers are present in Key System messages, then devices and/or users may be tracked. If the mitigations below are not applied this could include both tracking of users / devices over time and associating multiple users of a given device. If not mitigated, such tracking may take three forms depending on the design of the Key System:
If a Key System permits keys to be stored and to be re-used between origins, then it may be possible for two origins to collude and track a unique user by recording their ability to access a common key.
Finally, if any user interface for user control of Key Systems presents data separately from data in HTTP session cookies or persistent storage, then users are likely to modify site authorization or delete data in one and not the others. This would allow sites to use the various features as redundant backup for each other, defeating a user's attempts to protect his privacy.
There are a number of techniques that can be used to mitigate these risks of tracking without user consent:
While these suggestions prevent trivial use of this feature for user tracking, they do not block it altogether. Within a single domain, a site can continue to track the user during a session, and can then pass all this information to a third party along with any identifying information (names, credit card numbers, addresses) obtained by the site. If a third party cooperates with multiple sites to obtain such information, and if identifiers are not per-origin, then a profile can still be created.
It is important to note that identifiers that are non-clearable, non-origin-specific or hardware-bound exceed the tracking impact of existing techniques such as Cookies or session identifiers embedded in URLs.
Thus, in addition to the various mitigations described above, if a browser supports a mode of operation intended to preserve user anonymity, then User Agent implementers should carefully consider whether access to Key Systems should be disabled in this mode.
Key Systems may store information on a user's device, or user agents may store information on behalf of Key Systems. Potentially, this could reveal information about a user to another user of the same device, including potentially the origins that have used a particular Key System (i.e. sites visited) or even the content that has been decrypted using a Key System.
If information stored by one origin affects the operation of the Key System for another origin, then potentially the sites visited or content viewed by a user on one site may be revealed to another, potentially malicious, site.
There are a number of techniques that can be used to mitigate these privacy risk to users:
User agent and CDM implementations that allow the CDM to persist data should:
In order to protect identifiers and other information discussed in previous sections, user agents may choose to only support the EME APIs and/or specific Key Systems (i.e. based on privacy and security risks) on secure origins. This is especially important if a user agent chooses to support a Key System implementation that exposes identifiers or other such information without effectively anonymizing it in transit (i.e. without encrypting identifiers).
Regardless of user agent limitations, applications should use secure transport (e.g. HTTPS) for all traffic containing messages from the CDM (i.e. all data passed from message
events and to update()
).
All user agents should properly handle Mixed Content to avoid exposure to insecure content or transport when the user agent or application wish to enforce secure origin and transport.
This section and its subsections are non-normative.
This section contains example solutions for various use cases using the proposed extensions. These are not the only solutions to these use cases. Video elements are used in the examples, but the same would apply to all media elements. In some cases, such as using synchronous XHR, the examples are simplified to keep the focus on the extensions.
In this simple example, the source file and clear-text license are hard-coded in the page. Only one session will ever be created.
<script> function load() { var video = document.getElementById("video"); if (!video.mediaKeys) { var promise = MediaKeys.create("org.w3.clearkey"); promise.catch( console.error.bind(console, "Unable to create MediaKeys") ); promise.then( function(createdMediaKeys) { return video.setMediaKeys(createdMediaKeys); } ).catch( console.error.bind(console, "Unable to set MediaKeys") ); promise.then( function(createdMediaKeys) { var initData = new Uint8Array([ ... ]); return createdMediaKeys.createSession("webm", initData); } ).then( function(keySession) { keySession.addEventListener("message", handleMessage, false); } ).catch( console.error.bind(console, "Unable to create key session") ); } } function handleMessage(event) { var keySession = event.target; var license = new Uint8Array([ ... ]); keySession.update(license).catch( console.error.bind(console, "update() failed") ); } </script> <body onload="load()"> <video src="foo.webm" autoplay id="video"></video> </body>
This example selects a supported Key System using the isTypeSupported()
method then uses
the Initialization Data from the media data to generate the license request and send it to the appropriate license server.
One of the supported key systems uses a serverCertificate, which is provided proactively.
<script> var keySystem; var licenseUrl; var serverCertificate; function selectKeySystem() { if (MediaKeys.isTypeSupported("com.example.somesystem", "webm", "video/webm; codecs='vp8, vorbis'")) { licenseUrl = "https://license.example.com/getkey"; // OR "https://example.<My Video Site domain>" keySystem = "com.example.somesystem"; serverCertificate = new Uint8Array([ ... ]); } else if (MediaKeys.isTypeSupported("com.foobar", "webm", "video/webm; codecs='vp8, vorbis'")) { licenseUrl = "https://license.foobar.com/request"; keySystem = "com.foobar"; } else { throw "Key System not supported"; } } function handleInitData(event) { var video = event.target; if (video.mediaKeysObject === undefined) { selectKeySystem(); video.mediaKeysObject = null; // Prevent entering this path again. video.pendingSessionData = []; // Will store all initData until the MediaKeys is ready. MediaKeys.create(keySystem).then( function(createdMediaKeys) { video.mediaKeysObject = createdMediaKeys; if (serverCertificate) createdMediaKeys.setServerCertificate(serverCertificate); for (var i = 0; i < video.pendingSessionData.length; i++) { var data = video.pendingSessionData[i]; createSession(video.mediaKeysObject, data.initDataType, data.initData); } video.pendingSessionData = []; return video.setMediaKeys(createdMediaKeys); } ).catch( console.error.bind(console, "Unable to create or use new MediaKeys") ); } addSession(video, event.initDataType, event.initData); } function addSession(video, initDataType, initData) { if (video.mediaKeysObject) { createSession(video.mediaKeysObject, initDataType, initData); } else { video.pendingSessionData.push({initDataType: initDataType, initData: initData}); } } function createSession(mediaKeys, initDataType, initData) { mediaKeys.createSession(initDataType, initData).then( function(keySession) { keySession.addEventListener("message", licenseRequestReady, false); } ).catch( console.error.bind(console, "Unable to create or initialize key session") ); } function licenseRequestReady(event) { var request = event.message; var xmlhttp = new XMLHttpRequest(); xmlhttp.keySession = event.target; xmlhttp.open("POST", licenseUrl); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { var license = new Uint8Array(xmlhttp.response); xmlhttp.keySession.update(license).catch( console.error.bind(console, "update() failed") ); } } xmlhttp.send(request); } </script> <video src="foo.webm" autoplay onencrypted="handleInitData(event)"></video>
Initialization is much simpler if encrypted events do not need to be handled during MediaKeys initialization. This can be accomplished either by providing the Initialization Data in other ways or setting the source after the MediaKeys object has been created. This example does the latter.
<script> var keySystem; var licenseUrl; var serverCertificate; var mediaKeys; // See the previous example for implementations of these functions. function selectKeySystem() { ... } function createSession(mediaKeys, initDataType, initData) { ... } function licenseRequestReady(event) { ... } function handleInitData(event) { createSession(mediaKeys, event.initDataType, event.initData); } selectKeySystem(); MediaKeys.create(keySystem).then( function(createdMediaKeys) { mediaKeys = createdMediaKeys; var video = document.getElementById("v"); video.src = "foo.webm"; if (serverCertificate) mediaKeys.setServerCertificate(serverCertificate); return video.setMediaKeys(mediaKeys); } ).catch( console.error.bind(console, "Unable to create or initialize key session") ); </script> <video id="v" autoplay onencrypted="handleInitData(event)"></video>
This is a more complete example showing all events being used.
Note that handleMessage()
could be called multiple times, including in response to the update()
call if multiple round trips are required and for any other reason the Key System might need to send a message.
<script> var keySystem; var licenseUrl; var serverCertificate; var mediaKeys; // See previous examples for implementations of these functions. function selectKeySystem() { ... } function handleInitData(event) { ... } // This replaces the implementation in the previous example. function createSession(mediaKeys, initDataType, initData) { mediaKeys.createSession(initDataType, initData).then( function(keySession) { keySession.addEventListener("message", handleMessage, false); keySession.addEventListener("keyschange", handleKeysChange, false); keySession.closed.then( console.log.bind(console, "Session closed") ); } ).catch( console.error.bind(console, "Unable to create or initialize key session") ); } function handleMessageResponse(keySession, response) { var license = new Uint8Array(response); keySession.update(license).catch( function(err) { console.error("update() failed: " + err); } ); } function sendMessage(message, keySession) { xmlhttp = new XMLHttpRequest(); xmlhttp.keySession = keySession; xmlhttp.open("POST", licenseUrl); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) handleMessageResponse(xmlhttp.keySession, xmlhttp.response); } xmlhttp.send(message); } function handleMessage(event) { sendMessage(event.message, event.target); } function handleKeysChange(event) { event.target.getUsableKeyIds().then( function(keyIdSequence) { // Process keyIdSequence and respond appropriately. } ).catch( console.error.bind(console, "Failed handling usable keys change") ); } function handleError(event) { // Report and do some bookkeeping with event.target.sessionId if necessary. } selectKeySystem(); MediaKeys.create(keySystem).then( function(createdMediaKeys) { mediaKeys = createdMediaKeys; var video = document.getElementById("v"); video.src = "foo.webm"; if (serverCertificate) mediaKeys.setServerCertificate(serverCertificate); return video.setMediaKeys(mediaKeys); } ).catch( console.error.bind(console, "Unable to create or use new MediaKeys") ); </script> <video id="v" autoplay onencrypted="handleInitData(event)"></video>
This example requests a persistent license for future use and stores it. It also provides functions for later retrieving the license and for destroying it.
<script> var keySystem; var licenseUrl; var serverCertificate; var mediaKeys; // See the previous examples for implementations of these functions. function selectKeySystem() { ... } function sendMessage(message, keySession) { ... } function handleMessage(event) { ... } function handleError(event) { ... } // Called if the application does not have a stored sessionId for the media resource. function createSession(mediaKeys, initDataType, initData) { mediaKeys.createSession(initDataType, initData, "persistent").then( function(keySession) { keySession.addEventListener("message", handleMessage, false); keySession.closed.then( function() { console.log("Session " + this.sessionId + " closed"); }.bind(keySession) ); // Store keySession.sessionId in the application. } ).catch( console.error.bind(console, "Unable to create or initialize a persistable key session") ); } // Called if the application has a stored sessionId for the media resource. function loadStoredSession(mediaKeys, sessionId) { mediaKeys.loadSession(sessionId).then( function(keySession) { if (!keySession) { console.error("No stored session with the ID " + sessionId + " was found."); // The application should remove its record of |sessionId|. return; } keySession.addEventListener("message", handleMessage, false); keySession.closed.then( console.log.bind(console, "Session closed") ); } ).catch( console.error.bind(console, "Unable to load or initialize the stored session with the ID " + sessionId) ); } // Called when the application wants to stop using the session without removing the stored license. function closeSession(keySession) { keySession.close(); } // Called when the application wants to remove the stored license. // The stored session data has not been completely removed until the promise returned by remove() is fulfilled. // The remove() call may initiate a series of messages to/from the server that must be completed before this occurs. function removeStoredSession(keySession) { keySession.remove().then( function() { console.log("Session " + this.sessionId + " removed"); // The application should remove its record of this.sessionId. }.bind(keySession) ).catch( console.error.bind(console, "Failed to remove the session") ); } // This replaces the implementation in the previous example. function handleMessageResponse(keySession, response) { var license = new Uint8Array(response); keySession.update(license).then( function() { // If this was the last required message from the server, the license is // now stored. Update the application state as appropriate. } ).catch( console.error.bind(console, "update() failed") ); } selectKeySystem(); MediaKeys.create(keySystem).then( function(createdMediaKeys) { mediaKeys = createdMediaKeys; var video = document.getElementById("v"); if (serverCertificate) mediaKeys.setServerCertificate(serverCertificate); return video.setMediaKeys(mediaKeys); } ).catch( console.error.bind(console, "Unable to create or use new MediaKeys") ); </script> <video id="v" src="foo.webm" autoplay></video>
| Version | Comment |
|---|---|
| 14 April 2014 | Use promises. |
| 1 April 2014 | Moved Container Guidelines to the Encrypted Media Extensions Stream Format and Initialization Data Format Registry. |
| 3 February 2014 | Produced candidate WD. |
| 17 September 2013 | Produced candidate WD. |
| 6 May 2013 | Produced updated candidate FPWD. |
| 14 January 2013 | Produced candidate FPWD. |
| 16 August 2012 | Converted to the object-oriented API. |
| 0.1b | Last non-object-oriented revision. |
| 0.1a | Corrects minor mistakes in 0.1. |
| 0.1 | Initial Proposal |


Without the People of Utopian, Utopian.io is little more than an ideal and vision. It is the community of contributors, moderators and community managers that make Utopian what it is.
In this weekly series we’ll introduce you to some of these amazing people and this week we begin with one of our most active community managers: @jestemkioskiem
I’m not really much more special than any of you - a young student with a passion for coding. Plenty of our users are far greater open source contributors, others are better developers, you get my point. We at Utopian are just a part of the bigger picture you’re in as well. The only difference between us is that we sacrificed everything to be here, and I encourage you all to do the same with your lives. Take risks, do what you love, never stop at good enough. That’s how you find places you truly belong to.
How long have you been involved in Open Source projects? Do you own / manage one or more of your own? And where does Utopian.io fit in?
Hmm… Truth be told - I didn’t do much in the Open Source space before Utopian. I loved the movement, I used it for learning code, but I hardly ever contributed. I wish I could, but I was not even barely good enough! When Utopian came around and I could suddenly do what I love and help support myself at the same time, my development skills got better and better every day. I would absolutely not be even a junior developer today if not for Utopian.
I do have plenty of projects of my own that you can find on github.com/jestemkioskiem, sadly all of them have been abandoned now due to time constraints, although some are working products even if the code is a little messy (a lot of people message me about SockoBot still). I’m hoping I can go back to my Steem Assistant interface soon, but only time will tell!
What does your professional skill-set offer the Open Source community and what type of contributions do you usually submit?
Back when I was a moderator, I used to contribute a lot. Mostly to my own projects, but I always made sure they were useful to the community of Steemians. As of now, I only ever contribute if I’m already doing something I or my friends need. That’s how my latest posts to Utopian, the Alexa JSON generator and EOS tutorial came to be. I already have my next project in mind though, and while it didn’t even cross my mind to bring it to Utopian until now, I think you guys will love it!
Which programming languages / platforms do you feel most at home with?
If you ever read the Discord’s general chat when I discuss code, you’ll know I’m all for Python. I’ve tried almost every modern language by now, but no other grinds my gears like my good old snake friend. That being said, a lot of people don’t know I really enjoy C# too. I learned it as a kid developing simple games and it stuck with me ever since. I love Unity, I’m just not all that about Game Development right now.
And while I definitely don’t feel at home with javascript yet, I’m trying. Hopefully by the end of the year at least a single line of code in Utopian will be authored by me!
How did you discover Utopian? What made you want to take part?
I think I’m in the minority when I say I remember exactly how I discovered Utopian. I came to steem back in late 2016, thanks to @dailydogger (who I can’t thank enough).  The opportunity to do stuff like we do here was not there, and that made me burn out quickly and leave. I came back in mid 2017 to post my thoughts to #polish, but I was still looking for opportunities.
One day, I saw @steeminator3000, an old friend of mine, post a translation to a project via Utopian. The moment I saw it, I knew this was awesome. My comment is there, you can read it, something along the lines of “Woah, that’s awesome, never knew you did this!”. I researched Utopian and decided to tag along for the ride. I was always proud of my English skills and I could never utilize them before then, so this was an opportunity to help, earn & feel good about myself!
What made you decide to take on additional roles in the organization itself?
Back when I was still all about translations, I used to see contributions with bad grammar or made with little to no effort to cash in in my language. I’ve asked around, but there seemed to be no polish moderators at the time. It felt unfair, not to me who put in actual effort, but to the project owners who would later use this and be laughed at. That’s why I asked @ruah to join his team, and I couldn’t be happier about the fact that I did. I saved dozens of projects from terrible translations and loved every second of it.
How do you see the future of Utopian? What would you want it to be, and where would you like to see yourself in it?
I think Utopian is far too revolutionary, the idea is way too good and the team working here is too passionate for the system to fail. I see Utopian as a future viable alternative for freelancers and a fantastic place for people that are just entry level in their field. This place is great for learning and improving yourself - it’s truly an Utopia for young, passionate people.
As per project owners, I can’t imagine projects not joining us once we make our systems fool proof and advertise to larger groups of people. It’s like hiring a freelancer, but for free (or not if you wish to provide a bounty), and it’s all open source. Right now I’m pushing for an option to set beneficiaries to make 100% of your post rewards go to charity. I think this is a very important step for people that want to come to us from Github and continue to do the same, for free, just with their passion.
I would love to see myself in the picture. Internal decision making and researching interesting system implementations (like I did with the Questionnaire) is really my thing! I might get burned out in the PR field, but even then I’ll find a place for myself here. I’m not leaving until you guys make me!
What other steem communities / initiatives are you part of?
Lately, not many. I still call myself a “Polish Community Activist” in my profile, which hopefully leads you to believe I browse and curate #polish. Other than that, I unfortunately rarely have time to engage in the other ones.
I know this is going to sound very narcissistic of me, but I think #polish is one of the best communities on the website. These guys chat on daily basis, have their own rules, even started their own SteemCleaners for just our tag. It’s structured, shitposts don’t make it far, there’s a wide range of quality content there. It really melts my heart to look how far we’ve come since I came back there last year, when only 1 person (huge shoutout to @noisy!) was curating the content.
If I see an opportunity to join a similar community, you’ll find me there. Also, expect me on DTube sometimes this summer!
What is the most interesting / weird / inspiring thing to personally happen to you on the Steem blockchain?
The weirdest thing ever was becoming a Supervisor here.
On one of our Bi Weekly meetings, @elear openly admitted that new Supervisors were needed and that the team will need to expand. He said he’s taking in the candidates. I was still new on Utopian, maybe 3 or 4 weeks by then, but I messaged him to ask - “How would I go about becoming a Supervisor in the future? I’m still new, but I love it here, I’d love to have more impact.”. Innocent question about how the team structure works here and what qualities I should aim for to hopefully get an opportunity later down the line. This lead him to ask on voice if anyone had anything against me becoming a Supervisor. No one spoke up. I changed my life by accident. I’ve learned to always take the shot.
Chat with us on Discord https://discord.gg/h52nFrV Support the Utopian Witness
This page is synchronized from the post: People of Utopian #1 - Jestemkioskiem


Name
E-mail
Comments
Would you like my resume or portfolio?
Both
Portfolio
Résumé
Neither
Instagram
Twitter
LinkedIn
Email<EMAIL_ADDRESS>Phone:<PHONE_NUMBER>
Address:
Joshua Rang
P.O. Box 77605
Indianapolis, IN 46227


I am a full stack software developer who specializes in client-side application development. I love diving into new technologies and am always on the look out for new tools to incorporate into my repertoire. I find great satisfaction in working closely with team members in an agile development environment to create clean, vibrant, and responsive pages.
A web-based machine learning platform aimed at users with a wide range of experience levels.
A “fantasy race-car” fan-engagement web-based application that allows IndyCar fans at the DXC Technology 600 to create their own car and watch it compete against other virtual users and real drivers based on live race data.
A cross-platform native mobile application for authenticating wine and spirits.
ClassMate is a classroom assistant designed to increase engagement between students and teachers. Users can create an account either as a student or a teacher. Once logged in functionality includes the ability to upload and check assignments, a digital handraise queue system, class roster and emergency contact lists, and a badge handout system for rewarding students for good participation and behavior.
A playful web-app for encouraging your coworkers' indoor voices. The application monitors the user's microphone input data and triggers audio-visual alerts when the volume reaches a user-specified threshold.
From a young age, my earliest passion has always been music. My studies in rock, R&B, jazz, and classical have brought me on many great adventures.
Several years in the service industry after college have left me with a constant craving for all things delicious.
In my free time I like to work on DIY projects, travel, try new food, and study astronomy.


--- title: Iris You'd Come for a Visit date: 2014-06-04 00:00:00 -06:00 categories: - whats-blooming layout: post blog-banner: whats-blooming-now-summer.jpg post-date: June 4, 2014 post-time: 4:12 PM blog-image: wbn-default.jpg ---
Beverly Sills Bearded Iris Iris 'Beverly Sills'
Black Falls Bearded Iris Iris 'Black Falls'
Epicenter Bearded Iris Iris 'Epicenter'
Light Beam Bearded Iris Iris 'Light Beam'
New Moon Bearded Iris Iris 'New Moon'
Sultry Mood Bearded Iris Iris 'Sultry Mood'
Trans Orange Bearded Iris Iris 'Trans Orange'
Yellow Flag Iris pseudoacorus
Caesar's Brother Siberian Iris Iris sibirica 'Caesar's Brother'
Mesmerizer Bearded Iris Iris x 'Mesmerizer'
The 2014 concert season has kicked off. There are still plenty of tickets left, so if you haven't purchased your tickets, please check the concerts page!


PROCURATOR
Home
Users
Hardware
Milestones
Sponsors
Judging
CURRENT PROJECTED DATE: 01-19-2018
ADD NEW
ALL MILESTONES


internal const val REQUEST_CREATE_CHILD: Int
Request code for starting CreatePersonActivity for result, to create a new Person which would be the child of the Person (parent).


{{ todaydate | lowercase }}
{{ description }}
note: yes, it is available.
note: no, it is not available.


The settings in this tab are only used when using the
Download button and not when using Filter & Display button below.
Configure Downloads
{{ form.file_type }}
The VCF file export contains the bare minimum information (genomic variant and genotype, coverage, allelic depth, genotype call quality score).
Most filters work but
please note that the HGMD public membership filtration is ignored as well as any filters for flags, comments, and ClinVar details.
{% for member in form.get_pedigree_with_samples %}
{% if member.has_gt_entries %}
{% with x=form.get_quality_field_names|keyvalue:member.patient|keyvalue:"export" %}
{{ form|keyvalue:x|as_crispy_field }}
{% endwith %}
{% endif %}
{% endfor %}


The ff-connect attribute connects a firebase data set to your HTML. ff-connect is required for firefight to work. Only child elements of ff-connect are controlled by firefight. A web page can have only one ff-connect attribute. atr-apiKey value can be found by clikcing "Add Firebase to your web app" in your firebase overview page. The <body ff-connect="myAppName" atr-apiKey="myAppKey"> element is an excellent place for the ff-connect attribute.
<div ff-connect="ff-demo" atr-apiKey="AIzaSyAROOIphKjkh96rn7exluKHbLuFfD9A_lo"> {{someValue}} </div> {{someValue}}
Change "someValue" inside the firebase emulator, hit enter and notice how the result changes. Notice how the second {{someValue}} doesn't get updated because it is not a child of ff-connect.
< Back To DOCS


'Licious Indeed
A League of Legends Club
'Licious Indeed is the culmination of several like-minded individuals that share a passion for video games, League of Legends in particular. They have been around the block a time or two and are looking to expand. Sign up today!


Home
Faculty
CS Concentration
IT Concentration
Scholarship Info
Student Organizations
Board of Visitors
Board of Visitors


Links To all My trainings
Training 1 - simple div stuffs
Training 2 - A website!!
Training 3 - Nav BAR
Training 4 - Flex-1
Training 5 - Scrollsnap
Training 6 - Glassmorphism
Training 7 - flex training 2/a>
Training 8 - nav bar 2 bootstrap