track.stop is not turning the camera off anymore - javascript

I have a webpage where I want user to take a picture with his laptop/phone camera.
Once he clicks on a button a modal is shown and the following js will start the camera stream to take the picture:
function startStreaming() {
if (null != cameraStream) {
var track = cameraStream.getTracks()[0];
track.stop();
stream.load();
cameraStream = null;
}
//const audioSource = audioInputSelect.value;
const videoSource = videoSelect.value;
const constraints = {
//audio: {deviceId: audioSource ? {exact: audioSource} : undefined},
video: {
deviceId: videoSource ? {
exact: videoSource
} : undefined
}
};
navigator.mediaDevices.getUserMedia(constraints).then(gotStream).then(gotDevices).catch(handleError);
var mediaSupport = 'mediaDevices' in navigator;
if (mediaSupport && null == cameraStream) {
const videoSource = videoSelect.value;
const constraints = {
video: {
deviceId: videoSource ? {
exact: videoSource
} : undefined
}
};
navigator.mediaDevices.getUserMedia(constraints)
.then(function (mediaStream) {
cameraStream = mediaStream;
stream.srcObject = mediaStream;
stream.play();
})
.catch(handleError);
} else {
alert('Your browser does not support media devices.');
return;
}
}
This is triggered by
$('#photoStudio').on('show.bs.modal', function (event) {
navigator.mediaDevices.enumerateDevices().then(gotDevices).catch(handleError);
startStreaming();
});
Then when I close the modal I want to stop the streaming but the led indicator next to my camera is still on)
$('#photoStudio').on('hide.bs.modal', function (event) {
stopStreaming();
});
where stopStreaming() is:
function stopStreaming() {
if (null != cameraStream) {
var track = cameraStream.getTracks()[0];
track.stop();
stream.load();
cameraStream = null;
}
}
I don't get any kind of error and I cannot find a way to debug why the camera is still running. Am I missing anything in the stopStreaming function?

If any track has not been stopped then your camera will still be active. In your stopStreaming function you only stop the first track in the returned array.
If you instead iterate through the tracks you may catch ones you aren't currently:
function stopStreaming() {
if (null != cameraStream) {
var tracks = cameraStream.getTracks();
// stop all the tracks, not just the first
tracks.forEach((track) => {
track.stop();
});
stream.load();
cameraStream = null;
}
}

this.camera_stream.getTracks().forEach((track) => {
console.log(track);
track.stop();
**track.enabled = false**
});
video.load()
this.camera_stream = null

Related

Networked Aframe Keeps turning video and audio on for a brief moment

