Search code examples
javascriptvideohtml5-videomediaelement.jsmediaelement

Javascript function to detect different keyframes of videos


I am developing a simple app where you can live compare two videos. A requirement for this project is that they are perfectly synced. I've done lots of research, and tried almost every single loaded event I could find over Stack Overflow, but no success. I have a new idea, I don't know if this is achievable with Javascript. First of all, here is my code:

HTML:

<video id="leftVideo" src="before.mp4" oncanplaythrough="leftVideoLoaded()" alt="before" loop="true" width="600" height="366"></video>
<video id="rightVideo" src="after.mp4" oncanplaythrough="rightVideoLoaded()" alt="after" loop="true" width="600" height="366"></video>

JS:

var leftVideoLoadedCheck = 0;
var rightVideoLoadedCheck = 0;
function leftVideoLoaded() {
    leftVideoLoadedCheck = 1;
}

function rightVideoLoaded() {
    rightVideoLoadedCheck = 1;
}

$(document).ready(function() {
    $("#container").beforeAfter();

    var leftPlayer = new MediaElement("leftVideo");
    var rightPlayer = new MediaElement("rightVideo");

    var refreashLoadedEvent = setInterval(function(){
        if(leftVideoLoadedCheck == 1 && rightVideoLoadedCheck == 1) {

            setTimeout(function() {
                leftPlayer.play();
                rightPlayer.play();
            }, 1000);

            clearInterval(refreashLoadedEvent);
        }
    }, 50);
});

Here is a live demo:

http://amarsyla.com/sandbox/beforeafter/

I got some good results, but if the videos keep looping a few times, they lose their syncing between them. Is there a way I can setup a function which runs every 500ms, and detects the keyframes of each video. When the keyframes are not the same, pause both videos, and start playing them again at one same keyframe. So, I am seeking for something like this:

setInterval(function() {
    firstVideoKeyframe = ...
    secondVideoKeyframe = ...

    if(firstVideoKeyframe != secondVideoKeyFrame) {
        firstVideo.pause();
        secondVideo.pause();

        var keyframeToPlay = Math.min(firstVideoKeyframe, secondVideoKeyframe);

        firstVideo.playAtKeyframe(keyframeToPlay);
        secondVideo.playAtKeyframe(Math.min(keyframeToPlay));
    }
}, 500);

I hope that's not something that only my imagination can do...

Thanks a lot.


Solution

  • If you're happy with how well it works to call .play() on both videos at once, then the simplest solution may be to not use loop="true" to make the videos loop. Instead, detect when both videos have ended (using the onended event), and then play them both again. (You might need to seek to the beginning of both, then wait for onseeked on both, to make sure they're both ready to play.)

    There is still a danger that one video will get behind the other, mainly because of buffering issues. You can detect where each video is in its playback by looking at .currentTime. If the two values of .currentTime are too far apart, then you'd want to assign one to be the value of the other. (Don't expect them to be exactly equal.)

    The "correct" way to synchronize two videos is using a MediaController, but as far as I know, no browser has implemented that yet. When they do, you can easily synchronize videos by setting the same value of the mediagroup attribute for both video elements (this automatically creates a MediaController and attaches it to both videos).