Closed rijk closed 6 years ago
I notice the same issue on http://cookpete.com/react-player/. To reproduce:
Expected (from OS X Safari):
Actual (iOS):
Note that onReady
isn't called until after playback has started. This is a problem for me, because I use the onReady
callback to deactivate a loading overlay (so as long as that is not called, the user cannot start playback).
Yup, can confirm I have the same issue on Mobile Chrome as well - the onReady event is not called. @rijk Have you tested on Android?
onReady
is simply bound to the canplay
event when playing video files:
My guess is that because iOS tends to prevent any loading etc before the user has interacted, the play
event is fired as a result of the user action, and the canplay
event catches up and gets fired later.
I guess a possible fix would be to manually call onReady
before onPlay
when the onplay
event fires on iOS only? And then prevent onReady
from firing again when canplay
fires shortly after.
Yup, this seems to be the problem. In my case, this created a serious usability issue, as I display a loading spinner overlay until I get the onReady
. So when I never get this, the user can never interact with the video :)
I worked around it now by checking the user agent for iOS, and disabling the page's loading state manually in that case.
If we can't do anything to fix this, I do think it would be a good idea to add this to the documentation, under onReady
or at least the mobile considerations.
PS: The problem was not related to the order of the calls — at least not for me. So, personally, I see no need to apply a fix there.
PS: The problem was not related to the order of the calls — at least not for me. So, personally, I see no need to apply a fix there.
So what was the problem in the end?
Right, so the problem was that the page just kept 'loading' when you opened it on an iPhone. Because the loading overlay blocked the user from interacting with the video. I think it would be good to add this to the onReady
docs, that on iOS it is not called until the user interacts with (clicks) the video.
Ah I see. Your loader was preventing user interaction, which was preventing play
and canplay
events, which was prevent onReady
, which was preventing the loader from being removed... etc.
There must be a combination that works. I wonder if load
, loadeddata
or progress
events would be better? I also wonder if adding autoPlay
to the <video>
would change things (it may fire some useful events without user interaction, even if it doesn't actually autoplay). I'll do some tests when I have some time.
You got it.
There must be a combination that works.
Maybe, but I'm not sure. When I test with your demo, no handler is called until you actually tap a button. Autoplay doesn't do anything either; iOS just doesn't allow playing unless the user has explicitly requested it (to save data, I suppose).
Autoplay doesn't do anything either; iOS just doesn't allow playing unless the user has explicitly requested it (to save data, I suppose).
Yep, this is a popular issue that people are constantly battling against.
I hope my patch could help anyone landing here:
componentDidMount() {
// on iOS we need to force the load event
// to get the onReady event firing properly
// where of course this.refs.video is so rendered: <ReactPlayer ref='video'>
const videoElement = this.refs.video.getInternalPlayer()
videoElement.load()
}
Awesome!
This is great, but the IOS 13 on tablets has changed the useragent for Safari. On iphone it says (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) but on iPad and iPad Pro its (Macintosh; Intel Mac OS X 10_15) just like on Safari desktop. Infuriating!
I have rolled a quick fix which is to test for Safari and also touch device and call the player.load() something like:
/**
* Modernizr's way of checking it
https://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript/4819886#4819886
*/
function testTouchDevice() {
var prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');
var mq = function(query) {
return window.matchMedia(query).matches;
}
if (('ontouchstart' in window) || window.DocumentTouch) {
return true;
}
// include the 'heartz' as a way to have a non matching MQ to help terminate the join
// https://git.io/vznFH
var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');
return mq(query);
}
var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
var isTouchDevice = testTouchDevice();
var isIOS = IOS || (isSafari && isTouchDevice);
...
if (isIOS){
this.player.load();
}
There is probably a better way of doing it a touch detection is not too reliable.
Checking on Safari, it works fine for the initial load, but not when you seek (OnSeek) it does not work, should it be monkey patched elsewhere for IOS?
@scottmcdonnell this is probably no longer relevant to you but #1004 and the fix in #1005 may be of interest.
I hope my patch could help anyone landing here:
componentDidMount() { // on iOS we need to force the load event // to get the onReady event firing properly // where of course this.refs.video is so rendered: <ReactPlayer ref='video'> const videoElement = this.refs.video.getInternalPlayer() videoElement.load() }
This worked well. Adding some more cases to avoid multiple calls.
<ReactPlayer
ref={(p) => {
if (p) {
setPlayerRef(p as any);
const vElement = p.getInternalPlayer() as HTMLVideoElement;
vElement && vElement.readyState !== 4 && vElement?.load();
}
}}
{...props}
/>
I am loading an .mp4 file from AWS. On OSX Safari it is working fine, no problems, but on iOS the
onReady
function is never called. This happens both on v0.25.3, and the latest (v1.2.1) so it's not a recent bug. I did not find any info about this in the "Mobile considerations" section, however.Is this a known issue? Are there any workarounds?