How to detect if Chrome/Safari/Firefox prevented autoplay for video? - google-chrome

Background
Since Chrome version 66, videos that should autoplay on my site may be prevented from playing if the user hasn't been on my site before.
<video src="..." autoplay></video>
Question
How do I detect if the video autoplay was disabled? And what can I do about it?

The autoplay attribute
According to web standard specifications, the autoplay attribute should only be a hint for what the browser should to with the media element. Neither of W3 of WHATWG web specifications mentions anything about when to prevent autoplay for media, which means that each browser probably have different implementations.
Autoplay policies
Autoplay policies implemented by each browser now govern whether video should be allowed to autoplay.
Chrome uses something they call Media
Engagement Index and you can read more about that here and their autoplay policy here.
Safari developers made a post on webkit.org
regarding this.
Firefox seems to put it in the hands of the user to choose if it's allowed or not (link).
Best practices
Detecting if autoplay is disabled
Instead of using autoplay on your element, you can use the play() method on the video and audio element to start playing your media. The play() method returns a promise in modern browsers (all according to the spec). If the promise rejects, it can indicate that autoplay is disabled in the current browser on your site.
can-autoplay is a library solely for detecting autoplay features for both video and audio elements.
When autoplay is disabled
The good thing is that when you know that autoplay is disabled you can, in some browsers, then mute the video and try the play() method again, while showing something in the UI that says that the video is playing while muted.
var video = document.querySelector('video');
var promise = video.play();
if (promise !== undefined) {
promise.then(_ => {
// Autoplay started!
}).catch(error => {
// Autoplay not allowed!
// Mute video and try to play again
video.muted = true;
video.play();
// Show something in the UI that the video is muted
});
}
<video src="https://www.w3schools.com/tags/movie.ogg" controls></video>

For me best solution was:
function _callback_onAutoplayBlocked() {
// do something, for example "show big play button"
}
function isSafari() {
var chr = window.navigator.userAgent.toLowerCase().indexOf("chrome") > -1;
var sfri = window.navigator.userAgent.toLowerCase().indexOf("safari") > -1;
return !chr && sfri;
}
function _checkAutoPlay(p) {
var s = window['Promise'] ? window['Promise'].toString() : '';
if (s.indexOf('function Promise()') !== -1 || s.indexOf('function ZoneAwarePromise()') !== -1) {
p.catch(function(error) {
console.error("_checkAutoPlay, error:", error)
if(error.name == "NotAllowedError") { // For Chrome/Firefox
console.error("_checkAutoPlay: error.name:", "NotAllowedError")
_callback_onAutoplayBlocked();
} else if (error.name == "AbortError" && isSafari()) { // Only for Safari
console.error("_checkAutoPlay: AbortError (Safari)")
_callback_onAutoplayBlocked();
} else {
console.error("_checkAutoPlay: happened something else ", error);
// throw error; // happened something else
}
}).then(function(){
console.log("_checkAutoPlay: then");
// Auto-play started
});
} else {
console.error("_checkAutoplay: promise could not work in your browser ", p);
}
}
var video1 = document.getElementById('video1');
_checkAutoPlay(video1.play());

Related

Autoplay stops after some time html5 video taken from google drive

