最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - html5 video & audio cache issue - Stack Overflow

programmeradmin0浏览0评论

I've written a custom media preloader which uses a series of XMLHttpRequests to load large amounts of media prior to displaying my ng2 app. It is a stakeholder requirement that all media is downloaded in full prior to use of the app.

 private loadFile(media: any) {
    return new Promise(function (resolve, reject) {
        var error: boolean = false;

        //for (var media of media.videos) {
        //TODO: Check how this loads.....
        //console.log("Now Loading video >> ", media, media.hasOwnProperty("path"));


        // Standard XHR to load an image
        var request = new XMLHttpRequest();
        request.open("GET", (<any>media).path);
        request.responseType = 'blob';

        // When the request loads, check whether it was successful
        request.onload = () => {
            if (request.status === 200) {
                resolve(request.response);
            }
            else
                // If it fails, reject the promise with a error message
                reject(Error('Media didn\'t load successfully; error code:' + request.statusText));
        };

        // If an error occurs
        request.onerror = () => {
            // Also deal with the case when the entire request fails to begin with
            // This is probably a network error, so reject the promise with an appropriate message
            reject(Error('There was a network error.'));
        };

        request.onreadystatechange = function () {
            if (request.readyState == 4) {
                console.log("Load Complete >> ", media, "from", request.status); // Another callback here
            }
        };

        // Every tick of the progress loader
        request.onprogress = data => console.log(data);

        // Send the request
        request.send();
    })
}

It works great and successfully loads in all the media that I feed it.

I only have 1 issue and that is in Chrome, when I reference a <video> or <audio> which has been pre-loaded,, it doesn't pull it from the cache, it instead re-downloads it from the server. (IE9 even pulls from cache)


Any audio and video elements will always re-download from the server...

<video width="640px" height="auto" controls autoplay preload="auto">
    <source src="./app/assets/video/Awaiting%20Video%20Master.mov" type="video/mp4"/>
</video>

<audio controls autoplay preload="auto">
    <source src="./app/assets/audio/1_2_4_audio1.mp3" type="audio/mp3" />
</audio>

This will always load from cache...

<img src="./app/assets/images/BTFG-BOLD_Fundamentals.png" />


Here are 2 screenshots, one from chrome and one from edge showing the network activitiy from the dev tools (both had their caches reset prior to launch)...

Chrome

Edge

The main difference that I notice is the request status is different between browsers when it es to rendering the content (post preloading). But why is this the case?

I found this SO post from 2013 which states that:

How video is buffered is browser implementation dependent and therefor may vary from browser to browser.

Various browsers can use different factors to determine to keep or to discard a part of the buffer. Old segments, disk space, memory and performance are typical factors.

Is this what is happening here? And if so, does anyone know a way to fix this so that chrome always attempts to pull the videos from cache?

I've written a custom media preloader which uses a series of XMLHttpRequests to load large amounts of media prior to displaying my ng2 app. It is a stakeholder requirement that all media is downloaded in full prior to use of the app.

 private loadFile(media: any) {
    return new Promise(function (resolve, reject) {
        var error: boolean = false;

        //for (var media of media.videos) {
        //TODO: Check how this loads.....
        //console.log("Now Loading video >> ", media, media.hasOwnProperty("path"));


        // Standard XHR to load an image
        var request = new XMLHttpRequest();
        request.open("GET", (<any>media).path);
        request.responseType = 'blob';

        // When the request loads, check whether it was successful
        request.onload = () => {
            if (request.status === 200) {
                resolve(request.response);
            }
            else
                // If it fails, reject the promise with a error message
                reject(Error('Media didn\'t load successfully; error code:' + request.statusText));
        };

        // If an error occurs
        request.onerror = () => {
            // Also deal with the case when the entire request fails to begin with
            // This is probably a network error, so reject the promise with an appropriate message
            reject(Error('There was a network error.'));
        };

        request.onreadystatechange = function () {
            if (request.readyState == 4) {
                console.log("Load Complete >> ", media, "from", request.status); // Another callback here
            }
        };

        // Every tick of the progress loader
        request.onprogress = data => console.log(data);

        // Send the request
        request.send();
    })
}

It works great and successfully loads in all the media that I feed it.

