This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 13871 - <video> behavior when setting currentTime to current playback position
Summary: <video> behavior when setting currentTime to current playback position
Status: CLOSED WORKSFORME
Alias: None
Product: HTML WG
Classification: Unclassified
Component: HTML5 spec (show other bugs)
Version: unspecified
Hardware: All All
: P2 normal
Target Milestone: ---
Assignee: Ian 'Hixie' Hickson
QA Contact: HTML WG Bugzilla archive list
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-08-23 05:19 UTC by Glenn Adams
Modified: 2011-10-20 12:49 UTC (History)
5 users (show)

See Also:


Attachments

Description Glenn Adams 2011-08-23 05:19:03 UTC
When setting HTMLMediaElement.currentTime to the same time as the current playback position, i.e., the current value of currentTime, then the operation should succeed (without exception) immediately, and without attempting to access media resource.

That is, performing

media.currentTime = media.currentTime

should always succeed, and should not cause a seek task to be queued.
Comment 1 Philip Jägenstedt 2011-08-23 12:18:25 UTC
Why? It seems more predictable if setting currentTime always does the same thing. Otherwise scripted UIs that depend on the seeked event being fired after currentTime is set would fail at random when the user happens to seek to a time that is the same as currentTime.
Comment 2 Glenn Adams 2011-08-23 16:07:03 UTC
(In reply to comment #1)
> Why? It seems more predictable if setting currentTime always does the same
> thing. Otherwise scripted UIs that depend on the seeked event being fired after
> currentTime is set would fail at random when the user happens to seek to a time
> that is the same as currentTime.

i notice this when running basic API tests on audio/video element interfaces to verify mutability of non-readonly IDL attributes, which in this case, basically reduced to:

e = document.createElement('video');
e.currentTime = e.currentTime;

the second statement results in an INVALID_STATE_ERROR exception because, in this devolutionary case,

(1) e.controller is null (no controller), and
(2) e.readyState == HAVE_NOTHING

part of the problem here is that "new playback position" is not well defined, since it can mean "new, different playback position" or "new, not necessarily different playback position";

i would also note that step (7) under 4.8.10.9, may result in an non-exception raising abort of the seek without firing a seeking event; this would occur "If there are no ranges given in the seekable attribute then set the seeking IDL attribute to false and abort these steps."

i would propose doing the following:

(1) defining "new playback position" as "new, different playback position", where different means != the current playback position as denoted (synchronically at set time) by the current value of currentTime;

(2) modify step (1) under 4.8.10.9 to read as follows:

"If the media element's readyState is HAVE_NOTHING, then (1) if the new playback position is the same as the current playback position, abort these steps; otherwise (2) raise an INVALID_STATE_ERR exception (if the seek was in response to a DOM method call or setting of an IDL attribute), and abort these steps."
Comment 3 Philip Jägenstedt 2011-08-24 07:02:25 UTC
(In reply to comment #2)

> i notice this when running basic API tests on audio/video element interfaces to
> verify mutability of non-readonly IDL attributes, which in this case, basically
> reduced to:
> 
> e = document.createElement('video');
> e.currentTime = e.currentTime;
> 
> the second statement results in an INVALID_STATE_ERROR exception because, in
> this devolutionary case,
> 
> (1) e.controller is null (no controller), and

This seems wrong, it will throw INVALID_STATE_ERROR if it *has* a controller.

> (2) e.readyState == HAVE_NOTHING

This will happen regardless of what you set currentTime to.

Neither of the above are related to what the old or new currentTime is.

> part of the problem here is that "new playback position" is not well defined,
> since it can mean "new, different playback position" or "new, not necessarily
> different playback position";

It's well defined, it just doesn't make a difference between these two cases.

> i would also note that step (7) under 4.8.10.9, may result in an non-exception
> raising abort of the seek without firing a seeking event; this would occur "If
> there are no ranges given in the seekable attribute then set the seeking IDL
> attribute to false and abort these steps."

Right, for a resource which is completely unseekable no seeked event will be fired. However, that will be the case regardless of what you set currentTime to, so that won't cause scripts to fail at random.

> i would propose doing the following:
> 
> (1) defining "new playback position" as "new, different playback position",
> where different means != the current playback position as denoted
> (synchronically at set time) by the current value of currentTime;

It seems like this could only work when the media is paused. If it is playing, currentTime is continuously changing so if you check for equality in step x, it will no longer be equal in step x+1.

> (2) modify step (1) under 4.8.10.9 to read as follows:
> 
> "If the media element's readyState is HAVE_NOTHING, then (1) if the new
> playback position is the same as the current playback position, abort these
> steps; otherwise (2) raise an INVALID_STATE_ERR exception (if the seek was in
> response to a DOM method call or setting of an IDL attribute), and abort these
> steps."

My main question is still "why?" What problem is being solved?
Comment 4 Glenn Adams 2011-08-24 07:43:16 UTC
(In reply to comment #3)
> (In reply to comment #2)
> 
> > i notice this when running basic API tests on audio/video element interfaces to
> > verify mutability of non-readonly IDL attributes, which in this case, basically
> > reduced to:
> > 
> > e = document.createElement('video');
> > e.currentTime = e.currentTime;
> > 
> > the second statement results in an INVALID_STATE_ERROR exception because, in
> > this devolutionary case,
> > 
> > (1) e.controller is null (no controller), and
> 
> This seems wrong, it will throw INVALID_STATE_ERROR if it *has* a controller.

sorry, had the logic inverted

> > (2) e.readyState == HAVE_NOTHING
> 
> This will happen regardless of what you set currentTime to.

I'm suggesting that should not be the case, i.e., an exception should not be raised if the new playback position is not different than the current playback position. In general, I would say that should hold regardless of the readyState, but I'm willing to constrain the interpretation to when readyState is HAVE_NOTHING and the new playback position does not differ.

> My main question is still "why?" What problem is being solved?

The problem is that the current behavior is not what one would expect. I do not expect a state exception when there is no difference in new and current playback position.

By my interpretation a playback position is not a *new* position unless it is different than the current position. As a consequence, no seek is being requested. So why incur an exception resulting from an erroneous attempt to seek to the current position?
Comment 5 Philip Jägenstedt 2011-08-24 08:05:35 UTC
(In reply to comment #4)
> The problem is that the current behavior is not what one would expect. I do not
> expect a state exception when there is no difference in new and current
> playback position.
> 
> By my interpretation a playback position is not a *new* position unless it is
> different than the current position. As a consequence, no seek is being
> requested. So why incur an exception resulting from an erroneous attempt to
> seek to the current position?

A few reasons come to mind:

1. It's simpler to implement, as you don't have to worry about comparing a fixed value with an ever-changing value.

2. It's more reliable for page authors, as UI won't break when one happens to click the seek bar exactly at currentTime.

3. It's impossible to seek in any meaningful way while readyState is HAVE_NOTHING. The sooner a page author is forced to realize this the better, and hiding the exception for this case masks the problem.
Comment 6 Glenn Adams 2011-08-24 16:40:17 UTC
(In reply to comment #5)
> (In reply to comment #4)
> > The problem is that the current behavior is not what one would expect. I do not
> > expect a state exception when there is no difference in new and current
> > playback position.
> > 
> > By my interpretation a playback position is not a *new* position unless it is
> > different than the current position. As a consequence, no seek is being
> > requested. So why incur an exception resulting from an erroneous attempt to
> > seek to the current position?
> 
> A few reasons come to mind:
> 
> 1. It's simpler to implement, as you don't have to worry about comparing a
> fixed value with an ever-changing value.
> 
> 2. It's more reliable for page authors, as UI won't break when one happens to
> click the seek bar exactly at currentTime.

Could you describe how an early exit from the seeking algorithm, i.e., without generating a seeking event, will "break" the page's UI?

I would expect it to act as a NO-OP, simply resulting in no update/change to a seek bar.

Since various other states on the resource may result in a seek exception, a realistic UI would not depend on every setting of currentTime to produce a seeking event in the first place.

> 3. It's impossible to seek in any meaningful way while readyState is
> HAVE_NOTHING.

I agree with this, however, I would assign higher priority to NO-OP semantics than to raising throwing an exception due to an attempt to seek to the current position, even when HAVE_NOTHING applies.
Comment 7 Philip Jägenstedt 2011-08-25 14:16:55 UTC
(In reply to comment #6)
> (In reply to comment #5)
> > 2. It's more reliable for page authors, as UI won't break when one happens to
> > click the seek bar exactly at currentTime.
> 
> Could you describe how an early exit from the seeking algorithm, i.e., without
> generating a seeking event, will "break" the page's UI?
> 
> I would expect it to act as a NO-OP, simply resulting in no update/change to a
> seek bar.

Something like this:

var v = document.querySelector('video');
var c = document.querySelector('.controls');
var s = document.querySelector('.spinner');
c.onclick = function() {
  var newTime = /* calculate new pos based on click position and duration */;
  v.currentTime = newTime;
  /* if readyState was HAVE_NOTHING an exception was thrown and the next line is not reached */
  s.display = 'block'; /* show seeking spinner on top of video/controls */
};
v.seeked = function() {
  s.display = 'none';
};

If the seeked event is not fired, the spinner won't be hidden possibly obscuring the video or the controls, in the worst case making it impossible to issue another seek which would get rid of it.

> Since various other states on the resource may result in a seek exception, a
> realistic UI would not depend on every setting of currentTime to produce a
> seeking event in the first place.

In the example, the spinner is not shown if an exception is thrown when setting currentTime.
Comment 8 Glenn Adams 2011-08-25 16:01:42 UTC
(In reply to comment #7)
> (In reply to comment #6)
> > (In reply to comment #5)
> > > 2. It's more reliable for page authors, as UI won't break when one happens to
> > > click the seek bar exactly at currentTime.
> > 
> > Could you describe how an early exit from the seeking algorithm, i.e., without
> > generating a seeking event, will "break" the page's UI?
> > 
> > I would expect it to act as a NO-OP, simply resulting in no update/change to a
> > seek bar.
> 
> Something like this:
> 
> var v = document.querySelector('video');
> var c = document.querySelector('.controls');
> var s = document.querySelector('.spinner');
> c.onclick = function() {
>   var newTime = /* calculate new pos based on click position and duration */;
>   v.currentTime = newTime;
>   /* if readyState was HAVE_NOTHING an exception was thrown and the next line
> is not reached */
>   s.display = 'block'; /* show seeking spinner on top of video/controls */
> };
> v.seeked = function() {
>   s.display = 'none';
> };
> 
> If the seeked event is not fired, the spinner won't be hidden possibly
> obscuring the video or the controls, in the worst case making it impossible to
> issue another seek which would get rid of it.
> 
> > Since various other states on the resource may result in a seek exception, a
> > realistic UI would not depend on every setting of currentTime to produce a
> > seeking event in the first place.
> 
> In the example, the spinner is not shown if an exception is thrown when setting
> currentTime.

ok, but one wonders about the wisdom of coding this to depend on exceptions; that is certainly not a wise approach to rely upon exceptions for flow control (cf. http://c2.com/cgi/wiki?DontUseExceptionsForFlowControl);

better code have been written as:

c.onclick = function() {
  if ( v.readyState > HTMLMediaElement.HAVE_NOTHING ) {
    var newTime = /* calculate new pos based on click position and duration */;
    v.currentTime = newTime;
    s.display = 'block';
  }
};

one might conclude your example was broken in the first place
Comment 9 Philip Jägenstedt 2011-08-26 06:52:11 UTC
(In reply to comment #8)

> one might conclude your example was broken in the first place

Most authors develop by trial and error, so any code that looks remotely reasonable and works in the common case is going to be deployed. That it is wrong doesn't matter, it's still the browser that didn't fire the seeked event that gets the blame and loses users.
Comment 10 Simon Pieters 2011-08-26 09:50:34 UTC
(In reply to comment #8)
> better code have been written as:
> 
> c.onclick = function() {
>   if ( v.readyState > HTMLMediaElement.HAVE_NOTHING ) {
>     var newTime = /* calculate new pos based on click position and duration */;
>     v.currentTime = newTime;
>     s.display = 'block';
>   }
> };
> 
> one might conclude your example was broken in the first place

It would still break in exactly the same way after this change.
Comment 11 Glenn Adams 2011-08-26 12:17:55 UTC
(In reply to comment #10)
> (In reply to comment #8)
> > better code have been written as:
> > 
> > c.onclick = function() {
> >   if ( v.readyState > HTMLMediaElement.HAVE_NOTHING ) {
> >     var newTime = /* calculate new pos based on click position and duration */;
> >     v.currentTime = newTime;
> >     s.display = 'block';
> >   }
> > };
> > 
> > one might conclude your example was broken in the first place
> 
> It would still break in exactly the same way after this change.

since I proposed that no exception be raised only when:

readyState == HAVE_NOTHING && newTime == currentTime

then it would not "break" the above code, which does not rely upon the exception when readyState == HAVE_NOTHING
Comment 12 Glenn Adams 2011-08-26 12:23:59 UTC
(In reply to comment #11)
> (In reply to comment #10)
> > (In reply to comment #8)
> > > better code have been written as:
> > > 
> > > c.onclick = function() {
> > >   if ( v.readyState > HTMLMediaElement.HAVE_NOTHING ) {
> > >     var newTime = /* calculate new pos based on click position and duration */;
> > >     v.currentTime = newTime;
> > >     s.display = 'block';
> > >   }
> > > };
> > > 
> > > one might conclude your example was broken in the first place
> > 
> > It would still break in exactly the same way after this change.
> 
> since I proposed that no exception be raised only when:
> 
> readyState == HAVE_NOTHING && newTime == currentTime
> 
> then it would not "break" the above code, which does not rely upon the
> exception when readyState == HAVE_NOTHING

a final comment about 'breaking' existing code: that is a red herring argument in the first place, since HTML5 is not final and is subject to breaking changes in at this stage of its development, particularly with respect to new features such as video/audio:

"Implementors should be aware that this specification is not stable. Implementors who are not taking part in the discussions are likely to find the specification changing out from under them in incompatible ways."
Comment 13 Philip Jägenstedt 2011-08-26 12:42:56 UTC
(In reply to comment #11)
> (In reply to comment #10)
> > (In reply to comment #8)
> > > better code have been written as:
> > > 
> > > c.onclick = function() {
> > >   if ( v.readyState > HTMLMediaElement.HAVE_NOTHING ) {
> > >     var newTime = /* calculate new pos based on click position and duration */;
> > >     v.currentTime = newTime;
> > >     s.display = 'block';
> > >   }
> > > };
> > > 
> > > one might conclude your example was broken in the first place
> > 
> > It would still break in exactly the same way after this change.
> 
> since I proposed that no exception be raised only when:
> 
> readyState == HAVE_NOTHING && newTime == currentTime
> 
> then it would not "break" the above code, which does not rely upon the
> exception when readyState == HAVE_NOTHING

It will break when readyState == HAVE_NOTHING && newTime == currentTime, which will happen, but rarely enough that most people won't discover it during testing.
Comment 14 Philip Jägenstedt 2011-08-26 12:46:49 UTC
(In reply to comment #12)
> a final comment about 'breaking' existing code

It is not only existing content that is the issue, we also have to consider the content that will inevitably be written given the copy+paste, trial+error development practices that thrive on the Web.

I will wait for the editors decision now, the pros and cons have been made quite clear.
Comment 15 Simon Pieters 2011-08-26 13:00:13 UTC
(In reply to comment #11)
> since I proposed that no exception be raised only when:
> 
> readyState == HAVE_NOTHING && newTime == currentTime
> 
> then it would not "break" the above code, which does not rely upon the
> exception when readyState == HAVE_NOTHING

I think you have misunderstood Philip's example. The exception was not the point.
The point is that the code expects to get a seeked event when the user clicks
on the controls, even if the user happens to click where the new seek position
matches the current position.
Comment 16 Glenn Adams 2011-08-26 13:13:31 UTC
(In reply to comment #15)
> (In reply to comment #11)
> > since I proposed that no exception be raised only when:
> > 
> > readyState == HAVE_NOTHING && newTime == currentTime
> > 
> > then it would not "break" the above code, which does not rely upon the
> > exception when readyState == HAVE_NOTHING
> 
> I think you have misunderstood Philip's example. The exception was not the
> point.
> The point is that the code expects to get a seeked event when the user clicks
> on the controls, even if the user happens to click where the new seek position
> matches the current position.

I understood it. I also have previously pointed out that even without the change I propose that the code cannot rely upon the seek event, since if there is no seekable range (see step 7 under section 4.8.10.9), the seek process is aborted without generating a seeking event. So your point is a red herring argument.

In any case, I'm limiting the effect of my proposed change on behavior of setting currentTime when readyState is HAVE_NOTHING, in which case the code could not have relied upon a seeking event in the first place.
Comment 17 Ian 'Hixie' Hickson 2011-09-05 04:07:48 UTC
EDITOR'S RESPONSE: This is an Editor's Response to your comment. If you are satisfied with this response, please change the state of this bug to CLOSED. If you have additional information and would like the editor to reconsider, please reopen this bug. If you would like to escalate the issue to the full HTML Working Group, please add the TrackerRequest keyword to this bug, and suggest title and text for the tracker issue; or you may create a tracker issue yourself, if you are able to do so. For more details, see this document:
   http://dev.w3.org/html5/decision-policy/decision-policy.html

Status: Partially Accepted
Change Description: no spec change
Rationale: Setting currentTime to itself always works now when there's no media controller and there is some media loaded. (It doesn't necessarily do nothing, though. For example, if the script has been running for non-zero time at the time this is done, it would in practice result in a backwards seek.)
Comment 18 Glenn Adams 2011-09-05 14:56:43 UTC
It does not "work" when there is no media controller and no media loaded. My position is that setting currentTime to itself in this scenario should be considered a NO-OP, and not cause an exception due to lack of media.

Furthermore, there is an inconsistency regarding treatment in the case where readyState is HAVE_NOTHING. In particular, we have:

4.8.10.6

"On setting, ... if the media element's readyState is HAVE_NOTHING, then it must raise an INVALID_STATE_ERR exception; ..."

4.8.10.9

"1. If the media element's readyState is HAVE_NOTHING, abort these steps."

Why should the first above (4.8.10.6) raise an exception when the second (4.8.10.9) does not?

I'm suggesting that in the case of HAVE_NOTHING, that setting currentTime to itself should not raise an exception and should be a NO-OP. One might also make the argument that setting currentTime to *any* value in the case of HAVE_NOTHING should also be a NO-OP.
Comment 19 Ian 'Hixie' Hickson 2011-10-19 23:56:56 UTC
EDITOR'S RESPONSE: This is an Editor's Response to your comment. If you are satisfied with this response, please change the state of this bug to CLOSED. If you have additional information and would like the editor to reconsider, please reopen this bug. If you would like to escalate the issue to the full HTML Working Group, please add the TrackerRequest keyword to this bug, and suggest title and text for the tracker issue; or you may create a tracker issue yourself, if you are able to do so. For more details, see this document:
   http://dev.w3.org/html5/decision-policy/decision-policy.html

Status: Accepted
Change Description: no spec change
Rationale: Setting .currentTime when there's no media now sets the start time for when the media loads, which hopefully addresses the above comment. (This was done as part of another bug.) Please don't hesitate to reopen this bug if that is still not sufficient, though.
Comment 20 Philip Jägenstedt 2011-10-20 07:32:49 UTC
(In reply to comment #19)

> (This was done as part of another bug.)

Bug 13503, for those who weren't aware of it (me).