I have a mp4 video inside html video tag. SRC= is given for the google drive video.
see my code below
<video auto play muted loop id="video" class="" src="https://drive.google.com/uc?export=download&id=1qSC6ySf6ZZldFRpuBx9EXvDsD-mfve1Z" type="video/mp4"> </video>
Note: when I am directly calling mp4 video from my server than it will not pause but when the video is called from google drive it gets paused after few minutes. As I am not showing any controls. I need the video to be played continuously.
the issues I'm getting is
Codepen link: https://codepen.io/5salman/pen/bGRMyKj
There's nothing inherit in Google Drive (that I can think of) that would cancel autoplay. More likely you're hitting an error event. The video will be loaded via "206 Partial Content" byte-range requests. If it doesn't end up being locally cached then that could make it more susceptible to network issues than on a server that doesn't support byte-range requests. Also consider looking at the network Devtools for any network errors.
Mitigation options:
Reduce the size of your video! It's 14mb for 20 seconds of video. Use Handbrake or something to re-encode the video to a smaller bitrate/size. That will reduce network traffic and make it less likely to choke the viewing device.
Add an error event handler on the <video> tag - this can help you with diagnostics, and you can also trigger the video to (try) to play again.
function reloadVideo(vidElement, preserveTime = true) {
if (preserveTime) {
// wait to set the current time again until after playable
const position = vidElement.currentTime;
var cb = vidElement.addEventListener('canplay', () => {
if (!isNaN(position) && position < vidElement.duration) {
// I recommend seeking just a frame or so ahead, just in case there was a decode error on the video
vidElement.currentTime = position + 1/30;
}
vidElement.removeEventListener('canplay', cb);
});
}
const src = vidElement.currentSrc;
vidElement.src = "";
vidElement.src = src;
}
var vidElement = document.querySelector('#video');
// retry on error
var retryErrorCodes = [MediaError.MEDIA_ERR_NETWORK, MediaError.MEDIA_ERR_DECODE];
var SECONDS_BEFORE_RETRY = 5;
vidElement.addEventListener('error', evt => {
if (retryErrorCodes.includes(vidElement.error.code)) {
setTimeout(reloadVideo, SECONDS_BEFORE_RETRY * 1000, vidElement);
}
}
(Not Recommended) if you need it to keep running you can add a setInterval function that restarts the video if it's paused. Keep in mind that these "zombie" functions can get annoying if you don't manage them carefully.
// use setInterval to keep retrying the playback
var SECONDS_BETWEEN_PLAYING_CHECKS = 10;
var keepPlayingTimer = setInterval(function () {
var vidElement = document.querySelector('#video');
// make sure the element is still there
if (vidElement && typeof vidElement.play === 'function') {
// if it's not playing start it playing again
if (vidElement.paused) {
vidElement.play();
}
} else {
// don't call this function again if video element is gone
clearInterval(keepPlayingTimer);
}
}, SECONDS_BETWEEN_PLAYING_CHECKS * 1000);

Video not play or autoplay in chrome, even muted - sometimes yes, sometimes no

I can't find a solution for this. Sorry to put angular and html as tags - it's because I've no clue who is the guilted.
Yes, I saw on Stack Overflow and many others sources that a video "muted" basically has no restrictions. But in my scenario... well, sometimes it works, sometimes it's not works...sometimes I do a refresh and it's works.
Very random.
<video *ngIf="p.background.type==='video'" [ngStyle]="{opacity: p.background.backgroundOpacity}"
class="backgroundElement" src="../assets/backgrounds/videos/{{p.background.file}}"
[ngClass]="{currentVideo: i === this.pageIndex}"
muted loop></video>
const video = this.elRef.nativeElement.querySelector('.currentVideo').play();
if (video !== undefined) {
video.then(_ => {
console.log('worked at this time');
}).catch(error => {
console.log(error);
});
}
}, 2000);
The error I got from promise is : message: "play() failed because the user didn't interact with the document first."
But it's a muted video. So, any suggestion ?

Detect if Vimeo video will autoplay

I am working with Vimeo and their autoplay feature. The issue is that autoplay won't work on mobile devices and I have some timers that assume the video did autoplay. I know an event is fired when the video plays, but I can seem to find anything on how to tell if autoplay is working. Is there a way to tell if autoplay is working programaticly. What I would want to do is something like this..
Does autoplay work on this device or did autoplay fire?
If not forget the timers and show all content now.
As you say, Vimeo Player's autoplay feature is not working on mobile devices (this is informed in this article) and also the JavaScript API seems to be broken on those devices. You can still use Vimeo's Player JavaScript API to manipulate videos on non-mobile devices and to infer whether mobile devices support JavaScript API.
Determining if autoplay worked on non-mobile devices:
To check if autoplay has started on those devices were vimeo player works you could use the play event in combination with player.getPaused() on page ready.
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://player.vimeo.com/api/player.js"></script>
</head>
<body>
<iframe id="player" src="https://player.vimeo.com/video/78716964?autoplay=1&title=0&byline=0&portrait=0" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
<script>
var player = new Vimeo.Player($('#player'));
var state = { autoplaying : false };
player.on('play', () => {
state.autoplaying = true;
player.off('play');
});
player.getPaused().then((paused) => {
var playing = !paused;
state.autoplaying = state.autoplaying || playing;
});
</script>
</body>
</html>
Determining if autoplay worked on mobile devices:
Right now there is no clear way to determine this on mobile devices since JavaScript API is broken. You could try to detect whether JavaScript API is enabled or not by calling any API method and wait for the promise result. If result didn't come in a short period of time, then you can say that API is not working. So, just let's say autoplay didn't work.
var player = new Vimeo.Player($('#player'));
var state = {
autoplaying : false,
apiEnabled : null
};
player.on('play', () => {
state.autoplaying = true;
player.off('play');
});
setTimeout(() => {
if (state.apiEnabled === null) {
state.apiEnabled = false;
// Do something
}
}, 100);
player.getPaused().then((paused) => {
state.apiEnabled = true;
// Do something
});
You could also use some technic like detecting user agent to determine if you are in a mobile device and therefore assume autoplay didn't execute.
I recommend you not to hack into Vimeo's iframe to determine autoplay state. Public APIs should be the way to go in order to ensure your script will work in the future.

