Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Select rear camera on Android device - jsartoolkit5

I am building a project similar to this example with jsartoolkit5, and I would like to be able to select the back camera of my device instead of letting Chrome on Android select the front one as default.

According to the example in this demo, I have added the code below to switch camera automatically if the device has a back camera.

var videoElement = document.querySelector('canvas');
function successCallback(stream) {
  window.stream = stream; // make stream available to console
  videoElement.src = window.URL.createObjectURL(stream);
  videoElement.play();
}

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

navigator.mediaDevices.enumerateDevices().then(
  function(devices) {
    for (var i = 0; i < devices.length; i++) {
      if (devices[i].kind == 'videoinput' && devices[i].label.indexOf('back') !== -1) {
        if (window.stream) {
          videoElement.src = null;
          window.stream.stop();
        }
        var constraints = {
          video: {
            optional: [{
              sourceId: devices[i].deviceId
            }]
          }
        };
        navigator.getUserMedia(constraints, successCallback, errorCallback);
      }
    }
  }
); 

The issue is that it works perfectly for a <video> tag, but unluckily jsartoolkit renders the content inside a canvas and it consequently throws an error. I have also tried to follow the instructions in this closed issue in the Github repository, but this time I get the following error: DOMException: play() can only be initiated by a user gesture.

Do you know or have any suggestion on how to solve this issue?

Thanks in advance for your replies!

like image 454
d_z90 Avatar asked Jan 18 '17 16:01

d_z90


1 Answers

Main problem :

You are mixing old and new getUserMedia syntax.
navigator.getUserMedia is deprecated, and navigator.mediaDevices.getUserMedia should be preferred. Also, I think that optional is not part of the constraints dictionary anymore.

Default Solution

This part is almost a duplicate of this answer : https://stackoverflow.com/a/32364912/3702797

You should be able to call directly

navigator.mediaDevices.getUserMedia({
  video: {
    facingMode: {
      exact: 'environment'
      }
    }
  })

But chrome still has this bug, and even if @jib's answer states that it should work with adpater.js polyfill, I myself were unable to make it work on my chrome for Android.

So previous syntax will currently work only on Firefox for Android.

For chrome, you'll indeed need to use enumerateDevices, along with adapter.js to make it work, but don't mix up the syntax, and everything should be fine :

let handleStream = s => {
  document.body.append(
    Object.assign(document.createElement('video'), {
      autoplay: true,
      srcObject: s
    })
  );
}

navigator.mediaDevices.enumerateDevices().then(devices => {
  let sourceId = null;
  // enumerate all devices
  for (var device of devices) {
    // if there is still no video input, or if this is the rear camera
    if (device.kind == 'videoinput' &&
        (!sourceId || device.label.indexOf('back') !== -1)) {
      sourceId = device.deviceId;
    }
  }
  // we didn't find any video input
  if (!sourceId) {
    throw 'no video input';
  }
  let constraints = {
    video: {
      sourceId: sourceId
    }
  };
  navigator.mediaDevices.getUserMedia(constraints)
    .then(handleStream);
});
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

Fiddle for chrome which need https.

Make it work with jsartoolkit

You'll have to fork jsartoolkit project and edit artoolkit.api.js.

The main project currently disables mediaDevices.getUserMedia(), so you'll need to enable it again, and you'll also have to add a check for an sourceId option, that we'll add later in the ARController.getUserMediaThreeScene() call.

You can find a rough and ugly implementation of these edits in this fork.

So once it is done, you'll have to rebuild the js files, and then remember to include adapter.js polyfill in your code.

Here is a working fiddle that uses one of the project's demo.

like image 90
Kaiido Avatar answered Sep 21 '22 07:09

Kaiido