I’m trying to build a web app which takes photos using webcam or mobile camera depending on the device. I have already made a button which changes the constraints.facingmode so the user can use both cameras ( “environment”, “user” ) if device supports it. The problem is that when I enable flashlight support as well, by creating a button and setting it as the flashlight toggler like that:
const SUPPORTS_MEDIA_DEVICES = 'mediaDevices' in navigator; if (SUPPORTS_MEDIA_DEVICES) { const track = stream.getVideoTracks()[0]; const imageCapture = new ImageCapture(track); const photoCapabilities = imageCapture.getPhotoCapabilities().then(() => { const btn = document.querySelector('.toggleCameraTorch'); btn.style.visibility = 'visible'; btn.addEventListener('click', function () { try { track.applyConstraints({ advanced: [{ torch: !wheelsfs.videoConstraint.torchState }] }); wheelsfs.videoConstraint.torchState = !wheelsfs.videoConstraint.torchState; } catch(e) { alert(e.message); } }); }); }
After that, the flashlight is working perfectly but I no longer have the option to swap camera ( facingmode ). When I’m trying to change the camera I get the error “could not start video source”. Like the camera is already being used by something.
This is how I’m changing camera – facingmode:
wheelsfs.videoConstraint.facingMode.exact = wheelsfs.videoConstraint.facingMode.exact == "environment" ? "user" : "environment"; var cameraInput = wheelsfs.videoConstraint.facingMode.exact; wheelsfs.videoTrue.srcObject && wheelsfs.videoTrue.srcObject.getTracks().forEach(t => t.stop()); wheelsfs.videoConstraint = { video: { width: { ideal: trueWidth }, height: { ideal: trueHeight }, facingMode: { ideal: "environment" } }, facingMode: { exact: cameraInput } }; navigator.mediaDevices.getUserMedia({ video: wheelsfs.videoConstraint }).then(function (stream) { wheelsfs.videoTrue.srcObject = stream; wheelsfs.videoTrue.play(); const SUPPORTS_MEDIA_DEVICES = 'mediaDevices' in navigator; if (SUPPORTS_MEDIA_DEVICES) { const track = stream.getVideoTracks()[0]; const imageCapture = new ImageCapture(track); const photoCapabilities = imageCapture.getPhotoCapabilities().then(() => { const btn = document.querySelector('.toggleCameraTorch'); btn.style.visibility = 'visible'; btn.addEventListener('click', function () { try { track.applyConstraints({ advanced: [{ torch: !wheelsfs.videoConstraint.torchState }] }); wheelsfs.videoConstraint.torchState = !wheelsfs.videoConstraint.torchState; } catch (e) { alert(e.message); } }); }); } }).catch((e) => { console.log(e.message); }
Advertisement
Answer
Solved it by storing the stream.getVideoTracks()[0] to a variable and then calling stop() on it before changing the camera (facingmode).
So when I do:
if (SUPPORTS_MEDIA_DEVICES) { wheelsfs.track = stream.getVideoTracks()[0]; const imageCapture = new ImageCapture(wheelsfs.track); const photoCapabilities = imageCapture.getPhotoCapabilities().then(() => { const btn = document.querySelector('.toggleCameraTorch'); btn.style.visibility = 'visible'; btn.addEventListener('click', function () { try { wheelsfs.track.applyConstraints({ advanced: [{ torch: !wheelsfs.videoConstraint.torchState }] }); wheelsfs.videoConstraint.torchState = !wheelsfs.videoConstraint.torchState; } catch (e) { alert(e.message); } }); }); }
In the 2nd line I save the track in a public variable and then when the function that changes the camera that is being used is called, I make sure I run “wheelsfs.track.stop();” just before the navigator.mediaDevices.getUserMedia call.