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 badges1 Answer
Reset to default 3Not 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>