Time Ranges

Discussion

In the current HTML5 draft cue ranges are available using a DOM API.

This way of doing ranges is less than ideal.

First of all, it is hard to use. The ranges must be added by script, can't be supplied with the media, and the callbacks are awkward to handle. The only way to identify the range a received callback applies to is by creating not one but two separate functions for each range: one for enter, one for exit. While creating functions on-demand is easy in JavaScript it does fall under advanced techniques that most authors will be unfamiliar with. This kind of feature is also not available in all languages that might provide access to the DOM API.

Secondly this mechanism is not very powerful. You can't do anything else with the ranges besides receiving callbacks and removing them. You can't modify them. They are not visible to scripts or CSS. You can't link to them. You can't link out from them. 

Thirdly, a script is somewhat strange place to define the ranges. A set of ranges usually relates closely to some particular piece of media content. The same set of ranges rarely makes much sense in the context of some other content. It seems that ranges should be defined or supplied along with the media content.

Fourth, this kind of callback API is pretty strange creature in the HTML specification. The only other callback APIs are things like setTimeout() and the new SQL API which don't have associated elements. Events are the callback mechanism for everything else.

In SMIL the equivalent concept is the <area> element which is used like this:

<video src="http://www.example.org/CoolStuff">
            <area id="area1" begin="0s" end="5s"/>
            <area id="area2" begin="5s" end="10s"/>
</video>

This kind of approach has several advantages.

The main disadvantage is the relative difficulty of creating ranges from JavaScript since it requires creating elements and giving them attributes. Some sort of shortcut interface could be provided, of course, perhaps similar to the existing API.

The SMIL definition is perhaps a little broad and also the name is not ideal, if the element is primarily used for generating events vs. linking.

We would like to suggest a <timerange> element that can be used as a child of the <video> and <audio> elements.

The timerange element

Media elements may have timeranges as child elements. A timerange represents an interval for which events will fire when the current playback time enters or leaves the interval.

Contexts in which this element may be used:

As a child of a media element, after all source elements, and before any other content.

Content model:

Empty.

Element-specific attributes:

DOM interface:

interface HTMLTimetimeElement : HTMLElement {
           attribute float start; 
           attribute float end;
           attribute boolean pauseOnExit;
           readonly attribute boolean active;
};

The start and end attributes of each timerange are 'clipped' to the effective start and effective end of the media resource. If the value of the end attribute is less than the value of the start attribute, it is automatically set to be equal to the start attribute.

If start is not supplied, its default value is negative infinity, which means it gets assigned the effective start of the media resource.

If end is not supplied, its default value is positive infinity, which means it gets assigned the effective end of the media resource.

The timerange elements may be in any order (they might not be sorted) and may overlap.

The "active" attribute of a time range is true when the currentTime of associated media element is greater to or equal to start and less than end. Otherwise it is false. Before any events fire, the active value of all timeranges is false, and it is also false when the current placyback position is at the effective end of the media resource.

The ontimeenter and ontimeexit attributes are a convenient way to supply a handler for the corresponding events.

Behavior

When the active attribute of a timerange changes value from false to true a simple event "timeEnter" is fired. When the active attribute changes value from true to false a simple event "timeExit" is fired. (These two names are deliberately similar to the spatial mouse events, but there is no timeMoved as it would fire continuously).

If the same time value represents the end of one timerange and the start of another, then the timeExit event will fire before the timeEnter event. The "active" attribute of all ranges is set before all events are dispatched, for any time instant (so interrogating the DOM tree reveals the state that corresponds to the current playback position).

When the current playback position of a media element changes (e.g. due to playback or seeking), the user agent must run the following steps. If the current playback position changes while the steps are running, then the user agent must wait for the steps to complete, and then must immediately rerun the steps. These steps must also be run when a timerange element is added as a child of the media element, after all scripts have finished executing. If a timerange is removed as a child of the media element while it is active, the timerange is set inactive, its timeExit event is fired, and then it is removed.

(These steps are thus run as often as possible or needed — if one iteration takes a long time, this can cause certain ranges to be skipped over as the user agent rushes ahead to "catch up". Effectively time is "sampled" as often as both possible and necessary. This means that events for timeranges in which start equals end might never fire, but such timeranges are legal and user agents should attempt to fire their events.)

  1. Let current ranges be an unordered list of timeranges, initialised to contain all the timeranges of the media element whose start times are less than or equal to the current playback position and whose end times are greater than the current playback position.
  2. Let other ranges be an unordered list of timeranges, initialised to contain all the timeranges of the media element that are not present in current ranges.
  3. Divide the ranges into four sets:
    1. the passive set: other ranges which have their "active" boolean set to "false" (inactive).
    2. the active set: current ranges which have their "active" boolean set to "true" (active).
    3. the entry set: current ranges which have their "active" boolean set to "false" (inactive).
    4. the exit set: other ranges which have their "active" boolean set to "true" (active).
  4. Set the "active" boolean of all the timeranges in the entry set to "true" (active), and the "active" boolean of all the timeranges in the exit set to "false" (inactive).
  5. If the entry set and exit set are both empty, then abort these steps.
  6. If the time was reached through the usual monotonic increase of the current playback position during normal playback, the user agent must then fire a simple event called timeupdate at the media element. (In the other cases, such as explicit seeks, relevant events get fired as part of the overall process of changing the current playback position.)
  7. If the time was reached through the usual monotonic increase of the current playback position during normal playback, and there are timeranges in the exit set that have their "pauseonexit" boolean set to "true", then immediately act as if the element's pause() method had been invoked. (In the other cases, such as explicit seeks, playback is not paused by exiting a timerange, even if that timerange has its "pauseonexit" boolean set to "true".)
  8. Fire all the "timeExit" events for all of the timeranges in the exit set, in any order.
  9. Fire all the "timeEnter" events for all of the timeranges in the entry set, in any order.

(Note that immediately before playback starts, and when the current playback position reaches the end of the media, there are no active timeranges.)

Constructor:

TimeRange(in float start, in float end);
TimeRange(in float start, in float end, in boolean pauseOnExit);

Example

<video src="http://www.example.org/CoolStuff">
            <timerange id=r1 end=10s></timerange>
            <timerange id=r2 end=20s></timerange>
            <timerange id=r3 start=30s ></timerange>
</video>

video.addEventListener("timeEnter", function (event) {
            switch (event.target.id) {
            case r1: 
                        // do stuff
            case r2: 
                        // do more stuff
            }
}, true);

Notes and Future Work

Note that there is an existing concept called timeranges in the HTML5 specification; a new name needs to be found for one or the other.

The event listeners should probably be added to HTMLElement where other listener attributes are. (You should be able to capture events everywhere, not just on target.)

It's possible that the identifiers of the time-ranges could be used as 'anchor points' within the media, so that activating them as a URL would cause the user agent to seek to the beginning of that range. This is related to the Media Fragments working group.

Similarly, it's possible that href URLs could be added to timeranges, such that if the user clicked on the media during the range, the href URL would be dispatched. (This could also be integrated with a usemap).

Both of these have scoping and other implications and we think can be left to further study.

The existing API for adding a timerange to the DOM could be retained as a convenient shortcut, but we don't think it is needed.

There could be a timemoved event fired at timeranges that want it, but there would need to be some sort of movement granularity attribute, or other control, or else it would effectively have to be run at every change of value of the current placyback position, up to and including saturating the CPU.