[EME] Object-oriented API design proposal

This proposal relates to the discussion in [1] and bug [2] as well as
others. In those discussions and the teleconference [3] this week, the
group agreed to pursue a session object-based design for the API. This
proposal is intended to provide a more concrete basis for discussion but is
not intended to be final. There are still some open questions, which I have
identified below the proposal.

Please provide feedback, discuss, and provide input on the questions so we
can try to close this issue during the teleconference in two weeks. If you
have questions that aren't directly related to this proposal or questions
(i.e. regarding the contents of initData) please start a new thread so we
can keep this one focused.

*Summary of Changes*

   - HTMLMediaElement has generateKeyRequest() and receives need key events/
   - MediaKeySession has addKey() and close() and receives keymessage,
   keyadded, and keyerror events.
   - All methods have less parameters because some are now represented as
   attributes of the session object.
   - There are only two custom event types, both with fewer attributes.
   This is because keyadded and keyerror are simple events.
   - All error information is obtained from the error attribute of the
   session object. code and systemCode are members of MediaKeyError. This more
   closely matches MediaError.


A couple caveats:

   - The proposal still uses video.generateKeyRequest() to create the
   object, though, that may change per
   http://lists.w3.org/Archives/Public/public-html-media/2012Jun/0134.html.
   - It does not currently define/support reuse of session objects as was
   discussed in the teleconference [3]. See
   http://lists.w3.org/Archives/Public/public-html-media/2012Jun/0133.html.


*Changes*
Below are updates of selected text from the original document. They show
the most important changes related to this proposal.

The following may not be readable in a plain text version of this email, so
I've included a plain text version of it at the very end of my email.

partial interface HTMLMediaElement {
  // Adds optional 'keySystem' parameter.
  DOMString canPlayType
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-canplaytype>(in
DOMString type, in DOMstring? keySystem
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_key-system>);

  // key sessions
  MediaKeySession
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-mediakeysession>
generateKeyRequest
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-generatekeyrequest>(in
DOMString keySystem
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_key-system>,
in Uint8Array? initData);
};

interface MediaKeySession : EventTarget
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-eventtarget>
{
  // error state
  readonly attribute MediaKeyError
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-mediakeyerror>?
error <#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-error>;

  // session properties
  readonly attribute DOMString keySystem
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-keysystem>;
  readonly attribute DOMString sessionId
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-sessionid>;

  // session operations
  void addKey <#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-addkey>(in
Uint8Array key);
  void close <#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-close>();
};

partial interface HTMLSourceElement {
             attribute DOMString keySystem
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-sourcekeysystem>;
};

The keySystem attribute is an identifier for the Key
System<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_key-system>
being
used.

The sessionId attribute is the Session
ID<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_session-id>
for
this objects and the associated key(s) or license(s).

The error attribute is a
MediaKeyError<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-mediakeyerror>
representing
the current error state of the session. It is null if there is no error.


interface MediaKeyError {
  const unsigned short MEDIA_KEYERR_UNKNOWN
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-media_keyerr_unknown>
= 1;
  const unsigned short MEDIA_KEYERR_CLIENT
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-media_keyerr_client>
= 2;
  const unsigned short MEDIA_KEYERR_SERVICE
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-media_keyerr_service>
= 3;
  const unsigned short MEDIA_KEYERR_OUTPUT
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-media_keyerr_output>
= 4;
  const unsigned short MEDIA_KEYERR_HARDWARECHANGE
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-media_keyerr_hardwarechange>
= 5;
  const unsigned short MEDIA_KEYERR_DOMAIN
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-media_keyerr_domain>
= 6;
  readonly attribute unsigned short code
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-code>;
  readonly attribute unsigned long systemCode
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-systemcode>;
};

session . error .
code<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-code>

Returns the current error's error code, from the list below.
session . error .
systemCode<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-systemcode>

Returns the current error's system code.

