简体   繁体   中英

getUserMedia detect front camera

I'm using the 'facingMode' constrain in order to switch between the two cameras, but I'm not able to fully decided whether the user end has an 'environment' camera (back facing camera).. it's not enough to to count the 'videoinput' of the returned promise of enumerateDevices function.

I tried searching for it and all I found was to use the video MediaTrack label and search for containing "facing back" string, which doesnt seem to be constant in all browsers (IOS for instance).

I'm sure there must be a better way:)

A second answer here. The selection of front ( facingMode:'user' ) and back ( facingMode:'environment' ) cameras is an issue for something I'm working on.

It's possible to tell which camera you're using if you have an open .getUserMedia() stream to a camera. stream.getTracks[0].getCapabilities() gives back an object with either a facingMode:'user' or facingMode:'environment' field in it. But you already must have a stream open to the particular device to get this.

Here's what I have discovered using multiple devices in a mobile-device test farm. These are the order of the device entries yielded by .enumerateDevices() .

tl;dr: On iOs devices , the front facing camera is the first videoinput device in the list, and the back-facing camera is the second one. On Android devices , the front facing camera or cameras have the string "front" in their device.label values and the back facing camra or cameras have the string "back".

iOS devices

  1. iOS devices running Mobile Safari: the device.label items are all localized to the currently selected national language.
  2. The devices always appear in this order in the device array, with the front camera always appearing as the first device in the array with device.kind:'videoinput' . Here is the list of devices labels I got from every iOS device I tried, auf Deutsch:
    • audioinput:iPhone Mikrofon
    • videoinput:Frontkamera
    • videoinput:Rückkamera
  3. Some iPhones have multiple camera lenses on the back. Nevertheless, the MediaStream APIs in Mobile Safari only show one camera.

You can tell you're on an iPhone when

navigator.userAgent.indexOf(' iPhone ') >= 0  

You can tell you're on an iPad when

    typeof navigator.maxTouchPoints === 'number' 
 && navigator.maxTouchPoints > 2
 && typeof navigator.vendor === 'string'
 && navigator.vendor.indexOf('Apple') >= 0

Notice that iPad Safari now lies and claims it's an Intel Mac. It presents this User-Agent string:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15

Android devices

  1. Android devices: the `device.label' items are not localized.

  2. The devices I looked at had different device.label lists. But the cameras I found all had either the string front or back in the device.label.

  3. A Pixel 3A running Android 11 has this list of devices.

    • audioinput:Standard
    • audioinput:Speakerphone
    • audioinput:Headset earpiece
    • videoinput:camera2 1, facing front
    • videoinput:camera2 0, facing back
    • audiooutput:Standard
  4. A Samsung SM-A205F running Android 9 has these devices in order.

    • audioinput:Default
    • audioinput:Speakerphone
    • audioinput:Headset earpiece
    • videoinput:camera2 1, facing front
    • videoinput:camera2 2, facing front
    • videoinput:camera2 0, facing back
    • audiooutput:Default

    This one enumerates two different front-facing selfiecams. The first one offers higher resolution than the second one.

  5. A Samsung SM-G925I running Android 6.0.1

    • audioinput:Default
    • audioinput:Speakerphone
    • audioinput:Headset earpiece
    • videoinput:camera2 1, facing front
    • videoinput:camera2 0, facing back
    • audiooutput:Default

And, by the way, the results of .getSupportedConstraints() don't help much: both iOS and Android give facingMode:true .

It's my experience, across a wide variety of mobile devices with English as their default language , that the device.label property you get back from enumerateDevices() contains the string 'front' or 'back' for the user or environment camera. But on some devices the string is in upper case. So you need to check for those strings in a case insensitive way.

Also, enumerateDevices() conceals the label values unless your user has granted permission to access media. In most browsers, the permission is sticky . That is, once a user has granted it to your web site, it stays granted. But on Mobile Safari devices the permission is not sticky : your user must grant it each time your page loads. You get your user to grant permission with a getUserMedia() call.

This code should let you know whether you have front and back cameras.

async function hasFrontBack() {
  let result = {hasBack: false, hasFront: false, videoDevices: []}
  try {
    const stream = await navigator.mediaDevices.getUserMedia(
      {video: true, audio: false})
    let devices = await navigator.mediaDevices.enumerateDevices()
    const videoDevices = devices.filter(device => {
      if (device.kind === 'videoinput') {
        if (device.label && device.label.length > 0) {
          if (device.label.toLowerCase().indexOf('back') >= 0) {
            result.hasBack = true
          } else if (device.label.toLowerCase().indexOf('front') >= 0) {
            result.hasFront = true
          } else { /* some other device label ... desktop browser? */ }
        }
        return true
      }
      return false
    })
    result.videoDevices = videoDevices
    /* drop stream */
    const tracks = stream.getTracks()
    if (tracks) {
      for (let t = 0; t < tracks.length; t++) tracks[t].stop()
    }
    return result
  }
  catch (ex) {
    /* log and swallow exception, this is a probe only */
    console.error(ex)
    return result
  }
}

Once you have this result, you can use getUserMedia() again without prompting the user for permission again.

To determine if we are using the mobile back camera, use this after the stream has loaded. Currently Firefox does not support getCapabilities() . Tested on MacOS/iOS/Android, Safari/Chrome/Firefox.

if (stream.getTracks()[0].getCapabilities) {
  isBackCamera = stream.getTracks()[0].getCapabilities()?.facingMode?.[0] === 'environment';
}

I found a decent solution, still not perfect but can help. determine by the MediaStreamTrack applied constraints:

MediaStream.getVideoTracks()[0].getConstraints().facingMode

As long as you are not using 'exact' while using 'facingMode', it won't be guaranteed..

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM