Time-syncing multiple videos


#1

Hi there,

For a new project we’re using 4 videoplanes, all running different Alpha-videos from different streaming-sources, which need to be time-synced.

In case one of these video’s needs to buffer, the other 3 videos should also stop (and buffer on) and the whole set of videos should start playing when the buffered video starts playing again.

On paper this seems rather simple, but I can’t figure out how to do this.

Possible solutions could be:

  • Prefered: synced in time; but I don’t know how to do this yet. Any ideas?
  • All-in-one video (large video) with parts of the video being masked for specific videoplanes, but I’ve read somewhere that this may cause problems when playing due to the large video-dimensions.
  • Pre-loading all the videos (= almost the same as using a embedded video); but I don’t know how to do this yet. Any ideas?

Does anyone have a (better) solution for this? Is it even possible to sync these videos?

Thanks in advance.

Ferdy


#2

Hi Ferdy,

Sounds like a really cool idea, but in practice it would be very hard to sync all streaming videos with no chance of disruption.

We have a ready event handler which waits until the defined object is ready to call the event. You could have a check to make sure all videos are in a ready state before they are played and initiate all the play function at the same time.

Ready event:

video.on(“ready”, () = >{
//Play video
}

The issues that would arise would be due to different video file sizes and whether they would buffer throughout their playtime. All the videos would be loaded using the same bandwidth as they are all in the same experience but if one is larger it will be more likely to buffer and become out of sync.

Even with this, it may still be possible by utilizing the videoplayer subsymbol exported functions and emitted events (as seen below).

The symbol emits the following events:
video:finish

  • video:paused
  • video:buffering
  • video:playing
  • video:error
  • video:aspectratio
  • video:resize
  • video:time
  • video:duration

You can also control video playback from outside this symbol using the following exported functions:

  • video.player.nodes.control.start()*
  • video.player.nodes.control.restart()
  • video.player.nodes.control.pause()*
  • video.player.nodes.control.time(t : number)
  • video.player.nodes.control.time() : number*
  • video.player.nodes.control.volume() : number[]
  • video_player.nodes.control.volume(v: number) : void

Hope this helps and please do let us know if you get this working!

George


Drop shadow of Alpha video
#3

Hi George,

I’ve got this working!

It’s not time-synced, but the code checks every 100ms if a (or multiple) video is buffering. If so, the non-buffering videos are paused. If none of the videos is buffering anymore, all the videos start playing again.

I’m not a script-guru so probably this can be scripted more efficiently. Please let me know if this can/ should be done differently. Hopefully this script will help anyone in the future.

I’ve got this on the ‘show’-script:

// ======================================================
// 4 different videos of bandmembers who are playing a song together
const Videoplayer_Drum = symbol.nodes.Videoplayer_Drum;
const Videoplayer_Sous = symbol.nodes.Videoplayer_Sous;
const Videoplayer_Left = symbol.nodes.Videoplayer_Left;
const Videoplayer_Right = symbol.nodes.Videoplayer_Right;

// ======================================================
// videoStates: saves the current state of the videos

// --------- States ----------
// -1 = not played yet
// 0 = buffering
// 1 = playing
// 2 = paused

// ------ Array layout ------
// videoStates[0] = Trom Video
// videoStates[1] = Sous Video
// videoStates[2] = Left Video
// videoStates[3] = Right video

// ---------- Notes ---------
// Default all videos are set to -1 (because they are not playing yet)
// ======================================================
let videoStates = [-1, -1, -1, -1];
let videoPlayers = [Videoplayer_Drum, Videoplayer_Sous, Videoplayer_Left, Videoplayer_Right];

// Determine video states
Videoplayer_Drum.on("video:buffering", () => { videoStates[0] = 0; });
Videoplayer_Drum.on("video:playing", () => { videoStates[0] = 1; });
Videoplayer_Drum.on("video:paused", () => { videoStates[0] = 2; });

Videoplayer_Sous.on("video:buffering", () => { videoStates[1] = 0; });
Videoplayer_Sous.on("video:playing", () => { videoStates[1] = 1; });
Videoplayer_Sous.on("video:paused", () => {	videoStates[1] = 2; });

Videoplayer_Left.on("video:buffering", () => { videoStates[2] = 0; });
Videoplayer_Left.on("video:playing", () => { videoStates[2] = 1; });
Videoplayer_Left.on("video:paused", () => { videoStates[2] = 2; });

Videoplayer_Right.on("video:buffering", () => { videoStates[3] = 0; });
Videoplayer_Right.on("video:playing", () => { videoStates[3] = 1; });
Videoplayer_Right.on("video:paused", () => { videoStates[3] = 2; });

// Check every 100ms if a video is buffering
Z.every(100, function() {
    // Check the array for playstate 0 (= buffering)
    if(videoStates.indexOf(0) > -1) {
        // At least one video is buffering: pause all videos
        pausePlayingVideo(videoStates);
    }
    else {
        // No video is buffering anymore: play all videos
        playAllVideos(videoStates);
    }
});

function pausePlayingVideo(videoStates) {
    // Pause videos that currently are playing because there is a buffering video
    console.log("==============");
    console.log("At least one video is buffering");
    // Loop through all videoStates
    for(let i=0;i<videoStates.length;i++) {
        // If a video is playing...
        if(videoStates[i] === 1) {
            //...pause it
            videoPlayers[i].nodes.control.pause();
            console.log("Video #" + i + " was playing and has been paused");
        }
    }
}
function playAllVideos(videoStates) {
    var videosToStart = [];
    console.log("===============");
    console.log("No videos buffering anymore; start the videos");

    // Loop through all videoStates
    for(let i=0;i<videoStates.length;i++) {
        // If a video is paused...
        if(videoStates[i] === 2) {
            // ...push it to array
            videosToStart.push[i];
        }
    }
    // To prevent auto-starting videos when all videos are paused (which they are when the Zappar-image is not-seen), 
    // check if 3 or less videos are paused (which always is true when a (or multiple) video is buffering)
    if(videosToStart.length <= (videoPlayers.length-1)) {
        // Loop throught all videos to start...
        for(let i=0;i<videosToStart.length;i++) {
            // ...and start them
            videoPlayers[videosToStart[i]].nodes.control.start();
            console.log("Video #" + i + " was paused and is playing now");
        }
    }
}