The code attribute of a
MediaKeySession<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-mediakeysession>
object
must return the code for the error, which must be one of the following:

*...skipping unchanged error code text...*
The systemCode attribute of a
MediaKeySession<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-mediakeysession>
object
is a Key System<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_key-system>-dependent
status code for the error that occurred. This allows a more granular status
to be returned than the more general
errorCode<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-errorcode>
. It should be 0 if there is no associated status code or such status codes
are not supported by the Key System.
Events 3.1. Event Definitions

[Constructor(DOMString type, optional MediaKeyMessageEventInit
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-mediakeymessageeventinit>
eventInitDict)]
interface MediaKeyMessageEvent : Event
<http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#event> {
  readonly attribute Uint8Array message
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-message>;
  readonly attribute DOMString? destinationURL
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-destinationurl>;
};

dictionary MediaKeyMessageEventInit : EventInit
<http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#eventinit> {
  Uint8Array message
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-message>;
  DOMString? destinationURL
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-destinationurl>;
};

[Constructor(DOMString type, optional MediaKeyNeededEventInit
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-mediakeyneededeventinit>
eventInitDict)]
interface MediaKeyNeededEvent : Event
<http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#event> {
  readonly attribute Uint8Array? initData
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-initdata>;
};

dictionary MediaKeyNeededEventInit : EventInit
<http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#eventinit> {
  Uint8Array? initData
<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-initdata>;
};

event . destinationURL<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-destinationurl>

Returns the URL to send the
message<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-message>
 to.
event . initData<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-initdata>

Returns the Initialization
Data<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_initialization-data>
related
to the event.
event . message<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-message>

Returns the message (i.e. key request) to send.

The initData attribute contains Initialization
Data<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_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 send the
message<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-message>
to.
An application *may* override this. In some cases, it may have been
provided by the media data<http://dev.w3.org/html5/spec/video.html#media-data>
. It may be null.

If a response (i.e. a license) is necessary, applications should use one of
the new methods<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-htmlmediaelement>
to
provide the response.
 3.2. Event Summary

The following events are fired at
MediaKeySession<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-mediakeysession>
.
 Event nameInterface Dispatched when...Preconditions keyadded Simple
eventA key has been added as the result of a
addKey()<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-addkey>
 call. keyerror Simple event An error occurs in the session. keymessage
MediaKeyMessageEvent<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-mediakeymessageevent>A
message has been generated (and
likely needs to be sent to a server). For example, a key request has been
generated as the result of
agenerateKeyRequest()<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-generatekeyrequest>
call
or another message must be sent in response to an
addKey()<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-addkey>
 call.

The following event is fired at
HTMLMediaElement<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-htmlmediaelement>
.
Event name InterfaceDispatched when... Preconditions needkey
MediaKeyNeededEvent<#1383187a569bf054_138309ca0b801945_1383098cbd9c7783_13830982a754ed58_dom-mediakeyneededevent>The
user agent needs a key or license to begin or continue playback.

It may have encountered media
data<http://dev.w3.org/html5/spec/video.html#media-data> that
may/does require decryption to load or play OR need a new key/license to
continue playback.
readyState<http://dev.w3.org/html5/spec/video.html#dom-media-readystate>
is
equal to HAVE_METADATA<http://dev.w3.org/html5/spec/video.html#dom-media-have_metadata>
or
greater. It is possible that the element is playing or has played.




*Other Text Additions and Notes*

   - generateKeyRequest() returns null if keySystem is not supported. Even
   if a valid object is returned, an error may still occur. Applications
   should handle the keyerror event upon creation to detect such errors.
   - CDMs must always fire a keymessage or keyerror after
   generateKeyRequest() unless a null object was returned.
   - The CDM should treat all key identifiers in the initData the same.
   That is, it should generate a license request for all of them.
   <non-normative>For example, regardless of whether one or more of the key
   IDs is already known in a different session. In addition to being simpler,
   this avoids potential problems if the other session is
   closed.</non-normative>
   - Note: Session IDs are required. (For consistency and because it is
   simple to implement.)
   - Note: Each initData value, whether it be from the same stream or
   multiple streams (e.g. audio and video) must be in a separate session
   object and thus has a different session ID.