I'm trying to build a website with Networked Aframe. I'm using the easyRTC adapter, which allows for audio and video streaming. I am trying to have the user initially join the room without their video or audio on. I've tried to turn off video and audio when entering, but the audio and video are still sent for a brief moment before it stops being sent. I tried to turn off the video stream but that yielded no luck as the webcam indicator is still on when the video is "off" and the video is still sent for a brief moment. Any help will be appreciated.
AFRAME.registerComponent('dynamic-room', {
init: function () {
const el = this.el;
const params = this.getUrlParams();
const networkedComp = {
room: params.room,
debug: true,
audio: true,
onConnect: onConnecth,
adapter: "easyrtc",
video: true
};
console.info('Init networked-aframe with settings:', networkedComp);
console.log(this.el);
console.log("setting it", this.el.setAttribute('networked-scene', networkedComp));
document.body.addEventListener('clientConnected', function (evt) {
onConnecth();
});
this.el.emit("connect", null, false);
},
let cameraEnabled = true;
let micEnabled = true;
let screenEnabled = false;
const cameraButton = document.getElementById('cameraaction');
const micButton = document.getElementById('micaction');
const screenButton = document.getElementById("screenshareaction");
let first = true;
function onConnecth() {
if(!first) {
console.log("something went wrong");
}
else {
document.getElementById('player').setAttribute('player-info', 'name', getUrlParams().username);
NAF.connection.adapter.enableCamera(!cameraEnabled);
NAF.connection.adapter.enableMicrophone(!micEnabled);
console.log("First Load");
cameraEnabled = !cameraEnabled;
micEnabled = !micEnabled;
disableVideo();
finishLoad();
}
}
function disableVideo(){
const stream = NAF.connection.adapter.getMediaStream();
stream.getTracks().forEach(track => {
if (track.kind === 'video') {
track.stop();
}
})
}
function finishLoad(){
cameraButton.onclick = function () {
NAF.connection.adapter.enableCamera(!cameraEnabled);
cameraEnabled = !cameraEnabled;
if(!cameraEnabled){
disableVideo();
}
};
micButton.onclick = function () {
NAF.connection.adapter.enableMicrophone(!micEnabled);
micEnabled = !micEnabled;
};
}

Microphone not changing after setting new constraints

We are building an application in Electron and the microphone is not changing after setting a new deviceId. There is no error being shown.
//myId is an id retrieved from navigator.mediaDevices.enumerateDevices();
const constraints = {audio: {deviceId: {exact: myId}}};
navigator.mediaDevices.getUserMedia(constraints);
What are we doing wrong? When we test it on an external website, it looks like it is working correctly.
All our search results in nothing useful or on searches that are very outdated. Electron is a chromium browser. We have been stuck on this problem for more than a week. It doesn't work on Ubuntu and Windows 11 (only OS we have tested so far). We have not yet bothered with MacOs.
make sure myId is not null, because when you first call navigator.mediaDevices.enumerateDevices() it can return empty strings.
for me const constraints = {audio: {deviceId: {exact: myId}}}; return error because of exact. For me worked const constraints = {audio: {deviceId: myId}};
You missing one '}' at the end of const constraints = {audio: {deviceId: {exact: myId}};
If you set new devices you have to get new tracks from new stream and add to old stream.
It's working code that change microphone depending on which button you click.
script.js
let mediaStream;
let mic1Id, mic2Id;
navigator.mediaDevices.enumerateDevices().then((res) => {
mic1Id = res[2].deviceId;
mic2Id = res[3].deviceId;
const constraints = {
audio: {
deviceId: { exact: mic1Id },
},
};
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
mediaStream = stream;
var audio = document.querySelector("audio");
stream.getAudioTracks()[0].enabled = true;
audio.srcObject = stream;
});
});
function getAudioTrack(deviceId) {
const constraints = {
audio: {
deviceId: {
exact: deviceId,
},
},
};
return navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
return stream.getAudioTracks();
});
}
function replaceAudioTrack(stream, newAudioTrack) {
let audioTrack = stream.getAudioTracks()[0];
stream.removeTrack(audioTrack);
stream.addTrack(newAudioTrack);
}
let btn1 = document.querySelector("button:nth-of-type(1)");
let btn2 = document.querySelector("button:nth-of-type(2)");
btn1.onclick = () => {
getAudioTrack(mic1Id).then((res) => replaceAudioTrack(mediaStream, res[0]));
};
btn2.onclick = () => {
getAudioTrack(mic2Id).then((res) => replaceAudioTrack(mediaStream, res[0]));
};
index.html
<audio autoplay></audio>
<button>Mic 1</button>
<button>Mic 2</button>

Is window.localAudio a thing?

I found Mozilla's Getting browser microphone permission. It defines a function to request permission and listen to client's microphone as such:
function getLocalStream() {
navigator.mediaDevices.getUserMedia({video: false, audio: true}).then( stream => {
window.localStream = stream; // A
window.localAudio.srcObject = stream; // B
window.localAudio.autoplay = true; // C
}).catch( err => {
console.log("u got an error:" + err)
});
}
I checked in Chrome, Firefox and Safari - all of them throw an error about window.localAudio being undefined. Where did this tutorial get it from? Was window.localAudio ever a thing? What was it supposed to do?
I will try to give you something more useful than the question you have asked.
The function will create the element if it is not present and there are few options available. In the example I'm adding the newly created audio element to the body, but it will work even it is not added - it's a matter of choice.
<html>
<head>
<script>
var el;
function attachStream(stream, el, options) {
var item;
var URL = window.URL;
var element = el;
var opts = {
autoplay: true,
mirror: false,
muted: false,
audio: false,
disableContextMenu: false
};
if (options) {
for (item in options) {
opts[item] = options[item];
}
}
if (!element) {
element = document.createElement(opts.audio ? 'audio' : 'video');
} else if (element.tagName.toLowerCase() === 'audio') {
opts.audio = true;
}
if (opts.autoplay) element.autoplay = 'autoplay';
if (opts.muted) element.muted = true;
if (!opts.audio && opts.mirror) {
['', 'moz', 'webkit', 'o', 'ms'].forEach(function(prefix) {
var styleName = prefix ? prefix + 'Transform' : 'transform';
element.style[styleName] = 'scaleX(-1)';
});
}
element.srcObject = stream;
return element;
};
function getLocalStream() {
navigator.mediaDevices.getUserMedia({
video: false,
audio: true
}).then(
stream => {
var doesnotexist = !el;
el = attachStream(stream, el, {
audio: true,
autoplay: true
});
if (doesnotexist) document.body.appendChild(el);
}
).catch(err => {
console.log("u got an error:" + err)
});
}
window.addEventListener('DOMContentLoaded', (event) => {
getLocalStream();
});
</script>
</head>
<body>
</body>
</html>

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); }
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.

