Search code examples
javascriptgoogle-chromenavigatorsamsung-galaxymediadevices

navigator.mediaDevices.enumerateDevices() returns non-sensical labels and getUserMedia() seems to always be using the same device


I have a Samsung Galaxy S22 Ultra and I'm trying to use navigator.mediaDevices to (1) list all the cameras and (2) show output from any of the cameras.

Here are the device labels when I list all the cameras on my device:

  • camera2 1, facing front
  • camera2 3, facing front
  • camera2 2, facing back
  • camera2 0, facing back

As I understand it the phone has 5x total cameras:

  • a 108MP wide camera
  • a 12MP ultrawide camera
  • a 10MP telephoto
  • a 10MP periscope telephoto
  • front camera.

So 4x of the cameras are rear facing and 1x is front facing. So why is it that navigator.mediaDevices.enumerateDevices() returns something else completely different?

My next question is... when I use any of the deviceIds that navigator.mediaDevices.getUserMedia() returned I get the front facing camera view on my phone, even if I select a deviceId whose label is "facing back". What's up with that?

Here's my code:

<script>
navigator.mediaDevices.getUserMedia({video: true});

var getCameraSelection = async () => {
  var devices = await navigator.mediaDevices.enumerateDevices();
  var videoDevices = devices.filter(device => device.kind === 'videoinput');
  const options = videoDevices.map(videoDevice => {
    return `<li><a href="#" onclick="useCamera('${videoDevice.deviceId}')">${videoDevice.label}</a></li>`;
  });
  document.querySelector('#output').innerHTML = '<ul>' + options.join('') + '</ul>';
};

getCameraSelection();

var useCamera = async (deviceId) => {
  const stream = await navigator.mediaDevices.getUserMedia({
    deviceId: deviceId,
    video: true
  });
  var video = document.querySelector('video');
  video.srcObject = stream;
  video.play();
  return false;
};
</script>

<div id="output"></div>
<video autoplay></video>

Solution

  • I had a similar issue and it took me forever to figure out how to select a specific camera by device id. Unfortunately there are a lot of examples floating around out there that look very similar to yours that do not work as advertised. (Always check the language reference! https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)

    The issue is the constraints object you are providing to the getUserMedia() method. By providing { video: true }, the video stream provided to the browser uses the 'default' camera that the user's device provides.

    Instead, define deviceId as a property within the video property, like so:

    var useCamera = async (deviceId) => {
      const stream = await navigator.mediaDevices.getUserMedia({
        video: {
          deviceId: deviceId
        }
      });
      var video = document.querySelector('video');
      video.srcObject = stream;
      video.play();
      return false;
    };
    

    As you can see, this is similar to the other answer provided. In that example, although it does work, the camera isn't being selected by the deviceId but rather the first camera found by the browser with a constraint of facingMode: 'environment'. Depending on the user's device, you might get very different results. Also, it makes the getCameraSelection() function in your code rather useless as it totally ignores the deviceId.

    PS If you want to apply additional constraints such as zoom or torch (flashlight) it can be done in a similar manner. Just use the applyConstraints() method on the stream's video track. (Providing an example because it has an oddly specific syntax that also took me awhile to discover how to make it work).

    const stream = await navigator.mediaDevices.getUserMedia({
      video: {
        deviceId: deviceId
      }
    });
    
    // applying zoom and turning on flashlight
    stream.getVideoTracks()[0].applyConstraints([
      {
        zoom: 2 // 2x zoom
        torch: true // flashlight on
      }
    ]);