In most cases, generateKeyRequest() will return a valid object and fire
either a keymessage or keyerror event at it. Therefore, applications should
register handlers for the object as soon as it is created. For example:
  var session = video.generateKeyRequest(keySystem, initData);
  if (session) {
    session.onkeymessage = handleKeyMessage;
    session.onkeyerror = handleKeyError;
  }

*Referencing the Element for a Session*
There was a request in the teleconference to have a link back to the media
element. I do not think we need or should add this because the application
can handle this if it wants and it may present problems if we choose to
allow sharing of sessions among elements in the future. An application
might perform this association as shown below.
  var session = video.generateKeyRequest(keySystem, initData);
  if (session)
    session.element = video;
The application can then use this.element in the event handlers.

*Bugs Addressed*
This change addresses the following bugs:

   - https://www.w3.org/Bugs/Public/show_bug.cgi?id=16548 - Enforces
   that generateKeyRequest() is called in all cases [before addKey()] without
   any additional logic.
   - https://www.w3.org/Bugs/Public/show_bug.cgi?id=16549 - initData
   parameter can be removed from addKey.
   - https://www.w3.org/Bugs/Public/show_bug.cgi?id=16550 - sessionId
   parameter is eliminated, so checking them is obsolete.
   - https://www.w3.org/Bugs/Public/show_bug.cgi?id=16612 - All methods
   except the creation method are in a new object interface.
   - https://www.w3.org/Bugs/Public/show_bug.cgi?id=16613 - Sessions are
   represented as objects.
   - https://www.w3.org/Bugs/Public/show_bug.cgi?id=16614 - initData,
   generateKeyRequest() calls, their responses, and all subsequent messages
   and addKey calls can all be easily correlated.
   - https://www.w3.org/Bugs/Public/show_bug.cgi?id=17203 - We require
   session IDs for all key systems.


