7

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!

d_z90
  • 1,213
  • 2
  • 19
  • 48

1 Answers1

6

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.

Community
  • 1
  • 1
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • Thanks for your reply @Kaiido, the fiddle works on Android! I have still some issue with my code, which you can find in this fiddle: jsfiddle.net/yw7ksv92 (but it is not correctly working). I have tried to insert your fiddle both in my code and in the native library, but I am still seeing the front camera when running it. Do you know what the issue may be? – d_z90 Jan 24 '17 at 13:35
  • @d_z90, after fast-reading jsARtoolkit sources, **yes**, you need to fork the lib... Specifically around [this line](https://github.com/artoolkit/jsartoolkit5/blob/f7a0e8b4c2d6a52e783a1c847e53c733ce99ada8/js/artoolkit.api.js#L1118), you would need to add something along `if(configuration.sourceId){ constraints.sourceId = configuration.sourceId; }`. But it seems they did block the `navigator.mediaDevices.getUserMedia` call with an `if(false)` to only use the old syntax, which seems weird... I suspect they didn't have updated their code for Promises yet.. I may have a deeper look next few days. – Kaiido Jan 25 '17 at 02:02
  • 1
    @d_z90, I did an ugly fork of the project. You can probably work from there. – Kaiido Jan 29 '17 at 08:26