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!

48 thoughts to “How to preload an entire html5 video before play, SOLVED”

  1. Playing from a blob is really slow. I do not know why, but it is.
    Compared to video tag.
    However, the video tag must buffer from network.

  2. Hey Guys!

    I’m experimenting with HTML5 Ad and using Vimeo as an external server to hold the video.

    I used solution #4 whilst doing some tests, video preloads great! I have added some text layers and I want them to animate EXACTLY where I’ve added them. As the preload takes a second to load, the other layers are coming on sooner than they should.

    Any way that I can change this code slightly so all other elements wait to start until the video has fully loaded, then both start at the same time? I have a good understanding of this stuff, but not enough to work out what I need here!

    Thanks in advance

  3. Perhaps off topic but how do you play a video while it is being written to? I am capturing a video via gstreamer and saving it to an OGG file.At the sametime, I have a webpage that is playing that ogg file with the VIDEO tag. The video will only play upto its length at that time. Basically I have to refresh the web page every so often to get more of the video but that will have the video start from the beginning each time

    1. Like it says:

      Note: video preloading is included in the Preload spec, but is not currently implemented by browsers.

      And since some designers out there are special and want to use videos for every crap (like png24) browsers were programmed with safety measures. Like when we collectively decided after a couple of years that it’s actually annoying as hell when some crappy audio is blaring in the background without a way to pause it. Or when we stopped videos from autoplaying if they have audio. And chrome even goes one step beond and buffers just about 4-10 seconds and parts of chunks to skip to. Just to reduce the bandwidth load this shit causes.

  4. Hi,
    Good job Dinbror, thanks !!! I really appreciate the hacky solution 😉

    #Horace, Have you tried this :

    video.src = window.URL.createObjectURL(this.response);
    video.load();

    1. A great function (sampled) which be can used in video.js framework called by :

      loadvideo(“my_video_webm_source.php”, “video/webm”, “my_video-js_player_ID”);

      The source :

      /**** START VIDEO BLOB LOADER *****/

      function loadvideo(video_url, video_type, element_id ) {

      var xhr = new XMLHttpRequest();
      xhr.responseType = ‘blob’;

      xhr.onload = function() {

      var reader = new FileReader();

      reader.onloadend = function() {

      var byteCharacters = atob(reader.result.slice(reader.result.indexOf(‘,’) + 1));
      var byteNumbers = new Array(byteCharacters.length);

      for (var i = 0; i < byteCharacters.length; i++) {
      byteNumbers[i] = byteCharacters.charCodeAt(i);
      }

      var byteArray = new Uint8Array(byteNumbers);
      var blob = new Blob([byteArray], {type: video_type});
      var bloburl = URL.createObjectURL(blob);

      var video_player = videojs(element_id);
      video_player.src({ src: bloburl , type: video_type });

      }

      reader.readAsDataURL(xhr.response);
      };

      xhr.open('GET', video_url);
      xhr.send();

      }

      /**** END VIDEO BLOB LOADER *****/

  5. does solution 4 still work on iOS devices?

    it works great on my windows and linux desktops and on my android phone but on my ipad it doesn’t show any video. anyone else with that problem? what could be the reason?

    this is my code (i adapted it for the new video.srcObject):


    var r = new XMLHttpRequest();
    r.open(“GET”, “./animation.mp4”, true);
    r.responseType = “blob”;
    r.onload = function() {
    if(this.status === 200) {
    var video = $(“#animation video”)[0];
    try {
    video.srcObject = this.response;
    } catch(error) {
    video.src = window.URL.createObjectURL(this.response);
    }
    }
    }
    r.onerror = function() {}
    r.send();

  6. Hi i am not professional developer but have developed a template with video at header along with tage of
    , my video is not loading with page load . whole page loaded but video is nowhere . Help me plz. What should i do .

  7. I come with a basic proposition:
    Why not play the video muted and hidden; then showing it after ended or after a playing rate?

    //downloading and playing muted

    //Showing then full downloading
    $(“video_1”).show();

    1. it dose not work, just tryed it. the oploader puses if there is not enugh memery useage left, basically ram. but only a little % of it gose to the net browser.

  8. This works perfectly for me too, provided the network connection is great. When the network connection is not great, iot bombs saying the size of the video file does not match the file size indicated by the response header.

  9. Hey there, i did it the exact same way with a video from the same origin. It works in all browsers but in IE I get the Error “Invalid Source”.

    As it is a WordPress I used a non relative url.

  10. Hi,
    Because of the requirements, I have to preload a few videos, audios and images in a web page. I have been using solution 4 to create blob urls, saving them to to hidden fields. After making sure of all of the media downloaded,
    page flow gets started and when needed I find required blob url from hidden fields and assign it to the src of the media objects.

    This has been working fine, until they decided to depreciate createObjectUrl method. The warning is “URL.createObjectURL(stream) has been deprecated”. It seems solution 4 will not be a valid solution after some time.

    How do you think preloading many videos, audio and images can be done in my scenario? Any ideas?

    Thanks in advance.

    1. I cannot find any reference apart from your post that says anything about depricating createObjectUrl method

  11. if video size if is more than 500MB it will take lot of time to load the Video, Then how play the video fast. If possible please provide me the solution

    Thanks

    1. My thought as well…load the whole thing into RAM?? There’s a reason your browser’s not doing that…I do like the pause trick suggestion, thanks!

  12. req.onload or xhr.onload does not take any action until the video.mp4 end loading
    why i play loaded just
    if the video very large it doesn’t play until end loading

      1. I am running a 500MB video, getting the expected result, when the webserver is on my local network. When running on a webserver in cloud, it runs great, but only for video files that are small. When the video file is ~500MB, the ajax bomb on “ERR_COUNT_LENGTH_MISMATCH”

  13. 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?

  14. 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.

  15. 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.

  16. 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…

  17. Thanks, dinbror!

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

  18. 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.

  19. 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.

  20. That won’t work crossdomain thought right?
    (i.e. loading the video from a CDN)

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

        1. you just copy and pasted – you need to set up a few things – specifically the reference between:

          and in your code where you are using video.whatever…

          so add a reference at the top of your js. something like:
          var video = document.getElementsByTagName(‘video’)[0];

Leave a Reply to FrostyLeaf Cancel reply

Your email address will not be published.