Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AutoPlay for HTML5 <video>: detect "Unhandled Promise Rejection: " #272

Closed
odGit opened this issue Aug 1, 2018 · 3 comments
Closed

AutoPlay for HTML5 <video>: detect "Unhandled Promise Rejection: " #272

odGit opened this issue Aug 1, 2018 · 3 comments

Comments

@odGit
Copy link

odGit commented Aug 1, 2018

Current state of affairs

With desktop Safari, Chrome and others starting to block playback for HTML5 <audio> and <video> "without user action" similar to iOS Safari and mobile Chrome, with some special rules and exceptions for major sites like YouTube et al.
The Webkit announcement. Important note: users can disable auto-play entirely, even for silent videos.
https://webkit.org/blog/7734/auto-play-policy-changes-for-macos/

The Chromium project is following the same policy from version M66 Stable.
https://sites.google.com/a/chromium.org/dev/audio-video/autoplay

Possible solution

On iOS Safari 11 the media element's method .play() returns following:
Unhandled Promise Rejection: NotAllowedError (DOM Exception 35): The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
Anyway, I get to catch this while using rx-player ?

The recommended way by Apple is:

var promise = document.querySelector('video').play();

if (promise !== undefined) {
    promise.catch(error => {
        // Auto-play was prevented
        // Show a UI element to let the user manually start playback
    }).then(() => {
        // Auto-play started
    });
}
@peaBerberian
Copy link
Collaborator

peaBerberian commented Aug 2, 2018

Hello,

We were working on it!

For just the rejection of play part, I see two things that would work:

  1. Emit the fact that we couldn't autoPlay as a warning and let the UI react to it

We already have a warning event which communicates about various errors when they are not fatal (they do not stop playback).

We could add a specific "AUTOPLAY_NOT_ALLOWED" error in the rx-player, which will be sent via the warning event when this case arise.

Then, the task of managing that particular case will be on the UI side, having something like:

player.addEventListener("warning", function handleWarnings(error) {
  if (error.code === "AUTOPLAY_NOT_ALLOWED") {
    displayAutoPlayNotAllowedPopUp();
  }
});
  1. Provoke the auto-play feature directly on the UI

This solution can already work today.

The rx-player usually goes through several states when starting a content in autoPlay mode, in this order:
LOADING -> LOADED -> PLAYING

  • The rx-player has the PLAYING state when loadVideo is called
  • it goes through its LOADED state when the "canplay" event is received from the media element
  • just after that, the play method is called,
  • the player changes to the PLAYING state once the "play" event is received from the media element.

Based on that, you can manage the autoPlay feature yourself just by:

  1. setting autoPlay to false (or not setting it) in loadVideo
  2. wait for the playerStateChange event with the payload LOADED
  3. call and manage the play call yourself

Here we would have in the UI:

player.addEventListener("playerStateChange", function handleStateChanges(state) {
  if (state === "LOADED") {
    // try to auto-play
    var promise = document.querySelector('video').play();
    if (promise !== undefined) {
      promise.catch(error => {
        if (error.name === "NotAllowedError") {
          displayAutoPlayNotAllowedPopUp();
        }
      });
    }
  }
});

The first point is being implemented right now, the second one has the advantage of already being available.


Then again, we seem to have another issue for Safari:

From the very few tests we could do on it (it is kind of hard for us to do for dumb reasons -> we mostly work on computers dual booting linux + windows), it seems that this browser also blocks the canplay and the loadeddata event.

We will continue our tests, but if this is true, it means that it will be difficult for us to send the LOADED event like we do for other browsers.
It also means that both of the solution exposed higher in this post will not work right now for Safari.

In that case, we will have to rely on another method to switch to the LOADED state (from the size of the current buffer, the ready state etc.).

EDIT: added precision about what a "fatal" error means

@odGit
Copy link
Author

odGit commented Aug 3, 2018

Thank you for the update.
Currently got this temporal fix for autoPlay, which works on iOS and Android.
Create rx-player with

const rxPlayer = createModule(VIDEO_PLAYER, { videoElement: videoEl }); 
rxPlayer.dispatch(MUTE);  
rxPlayer.dispatch(LOAD, {
        url: action.video.url,
        transport: action.video.containerType,
        autoPlay: state.autoPlay,
        startAt: {
          position: action.position,
        },
      });

I had to sligthly alter MUTE/UNMUTE functions

MUTE: () => {
      const videoEl = player.getVideoElement();  <-- Get current video element
      videoEl.muted=true; <-- add muted to video element
      player.mute();
    },
UNMUTE: () => {
      const videoEl = player.getVideoElement();
      videoEl.muted=false;
      player.unMute();
    },

In React.render() video tag is as following:

{ this.props.autoPlay ?
            <video
              playsInline
              autoPlay <-- requried for mobile to autoPlay 
            /> : <video
            playsInline
          /> } 

The autoPlay attribute won't be diabled by autoplay="false" the atribute itself should be removed. Here is more info on autoPlay attribute.

NOTE: In my code base MUTE and UNMUTE did not work without muted attribute in video tag on both Android and iOS. I have not had time to test in rx-player's demo code base.

P.S. Gladly would help with testing, my dev setup is OS and got iOS test devices.

@peaBerberian
Copy link
Collaborator

peaBerberian commented Aug 27, 2018

We added a "warning" event in the v3.6.0 that should trigger in case of a "NotAllowedError" when the browser blocks autoplay.

In that case, the "warning" event contains a "MEDIA_ERROR" with the code: "MEDIA_ERR_BLOCKED_AUTOPLAY".

Warnings/errors and how to handle them is documented here.

Basically, you will have to do something allong the line of:

player.addEventListener("warning", (warning) => {
  if (warning.code === "MEDIA_ERR_BLOCKED_AUTOPLAY") {
     // show UI to start the stream manually
  }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants