html5视频&音频缓存问题

时间:2017-02-01 23:52:20

标签: javascript html google-chrome caching

我编写了一个自定义媒体预加载器,它在显示XMLHttpRequests之前使用一系列ng2 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();
    })
}

它很好用,并成功加载到我提供的所有媒体中。

我只有1个问题,而且是 Chrome ,当我引用预先加载的<video><audio>时,它不会将其拉出来从缓存中,它反而从服务器重新下载它。 (IE9甚至从缓存中拉出)

<小时/> 任何音频和视频元素将始终从服务器重新下载...

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

这将始终从缓存加载...

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

以下是两个截图,一个来自chrome,另一个来自边缘,显示来自开发工具的网络活动(两者都在发布之前重置了缓存)...

enter image description here

enter image description here

我注意到的主要区别是,在渲染内容时(浏览器预加载),浏览器之间的请求状态不同。但为什么会这样呢?

我在2013年发现了this SO帖子,其中指出:

  

视频的缓冲方式取决于浏览器的实现,因此浏览器可能会有所不同。

     

各种浏览器可以使用不同的因素来确定保留或丢弃缓冲区的一部分。旧段,磁盘空间,内存和性能是典型因素。

这是发生在这里的事吗?如果是这样,有没有人知道解决这个问题的方法,以便chrome总是试图从缓存中提取视频?

1 个答案:

答案 0 :(得分:2)

不确定缓存问题是否是一个chrome bug,但你做的事情对我来说似乎很奇怪。

您正在预加载您的媒体,或实际上完全下载,但随后将mediaElement设置为原始来源。

当我们通过mediaElement(<audio><video>)加载媒体时,浏览器会发出range个请求,即它不会下载完整文件,只会下载它需要不间断地进行游戏 这就是你得到206 Partial content回复的原因。这也可能是为什么chrome不会将它识别为相同的请求,因此不再使用缓存我不确定它是否是Chrome错误

但是既然您已经下载了完整文件,为什么不将mediaElement的src设置为此下载文件?

// 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

工作示例:(在我的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.com/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>

或使用fetch API:

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

loadVideo('https://dl.dropboxusercontent.com/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>