Bug 18515 - Provide more details on behavior of the media element when the key for an encrypted block is not available
Summary: Provide more details on behavior of the media element when the key for an enc...
Status: RESOLVED FIXED
Alias: None
Product: HTML WG
Classification: Unclassified
Component: Encrypted Media Extensions (show other bugs)
Version: unspecified
Hardware: All All
: P3 normal
Target Milestone: ---
Assignee: Jerry Smith
QA Contact: HTML WG Bugzilla archive list
URL:
Whiteboard:
Keywords:
Depends on: 24368
Blocks:
  Show dependency treegraph
 
Reported: 2012-08-10 04:12 UTC by David Dorwin
Modified: 2014-12-20 01:14 UTC (History)
7 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description David Dorwin 2012-08-10 04:12:55 UTC
This relates to the "Key Presence" step of the Encrypted Block Encountered algorithm [1]. We should provide more guidance on the behavior of the media element when the key for an encrypted block is not available and what it means to be "waiting for a key" [2].

v0.1 of says, "The media element is said to be potentially playing unless playback stops because the stream cannot be decrypted, in which case the media element is said to be waiting for a key." (Note that this is not a MediaError because there are legitimate use cases where a key may have been requested as a result of the needkey event from the Potentially Encrypted Stream Encountered algorithm [3] but has not yet been received by the time the key is required.) We should probably be explicit about how to handle multiple streams and whether to drop frames.

Some questions to address:
1) What should be the state of media element?
2) What happens on seek?
   - Flush just like normal and make sure the decoders, etc. are unblocked?
   - Will waiting on the key cause non-EME events to be fired? Is that okay?
3) What should happen to playback if not all streams need a key to continue?
4) If playback is suspended in one media element, how does this affect an associated MediaController?

Related to the last two questions above, should user agents drop frames or suspend playback when at least one but not all streams need a key to continue? Dropping frames may be desirable if some content (i.e. audio) can be played without the missing key, but continuing one stream may not be possible in some implementations (i.e. if an audio stream needs a key). Choosing to drop frames may also mean that, for example, the initial video frames are never displayed in some use case and media combinations.

Some specific scenarios to consider:
 A) Video needs a key but audio does not.
 B) Audio needs a key but video does not.
 C) There is more than one audio or video stream.
 D) A video or audio stream that is not being rendered needs a key.

Note that if audio is driving playback (time) then playback will probably continue in the first scenario unless explicitly paused. This behavior would be different than the opposite (second) scenario.

In all cases, playback should resume when the key is provided.


[1] http://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1a/encrypted-media/encrypted-media.html#algorithms-enrypted-block
[2] http://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1a/encrypted-media/encrypted-media.html#waiting-for-a-key
[3] http://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1a/encrypted-media/encrypted-media.html#algorithms-encrypted-stream
Comment 1 Mark Watson 2012-08-10 18:30:57 UTC
I think when a key is needed to decrypt a frame, but a key is not (yet) available (and a needkey event has been fired and handled), then the element should behave exactly as it does today if the frame itself is not available. For example if receiving the media over the network and the next frame has not arrived yet. i.e. this should look just like a network stall.

When the key is provided, the behavior should be just the same as when frames have arrived in the network stall case.

This should answer all the questions about multiple streams, MediaController etc. above.
Comment 2 Adrian Bateman [MSFT] 2012-08-28 21:31:47 UTC
Assigned to David.
Comment 3 David Dorwin 2013-03-10 04:23:13 UTC
The stalled event [1] is related to resource fetching [2] and not necessarily the next frame being available. When _data_ for the next frame is not available, it is a blocked media element [3]. That still doesn't address the case where the data is available but is not ready for rendering in time (i.e. has not been decoded).

The lack of a frame for rendering because it can't be decrypted is closer to not having a frame from the decoder, though it could persist for longer periods.

The HTML5 spec doesn't appear to address the case when data or frames for one stream is available and another is not for the same media element.

