Another: Force Chrome to fully buffer mp4 video
Asked Answered
D

3

27

I've seen a few threads about this, but with no answers, so I thought I'd add another to the grave yard of polluted youtube related pages.

I've got a 100MB mp4 video that needs to be fully downloaded by the browser,

However theres no event that fires when its been fully downloaded, and Chrome seems to stop buffering any more of the video until the current video time has almost reached the end of the buffer, then it requests more.

How can I get the browsers to fully download the video and buffer it 100%?

Thanks

Dominicdominica answered 15/8, 2013 at 11:19 Comment(0)
B
47

Sadly Chrome - like other HTML5 browsers - tries to be smart about what it's downloading and avoids unnecessary bandwidth usage... this means that sometimes we're left with a sub-optimal experience (ironically YouTube suffers from this so much that there are extensions to force more pre-buffering!)

That said, I've not found this to be required too often with a good server and a good connection - but if you need something the only solution I have found is a bit of a sledgehammer... use Ajax to download the file that you want to play and then load that into the source for the video element.

The sample below assumes your browser supports m4v/mp4 - you'll probably want to use the canPlayType test to select the most appropriate video format for your users if you can't guarantee (eg) Chrome. You'll also want to test to see how well it handles videos of the size you want (I've tried some fairly large ones but not sure what upper limit this will reliably handle)

The sample is also pretty simple... it kicks off the request and doesn't communicate anything to the user while it's loading - with your size video you're probably going to want to show a progress bar just to keep them interested...

the other downside of this process is that it's not going to start playing until you've fully downloaded the file - if you want to go deeper into displaying the video dynamically from your buffer then you'll need to start delving into the bleeding edge (at the moment) world of MediaSource as described in posts like http://francisshanahan.com/index.php/2013/build-an-mpeg-dash-player-from-scratch/

<!DOCTYPE html>
<html>
<head>
<title>Preload video via AJAX</title>
</head>
<body>
<script>
console.log("Downloading video...hellip;Please wait...")
var xhr = new XMLHttpRequest();
xhr.open('GET', 'BigBuck.m4v', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
  if (this.status == 200) {
    console.log("got it");
    var myBlob = this.response;
    var vid = (window.webkitURL || window.URL).createObjectURL(myBlob);
    // myBlob is now the blob that the object URL pointed to.
    var video = document.getElementById("video");
    console.log("Loading video into element");
    video.src = vid;
    // not needed if autoplay is set for the video element
    // video.play()
   }
  }

xhr.send();
</script>

<video id="video" controls autoplay></video>

</body>
</html>
Buttock answered 18/8, 2013 at 0:49 Comment(2)
Blob not working in Samsung Internet (default browser of samsung) 8.2 versionExteroceptor
May I ask, how could I do it as a end user video viewer instead of the developer of the website?Kitkitchen
T
4

My solution is for a much smaller video than the OP described, however the desired behaviour is the same: Prevent the video from beginning playback until the video is fully buffered. Video should play for returning users if the video is already cached.

Below answer was heavily influenced by this answer and has been tested on Chrome, Safari and Firefox.

Javascript file:

    $(document).ready(function(){
      // The video_length_round variable is video specific.
      // In this example the video is 10.88 seconds long, rounded up to 11.
      var video_length_round = 11;

      var video = document.getElementById('home_video_element');
      var mp4Source = document.getElementById('mp4Source');

      // Ensure home_video_element present on page
      if(typeof video !== 'undefined'){

        // Ensure video element has an mp4Source, if so then update video src
        if(typeof mp4Source !== 'undefined'){
          $(mp4Source).attr('src', '/assets/homepage_video.mp4');
        }

        video.load();

        // Ensure video is fully buffered before playing
        video.addEventListener("canplay", function() {
          this.removeEventListener("canplay", arguments.callee, false);

          if(Math.round(video.buffered.end(0)) >= video_length_round){
            // Video is already loaded
            this.play();

          } else {
            // Monitor video buffer progress before playing
            video.addEventListener("progress", function() {
              if(Math.round(video.buffered.end(0)) == video_length_round){
                this.removeEventListener("progress", arguments.callee, false);
                video.play();
              }
            }, false);
          }
        }, false);
      }
    });

Above javascript updates the source of a video element such as this one:

    <video id="home_video_element" loop="" poster="/assets/home/video_poster_name.jpg" preload="auto">
      <source id="mp4Source" type="video/mp4">
    </video>
Tumescent answered 11/7, 2015 at 13:50 Comment(0)
I
0

I had a similar problem I was trying to solve where, I wanted chrome to more aggressively load videos for users on desktop on a unmetered connection. I tried looking into MediaSource as one of the answers above suggested, but using that would involve me having to encode my mp4s as fragmented mp4s and that seemed like too much work.

Eventually I settled on this behavior:

<!DOCTYPE html>
<html>
<head>
  <title>Test</title>
</head>

<script>
  function load(){
    const url = document.getElementById('url').value
    const vid = document.getElementById('video')
    vid.src = url;
    const progressElem = document.getElementById('progress')

    const xhr = new XMLHttpRequest();
    xhr.onload = function() {
        const wasPaused = vid.paused
        const time =  vid.currentTime;
        vid.src = URL.createObjectURL(xhr.response);
        vid.currentTime = time;
        if(!wasPaused)
          vid.play();
    };
    xhr.onprogress = onprogress = function (event) {
      progressElem.innerText = 'Download Progress: ' + (event.loaded / event.total);
    };

    xhr.responseType = "blob";
    xhr.open("GET", url)
    xhr.send();
  }
</script>


<body style="padding-left: 30px">
    <h1>Testing video loading</h1>
    <input type="text" id="url" value="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" size="200">
    <br>
    <button onclick="load()">Load Video</button>
    <br>
    <h3 id="progress"></h3>
    <br>
    <video id="video" style="max-width:min(800px, 100vw)" controls></video>
</body>
</html>

Basically I would start off by letting the user stream the video natively from their browser, but I would also start downloading the entire file and once that was done I would just swap in the downloaded data to vid.src and make sure the player was in the correct state. Note that this does create a slight stutter/buffering effect in the video.

This solution is definitely not the most bandwidth efficient, but it seems to work reasonably well for 1-2GB video files on desktops.

I should also mention that, I think you should only do this in Chrome because there may be better solutions for other browsers. For example in Firefox there are settings users can access which control how much of a video gets buffered.

Insomniac answered 1/10, 2020 at 1:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.