Android "ended" and "pause" events on <video>

In working on implementing playlists for the Brightcove Smart Players, I discovered some pretty annoying discrepancies between the HTML5 video specification and how Android handles video objects.  I've created a droidfix GitHub repository which captures an example of how to solve these problems.

The Problems

On Android devices, similar to the iPhone and iPod touch, video is a modal experience; that is, rather than playing the video within the context of the page, the video is immediately expanded to a full-screen experience. We can debate whether this approach is right or wrong all day long if you'd like; but for now let's just accept it and move on.

On the Android platform, the video player is opened as if it were a new page. To return to the page from the playing (or paused) video, the user must use the device's back button.  Several things do (or don't) happen when you hit the back button:

  1. An "ended" event fires
  2. The currentTime property of the video is reset to 0
  3. A "pause" event does not fire

The ended event is particularly troubling when building a playlist-driven player as the ended event can be used to signal to the playlist that it's time to load the next video. It creates a funny (for the developer; probably not the user) inescapable scenario when the user pauses the video and the next item on the playlist loads immediately until the end of the playlist is reached.

Workaround

I'll point you to the GitHub version of droidfix.js to see the code; including an example page showing the code in action. Here's a quick description of the workaround:

First, when timeupdate events are fired, we store the furthest forward position in the video that has played. We have to do this because of item #2 above; once the video actually ends, we'll lose the last time index.

Now, when an ended event is received, we check to ensure the video has really reached the state specified for an ended event. In other browsers, we check that the currentTime property of the video is equal to the video's duration; if that's true, we've reached the end of the video.  For android, we check that the furthest forward position variable we stored earlier is within 2 seconds of the end of the video. Why 2 seconds? Well, the Android browser doesn't fire a timeupdate event at the end of the video; furthermore, it also doesn't seem to be consistent as to how far from the end of the video it fires its last timeupdate. In my testing, 2 seconds was a long enough buffer to include all examples; it also has the side-effect of being short enough that we can reasonably assert that beyond that point, the user has "completed' the video.

If we determine that the ended event is legit, we fire a series of custom listeners set up in droidfix. However, if we determine this is android telling us that the video has ended when it hasn't, we create a custom pause event and fire it so handlers bound to pause will be triggered.

Go fork and wreak havoc!

I put the example code on GitHub with a pretty descriptive README of how it works along with the current known issues / caveats. Feel free to grab it, modify, comment, etc. If nothing else, it provides an example that you can integrate with your own video solutions.