18

I am currently using an Android tablet and GetUserMedia to take pictures in my program.

Apparently, the default camera used by GetUserMedia is the front camera. How do I use the rear camera as a default?

Here's my code for GetUserMedia:

        navigator.getUserMedia({
            "audio": false,
            "video": {
                mandatory: {
                    minWidth: this.params.dest_width,
                    minHeight: this.params.dest_height,
                    //facingMode: "environment",
                },
            }
        }, 
        function(stream) {
            // got access, attach stream to video
            video.src = window.URL.createObjectURL( stream ) || stream;
            Webcam.stream = stream;
            Webcam.loaded = true;
            Webcam.live = true;
            Webcam.dispatch('load');
            Webcam.dispatch('live');
            Webcam.flip();
        },
        function(err) {
            return self.dispatch('error', "Could not access webcam.");
        });

I inserted facingMode in the "mandatory" part but didn't work.

Please help.

Pang
  • 9,564
  • 146
  • 81
  • 122
Mikhail
  • 201
  • 1
  • 2
  • 4

7 Answers7

23

Update: facingMode is now available in Chrome for Android through the adapter.js polyfill!

facingMode is not yet implemented in Chrome for Android, but works natively in Firefox for Android.

You must use standard constraints however: (use https fiddle for Chrome):

var gum = mode => 
  navigator.mediaDevices.getUserMedia({video: {facingMode: {exact: mode}}})
  .then(stream => (video.srcObject = stream))
  .catch(e => log(e));

var stop = () => video.srcObject && video.srcObject.getTracks().forEach(t => t.stop());

var log = msg => div.innerHTML += msg + "<br>";
<button onclick="stop();gum('user')">Front</button>
<button onclick="stop();gum('environment')">Back</button>
<div id="div"></div><br>
<video id="video" height="320" autoplay></video>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

The { exact: } syntax means the constraint is required, and things fail if the user doesn't have the right camera. If you leave it out then the constraint is optional, which in Firefox for Android means it only changes the default in the camera chooser in the permission prompt.

Community
  • 1
  • 1