*Open Questions*

   1. Should we create the object using "new MediaKeySession" rather than
   video.generateKeyRequest()? See
   http://lists.w3.org/Archives/Public/public-html-media/2012Jun/0134.html.
   2. Should we support reuse of session objects? See
   http://lists.w3.org/Archives/Public/public-html-media/2012Jun/0133.html.
   3. Do user agents need to support sessions objects from support multiple
   key systems on the same element?
      1. There is nothing preventing this in the proposal, but it seems
      like a problem for implementations. It would definitely make the Chrome
      implementation more complex.
   4. Is sessionId valid upon creation of the object or only after the
   first keymessage is received?
      1. The former is simplest to reason about but requires that the
      session IDs be allocated synchronously (and possibly by the user agent
      instead of the CDM).
   5. Do we still need a cancel()/close() method on the session object or
   is destroying the object (after garbage collection) sufficient?
      1. Is the answer to the above questions sufficient for explicitly
      destroying the keys (
      https://www.w3.org/Bugs/Public/show_bug.cgi?id=16547)?
   6. Should we allow session objects to be shared (
   https://www.w3.org/Bugs/Public/show_bug.cgi?id=16615 and
   https://www.w3.org/Bugs/Public/show_bug.cgi?id=17202)?
      1. We could punt on this until we have real use cases, but some
      decisions we make now may affect this. For example, see question #1.
   7. Should we drop "key" from the name of events now fired at the object?
      1. "key" is implicit since they are fired at a key session object.
      2. However, it may be easier to discuss "keyerror" events rather than
      just "error" events, which could refer to any object. Specifically, it
      might be confused with
      http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#event-media-error
      .



[1] http://lists.w3.org/Archives/Public/public-html-media/2012Jun/0054.html
[2] https://www.w3.org/Bugs/Public/show_bug.cgi?id=16613
[3] http://www.w3.org/2012/06/26-html-media-minutes.html



========== Begin Plain Text Version of Changes Section ==========

partial interface HTMLMediaElement {
  // Adds optional 'keySystem' parameter.
  DOMString canPlayType(in DOMString type, in DOMstring? keySystem);

  // key sessions
  MediaKeySession generateKeyRequest(in DOMString keySystem, in Uint8Array?
initData);
};

interface MediaKeySession : EventTarget {
  // error state
  readonly attribute MediaKeyError? error;

  // session properties
  readonly attribute DOMString keySystem;
  readonly attribute DOMString sessionId;

  // session operations
  void addKey(in Uint8Array key);
  void close();
};

partial interface HTMLSourceElement {
             attribute DOMString keySystem;
};
The keySystem attribute is an identifier for the Key System being used.

The sessionId attribute is the Session ID for this objects and the
associated key(s) or license(s).

The error attribute is a MediaKeyError representing the current error state
of the session. It is null if there is no error.

interface MediaKeyError {
  const unsigned short MEDIA_KEYERR_UNKNOWN = 1;
  const unsigned short MEDIA_KEYERR_CLIENT = 2;
  const unsigned short MEDIA_KEYERR_SERVICE = 3;
  const unsigned short MEDIA_KEYERR_OUTPUT = 4;
  const unsigned short MEDIA_KEYERR_HARDWARECHANGE = 5;
  const unsigned short MEDIA_KEYERR_DOMAIN = 6;
  readonly attribute unsigned short code;
  readonly attribute unsigned long systemCode;
};

session . error . code
Returns the current error's error code, from the list below.

session . error . systemCode
Returns the current error's system code.

The code attribute of a MediaKeySession object must return the code for the
error, which must be one of the following:

...skipping unchanged error code text...

The systemCode attribute of a MediaKeySession object is a Key
System-dependent status code for the error that occurred. This allows a
more granular status to be returned than the more general errorCode. It
should be 0 if there is no associated status code or such status codes are
not supported by the Key System.


Events

3.1. Event Definitions

[Constructor(DOMString type, optional MediaKeyMessageEventInit
eventInitDict)]
interface MediaKeyMessageEvent : Event {
  readonly attribute Uint8Array message;
  readonly attribute DOMString? destinationURL;
};

dictionary MediaKeyMessageEventInit : EventInit {
  Uint8Array message;
  DOMString? destinationURL;
};
[Constructor(DOMString type, optional MediaKeyNeededEventInit
eventInitDict)]
interface MediaKeyNeededEvent : Event {
  readonly attribute Uint8Array? initData;
};

dictionary MediaKeyNeededEventInit : EventInit {
  Uint8Array? initData;
};
event . destinationURL
Returns the URL to send the message to.

event . initData
Returns the Initialization Data related to the event.

event . message
Returns the message (i.e. key request) to send.

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 send the message to. An application may
override this. In some cases, it may have been provided by the media data.
It may be null.

If a response (i.e. a license) is necessary, applications should use one of
the new methods to provide the response.

3.2. Event Summary

The following events are fired at MediaKeySession.

Event name Interface Dispatched when... Preconditions
keyadded Simple event A key has been added as the result of a addKey() call.
keyerror Simple event An error occurs in the session.
keymessage MediaKeyMessageEvent A message has been generated (and likely
needs to be sent to a server). For example, a key request has been
generated as the result of agenerateKeyRequest() call or another message
must be sent in response to an addKey() call.
The following event is fired at HTMLMediaElement.

Event name Interface Dispatched when... Preconditions
needkey MediaKeyNeededEvent The user agent needs a key or license to begin
or continue playback.

It may have encountered media data that may/does require decryption to load
or play OR need a new key/license to continue playback. readyState is equal
to HAVE_METADATA or greater. It is possible that the element is playing or
has played.

Received on Thursday, 28 June 2012 05:24:57 UTC