Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GetUserMedia - facingmode

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.

like image 716
Mikhail Avatar asked Aug 19 '15 03:08

Mikhail


5 Answers

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.

like image 178
jib Avatar answered Oct 07 '22 17:10

jib


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);
});
like image 26
Miojo_Esperto Avatar answered Oct 07 '22 17:10

Miojo_Esperto


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.

like image 43
Peter Unsworth Avatar answered Oct 07 '22 16:10

Peter Unsworth


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.

like image 4
Théodore Tremblot Avatar answered Oct 07 '22 18:10

Théodore Tremblot


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);
    }
};
like image 2
Yifan Sun Avatar answered Oct 07 '22 18:10

Yifan Sun