Part of my current project involves loading external videos through HTML5's native video tag and then resizing them with Javascript to be the full height & width of the DOM.
My code seems to work perfectly on desktop browsers, but when I load up my project on my ipad the video doesn't get resized because the onloadedmetadata event never gets fired.
Here is a small code sample which reproduces the problem:
function init() {
var video = document.getElementById('viddy');
video.addEventListener('loadedmetadata', function(e){
var dimensions = [video.videoWidth, video.videoHeight];
alert(dimensions);
});
}
document.addEventListener("DOMContentLoaded", init, false);
<video id="viddy" autoplay>
<source src="http://media.w3.org/2010/05/sintel/trailer.webm" type='video/webm' />
<source src="http://www.w3schools.com/html/movie.mp4" type="video/mp4" />
</video>
http://jsfiddle.net/AUSNu/213/
I've even tried coding up a solution using jQuery, on the off-chance that the event may fire, but it still doesn't.
$('#viddy').on('loadedmetadata', function() {
alert('test');
});
I even went as far as enabling remote debugging through safari on my ipad, but still no output within the console.
Are there any workarounds to this? I couldn't find much info about this on the web / in documentation.
Unfortunately, there isn't really a way around this. Mobile Safari will not download any part of the video file until it gets a user interaction (i.e. some kind of touch event), not even the header, which is required to know the dimensions.
In your specific example, you need to enable controls on the video so the user can start it playing. (Or you can write your own code that starts it, but it has to be triggered by a touch or click event.) Once it starts playing, the loadedmetadata even will fire, and you can do what you want.
I recommend reading this other answer where someone else was trying to do pretty much the same thing. It discusses the problem in more detail along with a working link. Also, it addresses another problem with scaling the video that you will probably run into.
Safari on iPad (iOS6) does not scale HTML5 video to fill 100% of page width
Related
With the release of OSX High-Sierra*, one of the new features in Safari is that videos on websites will not auto play anymore and scripts can't start it either, just like on iOS. As a user, I like the feature, but as a developer it puts a problem before me: I have an in-browser HTML5 game that contains video. The videos do not get automatically played anymore unless the user changes their settings. This messes up the game flow.
My question is, can I somehow use the players' interaction with the game as a trigger for the video to start playing automatically, even if said activity is not directly linked to the video element?
I cannot use jQuery or other frameworks, because of a restraint that my employer has put on our development. The one exception is pixi.js which - among all other animations - we are also using to play our videos inside a pixi container.
*The same restriction also applies on Mobile Chrome.
Yes, you can bind on event that are not directly ones triggered on the video element:
btn.onclick = e => vid.play();
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>
So you can replace this button with any other splash screen requesting an user click, and you'll be granted access to play the video.
But to keep this ability, you must call at least once the video's play method inside the event handler itself.
Not working:
btn.onclick = e => {
// won't work, we're not in the event handler anymore
setTimeout(()=> vid.play().catch(console.error), 5000);
}
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>
Proper fix:
btn.onclick = e => {
vid.play().then(()=>vid.pause()); // grants full access to the video
setTimeout(()=> vid.play().catch(console.error), 5000);
}
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>
Ps: here is the list of trusted events as defined by the specs, I'm not sure if Safari limits itself to these, nor if it includes all of these.
Important note regarding Chrome and preparing multiple MediaElements
Chrome has a long-standing bug caused by the maximum simultaneous requests per host which does affect MediaElement playing in the page, limiting their number to 6.
This means that you can not use the method above to prepare more than 6 different MediaElements in your page.
At least two workarounds exist though:
It seems that once a MediaElement has been marked as user-approved, it will keep this state, even though you change its src. So you could prepare a maximum of MediaElements and then change their src when needed.
The Web Audio API, while also concerned by this user-gesture requirement can play any number of audio sources once allowed. So, thanks to the decodeAudioData() method, one could load all their audio resources as AudioBuffers, and even audio resources from videos medias, which images stream could just be displayed in a muted <video> element in parallel of the AudioBuffer.
In my case i was combining transparent video (with audio) with GSAP animation. The solution from Kaiido works perfectly!
First, on user interaction, start and pause the video:
videoPlayer.play().then(() => videoPlayer.pause());
After that you can play it whenever you want. Like this:
const tl = gsap.timeline();
tl.from('.element', {scale: 0, duration: 5);
tl.add(() => videoPlayer.play());
Video will play after the scale animation :).
Tested in Chrome, Safari on iPhone
With the release of OSX High-Sierra*, one of the new features in Safari is that videos on websites will not auto play anymore and scripts can't start it either, just like on iOS. As a user, I like the feature, but as a developer it puts a problem before me: I have an in-browser HTML5 game that contains video. The videos do not get automatically played anymore unless the user changes their settings. This messes up the game flow.
My question is, can I somehow use the players' interaction with the game as a trigger for the video to start playing automatically, even if said activity is not directly linked to the video element?
I cannot use jQuery or other frameworks, because of a restraint that my employer has put on our development. The one exception is pixi.js which - among all other animations - we are also using to play our videos inside a pixi container.
*The same restriction also applies on Mobile Chrome.
Yes, you can bind on event that are not directly ones triggered on the video element:
btn.onclick = e => vid.play();
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>
So you can replace this button with any other splash screen requesting an user click, and you'll be granted access to play the video.
But to keep this ability, you must call at least once the video's play method inside the event handler itself.
Not working:
btn.onclick = e => {
// won't work, we're not in the event handler anymore
setTimeout(()=> vid.play().catch(console.error), 5000);
}
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>
Proper fix:
btn.onclick = e => {
vid.play().then(()=>vid.pause()); // grants full access to the video
setTimeout(()=> vid.play().catch(console.error), 5000);
}
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>
Ps: here is the list of trusted events as defined by the specs, I'm not sure if Safari limits itself to these, nor if it includes all of these.
Important note regarding Chrome and preparing multiple MediaElements
Chrome has a long-standing bug caused by the maximum simultaneous requests per host which does affect MediaElement playing in the page, limiting their number to 6.
This means that you can not use the method above to prepare more than 6 different MediaElements in your page.
At least two workarounds exist though:
It seems that once a MediaElement has been marked as user-approved, it will keep this state, even though you change its src. So you could prepare a maximum of MediaElements and then change their src when needed.
The Web Audio API, while also concerned by this user-gesture requirement can play any number of audio sources once allowed. So, thanks to the decodeAudioData() method, one could load all their audio resources as AudioBuffers, and even audio resources from videos medias, which images stream could just be displayed in a muted <video> element in parallel of the AudioBuffer.
In my case i was combining transparent video (with audio) with GSAP animation. The solution from Kaiido works perfectly!
First, on user interaction, start and pause the video:
videoPlayer.play().then(() => videoPlayer.pause());
After that you can play it whenever you want. Like this:
const tl = gsap.timeline();
tl.from('.element', {scale: 0, duration: 5);
tl.add(() => videoPlayer.play());
Video will play after the scale animation :).
Tested in Chrome, Safari on iPhone
i'm currently playing around with video.js to make video watching a bit more reliable. The current case if: while watching a video, the connection gets interrupted (loss of internet connectivity).
With chrome it was no big deal: I created a function, attached the function to the error event and voila, if an error occurred, my function was able to recover (in this case its just trying to reload the video and seek to the last known position).
On firefox nothing happened at all. The error is never raised.
I tried to work around this a bit and noticed that firefox seems to think the file is fully loaded in case of a network interruption. So the bufferedPercent jumps to 1 and the loadedalldata event is also triggered. For me this seems to be just broken, but i'm unable to nail it down to a firefox or a video.js problem.
Anyone else having such problems or knows a better way to handle such problems?
Background info: to test the case, i run a HAProxy infront of two webservers running nginx who deliver the video files. To trigger my problem, i just kill the nginx which is currently delivering the stream. So a reconnect should work fine (as long as the other one is still working of course ;)
Thanks and regards,
Darkman
Could it be that you are binding the error on the video tag and not the source tag?
As stated here:
Instead of the error event being dispatched to the media element itself, it now gets delivered to the child elements corresponding to the sources resulting in the error.
<video controls id="videoTag" width="640" height="360" preload="auto">
<source src="pathto.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' id="mp4Source">
<source src="pathto.webm" type='video/webm; codecs="vp8.0, vorbis"' id="webmSource">
</video>
<script type="text/javascript">
document.getElementById('mp4Source').addEventListener('error', function(ev) {
alert('mp4Source error');
}, false);
document.getElementById('webmSource').addEventListener('error', function(ev) {
alert('webmSource error');
}, false);
document.getElementById('videoTag').addEventListener('error', function(ev) {
alert('videoTag error');
}, false);
document.getElementById('videoTag').addEventListener('stalled', function(ev) {
alert('videoTag stalled');
}, false);
</script>
EDIT: also check for the stalled event as it triggers when one disconnects the network while the media resource is downloading. Source.
When detecting playback/network errors on HTML5 video I normally use something that follows the code at end of this section.
Thanks
let's get down to the code:
I'm preparing the document with:
<video width="300" height="400" id="videoStage"></video><button onclick='video()'>Play video</button>
as for the JS function:
function video(){
var vid_url = "https://fbcdn-video-a.akamaihd.net/cfs-ak-ash4/v/82342/969/274169121921_63622.mp4?oh=99ef0d9285cbbd7adf8bc07a845dc0d1&oe=519E400F&__gda__=1369439362_83c7f900e92bdbaa201f49d35a7c144a";
stage = document.getElementById('videoStage');
stage.src = vid_url;
stage.play();
}
working code sample: http://jsbin.com/eviyel/1
this code works just fine and plays the video both on my desktop and my mobile device.
But(!) when the video() function is applied to document.ready - it only plays on my desktop and as for my mobile device it displays empty.
Any solution guys? 10x.
I found the answer myself:
Mobile browsers (such as chrome mobile and iOs's) must have a valid 'click' first to enable both video+audio playback.
Once that click as been achieved, the element that was used to play the video/audio is now playable.
You can now change the SRC of that element to play any new media source programmatically without having to click on the DOM once again.
Starting from iOS 10 autoplaying videos that are muted is allowed without user interaction:
https://webkit.org/blog/6784/new-video-policies-for-ios/
Android still seems to require user interaction, although it might be possible to use a touchend event from scrolling the page to start the video playback.
I have an HTML5 web-app that has sound effects. I'm trying to get these effects working in iOS5 and can't for the life of me.
Wondering if anyone has any work-arounds to get JS control of an HTML5 audio/video control in iOS5.
Or even a way to control multiple audio files with one click. As it stands right now, if I have 10 sound effects, I'd need 10 user clicks to get control of all of them, which is absurd!
Absurb, but you have to see it from iPhone or any mobile phone's point of view. It is a mobile phone going over a cellular network with bandwidth limitations, which many people know about from the recent Sprint commercial. They do not want users going over their bandwidth limit because some site is sending their phone a large amount of data without them taking action themselves.
The following is an excerpt from the official Safari Developer Library with more details.
User Control of Downloads Over Cellular Networks
In Safari on iOS (for all devices, including iPad), where the user may
be on a cellular network and be charged per data unit, preload and
autoplay are disabled. No data is loaded until the user initiates it.
This means the JavaScript play() and load() methods are also inactive
until the user initiates playback, unless the play() or load() method
is triggered by user action. In other words, a user-initiated Play
button works, but an onLoad="play()" event does not.
This plays the movie: <input type="button" value="Play" onClick="document.myMovie.play()">
This does nothing on iOS: <body onLoad="document.myMovie.play()">
Due to Apple, they have restricted the auto-play features to prevent cell data charges. In xcode4 i added a workaround though. In your "webViewDidFinishLoad" Send a javascript call to auto play the video and it works. I tried this in the html file with regular javascript but it did not work. Doing it through webViewDidFinishLoad did the trick though. In this example i want to auto play the video on my index.html page. I have a javascript function on that page called startVideo().
- (void)webViewDidFinishLoad:(UIWebView *)webView{
NSURLRequest* request = [webView request];
NSString *page = [request.URL lastPathComponent];
if ([page isEqualToString:#"index.html"]){
NSString *js = #"startVideo();";
[myWebMain stringByEvaluatingJavaScriptFromString:js];
}
}
And here's my javascript function:
<script>
function startVideo(){
var pElement3 = document.getElementById('myVideo');
pElement3.play();
}
</script>
And here's the html in case you're new to html video
<video id="myVideo" poster="index_poster.png" width="1024" height="768" xcontrols autoplay="autoplay">
<source src="flow.m4v" type="video/mp4"/>
browser not supports the video
</video>
Have you tried something like this??
function clickedOnce(){
$('audio').each(function(){
this.play();
});
}