Skip to content
Advertisement

Using camera flashlight not allowing to change facingmode – Navigator.mediaDevices

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.

Advertisement