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:
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.
I would expect this to just play normally since the click for playing was done in a user interaction on the page.
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:
https://github.com/videojs/video.js/issues/4765
https://github.com/videojs/video.js/pull/5822
I updated the description after some other tests to provide a simple example. @CookPete
Thanks
As far as I can tell, https://github.com/videojs/video.js/issues/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.
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.
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 馃憤
Yep, we will implement a solution like that, thanks for your help.
Most helpful comment
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
mutedprop isfalse).You can probably implement this yourself already: store
muted: falsein state, then useronErrorto listen for the error, andsetState({ muted: true })if it happens. You may also have to switchplayingfromfalsetotrue, or callgetInternalPlayer().play()to forceplay()to be called.Edit: Sorry, I assumed you were suggesting
ReactPlayerdo this. If you just meant that you are going to implement this in your app, then awesome 馃憤