using device camera for capturing image in reactjs

can I access the device camera and take a photo in ReactJs? The goal is to create a component that allows the camera to take pictures with the click of a button. According to my studies, I should use mediaDevices, but I am looking for a sample code in ReactJs. Please provide me with a sample code, or if you have experience implementing this, please guide me.
I have prepared a sample code that can be used as a component. This code snippet is applicable to devices that also have two cameras. If you want to take a video instead of a photo, you can also enable the audio feature in the outputs.
import React from "react";
class App extends React.Component {
constructor() {
super();
this.cameraNumber = 0;
this.state = {
imageDataURL: null,
};
}
initializeMedia = async () => {
this.setState({ imageDataURL: null });
if (!("mediaDevices" in navigator)) {
navigator.mediaDevices = {};
}
if (!("getUserMedia" in navigator.mediaDevices)) {
navigator.mediaDevices.getUserMedia = function (constraints) {
var getUserMedia =
navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
if (!getUserMedia) {
return Promise.reject(new Error("getUserMedia Not Implemented"));
}
return new Promise((resolve, reject) => {
getUserMedia.call(navigator, constraints, resolve, reject);
});
};
}
//Get the details of video inputs of the device
const videoInputs = await this.getListOfVideoInputs();
//The device has a camera
if (videoInputs.length) {
navigator.mediaDevices
.getUserMedia({
video: {
deviceId: {
exact: videoInputs[this.cameraNumber].deviceId,
},
},
})
.then((stream) => {
this.player.srcObject = stream;
})
.catch((error) => {
console.error(error);
});
} else {
alert("The device does not have a camera");
}
};
capturePicture = () => {
var canvas = document.createElement("canvas");
canvas.width = this.player.videoWidth;
canvas.height = this.player.videoHeight;
var contex = canvas.getContext("2d");
contex.drawImage(this.player, 0, 0, canvas.width, canvas.height);
this.player.srcObject.getVideoTracks().forEach((track) => {
track.stop();
});
console.log(canvas.toDataURL());
this.setState({ imageDataURL: canvas.toDataURL() });
};
switchCamera = async () => {
const listOfVideoInputs = await this.getListOfVideoInputs();
// The device has more than one camera
if (listOfVideoInputs.length > 1) {
if (this.player.srcObject) {
this.player.srcObject.getVideoTracks().forEach((track) => {
track.stop();
});
}
// switch to second camera
if (this.cameraNumber === 0) {
this.cameraNumber = 1;
}
// switch to first camera
else if (this.cameraNumber === 1) {
this.cameraNumber = 0;
}
// Restart based on camera input
this.initializeMedia();
} else if (listOfVideoInputs.length === 1) {
alert("The device has only one camera");
} else {
alert("The device does not have a camera");
}
};
getListOfVideoInputs = async () => {
// Get the details of audio and video output of the device
const enumerateDevices = await navigator.mediaDevices.enumerateDevices();
//Filter video outputs (for devices with multiple cameras)
return enumerateDevices.filter((device) => device.kind === "videoinput");
};
render() {
const playerORImage = Boolean(this.state.imageDataURL) ? (
<img src={this.state.imageDataURL} alt="cameraPic" />
) : (
<video
ref={(refrence) => {
this.player = refrence;
}}
autoPlay
></video>
);
return (
<div className="App">
{playerORImage}
<button onClick={this.initializeMedia}>Take Photo</button>
<button onClick={this.capturePicture}>Capture</button>
<button onClick={this.switchCamera}>Switch</button>
</div>
);
}
}
export default App;

Categories

Resources