GetUserMedia - facingmode
Asked Answered
A

7

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.

Asphyxia answered 19/8, 2015 at 3:38 Comment(0)
G
24

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.

Gesualdo answered 2/9, 2015 at 23:48 Comment(6)
Alternatively, you could use enumerateDevices() to let the user flip cameras. See webrtc.github.io/samples/src/content/devices/input-outputGesualdo
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.Otherwhere
@Otherwhere A fix to adapter is forthcoming.Gesualdo
bugs.chromium.org/p/chromium/issues/detail?id=290161 is now fixed, facingMode should work!Myrtamyrtaceous
Thanks,, you saved my job :DJalapa
fixed it without exact: it works jsfiddle.net/ugx4zqcwRepeated
M
10

Using Peter's code (https://mcmap.net/q/429425/-getusermedia-facingmode) 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);
});
Mixed answered 16/3, 2017 at 21:28 Comment(3)
This kind of works for some cases, but for iOS11 this fails. Instead i am using constraints.video.facingMode = { exact: "environment" }Quirinal
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)Pinite
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 deviceChalcanthite
N
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.

Neologism answered 12/1, 2017 at 16:37 Comment(2)
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?Sacristan
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.Quirinal
K
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.

Kreg answered 18/2, 2017 at 19:49 Comment(0)
G
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);
    }
};
Geyserite answered 20/2, 2017 at 6:18 Comment(0)
M
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;

})
Milamilady answered 26/9, 2018 at 11:55 Comment(0)
O
-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' )}
Onega answered 21/3, 2021 at 2:6 Comment(1)
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 deviceChalcanthite

© 2022 - 2024 — McMap. All rights reserved.