I only have 1 issue and that is in Chrome, when I reference a <video> or <audio> which has been pre-loaded,, it doesn't pull it from the cache, it instead re-downloads it from the server. (IE9 even pulls from cache)


Any audio and video elements will always re-download from the server...

<video width="640px" height="auto" controls autoplay preload="auto">
    <source src="./app/assets/video/Awaiting%20Video%20Master.mov" type="video/mp4"/>
</video>

<audio controls autoplay preload="auto">
    <source src="./app/assets/audio/1_2_4_audio1.mp3" type="audio/mp3" />
</audio>

This will always load from cache...

<img src="./app/assets/images/BTFG-BOLD_Fundamentals.png" />


Here are 2 screenshots, one from chrome and one from edge showing the network activitiy from the dev tools (both had their caches reset prior to launch)...

Chrome

Edge

The main difference that I notice is the request status is different between browsers when it es to rendering the content (post preloading). But why is this the case?

I found this SO post from 2013 which states that:

How video is buffered is browser implementation dependent and therefor may vary from browser to browser.

Various browsers can use different factors to determine to keep or to discard a part of the buffer. Old segments, disk space, memory and performance are typical factors.

Is this what is happening here? And if so, does anyone know a way to fix this so that chrome always attempts to pull the videos from cache?

Share Improve this question edited May 23, 2017 at 11:47 CommunityBot 11 silver badge asked Feb 1, 2017 at 23:52 ZzeZze 18.9k14 gold badges95 silver badges125 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 3

Not sure if the caching issue is a chrome bug, but what you do seems really odd to me.

You are preloading your media, or actually, downloading it entirely, but then set the mediaElement to the original source.

When we load media through a mediaElement (<audio> or <video>), the browser will make range requests, i.e, it won't download the full file, but only what it needs to play without interruption.
That's why you get 206 Partial content responses. It's also probably why chrome doesn't recognize it as being the same requests, and hence doesn't use the cache once again I'm not sure if it's a chrome bug or not.

But since you already did download the full file, why don't you set your mediaElement's src to this downloaded file ?

// since you are setting the hr reponseType to `'blob'`
mediaElement.src = URL.createObjectURL(request.response);
// don't forget to URL.revokeObjectURL(mediaElement.src) when loaded

Working example : (which triggers a weird bug on my FF...)

function loadVideo(url) {
  return new Promise((resolve, reject) => { // here we download it entirely
      let request = new XMLHttpRequest();
      request.responseType = 'blob';
      request.onload = (evt)=>resolve(request.response);
      request.onerror = reject;
      request.open('GET', url);
      request.send();
    }).then((blob)=> 
    	new Promise((resolve, reject)=>{
    		resolve(URL.createObjectURL(blob)); // return the blobURL directly
    		})
    	);

}

loadVideo('https://dl.dropboxusercontent./s/bch2j17v6ny4ako/movie720p.mp4')
  .then(blobUrl => { // now it's loaded
    document.body.className = 'loaded';
    let vid = document.querySelector('video');
    vid.src = blobUrl; // we just set our mediaElement's src to this blobURL
    vid.onload = () => URL.revokeObjectURL(blobUrl);
  }).catch((err) => console.log(err));
video{
  display: none;
  }
.loaded p{
  display: none;
  }
.loaded video{
  display: unset;
  }
<p>loading.. please wait</p>
<video controls></video>

Or with the fetch API :

function loadVideo(url) {
  return fetch(url)
    .then(resp => resp.blob())
    .then(blob => URL.createObjectURL(blob));
}

loadVideo('https://dl.dropboxusercontent./s/bch2j17v6ny4ako/movie720p.mp4')
  .then(blobUrl => { // now it's loaded
    document.body.className = 'loaded';
    let vid = document.querySelector('video');
    vid.src = blobUrl; // we just set our mediaElement's src to this blobURL
    vid.onload = () => URL.revokeObjectURL(blobUrl);
  }).catch((err) => console.log(err));
video {
  display: none;
}
.loaded p {
  display: none;
}
.loaded video {
  display: unset;
}
<p>loading.. please wait</p>
<video controls></video>

发布评论

评论列表(0)

  1. 暂无评论