How can I cause an HLS (m3u8) video to loop in Safari?

I took an mp4 video, encoded it for HTTP Live Streaming (HLS) using ffmpeg — resulting in a series of myvideo.ts files and a myvideo.m3u8 playlist — and am attempting to play it using the HTML <video> tag in Safari, with the native HLS capabilities of that browser:
<video id="myvideo" src="myvideo.m3u8" loop="loop"></video>
It plays, once. But despite the "loop" attribute in the video tag, it doesn't loop. It stays frozen on the last frame of the video.
If I try to detect the end of the video using an event listener as described here:
Detect when an HTML5 video finishes
… that event never seems to fire.
The "paused" property in javascript (document.getElementById('myvideo').paused) evaluates to false, even after the video has played once and stopped.
How can I get the video to loop in Safari?
HLS is intended to be a live stream, so it never actually "finishes" in order to automatically loop. I used a JavaScript timer as a hack to get around this:
var LOOP_WAIT_TIME_MS = 1000,
vid = document.getElementById("myvideo"),
loopTimeout;
vid.addEventListener('play', function() {
if (!/\.m3u8$/.test(vid.currentSrc)) return;
loopTimeout = window.setTimeout(function() {
loopTimeout = null;
vid.pause();
vid.play();
}, (vid.duration - vid.currentTime) * 1000 + LOOP_WAIT_TIME_MS);
});
vid.addEventListener('pause', function() {
if (!/\.m3u8$/.test(vid.currentSrc)) return;
if (loopTimeout) {
clearTimeout(loopTimeout);
loopTimeout = null;
}
});

Unable to bind Error event for Video element

I have a video tag,below is html
<video id="videoId" controls="controls">
<source src="../video/trailer.mp4" type="video/mp4" />
</video>
I want to add onError event on this video element .I have write the code that fire but its not work how can I bind it.May be I am missing somethng
window.onerror=function(){
var myvid = document.getElementById('videoId');
if (myvid.error) {
switch (myvid.error.code) {
case myvid.error.MEDIA_ERR_ABORTED:
alert("You stopped the video.");
break;
case myvid.error.MEDIA_ERR_NETWORK:
alert("Network error - please try again later.");
break;
case myvid.error.MEDIA_ERR_DECODE:
alert("Video is broken..");
break;
case myvid.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
alert("Sorry, your browser can't play this video.");
break;
}
}
}
Thanks in advance for help.
well, i m not sure if you can get the error over the window element... but if you look in the html5 specififation here: http://www.w3.org/TR/2010/WD-html5-20100624/video.html#video , you ll find following script block:
<script>
function failed(e) {
// video playback failed - show a message saying why
switch (e.target.error.code) {
case e.target.error.MEDIA_ERR_ABORTED:
alert('You aborted the video playback.');
break;
case e.target.error.MEDIA_ERR_NETWORK:
alert('A network error caused the video download to fail part-way.');
break;
case e.target.error.MEDIA_ERR_DECODE:
alert('The video playback was aborted due to a corruption problem or because the video used features your browser did not support.');
break;
case e.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
alert('The video could not be loaded, either because the server or network failed or because the format is not supported.');
break;
default:
alert('An unknown error occurred.');
break;
}
}
</script>
<p><video src="tgif.vid" autoplay controls onerror="failed(event)"></video></p>
<p>Download the video file.</p>
Important is, the loading error occurs not on the video tag, but on source tags inside video tag, and the event must be attached to src attribute of source tag, then you can use parentNode.id to address the proper video tag and do something with the error when it occurs and exactly where it occurs.
I use a serie of numbered video tags and an array with Popcorn.js objects to handle videos on page.
There is my solution here:
$("source").error(function () {
var tagStr = this.parentNode.id;
var pop = videos[tagStr.charAt(str.length-1)];
if (pop.media.networkState == pop.media.NETWORK_NO_SOURCE) {
doSomething;
}
}).attr("src");