Skip to main content

How to preload an entire html5 video before play, SOLVED

For a customer I had to implement an experience where depending on the user’s choice parts of a video should play. It should play instantly hence the video had to be fully downloaded before the user was allowed to interact with it. So how do you force a video to preload an entire video?

Solution #1, preload attribute (no good)

The obvious and first solution I tried was adding the preload attribute to the video element

<video src="video.mp4" preload></video>

It may have one of the following values (source):

  • none: indicates that the video should not be preloaded.
  • metadata: indicates that only video metadata (e.g. length) is fetched.
  • auto: indicates that the whole video file could be downloaded, even if the user is not expected to use it.
  • the empty string: synonym of the auto value.

But the video preload attribute was not exactly what I through. It only gives the browser a hint about what you thinks will lead to the best user experience thus you can’t be sure that the whole video is downloaded.

Solution #2, canplaythrough event (no good)

The video element has a canplaythrough event [source]. The canplaythrough event is fired when the user agent can play the media

video.addEventListener("canplaythrough", function() {
  // Ready to play whole video?
}, false);

Wonderful, exactly what I need! However the canplaythrough event is fired when the user agent can play the media, AND estimates that enough data has been loaded to play the media up to its end without having to stop for further buffering of content.

So again we can’t be sure that the whole video is downloaded. Todays browsers are “too” clever. They don’t want to waste bandwidth and download more than needed.

Solution #3, play, pause and the progress event (works but hacky)

The progress event occurs while downloading media content like images, audio, videos etc [source].

video.addEventListener("progress", function() {
  // When buffer is 1 whole video is buffered
  if (Math.round(video.buffered.end(0)) / Math.round(video.seekable.end(0)) === 1) {
    // Entire video is downloaded
 }
}, false);

Every time the progress event triggers we can check if the video is fully buffered (equals 1) which is great news! Unfortunately, there is a catch. Modern browser supports partial downloads which means that they initially only buffers “what’s needed” and then stops buffering. It will continue only buffering “what’s needed” when the video starts playing. That means the video will never be fully buffered.

There is however a hack. If we start playing the video and afterwards pause it it’ll continue buffering until the video is fully buffered. Remember to mute and hide the video if you’re going this way. For me it was too much of a hack and not a valid solution.

Solution #4, ajax and bloburl (works)

You can force the browser to download a video by making a GET request to the video url. If the response type is blob you can afterwards create an object URL. The URL lifetime is tied to the document in the window on which it was created so you can set it as source on the video player. And voila you now have a video player with a entirely preloaded video:

var req = new XMLHttpRequest();
req.open('GET', 'video.mp4', true);
req.responseType = 'blob';

req.onload = function() {
   // Onload is triggered even on 404
   // so we need to check the status code
   if (this.status === 200) {
      var videoBlob = this.response;
      var vid = URL.createObjectURL(videoBlob); // IE10+
      // Video is now downloaded
      // and we can set it as source on the video element
      video.src = vid;
   }
}
req.onerror = function() {
   // Error
}

req.send();

Notes:
createObjectURL() is supported from IE10+. Be aware that each time you call createObjectURL(), a new object URL is created, even if you’ve already created one for the same object [source].

Because we making a ajax request you might need to handle CORS!

20 thoughts on “How to preload an entire html5 video before play, SOLVED

  1. I have an html5 video/player i want to full preload/buffer. I don’t know how to use these methods /solution you guys are posting. Can you please like make a video or do a guide on how to use these method for a html5 video/player?

  2. Another simple solution. I know it at least work with Safary, Chrom and Firefox. In this case, I am using the property “buffered” and “duration” of the video object.

    durationLeft = $(“#myVideo”).attr(‘duration’) – $(“#myVideo”).attr(‘buffered’).end(0);
    if (durationLeft == 0) {
    # Do whatever need. Video is now fully loaded.
    }

    The above can be called on a an interval (just to be safe). However, I am now only calling the above on “progress” event from the browser. All browsers I have tested called the progress once after all data is loaded.

  3. Hi,
    Thank you for a helpful blog.
    I’m using solution #4; however, how can I get to work when wanting to load several videos?
    Any help is much appreciated,
    Gorp

    1. You can call the videos back to back causing them to all load asynchronously or call the next video in the success in order to chain the video loads.

  4. Thank you this is a great solution (#4). Now I am trying to figure out how to cache/load from cache as it is completely reloading each time…

  5. Thanks, dinbror!

    The #4 solution works ideally when video looping is needed without continuously streaming from the network.

  6. You have used the word “why” three times in your article, but none of the uses are correct. “Hence”, “thus”, “so”, are the words you are missing there.

  7. On Solution #4, I had CORS problems like previous comments and instead of doing access headers way, I used responseType = “arraybuffer” and new Blob([xhr.response], {type: “video/mp4”}). if you add solution for CORS at the bottom that would be nice.

  8. Hi there and thanks! It seems to work but I get an error: Uncaught ReferenceError: video is not defined. Any ideas?

Leave a Reply

Your email address will not be published. Required fields are marked *