I am developing a Vuejs based PWA. I have implemented a component to take pictures using the device built-in camera. It is using
navigator.mediaDevices.getUserMedia API with a video element to display the stream from the camera.canva and its getContext("2d").drawImage methods to get the picture from the stream and show it to the usernavigator.mediaDevices.enumerateDevices API to get a list of available cameras and the constraint parameter of getUserMedia to switch between these.This is all working great :)
But now I have stumbled on a new issue: some recent smartphones have multiple rear cameras that the built-in camera software would combine to take only one shoot (one camera would be good at low exposure, another one at high exposure and yet another one in some other conditions, etc.). In this case my app will show all the cameras and will let the user use them independently. The intended behavior would be to combine them just like the built-in software. How is that possible?
After trying on multiple devices having 1 (front), 2 (back and front) and multiples camera sensors (1 front, several back), my solution consists in using the label property of the MediaDeviceInfo object returned by mediaDevices.enumerateDevices():
Then my code getting the id of the front camera and of the combined backed camera is the following (it assumes there is always one and only one front camera):
return navigator.mediaDevices.enumerateDevices()
.then(mediaDevices => {
let cameras = mediaDevices.filter(mediaDevice => mediaDevice.kind === 'videoinput');
console.log("#potential camera=" + cameras.length);
if(cameras.length<=2)
return cameras.map(x => x.deviceId);
else {
let front = cameras.find(x => x.label.match(/\bfront\b/));
let back = cameras.filter(x => x.label.match(/\bback\b/) && x.label.match(/\b0\b/))
if(!front || back.length!=1) {
console.log("no clear front and back, taking first and last one - cameras=", cameras);
return [cameras[0].deviceId, cameras[cameras.length-1].deviceId];
}
else {
console.log("clear front and back");
return [front.deviceId, back[0].deviceId];
}
}
}