15

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?

smitop
  • 4,770
  • 2
  • 20
  • 53
maxpaj
  • 6,029
  • 5
  • 35
  • 56
  • Chrome seems to prevent autoplay sometimes even if you've visited the site 100 times, but only sometimes. The inconsistency makes testing extemely hard, and I don't think VideoJS works with the promise framework detailed in the accepted answer. – trysis Jan 31 '19 at 17:34
  • Yes, the Media Engagement Index may seem like black magic, but it is clearly stated in https://docs.google.com/document/d/1_278v_plodvgtXSgnEJ0yjZJLg14Ogf-ekAFNymAJoU/edit# how it is measured. VideoJS does support returning a promise when it is available in the browser. https://docs.videojs.com/player.js.html#line2172 – maxpaj Jan 31 '19 at 18:08
  • Testing Tip for Safari on iOS: Turn on low battery mode and it will block auto-play. This is the easiest way to test. Not sure what Android does. – Simon_Weaver Jan 15 '21 at 19:36

2 Answers2

35

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>
maxpaj
  • 6,029
  • 5
  • 35
  • 56
  • @sea-kg, what exactly isn't working? Could be that your version of Safari does not support video.play() returning a Promise. – maxpaj Feb 20 '19 at 10:06
  • 1
    Safari version is Version 12.0.3 (14606.4.5). Detect not working correctly In my case best solution was handle errors NotAllowedError for Chrome And Firefox and "AbortError" only for Safari – sea-kg Feb 27 '19 at 07:25
  • is there any way to do this without attempting to play the audio first? – But those new buttons though.. Aug 15 '19 at 04:57
  • Not sure, but if the audio does play, you can always pause it immediately after it successfully plays. – maxpaj Aug 16 '19 at 14:58
2

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());
sea-kg
  • 317
  • 2
  • 5