In my application a user can make calls if he can produce an audio stream. So, I need to require access to the microphone (audio stream). Without it the application should throw an error. Video is optional. So, I call navigator.getUserMedia
and put constraints
like this:
{ audio: true, video: false }
And it throws an error when a microphone is not present, just like I need. But a side effect from this is that if the user also has access to a camera, video is not present in stream.
But if I set both audio
and video
to true
I would have an error in cases when users have a microphone but do not have access to a camera (which is ok according to my application logic)
How I can make a video stream optional get getUserMedia
?
In my application a user can make calls if he can produce an audio stream. So, I need to require access to the microphone (audio stream). Without it the application should throw an error. Video is optional. So, I call navigator.getUserMedia
and put constraints
like this:
{ audio: true, video: false }
And it throws an error when a microphone is not present, just like I need. But a side effect from this is that if the user also has access to a camera, video is not present in stream.
But if I set both audio
and video
to true
I would have an error in cases when users have a microphone but do not have access to a camera (which is ok according to my application logic)
How I can make a video stream optional get getUserMedia
?
- Check github./alongubkin/phonertc or refer to @Alon Gubkin. – barak manos Commented Aug 14, 2014 at 12:53
- @barakmanos sc-cdn.scaleengine/i/a09d947bd5fe171b8c8fdc1b1f9f1a00.jpg – SET001 Commented Aug 14, 2014 at 13:48
- Then use the other option that I mentioned... – barak manos Commented Aug 14, 2014 at 13:50
3 Answers
Reset to default 12There now exists another way. You can now check directly whether the user has a camera and/or a microphone before calling getUserMedia
:
navigator.mediaDevices.enumerateDevices()
.then(devices => {
const cams = devices.filter(device => device.kind == "videoinput");
const mics = devices.filter(device => device.kind == "audioinput");
const constraints = { video: cams.length > 0, audio: mics.length > 0 };
return navigator.mediaDevices.getUserMedia(constraints);
})
.then(stream => video.srcObject = stream)
.catch(failed);
The other answer works as well, but this may be a bit cleaner.
Note that this is using enumerateDevices
, which returns a list of devices with information. The amount of information returned is limited for privacy reasons ahead of calling getUserMedia
, but it will still reveal whether the user has at least one camera and/or at least one microphone, which is all we need here.
A solution I have found is to call getUserMedia
with video and audio enabled, if the call fails(meaning that they either don't have a camera or a mic) then you call getUserMedia
again from the failure callback that you provide requesting access to the microphone only.
var failedLocalAudioAndVideoStreamCallBack = function (error) {
getUserMedia({ audio: true, video: false },
gotLocalAudioStreamCallBack, failedLocalAudioStreamCallBack )});
}
getUserMedia({ audio: true, video: true },
gotLocalAudioAndVideoStreamCallBack, failedLocalAudioAndVideoStreamCallBack);
Of course, you can handle successes and failures however you like.
NOTE: if there is no camera, the pop-up requesting the initial camera feed(that will fail) never occurs. So, the user will only get one request for access(which makes this solution a tad bit more palatable).
The accepted answer doesn't handle the case in which they have rejected camera permission but allowed mic permission (or vice versa). If you assume you have both, and proceed to ask for a camera and mic in your getUserMedia
call, you'll get permission denied.
const devices = await navigator.mediaDevices.enumerateDevices()
const mics = devices.filter(device => device.kind == "audioinput")
const cams = devices.filter(device => device.kind == "videoinput")
const allowedMicPermission = mics.some(device => device.label != '')
const allowedWebcamPermission = cams.some(device => device.label != '')
const hasMic = mics.length > 0
const hasCam = cams.length > 0
// if no permission, assume we haven't asked them yet.
const constraints = !allowedMicPermission && !allowedWebcamPermission
? { audio: hasMic, video: hasCam}
: { audio: allowedMicPermission && hasMic, video: allowedWebcamPermission && hasCam }
return navigator.mediaDevices.getUserMedia(constraints);
This is all to try to infer what permissions the user has given us. A much easier way is to ask the browser what permissions the user has given us, but that only works in Chrome right now, and doesn't seem likely to change.