[1] http://www.w3.org/TR/2012/CR-html5-20121217/embedded-content-0.html#event-media-stalled
[2] http://www.w3.org/TR/2012/CR-html5-20121217/embedded-content-0.html#stall-timeout
[3] http://www.w3.org/TR/2012/CR-html5-20121217/embedded-content-0.html#blocked-media-element
Comment 4 Joe Steele 2013-03-11 15:23:20 UTC
(In reply to comment #3)
> The lack of a frame for rendering because it can't be decrypted is closer to
> not having a frame from the decoder, though it could persist for longer
> periods.

Is the decoder making the determination that the frame is encrypted? I.e. are you expecting to try and decode and then get a failure? That could be problematic on platforms with hardware decoder that might not respond well to trying to decode an encrypted or partially encrypted frame. 

I think it would be better for the decoder to not receive the frame until it can assume that it is plaintext. In this scenario, the "waiting for key" is the same as "network stall" as far as the decoder is concerned.
Comment 5 Joe Steele 2013-03-25 17:00:18 UTC
I believe this case is functionally equivalent to the case where a media element is paused for in-band content. 

Here is some proposed text to handle the "waiting for key" scenario around Section 5.7 (Key Presence).

==============
If there is an event handler for _needkey_:
  If a MediaKeyNeeded event has not been fired for this block key id:
    Queue a task to fire a simple event named _needkey_ at the media element. 
      The event is of type MediaKeyNeededEvent and has:
        initData = block initData
    Start a timer based on the user agents "key stall timeout".
      On timer completion:
        If block key is still null, queue a task to fire a simple event named _waiting_ at the media element.

If a MediaKeyNeeded event has been fired for this block key id:
  User agent should behave as if media element is paused for in-band content (http://www.w3.org/TR/2012/CR-html5-20121217/embedded-content-0.html#paused-for-in-band-content)
  Wait until:
    Block key is not null OR resource fetch algorithm has been aborted

==============

Some open issues with this:
* Should "key stall timeout" be the "stall timeout" from the resource fetch algorithm?
* I am not sure aborting when resource fetch algorithm is aborted is always going to be appropriate, since the resource may have been completely fetched already.
Comment 6 David Dorwin 2013-04-23 18:06:55 UTC
Summary of the discussion in the March 12th telecon:

* joesteele: I see this as a network stall not a decoder stall. Main issue - what is the time period before something else happens.
markw: if you don't have the key you don't know how long it will take to get the key (could be seconds) so this is more similar to network stall. For decoding you know you're going to do this soon. As a user i probably don't care whether it is network or key blocking.
* ddorwin: I separate it from network stall because we have the data, we've de-muxed it, and we're trying to decrypt. Between de-muxing and decoding we know we have a problem with a key
* markw: The behaviour on the API surface seem most similar to network stall regardless of how it gets surfaced. We could describe it as "just like a network stall" or with more detailed steps but the behaviour should be the same.
Comment 7 David Dorwin 2013-04-23 18:49:03 UTC
The easiest thing to do (or at least to specify) is to just follow what the HTMLMediaElement spec says should happen if readyState is not HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.

While it may not be explicit, we can probably decipher what should happen when one stream/track is available but others are not. MSE and EME should probably follow this same behavior when a buffer and key, respectively, are not available. We would probably need a good reason to override this behavior for missing key(s).

To encourage compatibility, it might be a good idea to (non-normatively) describe this behavior.
Comment 8 David Dorwin 2013-04-23 20:22:25 UTC
We may also need to add changing of readyState to some of our algorithms (as MSE has done). We may need to clarify the meaning of "data" for these states since available is likely to be interpreted as the stream is available but would now mean available unencrypted.
Comment 9 Mark Watson 2013-05-21 15:46:36 UTC
I agree with the proposal to just follow what the HTMLMediaElement spec says should happen if readyState is not HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.

And yes, the behaviour of EME and MSE should be similar for the case where some streams are decryptable/available and others are not.
Comment 10 Joe Steele 2013-06-07 22:21:49 UTC
I agree with the proposal to just follow what the HTMLMediaElement spec says should happen if readyState is not HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.

But I am not sure that fully answers the questions I raised. Does this mean the media element remains in the HAVE_FUTURE_DATA state forever? Or can the CDM signal that it has determined that the proper key will never be acquired and therefore playback should be stopped and an error reported?
Comment 11 David Dorwin 2013-08-01 22:54:41 UTC
(In reply to comment #10)
> I agree with the proposal to just follow what the HTMLMediaElement spec says
> should happen if readyState is not HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA.
> 
> But I am not sure that fully answers the questions I raised. Does this mean
> the media element remains in the HAVE_FUTURE_DATA state forever? Or can the
> CDM signal that it has determined that the proper key will never be acquired
> and therefore playback should be stopped and an error reported?

The proposal in Comment 7 was to behave the same as specified when readyState is not HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA. It didn't actually mean that readyState would change.

Comment 8 further adds that we may actually want/need to change readyState. We might define "data" as unencrypted data and say that the element does not have data unless it is/can be decrypted.

Any CDM signal or error would be reported via a MediaKeyError on a MediaKeySession. This would indicate a failure in the session, but it does not affect the state of the element (i.e. it could be resumed by providing the key in another session).
Comment 12 David Dorwin 2013-08-08 18:02:25 UTC
Before we can work on proposed text, we need to decide whether we want to behave like certain readyStates, as in comment 7, or actually change the readyState, as in comment 8. At first glance, the latter seems more correct but a lot more intrusive and I'm not sure what the implications or side effects would be.

What do others think?
Comment 13 Jerry Smith 2013-08-19 22:02:51 UTC
It doesn't seem that any of the current readyState values are well suited to indicate playback is waiting for a key.  In the context of MSE, they indicate buffer status and transitions between buffer states could be used to trigger appending additional data.  Using the existing defined states to also indicate key pending would seem to confuse how apps should respond.

There is a valid need to hold playback until the key is received.  If the media element readyState is HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA, then it is ready to play.  The MediaKeySession potentially can indicate a pending state that would be used to gate playback, though there is discussion on the utility of redundant events/states in the MediaKeySession life cycle bug (21854).
Comment 14 David Dorwin 2013-12-06 23:04:53 UTC
Jerry has an action to write a proposal for this: https://www.w3.org/html/wg/media/track/actions/51
Comment 15 Adrian Bateman [MSFT] 2013-12-18 00:00:06 UTC
Discussed status in the telcon 12/17:

* We have previously decided not to update the media element readyState when blocked waiting for a key because this would, for example, adversely impact other code dependent on readyState such as adaptive rate switching with MSE.

* We do want applications to have a way to detect the state where playback is blocked waiting for a key in order to update their UI to indicate waiting.

* One possible solution is to use the HTML5 waiting event for this purpose and to extended the media element with a waitingReason attribute that could indicate whether the waiting event is for network issues or EME or something else (in future).

Jerry and I are still discussing possibilities here and Jerry has the next action to update this bug.
Comment 16 Jerry Smith 2014-01-14 16:28:47 UTC
I’ve attached a proposed change below that adds a waitingFor attribute to the Media Element, and sets it to either data or mediakeys to signal whether the waiting event was triggered by readyState changes or the need for new media keys.


Add a new Media Element property to section 2. Media Element Extensions:
 
  attribute waitingFor;

With these states defined:

  enum waitingFor {
	“notwaiting”,
	“mediakeys”,
	“data”
  }; 

Add to the playing condition text under 4.2 Encrypted Block Encountered.
 
Existing:
The following paragraph is added to Playing the media resource.
•  A media element is said to be waiting for a key when it would be potentially playing but the user agent has reached a point in the media resource that must be decrypted for the resource to continue and the CDM does not have the necessary key. 
•  The media element leaves this state when seeking but could re-enter it if the same conditions exist.

New:
•  It is possible for media element to be playing protected media and then encounter content that requires new media keys.  For state changes caused by pending key requests or delivery, apply the first appropriate substeps from the following list:
   o  If media element was previously playing and had a waitingFor value of “notwaiting”: 
      -  If readyState changes queue a task to fire a waiting event, the user agent must also set the waitingFor attribute on the Media Element to “data”.
      -  If new mediaKeys are needed to continue and the element has not ended play back, the user agent must set the waitingFor attribute on the Media Element to “mediakeys”, queue a task to fire a simple event named timeupdate at the element, and queue a task to fire a simple event named waiting at the element.
   o  If media element was previously waiting and had a waitingFor value of “data”: 
      -  If readyState changes queue a task to fire a canplay event, the user agent must also set the waitingFor attribute on the Media Element to “notwaiting”.
   o  If media element was previously waiting and had a waitingFor value of “mediakeys”: 
      -  If mediaKeys becomes available for playback to proceed, the user agent must set the waitingFor attribute on the Media Element to “notwaiting”, queue a task to fire a simple event named timeupdate at the element, and queue a task to fire a simple event named canplay at the element.
Comment 17 David Dorwin 2014-01-22 23:13:54 UTC
(In reply to David Dorwin from comment #0)
> Some questions to address:
> 1) What should be the state of media element?
This proposed change in comment #16 addresses this issue, at least from an attribute/event point of view.
The overall idea seems fine. I'd like hear more feedback since this affects HTMLMediaElement.

> 2) What happens on seek?
>    - Flush just like normal and make sure the decoders, etc. are unblocked?
>    - Will waiting on the key cause non-EME events to be fired? Is that okay?
The proposal probably needs to be extended to cover seeking (question #2), both as it relates to the waitingFor attribute, any EME state, flushing, etc.

> 3) What should happen to playback if not all streams need a key to continue?
> 4) If playback is suspended in one media element, how does this affect an
> associated MediaController?
The proposed change tells app developers how to detect the condition, but they still don't know what playback behavior to expect, especially when a subset of streams is unavailable.


I broke the second part (basically, questions #3 and #4) of the original bug into bug 24368 to avoid having two separate threads in this bug. Let's continue to work on #1 and #2 in this bug.
Comment 18 David Dorwin 2014-01-22 23:57:07 UTC
(In reply to Jerry Smith from comment #16)
> I’ve attached a proposed change below that adds a waitingFor attribute to
> the Media Element, and sets it to either data or mediakeys to signal whether
> the waiting event was triggered by readyState changes or the need for new
> media keys.
> 
> 
> Add a new Media Element property to section 2. Media Element Extensions:
s/property/attribute/?

Do we recommend adding the waitingFor attribute to HTMLMediaElement as part of HTML 5.x or only when EME is implemented? While the current use case is EME, it seems generally useful, especially since an HTML5 waiting event might be transitory and because MSE can cause similar conditions (see bug 24368).

>   attribute waitingFor;
  ^ "readonly"

> With these states defined:
> 
>   enum waitingFor {
> 	“notwaiting”,
Maybe "nothing" or "none". Then it reads as "Waiting for nothing/none."

> 	“mediakeys”,
The element is really waiting for a "key" or for "decryption" to occur. The latter may be more inclusive, but "key" is probably sufficient.
These names also work regardless of whether a UA or application support EME or decryption. (This is similar the last purpose of MEDIA_ERR_ENCRYPTED before it was removed - #2 in https://www.w3.org/Bugs/Public/show_bug.cgi?id=21203#c30 is essentially one of the issues we're trying to solve in this bug.)

"mediakeys" sounds too much like the mediaKeys attribute/object (which may actually be valid but not have the needed key).
However, as discussed when choosing the attribute name recently (bug 24117), there could potentially be other types of keys or key-providing mechanisms. Would we want one "key" value for all such key dependencies?
I still think "key" is better overall rather than a confusing name to avoid a conflict that will probably never occur. "decryption" addresses both issues.

> 	“data”
It seems this should be listed second since it is more common and applies to UAs that don't support encrypted media.

Depending on how we deal with the playback scenario (bug 24368) we might want "audiodata", "videodata", etc. Of course, I'm not sure what that looks like when there are multiple tracks of a single type. Maybe waitingFor should be on AudioTrack, VideoTrack, etc. if we want to allow some streams to continue. This will depend on bug 24368.

>   }; 
> 
> Add to the playing condition text under 4.2 Encrypted Block Encountered.
>  
> Existing:
> The following paragraph is added to Playing the media resource.
> •  A media element is said to be waiting for a key when it would be
> potentially playing but the user agent has reached a point in the media
> resource that must be decrypted for the resource to continue and the CDM
> does not have the necessary key. 
> •  The media element leaves this state when seeking but could re-enter it if
> the same conditions exist.
> 
> New:
Where exactly should this text be added? The Existing text was "inserted" into a specific location in the HTML5 spec.

> •  It is possible for media element to be playing protected media and then
> encounter content that requires new media keys. 
"playing protected media" could imply "encrypted frames"; this and "new [media] keys" could lead one to believe this should only apply when there is a key rotation after having already played encrypted content.

Also, to avoid confusion with "MediaKeys", I suggest s/media keys/decryption key(s)/.
> For state changes caused by
> pending key requests or delivery, apply the first appropriate substeps from
We should probably generalize this to any time a key is needed, regardless of whether there is a pending key request.
The message to the application is the same, and the HTMLMediaElement does not actually know whether there is an outstanding request - only the MediaKeySession would.

> the following list:
>    o  If media element was previously playing and had a waitingFor value of
> “notwaiting”: 
>       -  If readyState changes queue a task to fire a waiting event, the
This if clause is ambiguous. It might be better to say "If a readyState change queues a task..." if that is the intent. Or maybe there is something other than the fact that the event was queued that we can use as the "trigger".

> user agent must also set the waitingFor attribute on the Media Element to
> “data”.
It seems that this substep would apply to any time a waiting event is fired and not just when caused by an encryption-related wait (as limited by the solid bullet ('•') above.

>       -  If new mediaKeys are needed to continue and the element has not
Same concerns about "new" and - especially - "mediaKeys".

> ended play back, the user agent must set the waitingFor attribute on the
> Media Element to “mediakeys”, queue a task to fire a simple event named
> timeupdate at the element, and queue a task to fire a simple event named
> waiting at the element.
Why is timeupdate fired?

>    o  If media element was previously waiting and had a waitingFor value of
> “data”: 
Does it matter what the readyState changed *to* or do we do this on any state change? This comment might apply to other [sub-]steps as well.

>       -  If readyState changes queue a task to fire a canplay event, the
Same comment as above about the if clause.

> user agent must also set the waitingFor attribute on the Media Element to
> “notwaiting”.
>    o  If media element was previously waiting and had a waitingFor value of
> “mediakeys”: 
>       -  If mediaKeys becomes available for playback to proceed, the user
> agent must set the waitingFor attribute on the Media Element to
> “notwaiting”, queue a task to fire a simple event named timeupdate at the
> element, and queue a task to fire a simple event named canplay at the
> element.
Should this last step be added to step 5 of the update() algorithm where we currently resume playback if "waiting for a key", which is the Existing text that is removed in this proposal.
We might need to say something like "Upon successful resumption of playback, ...<actions specified above>."
Comment 19 Jerry Smith 2014-01-27 23:18:49 UTC
In reply to David from comment #18:

These suggestions all seem fine to me:
-	Set waitingFor to readonly
-	Use waitingFor “none” instead of “notwaiting”
-	Use waitingFor "key" instead of "mediakeys"
-	Put "data" second in the waitingFor enum order
-	Substitute "media keys/decryption keys" for "media keys" in the algorithm language
-	Change one from “playing protected media” to “playing media” to allow for the case where encrypted frames are encountered generally.
-	Altered wording to apply algorithm steps generally (not just to key related waiting).

Left open:
-	Whether to specify “audiodata” or “videodata” under “waitingFor”.  Depends on bug 24368.
-	Whether to add waitingFor directly to the HTMLMediaElement in the HTML5.x spec.  I put it in the EME document because that’s where the need arose.  I agree it might be used more broadly.

Questions:
-	Where should new text be added?  A:  The intent was to add it with the referenced existing text in the same section as the current addition (under “Playing the media resource”).
-	Should ReadState states be explicitly described?  A: My intent was to key on ReadyState changes that triggered “waiting” and “canplay” events.  I believe that completely describes what is intended.
-	How can this be extended for seeking?  On seeking, the normal “seeking” event would be triggered, reflecting the new state.  Is this not sufficient?

Clarification:  Existing text is intended to be retained, with “new” text added below it.

========

Updated change description:
 
  readonly attribute waitingFor;

With these states defined:

  enum waitingFor {
	“none”,
	“data”
	“key”,
  }; 

Add to the playing condition text under 4.2 Encrypted Block Encountered.
 
Following this existing text:
----------------------------
The following paragraph is added to Playing the media resource.
A media element is said to be waiting for a key when it would be potentially playing but the user agent has reached a point in the media resource that must be decrypted for the resource to continue and the CDM does not have the necessary key. 
The media element leaves this state when seeking but could re-enter it if the same conditions exist.

Add this new text:
-----------------
It is possible for media element to be playing media and encounter content that requires media keys/decryption keys.  To accommodate changes caused by key status, apply the first appropriate substeps from the following list:

o	If media element was previously playing and had a waitingFor value of “none”: 
	If a readyState change queues a task to fire a waiting event, the user agent must also set the waitingFor attribute on the Media Element to “data”.
	If a media key(s) /decryption key(s) is needed to continue and the element has not ended playback, the user agent must set the waitingFor attribute on the Media Element to “key”, queue a task to fire a simple event named timeupdate at the element, and queue a task to fire a simple event named waiting at the element.
o	If media element was previously waiting and had a waitingFor value of “data”: 
	If a readyState change queues a task to fire a canplay event, the user agent must also set the waitingFor attribute on the Media Element to “none”.
o	If media element was previously waiting and had a waitingFor value of “key”: 
	If a media key(s) /decryption key(s) becomes available for playback to proceed, the user agent must set the waitingFor attribute on the Media Element to “none”, queue a task to fire a simple event named timeupdate at the element, and queue a task to fire a simple event named canplay at the element.
Comment 20 Adrian Bateman [MSFT] 2014-02-21 23:05:29 UTC
Assigning to me to implement in the spec. Need to include the change proposed in bug 24368 at the same time.
Comment 21 Adrian Bateman [MSFT] 2014-02-24 20:07:19 UTC
I think I've capture this correctly. Please review.
https://dvcs.w3.org/hg/html-media/rev/2346418fc472
Comment 22 David Dorwin 2014-03-24 22:22:10 UTC
I made some minor changes to the text in https://dvcs.w3.org/hg/html-media/rev/e24493b938d9.
Comment 23 David Dorwin 2014-03-24 22:23:17 UTC
Thanks for adding the proposal to the spec - this makes it easier to discuss. My comments are below.

* The intent of the following text is unclear - and perhaps unnecessary given the paragraph above it.
"It is possible for a media element to be playing media and encounter content that requires decryption keys. To accommodate changes caused by key status,"
* The text following it is written like an algorithm, but each item probably belongs in a different algorithm.
  - Some of them probably belong in the main Encrypted Block Encountered and update() algorithms. The former definitely needs to be updated.
  - The rest might be added to any algorithm that fires a "canplay" event.
  - The meaning of each "waitingFor" value and its possible transitions may be useful, but that is probably better done in a non-normative table like the Event Summary.
* In bug 24368, I proposed adding something like ", and suspend playback" to the end of the sentence where a simple event named waiting is fired. I see the other suggestion was adopted, so maybe this was an oversight.
Comment 24 Jerry Smith 2014-04-09 19:09:41 UTC
I agree that the algorithm steps can be distributed as David suggests.  That would more closely follow the existing model for describing canplay event behaviors.

"and suspend playback" should also be added.
Comment 25 Jerry Smith 2014-05-13 01:23:29 UTC
Since this is a distributed change, I'd like to confirm the approach before committing changes.  The request is to update the Encrypted Block Encountered section to indicate a waiting for keys event, and distribute the management of the waitingFor attribute to other algorithms that queue canplay or waiting events.

A possible approach:

1.  Add an algorithm to Queue a "waiting" Event similar to Queue an "error" Event.  Something like:

The following steps are run:
1.	Let the media element be the specified media element object.
2.	Set the waitingFor attribute on the Media Element to "key".
3.	Queue a task to fire a simple event named timeupdate at the element.
4.	Queue a task to fire a simple event named waiting at the element.
5.	Suspend playback.

2.  Include language under Encrypted Block Encountered to run the waiting event algorithm when no keys are available to decrypt the content.  Like:

Run the following steps: 
1.	Let media element be the media element associated with the MediaKeySession.
2.	Run the Queue a "waiting" Event algorithm on the media element.
3.	Abort these steps.

3.  Update current algorithms that queue "canplay" and "waiting" events to properly set the waitingFor attribute state.  This will affect the ReadyState playback algorithms under Playing the Media Resource.

There are also two steps that attempt to resume playback when waiting for keys.  If resumption succeeds, we'll need to reset waitingFor to none.  I haven't found yet where to make that change.
Comment 26 David Dorwin 2014-05-13 01:35:22 UTC
#1 and #2 sound good.

We/you will have to figure out the best way to document changes to algorithms defined in HTML5. I'm less concerned about explicit steps here. We could say something like "Whenever doing X, also do Y." Or we could add a step like in https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#dom-sourcekeysystem.

Don't worry about changing the resume text at this point. I need to update that. My plan is to have a Attempt to Resume algorithm that is called from Update Usable Key IDs, which will also be called at least one more time (bug 25594). You can assign this bug to me to resolve this last point if the other items are fixed before the other bug.
Comment 27 Jerry Smith 2014-05-19 19:36:57 UTC
I've committed these changes under https://dvcs.w3.org/hg/html-media/rev/0aecda2b15c4

I have left resume open, at David's recommendation.
Comment 28 David Dorwin 2014-05-22 23:29:25 UTC
I made some minor changes to Jerry's changeset in https://dvcs.w3.org/hg/html-media/rev/a3759a1510ea.

I addressed resume in https://dvcs.w3.org/hg/html-media/rev/6f23a0371a5a and https://dvcs.w3.org/hg/html-media/rev/eb3f8e2d323c.

It's difficult to document when to call canplay on resume since there can be multiple blocked tracks. I think the text is sufficient to describe the desired behavior, though.

I didn't see an answer to this question: Why is timeupdate fired? The current playback position hasn't changed, but I guess this is consistent with the steps for "If the previous ready state was HAVE_FUTURE_DATA or more, and the new ready state is HAVE_CURRENT_DATA or less" in HTML5.

I did not add a step to fire "timeupdate" when resuming. It's not fired "If the new ready state is HAVE_ENOUGH_DATA" is in HTML5.

Do we need to add the following to the resume steps (in addition to "canplay")?
"if the element's paused attribute is false, queue a task to fire a simple event named playing at the element."

If we can answer these questions, I think we can close this bug.
Comment 29 David Dorwin 2014-05-23 00:08:01 UTC
Domenic recently added a section on using promises for state transitions to the TAG's promises guide: https://github.com/w3ctag/promises-guide#more-general-state-transitions. We could use this for "waiting for a key" (instead of "waiting" and waitingFor="key").

The main advantage would be that authors can react to a "wait" that has already occurred. Checking waitingFor has the same effect.

There is also the tradeoff of authors already using and being familiar with "waiting" vs. changing the meaning of an established event and adding another attribute.
Comment 30 Jerry Smith 2014-06-16 19:15:05 UTC
I believe promises may be a viable option to the waitingFor attribute, and might be cleaner.  I agree it has an advantage on current implementations that already use waiting.

Do you have a specific proposal on this?  I assume you are thinking that createSession/loadSession promise waits would be used to communicate waiting for a key.  The completed promise would then allow playback to resume.  Correct?
Comment 31 David Dorwin 2014-06-16 20:53:41 UTC
(In reply to Jerry Smith from comment #30)
> Do you have a specific proposal on this?  I assume you are thinking that
> createSession/loadSession promise waits would be used to communicate waiting
> for a key.  The completed promise would then allow playback to resume. 
> Correct?

I'm not sure what you mean, but here is a rough proposal.

Add the following to HTMLMediaElement:
  readonly attribute Promise<any> waitingForKey;

The promise would be resolved in the Queue a "waiting" Event algorithm.

The promise object behind the attribute would be replaced in the Attempt to Resume Playback If Necessary algorithm before queueing the canplay event[1].

As explained below, the replacement is somewhat complex because there is no obvious point when the promise should be reset (i.e. the application calling a method). Applications will need to know to listen to the "canplay" event, and we still might have a race condition. Maybe we can improve these areas. The current text is also a bit complicated for similar reasons.


[1] Specifically, I propose the following text (the text of steps 1, 4, and 5 are unchanged)

1. Let the media element be the specified HTMLMediaElement object.
2. If the promise represented by the waitingForKey attribute on the media element is not resolved, abort these steps.
3. Set the waitingForKey attribute on the media element to a new promise.
4. Attempt to resume the resource fetch algorithm by running the Encrypted Block Encountered algorithm. 
5. If the user agent can advance the current playback position in the direction of playback, queue a task to fire a simple event named canplay at the media element.

Note that a new promise is created before attempting to resume. If the resume fails, that promise will also be resolved. This ensures that the promise is "reset" even if playback cannot advance for other reasons. If an application happens to get that promise and there is still a key missing, that promise will also be rejected.

In any case, applications should listen for the "canplay" event and call .then() on the new promise.

There might be some race condition around step 2 above and another call to this algorithm (i.e. two update() calls close together while resume is still in progress).
Comment 32 David Dorwin 2014-06-16 21:02:21 UTC
(In reply to David Dorwin from comment #31)
One possible solution would be to ensure that the promise for any method that can invoke the Resume Playback If Necessary algorithm is not resolved until that algorithm has been completed. The application could then get the latest promise. We'd have to think more about the impact of the delay (and complexity) in resolving those promises.

We should also keep in mind that, although not shown in the algorithms, the Resume Playback If Necessary algorithm could be run for reasons other than those methods. Specifically, it may be invoked by the Update Usable Key IDs algorithm, which _could_ be run as the result of an existing key becoming valid.
Comment 33 David Dorwin 2014-06-17 15:43:29 UTC
The following should have said:
(In reply to David Dorwin from comment #32)
> (In reply to David Dorwin from comment #31)
> One possible solution to the timing issue with the promise solution...
Comment 34 Jerry Smith 2014-07-03 21:22:01 UTC
It seems that either approach could work.  I prefer the WaitingFor option because it seems more direct and intuitive than using promises to reflect state.  

Perhaps the issue of extending an already in use waiting event might be a decisive factor.  For me, extending the existing event is a positive.  Do others believe it will present problems during EME adoption?
Comment 35 David Dorwin 2014-07-08 21:38:53 UTC
As discussed in the telcon today, let's stick with waitingFor.

For the record, the following was mentioned as another advantage of promises in comment #29:
> The main advantage [of using a promise] would be that authors can react to a "wait" that has
> already occurred. Checking waitingFor has the same effect.
Comment 36 David Dorwin 2014-07-08 21:43:04 UTC
Back to the waitingFor solution, which is already implemented in the spec, we should address the following comments I made in comment #28:

> It's difficult to document when to call canplay on resume since there can be
> multiple blocked tracks. I think the text is sufficient to describe the
> desired behavior, though.
> 
> I didn't see an answer to this question: Why is timeupdate fired? The
> current playback position hasn't changed, but I guess this is consistent
> with the steps for "If the previous ready state was HAVE_FUTURE_DATA or
> more, and the new ready state is HAVE_CURRENT_DATA or less" in HTML5.
> 
> I did not add a step to fire "timeupdate" when resuming. It's not fired "If
> the new ready state is HAVE_ENOUGH_DATA" is in HTML5.
> 
> Do we need to add the following to the resume steps (in addition to
> "canplay")?
> "if the element's paused attribute is false, queue a task to fire a simple
> event named playing at the element."

We may also want to look at corner cases, alternative paths, race conditions, etc. like those I identified for a possible promises-based solution in comment #31 and comment #32.

Jerry (and others), please review and reply to the comments above. Once we're satisfied with the resolutions (and that there are not any corner cases we're missing), we can close this bug. I think we're close.
Comment 37 Jerry Smith 2014-07-11 21:13:55 UTC
I carried over timeupdate to be consistent with the readyState steps you pointed out.  

The timeupdate event is supposed to fire when “The current playback position changed as part of normal playback or in an especially interesting way, for example discontinuously.”  Based on that, I believe it should be removed from the waiting steps in EME.

I also agree with updating the resume steps to handle the playing case.  I believe the model is to provide both the canplay and playing events.  I propose inserting the playing event as a new step 5:

"5.  If the element's paused attribute is false, the user agent must queue a task to fire a simple event named playing at the element."

Do you have specific race conditions you think we need to manage?  Comment 31 specifically mentions two update() calls close together while resume is still in progress.  This might result in out of order resume/wait outcomes.  Is that your area of concern?
Comment 38 David Dorwin 2014-07-11 22:08:01 UTC
(In reply to Jerry Smith from comment #37)
> I carried over timeupdate to be consistent with the readyState steps you
> pointed out.  
> 
> The timeupdate event is supposed to fire when “The current playback position
> changed as part of normal playback or in an especially interesting way, for
> example discontinuously.”  Based on that, I believe it should be removed
> from the waiting steps in EME.

Sounds good.

> I also agree with updating the resume steps to handle the playing case.  I
> believe the model is to provide both the canplay and playing events.  I
> propose inserting the playing event as a new step 5:
> 
> "5.  If the element's paused attribute is false, the user agent must queue a
> task to fire a simple event named playing at the element."

Sounds good. We need to check that the resume was successful first, right? Maybe we need to restructure step 4 to block the new #5 as well (unless there is a separate condition for "playing".

> Do you have specific race conditions you think we need to manage?  Comment
> 31 specifically mentions two update() calls close together while resume is
> still in progress.  This might result in out of order resume/wait outcomes. 
> Is that your area of concern?

No, I haven't thought through the non-promises case since I wrote the changeset in comment #28. There were obviously some in the promises case, though. Let's address the items above then ask people to review.
Comment 39 Jerry Smith 2014-07-28 23:25:55 UTC
Implemented queue waiting and resume playback steps agreed to previously.

https://dvcs.w3.org/hg/html-media/rev/a4d5a96c1329
Comment 40 David Dorwin 2014-08-06 20:54:56 UTC
I believe the changeset has the structural problem I mentioned in comment #38. I think it should be as follows:

4. If the user agent can advance the current playback position in the direction of playback, run the following steps:
     1. Queue a task to fire a simple event named canplay at the media element.
     2. 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".
Comment 41 Jerry Smith 2014-08-12 16:12:38 UTC
This structural change looks correct to me.  The playing event should only fire if the media element can play.
Comment 42 David Dorwin 2014-08-12 20:46:25 UTC
https://dvcs.w3.org/hg/html-media/rev/fd45eddf3e34 implements the changes proposed in comment #40.
Comment 43 David Dorwin 2014-08-12 20:48:00 UTC
The behavior is now defined. We should keep potential race conditions and other timing issues in mind during implementation and testing and open a new bug if any are found.
Comment 44 David Dorwin 2014-12-20 01:14:46 UTC
https://github.com/w3c/encrypted-media/issues/7 describes problems with the solution implemented as the result of this bug.