I'm using AudioContext, programatically, with Typescript.
Here's my code:
/**
* Checks for getUserMedia
*
* @params: none
* @returns: any
*/
public hasGetUserMedia(): any {
const mediaservices = !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
console.log('Media Services: ', navigator.mediaDevices);
return mediaservices;
}
/**
* A function to get the return value of hasGetUserMedia
*
* @params: none
* @return: none
*/
public isUserMediaGood(micstatus: boolean) {
const self = this;
if (this.hasGetUserMedia()) {
// Good to go!
self.isUserMediaThere = true;
console.log('We have User Media Houston!');
// Now accessInputDevice
this.accessInputDevice(micstatus);
} else {
// Oops!
self.isUserMediaThere = false;
console.log('WARNING: getUserMedia() is not supported by your browser');
}
}
public accessInputDevice(micstatus: boolean) {
window.AudioContext = window.AudioContext;
const context = new AudioContext();
const constraints = {
audio: micstatus,
video: false
}
// initialization
if (localStorage.getItem('microphone') === null) {
// just assume it is prompt
localStorage.setItem('microphone', 'prompt');
}
// Then somewhere
navigator.getUserMedia({audio: true}, function (e) {
// http://stackoverflow.com/q/15993581/1008999
//
// In chrome, If your app is running from SSL (https://),
// this permission will be persistent.
// That is, users won't have to grant/deny access every time.
localStorage.setItem("voice_access", "granted");
}, function (err) {
if (err.name === 'PermissionDismissedError') {
localStorage.setItem('voice_access', 'prompt')
}
if (err.name === 'PermissionDeniedError') {
localStorage.setItem('voice_access', 'denied');
}
})
navigator.mediaDevices.getUserMedia(constraints)
.then((stream) => {
const microphone = context.createMediaStreamSource(stream);
const filter = context.createBiquadFilter();
// microphone -> filter -> destination
console.log('Mic: ', microphone);
microphone.connect(filter);
filter.connect(context.destination);
});
}
public gotDevices(deviceInfos: any) {
for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
const option = document.createElement('option');
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === 'audioinput') {
option.text = deviceInfo.label || 'microphone ';
// this.microphone.appendChild(option);
console.log('Found device: ', deviceInfo);
// } else if (deviceInfo.kind === 'videoinput') {
// option.text = deviceInfo.label || 'camera ' +
// (videoSelect.length + 1);
// videoSelect.appendChild(option);
} else {
console.log('Found another kind of device: ', deviceInfo);
}
}
}
public getStream() {
const self = this;
const constraints = {
audio: {
audio: false,
deviceId: {exact: self.microphone.id}
},
// video: {
// deviceId: {exact: videoSelect.value}
// }
};
navigator.mediaDevices.getUserMedia(constraints).
then(self.gotStream).catch(self.handleError);
}
public gotStream(stream: any) {
const self = this;
window.AudioContext = stream; // make stream available to console
self.microphone = stream;
}
public handleError(error: any) {
// log to console first
console.error('Error: ', error); /* handle the error */
if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') {
// required track is missing
} else if (error.name === 'NotReadableError' || error.name === 'TrackStartError') {
// webcam or mic are already in use
} else if (error.name === 'OverconstrainedError' || error.name === 'ConstraintNotSatisfiedError') {
// constraints can not be satisfied by avb. devices
} else if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {
// permission denied in browser
} else if (error.name === 'TypeError' || error.name === 'TypeError') {
// empty constraints object
} else {
// other errors
}
}
My problem is I get the error: Uncaught (in promise): TypeError: Failed to execute 'getUserMedia' on 'MediaDevices': At least one of audio and video must be requested
TypeError: Failed to execute 'getUserMedia' on 'MediaDevices': At least one of audio and video must be requested.
The error occurs here:
navigator.mediaDevices.getUserMedia(constraints)
.then((stream) => {
const microphone = context.createMediaStreamSource(stream);
const filter = context.createBiquadFilter();
// microphone -> filter -> destination
console.log('Mic: ', microphone);
microphone.connect(filter);
filter.connect(context.destination);
});
I'd like to know why this is happening?
UPDATE:
So, I implemented this solution from here:
Then I follow this link:
https://blog.addpipe.com/getusermedia-video-constraints/
Then found that the "TYPE ERROR:" is thrown when both the AUDIO: false and VIDEO: false.
I need to KEEP the video: FALSE. Why? I'm not interested in turning on the video and "terrify" the user thinking that our software is spying. This is a PRIVACY concern. https://blog.addpipe.com/common-getusermedia-errors/
This is the code I implemented:
NOTE: micstatus is being passed when the user CLICKS on an icon of a MICROPHONE. When the user CLICKS, it passes "OFF" or "FALSE". When the user clicks again, it passes "ON" or "TRUE". But, if the VIDEO stays "FALSE", then the TYPE ERROR fires. That's my problem. CHROME does not allow FALSE, FALSE being passed into the getUserMedia() method. The last error is FIRED. That's what I need to get around: KEEP VIDEO OFF and turn the AUDIO ON or OFF thus NOT throwing the TYPE ERROR.
var constraints = {
video: false,
audio: micstatus
}
navigator.mediaDevices.getUserMedia(constraints).then(function success(stream) {
/* do stuff */
this.success(success,stream);
}).catch(function (err) {
// log to console first
console.log(err); /* handle the error */
if (err.name == "NotFoundError" || err.name == "DevicesNotFoundError") {
// required track is missing
console.log('Required track is missing');
} else if (err.name == "NotReadableError" || err.name == "TrackStartError") {
// webcam or mic are already in use
console.log('Webcam or mic are already in use');
} else if (err.name == "OverconstrainedError" || err.name == "ConstraintNotSatisfiedError") {
// constraints can not be satisfied by avb. devices
console.log('Constraints can not be satisfied by available devices');
} else if (err.name == "NotAllowedError" || err.name == "PermissionDeniedError") {
// permission denied in browser
console.log('Permission Denied.');
} else if (err.name == "TypeError" || err.name == "TypeError") {
// empty constraints object
console.log('Both audio and video are FALSE');
} else {
// other errors
console.log('Sorry! Another error occurred.');
}
});
// SUCCESS FUNCTION
public success(status: any, stream: any): void {
// Success!!!
console.log('Success with the Audio Context', status);
console.log('Audio Context', stream);
}