8

I've the following constraints which are working perfectly fine over Chrome in Desktop (simulating mobile resolution)

const constraints = {
    audio: false,
    video: {
        width: screen.width,
        height: screen.height
    }
};

navigator.mediaDevices.getUserMedia(constraints).then(stream => {})

However when actually trying this on iPhone / Safari the camera doesn't respects this at all and gets super small or distorted - removing the width / height from the constraints makes it better ratio but not full screen at all, just centralized.

I've also tried with min / max constraints without lucky.

Is there any way to get this working on iPhones?

Spotted
  • 95
  • 2
  • 5
  • Hi @spotted did either of the answers below solve your problem? Please let me know if you would like further help. – Marcus Jul 25 '20 at 18:39

2 Answers2

11

I have built a few AR Websites which are mobile first. When you request a resolution the web browser sees if the resolution exists, and if it doesn't it then decides if it should emulate the feed for you. Not all browsers do emulation (even though it is part of the spec). This is why it may work in some browsers and not others. Safari won't emulate the resolution you are asking for with the camera you have picked (I presume the front).

You can read more about this here (different problem, but provides a deeper explaination): Why the difference in native camera resolution -vs- getUserMedia on iPad / iOS?

Solution

The way I tackled this is:

  1. Without canvas
  • Ask for a 720p feed, fallback to 480p feed if 720 gives an over-constrained error. This will work cross-browser.
  • Have a div element which is 100% width and height, fills the screen, and sets overlay to hidden.
  • Place the video element connected to the MediaStream inside, make it 100% height of the container. The parent div overlay hidden will in effect crop the sides. There will be no feed distortion.
  1. With canvas
  • Do not show the video element, use a canvas as the video view. Make the canvas the same size as your screen or the same aspect ratio and use CSS to make it fill the screen (latter is more performant).
  • Calculate the top, left, width and height variables to draw the video in the canvas (make sure your calculation centers the video). Make sure you do a cover calculation vs fill. The aim is to crop the parts of the video which do not need to be shown (I.e. like the descriptions of various methods in https://css-tricks.com/almanac/properties/o/object-fit) . Example on how to draw video into a canvas here: http://html5doctor.com/video-canvas-magic/

This will give you the same effect of what you are looking for. Production examples of something similar.

P.s. when I get time I can code an example, short on hours this week.

Marcus
  • 1,880
  • 20
  • 27
  • 1
    thanks for this. super simple, but super helpful. – Kyle Baker Feb 08 '21 at 02:39
  • I loved to see my face wearing make-up. LMFAO. Great work! – El Marce Mar 06 '22 at 06:32
  • In the HTML5 preview, how do I set the camera facingMode constraint to "environment"? – Remo Bajwa Aug 10 '22 at 01:57
  • In Video Track Constraints { video: { facingMode: "environment" } } however - from experience and last time I worked on this - its not always gurranteed and depending on browser it can grab any camera which matches your desired width and height. See https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints – Marcus Aug 11 '22 at 02:30
  • NB: I would be hesitent to use "exact" etc in the constraints such as "video": { "facingMode": { "exact": "environment" } }. Out of significant QA effort I built a list of video contraints per device (from userAgentData) and applied contraints I knew worked. Each browser on each platform handles this slightly differently and you may need a similar approach through QA testing. If this does not work message me and I am happy to help. – Marcus Aug 11 '22 at 02:35
  • I reccomend asking the user to pick the camera they would like to use and use a supported resolution. This is more gurranteed as you are using deviceId. see https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices https://webrtc.github.io/samples/src/content/devices/input-output/ – Marcus Aug 11 '22 at 02:39
6

There are a couple of quirks on mobile gUM() you need to know about.

First, if the device is in portrait orientation things work weirdly. You need to swap the width and height. So, let's say you're on a 480x640 device (do those even exist? who cares? it's an example). To get the appropriate size video you need

const constraints = {
   audio: false,
   video: {
    width: screen.height,
    height: screen.width
  }
};

I can't figure out exactly why it's like this. But it is. On iOS and Android devices.

Second, it's hard to get the cameras to deliver exactly the same resolution as the device screen size. I tweak the width and height to make them divisible by eight and I get a decent result.

Third, I figure the sizes I need by putting a <video ...> tag in my little web app with CSS that makes it fill the browser screen, then querying its size with

   const rect = videoElement.getBoundingClientRect()
   const width = rect.width > rect.height ? rect.width : rect.height
   const height = rect.width > rect.height ? rect.height : rect.width

This makes the mobile browser do the work of figuring out what size you actually need, and adapts nicely to the browser's various toolbars.

O. Jones
  • 103,626
  • 17
  • 118
  • 172
  • 2
    Requesting { width: 640, height: 480 } on iOS in portrait mode will return a camera MediaStream which is { width: 480, height: 640 }, and on orientation change the MediaStream flips its dimensions. Be careful around hardcoding the html elements width and height. Can cause warping. People should stick to known resolutions e.g those listed in https://en.wikipedia.org/wiki/Display_resolution. When you use an atypical resolution WebRTC spec states the browser will try emulate your desired feed. However this makes poor cross-browser code. If you want a consistent camera feed I would avoid this. – Marcus Jun 28 '20 at 12:31
  • The 640x480 resolution in my answer was an **example**. No actual devices, except some older 4:3 aspect ratio iPads, actually have that resolution. And the iOS on those? WebRTC? fugeddaboudit. – O. Jones Jun 29 '20 at 00:42
  • 1
    Can someone explain why mobile browsers swap the width and the height? I mean what sense does it make to request a resolution of WxH resolution (in getUserMedia constraints) and get HxW resolution on mobile device? (when holding the device straight) – Ron____ Feb 03 '21 at 19:37
  • 1
    "Why" questions are hard to answer. This is for sure though: browsers predate rotatable devices. It looks like landscape mode (w > h) is the design center. – O. Jones Feb 03 '21 at 21:02