Saturday, 17 October 2009

Removing The Media Element 'load' Event

Yesterday I checked in a patch that removes support for the 'load' event on <video> and <audio> elements. We simply never fire it. Also, the networkState attribute is now never NETWORK_LOADED. When we've read to the end of the media resource, networkState changes to NETWORK_IDLE. We plan to ship this change for Firefox 3.6.

There are several reasons we're doing this, even though it will break some Web content. One reason is that the spec says that 'load' should only fire when/if we guarantee that we will never again hit the network for data for that media resource. We never guarantee this, because our cache may always discard data. For example if you play through a large media file that doesn't fit in the cache, we'll read from the network again if you seek back to the beginning. If you play through a resource that does fit in the cache, and then play another large resource we might evict blocks from the first resource to make room for the second resource. So we never followed the spec here, and if we do follow the spec then we can never fire the 'load' event.

Another reason is that 'load' is a dangerous and fairly useless event. Web authors expect that every media element will eventually receive a 'load' event, because documents and images do, right? But that expectation is in vain, in many situations a 'load' event will never be fired. Furthermore authors cannot predict those situations because they depend on client cache size and policy. It is quite likely that the author will always get 'load' events during testing but some clients won't get a 'load' event and the site will be broken for those clients. So since the 'load' event won't ever fire in some browsers, and in other browsers will fail to fire in unpredictable situations, authors should never use it. In practice we see authors inappropriately using 'load' when they should be using another event like 'canplaythrough' or 'suspend'.

In fact, this argument is persuasive enough that 'load' for media elements and the NETWORK_LOADED state have been removed from the spec. There was consensus from Mozilla, Google, Apple and Opera developers that this is the right thing to do. I expect that other browsers will be following suit soon; in fact, apparently Chrome has never fired a 'load' event on media elements.

If you have been using 'load' on your <video> or <audio> elements, you should use a different event. There are several to choose from depending on what you actually want:


  • If you want to do something when the video metadata is available (e.g. duration or size), use 'loadedmetadata'.
  • If you want to do something when the first frame of video can be displayed, use 'loadeddata'.
  • If you want to do something when the browser has stopped downloading the resource, use 'suspend'. Note however that the browser might stop before reaching the end of the resource, e.g. if its cache fills up, or the element doesn't have 'autobuffer' set.
  • If you want to do something when there's enough data loaded to start playing, use 'canplay'.
  • If you want to do something when there's enough data loaded to start playing and playback should be able to make it to the end without stalling on the network, use 'canplaythrough'. Note however that 'canplaythrough' might not fire because the browser's cache fills up, in which case you'll get a 'suspend'.
  • If you want to do something when the video has finished playing, use 'ended'.



3 comments:

  1. Good bye load event, you won't be missed! Note that 'suspend' can also fire before any video data is loaded at all, if a poster image is present. This is so that authors can add 100s of videos with poster images without worrying about killing the network.

    ReplyDelete
  2. If I understand correctly, you do not offer an event to play the media when it has finished downloading.
    canplaythrough relies on the datarate not decreasing (which might happen) and suspend doesn't help either, as you say.
    So now what? How can I make the sound play when its done loading? I want to be absolutely sure it'll finish playing without buffering in the middle.

    ReplyDelete
  3. Robert O'Callahan14 November 2009 22:26

    You can never be absolutely sure of that. In Gecko (and Chrome) the media resource has never "finished downloading". It's just cached, and some of the data might be evicted to make room for other media data. You don't know how big the cache is, on a mobile device it might be quite small, so you can't know when that eviction might happen.
    canplaythrough is our best guess. Just use that. But you probably should also check for 'suspend' or fall back to a timeout, since we might never fire canplaythrough if the cache is small and your resource is large.

    ReplyDelete