Buffering videos fully before playing them

Hello!

I’m relatively new to JS and P5 with limited coding background, and I’m learning as I go. This is my first post here.

I’m building a website that needs to stream several videos simultaneously for a music/video project.

I’m using createVideo(), but it leads to some of the videos freezing while others continue, especially on slower internet connections. I am guessing that this is because the browser controls how much to buffer and play.

An engineer friend suggested trying to have all the videos buffer fully before playing them, so that they run concurrently without interruption. Is there a way to do this with JS?

The videos are small (300px width) and compressed, I am ok with waiting for them to fully buffer (or download?) before playing.

Here is how I’m doing it now, pretty straight forward:

let howManyVids = 8;
let videos = [];
let start = false;

function setup() {
    let cnv = createCanvas(windowWidth, windowHeight);
    cnv.style('display', 'block');
    background(255);
    frameRate(25);
    
    // something to click to avoid browser blocking autoplay:

    go = createP();  
    go.style('font-size', width/40);
    go.style('font-family', "Bahnschrift", "bold");
    go.position(width*0.1,height*0.1);
    go.html("click to start");
    go.mouseClicked(startDraw);
    
    // loading videos, for some reason this works here but doesn't in preload

    for (let i=0; i<howManyVids; i++) {
        videos[i] = createVideo("assets/"+i+".mp4");
        videos[i].hide();
    }  
}

function startDraw() {
    for (let i=0; i<videos.length; i++) {
        videos[i].play();
    }
    go.hide();
    start = true;
}


function draw() {
    if (start) {
        background(255);
        for (let i = 0; i< videos.length; i++ ) {
            image(videos[i], random(width*0.6), random (height*0.6)); //random positions for the purpose of this post 
        }
    }
}

Thank you!!

Hello and welcome to the forum! :tada:

The createVideo() function offers an optional callback that fires when the browser thinks it can play through the video to the end, without stopping for more buffering. There’s a simple example in the documentation showing how to set up that callback.

3 Likes

Thank you Sven!!

I tried using the callback function but it didn’t help, some of the videos still freeze even after they all triggered the callback before starting playback. Maybe it’s because I’m trying to run 8 videos at the same time, and the callback only checks if one of them would be ready to play on it’s own?

I found something that I tested and seems to work with one video:

I’m going to try putting this into my project and see.

Yes, the code will have to wait until the callback has been called eight times and then start playback. Something like this:

let howManyVids = 8;
let callbackCounter = 0;

// setup(), draw(), etc. removed for brevity.

function myCallback() {
  callbackCounter++;
  
  if (callbackCounter === howManyVids) {
    // should be safe to play videos now...
  }
}

Yes that’s exactly the method I tried! I only started playing the videos once all callback functions have been called, but the videos still intermittently froze while playing. Maybe something else in my code caused this, I’ll be taking a close look. Or, I’m hoping the Ajax solution might work, I didn’t get a chance to work on it yet, I’m putting it off it since I’ll be adding it blindly into my code as I don’t really understand it… :confused:

Welcome to the forums!

I know you mentioned the videos are small, but exactly how small are we talking?

Thank you! Happy to join in

The videos are 20-30mb each. It works well on most connections, but sometimes it just doesn’t, and it seems unpredictable when it’s going to work. One thing that seems consistent is that after a couple attempts it almost always works well, and from looking into network in developer tools, it looks like that’s when the video’s status is ‘cached’. That’s why I want to browser to fully download them before playing

On a trial run, the code below seems to be working great with smaller videos, but when trying with larger videos (60mb), the download for some of them failed with ERR_CONNECTION_CLOSED, in which case… what then? I know I have enough RAM to hold 8x60mb videos

let xhr = [];
let videos = [];
let loaded = 0;
let howMany = 8;
let start = false;
let click = false;
let vidsLoaded = false;
let wait = true;

function setup() {
    for (let i=0; i<howMany; i++) {
        xhr[i] = new XMLHttpRequest();
        xhr[i].open('GET', '0.mp4', true);
        xhr[i].responseType = 'blob';
        xhr[i].onload = function(e) {
            if (this.status == 200) {
                let myBlob = this.response;
                let vid = (window.webkitURL || window.URL).createObjectURL(myBlob);
                videos[loaded] = createVideo(vid);
                videos[loaded].hide();
                loaded++;
                if (loaded==howMany) {
                    vidsLoaded = true;
                }
            }
        }
        xhr[i].send();
    }
}

function vidPlay() {
    for (let i=0; i<videos.length; i++) {
        videos[i].play();
        videos[i].show();
    }
}

function mouseClicked() {
    click = true;
}

function draw() {
    if (vidsLoaded && click && wait) {
        vidPlay();
        wait = false;
    }
}

Something that comes to mind is that, while ~480MB is not a trivial amount, and while your computer has gigs of RAM, the browser itself may limit how much memory is allowed per instance.

But on the other hand, you mentioned it starts working fine once the videos have had time to cache :thinking:.

2 suggestions:

  1. Research if you can manually cache your videos on preload, or something.
  2. If you haven’t already, see if you can reduce the network/memory load by compressing your vids
1 Like