jib
  • 40,579
  • 17
  • 100
  • 158
  • 4
    Alternatively, you could use [`enumerateDevices()`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices) to let the user flip cameras. See https://webrtc.github.io/samples/src/content/devices/input-output/ – jib Sep 03 '15 at 00:00
  • Right now, on my phone, this isn't working. Chrome is claiming to support facingMode, but it's not actually working, and that seems to be messing up the adapter-latest.js. I was able to use the code in adapter-latest.js to come up with something that works though, a bit more like what Peter Unsworth's answer shows. – user12861 Feb 03 '17 at 15:16
  • @user12861 A [fix](https://github.com/webrtc/adapter/pull/371) to adapter is forthcoming. – jib Feb 21 '17 at 04:14
  • 1
    https://bugs.chromium.org/p/chromium/issues/detail?id=290161 is now fixed, facingMode should work! – Benjamin H Jun 22 '17 at 04:13
  • 1
    Thanks,, you saved my job :D – Alauddin Ahmed May 22 '18 at 04:58
  • fixed it without `exact:` it works https://jsfiddle.net/ugx4zqcw/ – SSpoke Mar 11 '20 at 10:45
10

Using Peter's code (https://stackoverflow.com/a/41618462/7723861) I came up with this solution to get the rear camera:

function handleSuccess(stream) {
  window.stream = stream; // make stream available to browser console
  video.srcObject = stream;
}

function handleError(error) {
  console.log('navigator.getUserMedia error: ', error);
}

var DEVICES = [];
var final = null;
navigator.mediaDevices.enumerateDevices()
    .then(function(devices) {

        var arrayLength = devices.length;
        for (var i = 0; i < arrayLength; i++)
        {
            var tempDevice = devices[i];
            //FOR EACH DEVICE, PUSH TO DEVICES LIST THOSE OF KIND VIDEOINPUT (cameras)
            //AND IF THE CAMERA HAS THE RIGHT FACEMODE ASSING IT TO "final"
            if (tempDevice.kind == "videoinput")
            {
                DEVICES.push(tempDevice);
                if(tempDevice.facingMode == "environment" ||tempDevice.label.indexOf("facing back")>=0 )
                    {final = tempDevice;}
            }
        }

        var totalCameras = DEVICES.length;
        //If couldnt find a suitable camera, pick the last one... you can change to what works for you
        if(final == null)
        {
            //console.log("no suitable camera, getting the last one");
            final = DEVICES[totalCameras-1];
        };

        //Set the constraints and call getUserMedia
        var constraints = {
        audio: false, 
        video: {
            deviceId: {exact: final.deviceId}
            }
        };

        navigator.mediaDevices.getUserMedia(constraints).
        then(handleSuccess).catch(handleError);

    })
    .catch(function(err) {
        console.log(err.name + ": " + err.message);
});
Community
  • 1
  • 1
Miojo_Esperto
  • 229
  • 2
  • 11
  • 1
    This kind of works for some cases, but for iOS11 this fails. Instead i am using ```constraints.video.facingMode = { exact: "environment" }``` – deweydb Nov 03 '17 at 22:38
  • 1
    I tried `facingMode` on several occasions with different computers and browsers - it almost never worked (but I also had other constraints set to exact, like width and height) – GameDroids Apr 10 '19 at 14:09
  • 2
    DO NOT USE LABEL TO IDENTIFY THE DEVICE: This code relies on device label, and device label is localized. For example, in iPhone 6S with Catalan language, labels would be "Càmera del darrere" and "Càmera del davant", so it would fail to select correct device – Miquel Jun 10 '21 at 12:57
4

Deploying our web app to android through Cordova, I tried multiple solutions to access the rear camera. The solution that worked for me was:

constraints = {
    audio: false,
    video: {
        width: 400,
        height: 300,
        deviceId: deviceId ? {exact: deviceId} : undefined
    }
};

Retrieving the deviceId through:

navigator.mediaDevices.enumerateDevices()
    .then(function(devices) {
        // devices is an array of accessible audio and video inputs. deviceId is the property I used to switch cameras
    })
    .catch(function(err) {
        console.log(err.name + ": " + error.message);
});

I chose not to use a Cordova plugin so that if we choose to move away from Cordova, there would not be such a hefty migration.

  • 3
    how do you know which one of the cameras in enumerateDevices is front and which is rear? I think the first one is user facing (front) and the second one is rear, but (where) is that specified? – anneb Feb 13 '17 at 16:43
  • 1
    So in some cases we can rely on device.label being 'camera 1, facing front' or 'camera 0, facing back'. But i have also seen some phones where this is not reported e.g. on LG D852 both device.labels for the camera devices are empty strings. – deweydb Nov 03 '17 at 21:10
4

A pretty dandy snippet you can use is:

var front = false;
document.getElementById('flip-button').onclick = function() { front =` !front; };
var constraints = { video: { facingMode: (front? "user" : "environment") } };

This should work for you hopefully.

BlackVegetable
  • 12,594
  • 8
  • 50
  • 82
2

In the newer version of Chrome (after v52) the adaper.js solutions seem not working. So I solve the problem by enumerating devices first. Here is my solution. I am not sure if there is a better way to flip the camera and show the video on the screen. But I have to stop the track first and get a new stream.

let Video = function() {
    let cameras = [];
    let currCameraIndex = 0;
    let constraints = {
        audio: true,
        video: {
          deviceId: { exact: "" }
        }
      };
    let videoCanvas = $('video#gum');


    this.initialize = function() {
      return enumerateDevices()
        .then(startVideo);
    };

    this.flipCamera = function() {
      currCameraIndex += 1; 
      if (currCameraIndex >= cameras.length) {
        currCameraIndex = 0;
      }

      if (window.stream) {
        window.stream.getVideoTracks()[0].stop();
      }
      return startVideo();
    };

    function enumerateDevices() {
      return navigator.mediaDevices.enumerateDevices()
        .then(function(devices) {
          devices.forEach(function(device) {
            console.log(device);
            if (device.kind === "videoinput") {
              cameras.push(device.deviceId);
            }
          });
          console.log(cameras);
        });
    }

    function startVideo() {
      constraints.video.deviceId.exact = cameras[currCameraIndex];
      return navigator.mediaDevices.getUserMedia(constraints)
        .then(handleSuccess).catch(handleError);
    }

    function handleSuccess(stream) {
      videoCanvas[0].srcObject = stream;
      window.stream = stream;
    }

    function handleError(error) {
      alert(error);
    }
};
Yifan Sun
  • 772
  • 1
  • 10
  • 25
0

This would work for rear camera :

navigator.mediaDevices.getUserMedia({
video: {facingMode: "environment",
 height:{<heightValueHere>},
 width : {<widthValueHere>} 
}
})
.then(function(stream){
window.stream = stream;
video.srcObject = stream;

})
-1

I use the following for webcam.js, call LoadWebcam ()

async function LoadWebcam () { 
var i
var frontdev 
var backdev
const tempStream = await navigator.mediaDevices.getUserMedia({video:true})
const devices = await navigator.mediaDevices.enumerateDevices()
//Check all the video input and find the backdev = back Cam
devices.forEach(function(device) {
    if (device.kind === 'videoinput') {
        if( device.label && device.label.length > 0 ) {
          if( device.label.toLowerCase().indexOf( 'back' ) >= 0 ) 
            backdev = device.deviceId
          else if( device.label.toLowerCase().indexOf( 'front' ) >= 0 )
            frontdev = device.deviceId
        }
    }
})
//Stop Stream
const tracks = tempStream.getTracks()
if( tracks ) 
  for( let t = 0; t < tracks.length; t++ ) tracks[t].stop()
//Load the webcam, 
Webcam.set({
    width: 320,
    height: 240,
    image_format: 'png',
    jpeg_quality: 90,
    flip_horiz: true,
    constraints: {
    video: true,
        deviceId: {exact: backdev }
    }
});
Webcam.attach( '#my_camera' )}
  • This code relies on device label, and device label is localized. For example, in iPhone 6S with Catalan language, labels would be "Càmera del darrere" and "Càmera del davant", so it would fail to select correct device – Miquel Jun 10 '21 at 12:59