Question

I have a video element which I am dynamically controlling with javascript. It has been working great. Today I noticed something strange.

I have attached the following event listeners.

function registerListeners(listen) {
    listen.addEventListener('canplaythrough', canStartPlayback);
    listen.addEventListener('ended', onFinish);
    listen.addEventListener('loadeddata', onLoadComplete);
    listen.addEventListener('playing', onPlay);
    listen.addEventListener('error', mediaError);
}

And the code for canStartPlayback onFinish and onPlay is as follows.

function canStartPlayback() {
    if (!playing) {
        playing = true;
        $('.module-content-video-loader').stopSpin();
        $('.module-content-video-loader').hide();
        media.play();
    }
}

function onFinish() {
    if (playing) {
        playing = false;
        media.pause();
        media.currentTime = 0.0;
    }
}

function onPlay() {
    playing = true;
    if (!pending) {
        $('#skip').show();
    }
}

Now, I am currently finding that after onFinish has completed, I am immediately seeing canStartPlayback being fired again.

This is a description of the canplaythrough from Mozilla

canplaythrough Sent when the ready state changes to CAN_PLAY_THROUGH, indicating that the entire media can be played without interruption, assuming the download rate remains at least at the current level. Note: Manually setting the currentTime will eventually fire a canplaythrough event in firefox. Other browsers might not fire this event.

I was under the impression, and have been seeing that canplaythrough is only fired once. Has chrome changed this recently and has anyone else found this to be occurring?

Was it helpful?

Solution

The canplaythrough event (like canplay) fires as the readyState property changes. These events and the property reflect how much video the browser is able to play from the current position. So if you seek to a new position that is not buffered, the browser can no longer play from your current position, so the readyState has to drop down to HAVE_METADATA. Once that new section is loaded, readyState eventually works its way back up to HAVE_ENOUGH_DATA, and the event will fire again.

As of this fix from 2009, Chrome mimics the behavior of Firefox, which is to drop the readyState down to HAVE_METADATA on every seek, even if the video is fully buffered. Presumably, this is because even if the video is loaded from the network, it still takes time to load that section back into memory and display the current frame.

If you need to make sure your event only gets handled once, you can maintain more detailed state, or you can remove the listener after it runs the first time, like this:

media.removeEventListener('canplaythrough', canStartPlayback);

Here's an example that fires a bunch of these events and logs them to the console so you can see them in action when you seek: http://jsbin.com/buparoye/1/edit

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top