Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to keep two videos synchronized in a webpage without plugins?

Tags:

html

video

Is there a way to use two HTML5 video tags on a page and keep the videos synchronized? Meaning that if the first video is at 15.2 seconds then the second video is at 15.2 seconds?

I've looked around and found SMIL but it looks like that only works in IE. I also tried to implement something of my own with jQuery and jMediaElement but there appears to be a lot of cases where the videos can get out of sync.

Has this been done before?

like image 571
Jared Avatar asked Apr 16 '10 17:04

Jared


1 Answers

The demo that was presented on html5demos works but it can easily go out of sync.

Here's an article providing a solution that makes use of requestAnimationFrame (info about it: Leaner, Meaner, Faster Animations with requestAnimationFrame, Animating with javascript: from setInterval to requestAnimationFrame) and it's much better: HTML5 Video: Synchronizing Playback of Two Videos.

Note that the demo provided there (hosted at jsfiddle) has a wrong link on a js source. I updated it on this new page:

http://jsfiddle.net/aqUNf/1/

Bear in mind browser support, this feature is supported by Firefox, Chrome, and from Safari 6 and IE 10 (see table for further details). Otherwise it falls back to setInterval, witch doesn't provide the same performance and battery-saving advantage.

(It uses Popcorn.js by the way, which is a really nice project by Mozilla)

And here's the code (directly taken from the demo):

var videos = {
    a: Popcorn("#a"),    
    b: Popcorn("#b"), 

},
scrub = $("#scrub"),
loadCount = 0, 
events = "play pause timeupdate seeking".split(/\s+/g);

// iterate both media sources
Popcorn.forEach( videos, function( media, type ) {

    // when each is ready... 
    media.listen( "canplayall", function() {

        // trigger a custom "sync" event
        this.trigger("sync");

        // set the max value of the "scrubber"
        scrub.attr("max", this.duration() );

    // Listen for the custom sync event...    
    }).listen( "sync", function() {

        // Once both items are loaded, sync events
        if ( ++loadCount == 2 ) {

            // Iterate all events and trigger them on the video B
            // whenever they occur on the video A
            events.forEach(function( event ) {

                videos.a.listen( event, function() {

                    // Avoid overkill events, trigger timeupdate manually
                    if ( event === "timeupdate" ) {

                        if ( !this.media.paused ) {
                            return;
                        } 
                        videos.b.trigger( "timeupdate" );

                        // update scrubber
                        scrub.val( this.currentTime() );

                        return;
                    }

                    if ( event === "seeking" ) {
                        videos.b.currentTime( this.currentTime() );
                    }

                    if ( event === "play" || event === "pause" ) {
                        videos.b[ event ]();
                    }
                });
            });
        }
    });
});

scrub.bind("change", function() {
    var val = this.value;
    videos.a.currentTime( val );
    videos.b.currentTime( val );
});

// With requestAnimationFrame, we can ensure that as 
// frequently as the browser would allow, 
// the video is resync'ed.
function sync() {
    videos.b.currentTime( 
        videos.a.currentTime()        
    );
    requestAnimFrame( sync );
}

sync();
like image 197
franzlorenzon Avatar answered Nov 18 '22 12:11

franzlorenzon