I am using webcam and microphone for my app. I want to release both the devices when I finish with the video and audio part. Webcam light is on, till the time I explicitly stop sharing it or refresh the page.
I tried implementing answers given in this question but without any luck - Stop/Close webcam which is opened by navigator.getUserMedia
var MediaStream = window.MediaStream;
if (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') {
MediaStream = webkitMediaStream;
}
/*global MediaStream:true */
if (typeof MediaStream !== 'undefined' && !('stop' in MediaStream.prototype)) {
MediaStream.prototype.stop = function() {
this.getAudioTracks().forEach(function(track) {
track.stop();
});
this.getVideoTracks().forEach(function(track) {
track.stop();
});
};
}
I am not sure what's wrong.
Related
I have published a react native application to google play store for android tv.
For tv, I have received notification for:
Play/Pause key event is not respected during playback
Your media apps that play video or music content must respect the play/pause key during playback. Please refer to our Media Play/Pause documentation and Update the Playback State documentation for details.
For example, your app does not pause the video being played when pressing the pause button on the Android TV controller.
How do I fix this issue for react-native android TV apps?
try adding play/pause Event Listeners
const _tvEventHandler = new TVEventHandler();
const [isPaused, setIsPaused] = useState(false);
const _enableTVEventHandler = () => {
_tvEventHandler.enable(this, function (cmp, evt) {
console.log(evt);
if (evt && evt.eventType === 'right') {
console.log(evt.eventType);
} else if (evt && evt.eventType === 'up') {
console.log(evt.eventType);
} else if (evt && evt.eventType === 'left') {
console.log(evt.eventType);
} else if (evt && evt.eventType === 'down') {
console.log(evt.eventType);
} else if (evt && evt.eventType === 'select') {
setIsPaused(!isPaused);
}
});
};
Banging my head to the wall with this one, I can't seem to understand what is holding on the camera video stream and not closing when MediaStreamTrack.stop() called.
I have a typescript class where I handle getting the WebRTC stream track and passing it using an observable event to a functional reactjs component, the below code is the component registering to the event and using state for the stream track.
const [videoStreamTrack, setVideoStreamTrack] = useState < MediaStreamTrack > (
null
)
useEffect(() => {
return () => {
videoStreamTrack?.stop()
videoElement.current.srcObject.getVideoTracks().forEach((track) => {
track.stop()
videoElement.current.srcObject.removeTrack(track)
})
videoElement.current.srcObject = null
}
}, [])
case RoomEvents.WebcamProducerAdded:
case RoomEvents.VideoStreamReplaced: {
if (result.data?.track) {
if (result.data.track.kind === 'video') {
previewVideoStreamTrack?.stop()
setPreviewVideoStreamTrack(null)
setVideoStreamTrack(result.data.track)
}
}
break
}
In the "Room" class I use the below code to grab the stream.
const videoDevice = this.webcam.device
if (!videoDevice) {
throw new Error('no webcam devices')
}
const userMedia = await navigator.mediaDevices.getUserMedia({
video: this.environmentPlatformService.isMobile ?
true : {
deviceId: {
exact: this.webcam.device.deviceId
},
...VIDEO_CONSTRAINS[this.webcam.resolution],
},
})
const videoTrack = userMedia.getVideoTracks()[0]
this.eventSubject.next({
eventName: RoomEvents.WebcamProducerAdded,
data: {
track: videoTrack,
},
})
I am holding to this.webcam.device details using the code below.
async updateInputOutputMediaDevices(): Promise < MediaDeviceInfo[] > {
await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
})
const devices = await navigator.mediaDevices.enumerateDevices()
await this.updateWebcams(devices)
await this.updateAudioInputs(devices)
await this.updateAudioOutputs(devices)
return devices
}
private async updateWebcams(devices: MediaDeviceInfo[]) {
this.webcams = new Map < string, MediaDeviceInfo > ()
for (const device of devices.filter((d) => d.kind === 'videoinput')) {
this.webcams.set(device.deviceId, device)
}
const array = Array.from(this.webcams.values())
this.eventSubject.next({
eventName: RoomEvents.CanChangeWebcam,
data: {
canChangeWebcam: array.length > 1,
mediaDevices: array,
},
})
}
Refreshing the page will close the camera and tab indicator.
useEffect(() => {
return () => {
videoStreamTrack?.stop()
videoElement.current.srcObject.getVideoTracks().forEach((track) => {
track.stop()
videoElement.current.srcObject.removeTrack(track)
})
videoElement.current.srcObject = null
}
}, [])
So here you are search and destroying video tracks. Seems right-ish; we'll see
async updateInputOutputMediaDevices(): Promise < MediaDeviceInfo[] > {
await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
})
const devices = await navigator.mediaDevices.enumerateDevices()
await this.updateWebcams(devices)
await this.updateAudioInputs(devices)
await this.updateAudioOutputs(devices)
return devices
}
Above I see there's a call for audio might be where the hiccups are? Can't overly examine but maybe you're opening both and closing just video? Try doing a loop through all tracks not just video and see what's there?
#blanknamefornow answer helped me nail the issue.
We are calling getUserMedia in multiple places not only in the
“room” class handling mediasoup actions but also fore
preview/device-selection/etc and didn’t really ever closed the
tracks retrieved.
Sometimes, those tracks are held into useState
variables and when component unmounted if you try to access the
variables they are already nulled by reactjs. The workaround is
since the HTML elements are still referenced stop the track when
needed. I believe this was the missing ingredient when trying to
figure it out.
I'm building a an Electron application with Vue.js that uses a webcam. The webcam works within the Electron application on one computer but it just shows a black screen on another. The only notable difference (I think) is that the machine where it works (Machine A) uses Node v14.15.0 and on the machine that it doesn't work (Machine B) uses v12.18.4
I have tested the webcam on Machine B separately. It works via the native camera app on windows and on this online tool. The interesting thing is that both the integrated and external webcams fail to work. As soon as I start the stream the light comes on but that's it. It seems that the promise from .getUserMedia() isn't resolving (see code snippet) but I can't identify why.
How can I get the webcam to stream?
let mediaInputs = [];
let devices = [];
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
console.log("enumerateDevices() not supported.");
return;
}
mediaInputs = await navigator.mediaDevices.enumerateDevices();
devices = await mediaInputs.filter(
(device) => device.kind === "videoinput"
);
//Stop any existing streams
if (this.video.srcObject !== undefined) {
const l = this.video.srcObject;
l.getTracks().forEach((track) => track.stop());
}
const sourceInfo = this.videoSources.find((o) => o.label === this.source);
const constraints = {
video: {
deviceId: sourceInfo.deviceId,
width: 1280,
height: 720,
},
audio: false,
};
try {
console.log('This line is logged');
//This is where I start the stream.
const stream = await navigator.mediaDevices.getUserMedia(constraints);
console.log('This line is never reached');
this.video = this.$refs.video;
this.video.srcObject = stream;
this.video.play();
} catch (error) {
this.showSnackbar(error);
console.error(error);
}
I just had to update to the latest version of Electron (11.1.1 at the time of writing) for it to work.
However if you're still having trouble, there's a GitHub thread on the topic that's still active
I'm writing an audio player, using Expo Audio, for an app I'm making for an online radio.
The audio comes from an online live stream and, I've successfully added the player and all the things related to it; however, the one issue I'm having is that if I pause the audio when I resume playing it the audio continues from when I paused it rather than from the current position and I need to pause it and play it again to get it to update to what's currently being played.
I play it with playAsync() and I've tried pausing with pauseAsync(), stopAsync(), setStatusAsync({ shouldPlay: false, positionMillis: 0 });
Any tips on how I can get it to work the way it should?
Here's the code I have for the audio player, it's a class from which then I create an instance of to be able to manage it from different places in the app:
class audioPlayer {
static instance = null;
static createInstance() {
var object = new audioPlayer();
return object;
}
_radioStream;
/**
* #returns {audioPlayer}
*/
static getInstance() {
if (audioPlayer.instance == null) {
audioPlayer.instance = audioPlayer.createInstance();
}
return audioPlayer.instance;
}
// Call this first to create a new audio element
createAudio() {
this._radioStream = new Audio.Sound();
};
async loadAudioAsync() {
try {
await this._radioStream.loadAsync(
{ uri: "radio straem"},
);
store.dispatch(setLiveState(true));
this.toggleAudio(); // Autoplay at start
return true;
} catch (error) {
if (error.code === "E_LOAD_ERROR") {
// In the case of an error we try to load again
setTimeout(this.loadAudioAsync, 10000);
throw new Error(error.code);
} else {
throw new Error(error);
};
};
};
async unloadAudioAsync() {
await this._radioStream.unloadAsync();
};
async getStatusAsync() {
return await this._radioStream.getStatusAsync();
};
async toggleAudio() {
// We're gonna play or pause depending on the status
let { isLoaded, isPlaying } = await this._radioStream.getStatusAsync();
// If the user presses the audio and the stream connection has been lost or something
// we try to load it again
if (!isLoaded) {
let res = await this.loadAudioAsync(); // Try to loadAudio again
if (res) this.toggleAudio(); // Retrigger the toggle to start playing
}
if (isLoaded && !isPlaying) {
store.dispatch(setPlayingStatus(true));
await this._radioStream.playAsync();
} else if (isLoaded && isPlaying) {
store.dispatch(setPlayingStatus(false));
await this._radioStream.setStatusAsync({ shouldPlay: false, positionMillis: 0 });
};
};
};
I just had the same exact problem (for my internet radio https://notylus.fr).
It's seems that I found a solution : instead of using
playbackInstance.pauseAsync()
I now use
playbackInstance.stopAsync()
AND for the play part, I add
await playbackInstance.playAsync() //play stream
playbackInstance.setPositionAsync(0) //ensure that you're at position 0
Last
Regards,
See related question: Navigator.mediaDevices.getUserMedia not working on iOS 12 Safari
We are trying to capture audio from user input user MediaDevices.getUserMedia and Audio Context
When the user clicks a button we check for available devices and then we capture their audio stream
let enumDevicePromise = navigator.mediaDevices.enumerateDevices()
.then(devices => devices.find(d => d.kind === "audioinput" && d.label !== "" && d.deviceId === "default"))
.catch((error) => {
// handle error
});
this.handleCheckEnumeratedDevices(enumDevicePromise); // capture device in backend
.....
navigator.mediaDevices
.getUserMedia({
audio: true,
video: false,
})
.then(stream => {
let AudioContext = window.AudioContext || window.webkitAudioContext;
if (AudioContext) {
let context = new AudioContext();
let source = context.createMediaStreamSource(stream);
let processor = context.createScriptProcessor(4096, 1, 1);
source.connect(processor);
processor.connect(context.destination);
processor.onaudioprocess = (event) => {
let audioIn = event.inputBuffer.getChannelData(0);
this.sendMessage(this.toInt16(audioIn));
}
} else {
// handle error, ie, Audio Context not supported
}
}).catch((error) => {
// handle error
});
});
This works fine on Chrome and Firefox, but on Safari 12 we are getting a Null response from the enumerate devices promise - despite allowing microphone permissions - and we because of that we aren't able to capture the audio stream
It happens because Mobile Safari doesn't expose "audioinput" kind of media devices. This is a known limitation.