Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Navigator.mediaDevices.getUserMedia not working on iOS 12 Safari

As of iOS 12, navigator.mediaDevices.getUserMedia() is returning an error in Safari.

To recreate this, open iPhone Web Inspector, then run this snippet in the console:

var constraints = { audio: true, video: { width: 1280, height: 720 } }; 

navigator.mediaDevices.getUserMedia(constraints)
  .then(function() {
    console.log('getUserMedia completed successfully.');
  })
  .catch(function(error) {
    console.log(error.name + ": " + error.message);
  });

You'll see that this runs successfully in desktop browsers, and in iOS 11 Safari, but fails in iOS 12 Safari.

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

Any idea why?

note: This is happening prior to the user being asked if their camera can be accessed, ruling out the possibility of it being because the user denied permission.

like image 957
Severisth Avatar asked Nov 26 '18 15:11

Severisth


People also ask

Does getUserMedia work on iOS?

Since iOS 11, getUserMedia is supposed to finally work on Apple devices. But in fact it does not work. The JavaScript sample code below works on all other OS: Blackberry, Android, etc...

Does getUserMedia work on Safari?

getUserMedia is deprecated, and not supported in Safari browser. You can see the Compatibility table taken from the MDN Docs. Instead, you can use the navigator. mediaDevices.

What is navigator MediaDevices getUserMedia?

The MediaDevices . getUserMedia() method prompts the user for permission to use a media input which produces a MediaStream with tracks containing the requested types of media.


3 Answers

Setting these three attributes before calling getUserMedia solved the problem for me:

    video.setAttribute('autoplay', '');
    video.setAttribute('muted', '');
    video.setAttribute('playsinline', '');

For some reason video.setAttribute() works but trying to assign the value directly to the video object for example video.muted = '' does not.

Also it appears that it's not necessary to call video.play().

Simply setting the video.srcObject to the stream returned by getUserMedia worked for me.

This medium post has a link to a working demo & source code.

like image 108
braitsch Avatar answered Oct 27 '22 18:10

braitsch


There are two possible reasons for immediate NotAllowedError at the moment:

1. getUserMedia requries https

Safari seems to require https for camera and mic access, both in iOS and OSX.

With an https link, iOS Safari 12 works for me; same link in http gets NotAllowedError.

Chrome has the same requirement. This is consistent with the direction of the specification, which recently has restricted getUserMedia to secure contexts. Browsers who have yet to update, still expose navigator.mediaDevices in http, but getUserMedia always rejects with NotAllowedError.

In the future, expect browsers to remove mediaDevices entirely in http, to comply with the spec.

2. getUserMedia requires feature policy in cross-origin iframes.

This appears new with Safari 12. In iframes, getUserMedia's feature policy is off by default for cross-origin content.

This works for me:

<iframe
  allow="camera;microphone"
  src="https://webrtc.github.io/samples/src/content/getusermedia/gum/">
</iframe>

This doesn't work:

<iframe src="https://webrtc.github.io/samples/src/content/getusermedia/gum/">
</iframe>

...and in addition to failing with NotAllowedError, Safari warns in web console:

The top-level frame has prevented a document with a different security origin to
call getUserMedia.

This is also a recent update to the spec.

like image 36
jib Avatar answered Oct 27 '22 18:10

jib


Turns out my particular issue was a bug in 12.01. Every device with that version had the issue, and as soon as I updated them to a newer version it went away.

like image 2
Severisth Avatar answered Oct 27 '22 19:10

Severisth