Handling device permissions

Accessing media input devices cams, mics, and screens can involve many and sometimes conflicting permissions layers. Browsers, operating systems, devices, and participants are all factors that can complicate whether audio or video is shared or hidden as desired.

This guide walks through common media device errors and how to help call participants resolve them, and then covers general best practices for handling media devices.

Common media device errors

Daily accesses participant media input devices through the MediaDevices interface.

The interface's getUserMedia() method prompts the user for device permissions. It then creates a MediaStream with the corresponding tracks if permission is granted.

If something goes wrong, the browser throws an exception, and Daily emits a camera-error event. We grouped these exceptions and their corresponding Daily errors into categories below. The categories include recommendations for how to handle each type of error. For full exception definitions and repro cases, see the reference table.

Feel free to borrow from our template help material when writing prompts to help participants solve these issues.

Device in use

This is most common on Windows when another app is using the participant's camera.

The browser will throw a NotReadableError or an AbortError.

The Daily camera-error event object's error.type value will specify one of the following: cam-in-use, mic-in-use, or cam-mic-in-use. For example:

To handle this error, alert participants that another application is using their device. Daily Prebuilt, for example, instructs participants to quit the other application and reload the Daily call.

Prompt in a video call asks participant to restart an application

Permissions denied

This happens when a participant has denied access to their media devices. This is one of the most common getUserMedia() errors.

In this case, the browser throws a NotAllowedError.

The Daily camera-error event object's errorMsg value will indicate "not allowed", for example:

Whether the participant denied access on purpose or by accident, it is important that the call UI reflects that the device is blocked. In Daily Prebuilt, the media icons are displayed in red with the text "Allow" underneath. When clicked, the participant sees instructions for how to unblock their devices.

Video call displays a prompt that instructs a participant how to unblock their camera

How to unblock a device can depend on the device's manufacturer and the browser that the participant is using. On some devices, such as iOS, a page refresh is enough to re-trigger the browser prompt asking for device access. On other devices, such as some Android phones, participants may need to go into their device's settings to allow camera and microphone permissions. Testing on multiple devices and browsers is recommended.

No available devices

If the browser can't find any media input devices, the event emitter throws a NotFoundError or an OverConstrainedError.

The Daily camera-error event object's errorMsg value will read "devices error", for example:

Prompting the participants to check their devices and reload the call can help solve this issue; that is how Daily Prebuilt handles it.

Video call displays prompt to participant to check their input devices

getUserMedia() exceptions and their definitions

Although the user granted permission to use the matching devices, a hardware error occurred at the operating system, browser, or Web page level which prevented access to the device.

Browsers: Chromium (Win 10)

To reproduce, Chromium (Win 10): Open another app that uses the cam, like Microsoft Camera, then start a Daily call.

Although the user and operating system both granted access to the hardware device, and no hardware issues occurred that would cause a NotReadableError, some problem occurred which prevented the device from being used.

Browsers: Firefox (Win 10)

To reproduce, Firefox (Win 10): Open another app that uses the cam, like Microsoft Camera, then start a Daily call.

One or more of the requested source devices cannot be used at this time. This will happen if the browsing context is insecure (that is, the page was loaded using HTTP rather than HTTPS). It also happens if the user has specified that the current browsing instance is not permitted access to the device, the user has denied access for the current session, or the user has denied all access to user media devices globally. On browsers that support managing media permissions with Feature-Policy, this error is returned if Feature-Policy is not configured to allow access to the input source(s).

Browsers: All

To reproduce, when prompted by the browser, either block device access or dismiss the permissions request.

No media tracks of the type specified were found that satisfy the given constraints.

Browsers: All but Safari

To reproduce, macOS, Chrome, Firefox: For laptops, close your laptop, connect to an external monitor, but do not connect an external camera. Then, start a call.

To reproduce, Win 10, Chrome and Firefox: Disable the built-in camera via Device Manager, then start a call.

The specified constraints resulted in no candidate devices which met the criteria requested. The error is an object of type OverconstrainedError, and has a constraint property whose string value is the name of a constraint which was impossible to meet, and a message property containing a human-readable string explaining the problem.

Browsers: All

To reproduce, set a track constraint that cannot be satisfied by a connected device. For example: callObject.setBandwidth({ trackConstraints: { width: { min: 3840 }}});

To reproduce, Safari-only: For laptops, close your laptop, connect to an external monitor, but do not connect an external camera. Then, start a call.

User media support is disabled on the Document on which getUserMedia() was called. The mechanism by which user media support is enabled and disabled is left up to the individual user agent.

Browsers: All

We do not have a recommended repro case for this, so please let us know if you do.

The list of constraints specified is empty, or has all constraints set to false. This can also happen if you try to call getUserMedia() in an insecure context, since navigator.mediaDevices is undefined in an insecure context.

Browsers: All, though we have not confirmed Chromium (Win 10) and Firefox (Win 10)

Use a call link with http instead of https.

Best practices for handling media devices

Prompt for all device permissions at once

It is critical to ask call participants for microphone and camera access at the same time. These device permissions are closely related at a low level in daily-js. Blocking one can unintentionally block the other. Prompting for both cam and mic permissions simultaneously not only prevents this error, but also is a better user experience because participants only have to answer one prompt.

Adjust instructions according to browser and device

Device manufacturers and browsers don't always handle device permissions in the same way. For example, allowing device access after the browser has thrown a NotAllowedError can differ per OS, device, and browser. For the best user experience, the call UI should reflect how to handle the error given the participant's device and browser. Daily Prebuilt, for example, shows a different access prompt on iOS and Android after a NotAllowedError:

Granting access prompt on iOS and Android after a NotAllowedError

You can use user agent detection to display information based on the user's device. At Daily, we use ua-parser-js to detect the user's browser and OS, so we can adjust our messaging accordingly.

Create a space for participants to set up devices before joining a call

A test call page, lobby, "green room" or a space by any other name where participants can set up their devices before joining a call provides a safe space for callers to read all prompts, accept or deny them, and configure their devices without the pressure of being live in a call. This also gives them the chance to resolve any unintended mistakes before entering the call.

To enable Daily Prebuilt's prejoin UI, set the enable_prejoin_ui property to true at either the room or the domain level. This can be done either through an API request, or from the "Create room" page of the Daily dashboard.

Lobby interface of Daily Prebuilt call prompts a participant to set up camera and mic

There are lots of ways to go about building a lobby in a custom application. The startCamera() method is a good place to start.

How to start calls with mic access only

By default, daily-js assumes that calls start with microphones and cameras turned on, and automatically asks for permissions to both.

If a participant blocks the request, the event emitter fires a camera-error event. Access to both devices is denied, so for the duration of the session, neither the microphone nor the camera will be accessible. This might not be what a participant wants. They might want to join the call with audio-only access (granting microphone permissions), for example, but block the camera entirely. To enable a participant to join a call with mic access granted but camera blocked:

  • Turn off Daily's default automatic permissions request by setting the start_video_off or start_audio_off properties to true at the room or the token level.
  • Call getUserMedia() before using daily-js to access participants' media streams.
  • Pass the streams to Daily. This can be done either via join(), using the audioSource and videoSource properties, or after join through setInputDevicesAsync().

iOS development and device permissions

Media device permission requests are async when using AVFoundation, Apple's audiovisual framework. The completion handler for these requests is called on arbitrary dispatch queue.

These permissions should never be blocked, as that can lead to deadlocks. An API that was intended to be synchronous and blocking may have to be changed to be async with a callback block when adapted for iOS.