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

Playing in iOS/Safari with user interaction not working after server request #618

Closed
MarcLopezAvila opened this issue Apr 5, 2019 · 5 comments

Comments

@MarcLopezAvila
Copy link

MarcLopezAvila commented Apr 5, 2019

Hi, first of all, thanks for your react-player component. It has helped our team to build different players in our projects but we are running into a big problem that for you may find to be easy to fix but we didn't find any solution yet:

Current Behavior

We have two projects:
https://www.cadena100.es/ and https://www.cope.es/directos/net1

Both of them use ReactPlayer as their's player core (FilePlayer) but we are unable to trigger a video/audio playing after making a server request for the video data. This request is triggered by a user interaction. This problem only happens in iOS and Safari.

We have the error:

NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.

This is related to the autoplay policies but we don't want to autoplay them, we just need to play it after a user's interaction on the page that will change the ReactPlayer props after we have the video/audio src that comes from server.

Expected Behavior

I would expect this to just play normally since the click for playing was done in a user interaction on the page.

Example:

import React, { useState } from 'react';
import ReactPlayer from 'react-player';

const TEST_VIDEO_SRC = 'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4';

const getMedia = () => new Promise((resolve) => {
  setTimeout(() => {
    resolve(TEST_VIDEO_SRC);
  }, 2000);
});

const Example = () => {
  const [url, setUrl] = useState(null);
  return (
    <section>
      <div onClick={() => getMedia().then(setUrl)}>
        Play Video
      </div>
      <ReactPlayer
        url={url}
        playsinline
        playing={!!url}
        onError={error => console.log(error)}
      />
    </section>
  );
};

export default Example;

If I remove the promise and I just execute setUrl it works normally. Do you know what are we doing wrong?

Thanks in advance.

PD: Maybe related:
videojs/video.js#4765
videojs/video.js#5822

@MarcLopezAvila MarcLopezAvila changed the title Autoplay in iOS with user interaction not working Playing in iOS with user interaction not working Apr 5, 2019
@MarcLopezAvila MarcLopezAvila changed the title Playing in iOS with user interaction not working Playing in iOS with user interaction not working after server request Apr 7, 2019
@MarcLopezAvila MarcLopezAvila changed the title Playing in iOS with user interaction not working after server request Playing in iOS/Safari with user interaction not working after server request Apr 7, 2019
@MarcLopezAvila
Copy link
Author

I updated the description after some other tests to provide a simple example. @cookpete

Thanks

@cookpete
Copy link
Owner

cookpete commented Apr 8, 2019

As far as I can tell, videojs/video.js#4765 pretty much nails it. iOS has a very strict policy on what can start media. In your example it is the resolution of a promise that tries to start playing something, and not a direct user action. Calling setUrl directly works because it is then the direct result of a user action.

I honestly don't know what can be done about this, and looking at the video.js issue it seems like they are struggling too.

Perhaps video.js should be added as a supported ReactPlayer player? Then we can enjoy all of the stupid nuances like this that those guys have already fixed.

@MarcLopezAvila
Copy link
Author

In any case their issue is related but doesn't fix this use case where we must have this async call before play to get the media data.

If I understand correctly they are talking about removing an async code (timeout) they had in their codebase that was causing the issue. The final fix though was to player.load() once they have the video src to make it ready for the user gesture.

I don't think their solution works for us because we need that async call for now.

Probably we go for the solution where if we find a NotAllowedError we play it as muted. Because after the user successfully plays something with sound, all the other videos/audios work normally.

@cookpete
Copy link
Owner

cookpete commented Apr 8, 2019

Probably we go for the solution where if we find a NotAllowedError we play it as muted.

This is too much of an assumption to make on behalf of the user. We don't know that devs/users will be happy with the media playing anyway if the browser is not allowing it to play. They might be happy to wait for direct user interaction. It also creates a weird dissonance between props and reality (something is playing muted even though muted prop is false).

You can probably implement this yourself already: store muted: false in state, then user onError to listen for the error, and setState({ muted: true }) if it happens. You may also have to switch playing from false to true, or call getInternalPlayer().play() to force play() to be called.


Edit: Sorry, I assumed you were suggesting ReactPlayer do this. If you just meant that you are going to implement this in your app, then awesome 👍

@MarcLopezAvila
Copy link
Author

Yep, we will implement a solution like that, thanks for your help.

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