Why use this instead of the native navigator.mediaDevices.getUserMedia()
?
Since the SDK is built to be compatible with multiple browsers, it has to be able to handle media, devices,
and permissions across all the supported browsers. Currently, the supported browsers are
Chrome, Firefox, & Edge (but most any browser running on Chromium will work including Chromium Embedded Framework
desktop applications).
It was decided to expose the underlying media implementation to help consumers leverage all the shimming, error handling, and heavy lifting involved with media implementations.
This is a very basic usage of the WebRTC SDK Media utility. Be sure to read through the documentation for more advanced usage. For the remainder of this documentation, it will be assumed that there is already an initialized sdk instance.
import { ISdkMediaState, SdkMediaStateWithType } from 'genesys-cloud-webrtc-sdk';
/* async function for demonstration */
async function run () {
/* fetch the beginning media state sync synchronously, then update it in the `on('state')` listener */
let mediaState: ISdkMediaState = sdk.media.getState();
sdk.media.on('state', (state: SdkMediaStateWithType) => {
// emits every time devices or permissions change
// this value can always be synchronously attained
// using `sdk.media.getState()`
mediaState = state;
});
/* request permissions for the desired media types */
try {
await sdkMedia.requestMediaPermissions('audio');
} catch (e) {
// error getting permissions. this usually means the user denied permissions
// to verify, you can check the error type or check the mediaState (preferred)
const { hasMicPermissions } = sdk.media.getState(); // get the current media state
if (!hasMicPermissions) {
// decide what to do. the sdk cannot function
// without media permissions. generally it is best
// to kick the user out of the app and explain how they
// can grant permissions for their specific browser
}
}
/* if we get here, that means we have `audio` permissions and devices have been enumerated */
const devices = mediaState.devices;
const audioDevices = mediaState.audioDevices; // or just grab microphones
/* you can request media through the sdk. if there are permissions issues, the state will be updated */
const micId = audioDevices[0].deviceId;
const audioStream = await sdk.media.startMedia({ audio: micId });
/* this is a mock session object. the real session would be attained through the `sdk.on('sessionStarted', evt)` listener */
const sessionToAccept = { id: 'some-hash-id', ...restOfSessionObject };
/* you can even accept a pending session with media you already have */
sdk.acceptSession({
sessionId: sessionToAccept.id,
mediaStream: audioStream
});
/* OR you can even accept a pending session with a deviceId and the sdk will create the media */
sdk.acceptSession({
sessionId: sessionToAccept.id,
audioDeviceId: micId,
});
}
run();
Be sure to check out the SdkMediaStateWithType interface for more information about the ISdkMediaState
object.
Function to gain permissions for a given media type. This function should
be called early after constructing the SDK and before calling
sdk.media.startMedia()
to ensure permissions are granted.
This function will call through to startMedia
to get a MediaStream
for the desired media permissions. That is the only surefire way to
gain permissions across all browsers & platforms.
It will also call through to sdk.media.enumerateDevices()
to ensure
all devices have been loaded after permissions have been granted.
The media state will be updated with permissions and an event emitted
on sdk.media.on('permissions', evt)
with any outcomes.
An error will be thrown if permissions are not granted by either the browser
or the OS (specifically for macOS). With the one exception of the microphone
permission on the OS level. If the microphone permission has not been granted on
the OS level, macOS will still allow the browser to attain an audio track for
the microphone. However, the track will act as if it is in a "hardware mute"
state. There is no API available for the browser to know the microphone is
in a "hardware mute" state. To see if a microphone may be in a "hardware mute"
state, you can listen for microphone volume events on
sdk.media.on('audioTrackVolume', evt)
and add logic to respond to no volume
coming through the microhpone.
If preserveMedia
is true
, the MediaStream
attained through the
startMedia()
will be returned to the caller. If not, the media will
be destroyed and undefined
returned.
options
can be any valid deviceId or other media options defined in
IMediaRequestOptions. These options will be passed to
the startMedia()
call (which is used to gain permissions)
Note #1: the default option for the media type will be
true
(which is SDK default device). If a value offalse
orundefined
is passed in, it will always usetrue
. Any options for the other media type will be ignored. Example:
await requestMediaPermissions(
'audio',
false,
{
audio: false,
video: 'some-video-device-id',
videoFrameRate: 30
}
);
// since type === 'audio', the options will be converted to:
{
// a valid option must be set (`false|undefined` are invalid)
audio: true,
// video will be ignored since permissions are requested one at a time
video: false
}
Note #2: if requesting
'both'
, it will always ask for both permision types even if the first is denied. For example, ifaudio
permissions are denied, this function will still ask forvideo
permissions. If at least one media permission was denied, it will throw the error. Exception is if IMediaRequestOptions'preserveMediaIfOneTypeFails
option istrue
, then it will only the error if both media types fail. And then it will throw the audio denied error.
Note #3: in some browsers, permissions are not always guaranteed even after using this function to gain permissions. See Firefox Behavior for more details. It is recommended to use the permissions event to watch for changes in permissions since permissions could be denied anytime startMedia() is called.
Declaration:
requestMediaPermissions(mediaType: 'audio' | 'video' | 'both', preserveMedia?: boolean, options?: IMediaRequestOptions): Promise<MediaStream | void>;
Params:
mediaType: 'audio' | 'video' | 'both'
– mediaType media type to request permissions for ('audio' | 'video' | 'both'
)preserveMedia?: boolean
– Optional: Defaults tofalse
– flag to return media after permissions passoptions?: IMediaRequestOptions
– Optional: Defaults to mediaTypetrue
. See IMediaRequestOptions for additional details. See note above explaining mediaType always being requested.
Returns: a promise either containing a MediaStream
or undefined
depending on the value of preserveMedia
Call to enumerate available devices. This will update the
cache of devices and emit events on 'state'
& 'devices'
If the devices returned from the browser are the same as the cached
devices, a new event will NOT emit. To force an emit pass in true
.
It is highly recommended that sdk.media.requestMediaPermissions('audio' | 'video' | 'both')
be called at least once to ensure permissions are granted before loading devices.
See requestMediaPermissions() for more details.
Note: if media permissions have not been granted by the browser, enumerated devices will not return the full list of devices and/or the devices will not have ids/labels (varies per browser).
Declaration:
enumerateDevices(forceEmit?: boolean): Promise<MediaDeviceInfo[]>;
Params:
forceEmit?: boolean
– Optional: Defaults tofalse
– force an event to emit if the devices have not changed from the cached devices
Returns: a promise containing the devices enumerated
from navigator.mediaDevices.enumerateDevices()
Create media with video and/or audio. See IMediaRequestOptions for more information about available options.
It is highly recommended that sdk.media.requestMediaPermissions('audio' | 'video' | 'both')
be called with the desired media type(s) before using startMedia
. This will ensure
all media permissions have been granted before starting media. If requestMediaPermissions()
has not been called, this function will call it with preserveMedia = true
and use
the returning media.
getUserMedia
is requested one media type at a time. If requesting both audio
and video
, getUserMedia
will be called two times -- 1st with audio
and 2nd
with video
. The two calls will be combined into a single MediaStream
and
returned to the caller. If one of the media types fail, execution of this
function will stop and the error thrown (any successful media will be destroyed).
This is is line with getUserMedia
's current behavior in the browser.
If IMediaRequestOptions.retryOnFailure
is true
(default), the SDK will have the following behavior:
- If the fail was due to a Permissions issue, it will NOT retry
- For
video
only: some browsers/hardward configurations throw an error for invalid resolution requests. Ifvideo
was requested with avideoResolution
value (could be a SDK default), it will retry video with the same passed in value but with no resolution. - If a deviceId was requested and there was a failure, this will retry media with the SDK default deviceId for that media type.
- If the SDK default deviceId fails (or it didn't exist), then this
will retry with system defaults and no other options (such as
deviceId
s,videoFrameRate
, &videoResolution
) - If system defaults fails, it will throw the error and stop attempting to retry.
Note: if using
retryOnFailure
it is recommended to check the media returned to ensure you received the desired device.
If IMediaRequestOptions.preserveMediaIfOneTypeFails
is true
(default is false
),
both audio
and video
media types are requested, and only one type of media fails
then the media error for the failed media type will be ignored and the successful media will be
returned. See IMediaRequestOptions
for more information.
Warning: if
sdk.media.requestPermissions(type)
has NOT been called before callingstartMedia
,startMedia
will callsdk.media.requestPermissions(type)
. If callingstartMedia
with bothaudio
andvideo
before requesting permissions,startMedia
will attempt to gain permissions foraudio
first and thenvideo
(because media permissions must be requested one at a time). Ifaudio
fails, it will not attempt to gain permissions forvideo
– the error will stop execution.
Declaration:
startMedia(mediaReqOptions?: IMediaRequestOptions, retryOnFailure?: boolean): Promise<MediaStream>;
Params:
mediaReqOptions?: IMediaRequestOptions
– Optional: defaults to{ video: true, audio: true }
– request video and/or audio with a default device or deviceId. See IMediaRequestOptions for more details.retryOnFailure?: boolean
– Optional: (this option is deprectated – usemediaReqOptions.retryOnFailure
) defaulttrue
– whether the sdk should retry on an error
Returns: a promise containing a MediaStream
with the requested media
Creates a MediaStream
from the screen (this will prompt for user screen selection).
Note: see Screen Share in Firefox for specific screen share permission information
Declaration:
startDisplayMedia(): Promise<MediaStream>;
Params: none
Returns: a promise containing a MediaStream
with the requested screen media
Look for a valid deviceId in the cached media devices
based on the passed in deviceId
. This will follow these steps looking for a device:
- If
deviceId
is astring
, it will look for that device and return it if found - If device could not be found or
deviceId
was not astring
, it will look for the sdk default device - If device could not be found it will return
undefined
Declaration:
getValidDeviceId(kind: MediaDeviceKind, deviceId: string | boolean | null, ...sessions: IExtendedMediaSession[]): string | undefined;
Params:
kind: MediaDeviceKind
– kind desired device kinddeviceId: string | boolean | null
–deviceId
for specific device to look for,true
for sdk default device, ornull
for system default...sessions: IExtendedMediaSession[]
– Optional: any active sessions (used for logging)
Returns: a string
if a valid deviceId was found, or undefined
if
no device could be found.
Helper function to quickly get a valid SDK media request param. This is mostly an internally used function to ensure a valid SDK media request param was used to accept a session. See ISdkMediaDeviceIds for more details on requesting media from the SDK.
Example: string | true | null
are valid and be returned as is.
undefined | false
will return true
(which will use SDK default deviceId)
getValidSdkMediaRequestDeviceId(deviceId?: string | boolean | null): string | null | true;
Params:
deviceId?: string | boolean | null
– media request param to validate
Returns: a valid requestable SDK media value string|null|true
Get a copy of the current media state
Declaration:
getState(): ISdkMediaState;
Params: none
Returns: the current sdk media state. See ISdkMediaState
Get the current cached media devices
Declaration:
getDevices(): MediaDeviceInfo[];
Params: none
Returns: an array of all cached devices
Get the current cached audio devices
Declaration:
getAudioDevices(): MediaDeviceInfo[];
Params: none
Returns: an array of all cached audio devices
Get the current cached video devices
Declaration:
getVideoDevices(): MediaDeviceInfo[];
Params: none
Returns: an array of all cached video devices
Get the current cached output devices
Declaration:
getOutputDevices(): MediaDeviceInfo[];
Params: none
Returns: an array of all cached output devices
This will return all active media tracks that were created by the sdk
Declaration:
getAllActiveMediaTracks(): MediaStreamTrack[];
Params: none
Returns: an array of all active media tracks created by the sdk
Look through the cached devices and match based on
the passed in track's kind
and label
.
Declaration:
findCachedDeviceByTrackLabel(track?: MediaStreamTrack): MediaDeviceInfo | undefined;
Params:
track?: MediaStreamTrack
– Optional:MediaStreamTrack
with the label to search for.
Returns: the found device or undefined
if the
device could not be found.
Look through the cached output devices and match based on the passed in output deviceId.
Declaration:
findCachedOutputDeviceById(id?: string): MediaDeviceInfo | undefined;
Params:
id?: string
– Optional: output deviceId
Returns: the found device or undefined
if the device could not be found.
Determine if the passed in device exists in the cached devices
Declaration:
doesDeviceExistInCache(device?: MediaDeviceInfo): boolean;
Params:
device?: MediaDeviceInfo
– Optional: device to look for
Returns: boolean whether the device was found
This will remove all media listeners, stop any existing media, and stop listening for device changes.
WARNING: calling this effectively renders the SDK instance useless. A new instance will need to be created after this has been called. Declaration:
destroy(): void;
Params: none
Returns: void
Determine if the passed in error is a media permissions error.
Declaration:
isPermissionsError (error: Error): boolean;
Params:
error: Error
– error to check
Returns: boolean
whether the error denied permissions or not
SDK Media Utility implements the same EventEmitter
interface and strict-typings that the base WebRTC SDK does.
See SDK Events for the full list of inherited functions.
Event emitted for microphone volume changes. Event includes the media track, volume average, if the mic is muted, and the sessionId (if available).
The sessionId will only be available if the media was created
with a session
or sessionId
passed into with the
media request options (See IMediaRequestOptions).
This will emit every 100ms
with the average volume during that time range.
This event can be used to determine if a microphone is not picking up audio. Reasons for this can be the microphone is on "hardware mute" or the OS does not have microphone permissions for the given browser being used. Both of these reasons cannot be detected on the media track and can only be "guessed" based on no audio being picked up from the mic.
Declaration:
sdk.media.on('audioTrackVolume', (event: {
track: MediaStreamTrack;
volume: number;
muted: boolean;
sessionId?: string;
}) => { });
Value of event:
track: MediaStreamTrack
– the audio track the volume is emitting forvolume: number
– average volume of audio received during time rangemuted: boolean
– flag indicating if the track is notenabled
or ismuted
. If this istrue
, the volume will always be0
sessionId?: string
– Optional: the sessionId using this audio track.
Event emitted whenever the media state changes.
event.eventType
will match the other event that
is emitted on.
Example: if the devices changed, the following will emit:
sdk.media.on('state', evt => {/* evt.eventType === 'devices' */});
which means that the 'devices'
event will also emit
sdk.media.on('devices', evt => {/* evt.eventType === 'devices' */});
Declaration:
sdk.media.on('state', (state: SdkMediaStateWithType) => { });
Value of event:
- See SdkMediaStateWithType
- See the devices event and permissions event for info on other state events
Event when devices change.
Note: this will only fire when devices change. For example: if
sdk.media.enumerateDevices()
is called multiple times,sdk.media.on('devices', evt) will only fire once _unless_ the devices are different on subsequent enumerations. This ensures
enumerateDevices()` can be called many times without the event emitting with duplicate data.
devices: SdkMediaStateWithType; Declaration:
sdk.media.on('devices', (state: SdkMediaStateWithType) => { /* evt.eventType === 'devices' */});
Value of event:
Event when media permissions change. Values that trigger this event are any of the following in the ISdkMediaState:
hasMicPermissions: boolean;
hasCameraPermissions: boolean;
micPermissionsRequested: boolean;
cameraPermissionsRequested: boolean;
For example, when calling through to
sdk.media.requestMediaPermissions('audio')
, this
event will emit two times:
- for
micPermissionsRequested
changing totrue
(which happens right before requestinggetUserMedia()
) event will emit two times: - for
hasMicPermissions
changing totrue
orfalse
depending on the outcome of thegetUserMedia()
request
Note: in some browsers, permissions are not always guaranteed even after using requestMediaPermissions() to gain permissions. See Firefox Behavior for more details. It is recommended to use this event to watch for changes in permissions since permissions could be denied anytime startMedia() is called.
Declaration:
sdk.media.on('permissions', (state: SdkMediaStateWithType) => { /* evt.eventType === 'permissions' */});
Value of event:
This is used when emitting state changes on sdk.media.on('state', (evt: SdkMediaStateWithType) => {})
to determine what event type caused the state change.
type SdkMediaStateWithType = ISdkMediaState & {
eventType: SdkMediaEventTypes;
};
- See ISdkMediaState for inherited properties
eventType: SdkMediaEventTypes
– event type that triggered the event- Available options:
state | devices | permissions
- Note:
audioTrackVolume
events do not trigger'state'
events
- Note:
- See Events for more details on when these events emit
- Available options:
The current state of devices, permissions, and other media related information:
interface ISdkMediaState {
devices: MediaDeviceInfo[];
oldDevices: MediaDeviceInfo[];
audioDevices: MediaDeviceInfo[];
videoDevices: MediaDeviceInfo[];
outputDevices: MediaDeviceInfo[];
hasMic: boolean;
hasCamera: boolean;
hasMicPermissions: boolean;
hasCameraPermissions: boolean;
hasOutputDeviceSupport: boolean;
micPermissionsRequested: boolean;
cameraPermissionsRequested: boolean;
}
devices: MediaDeviceInfo[]
– a list of all current devicesoldDevices: MediaDeviceInfo[]
– a list of all devices from the last emission. This will only differ fromdevices
ifdevices
changed. Otherwise, these devices will matchdevices
. This is useful for diffing which devices changed.audioDevices: MediaDeviceInfo[]
– a list of all current audio (microphone) devicesvideoDevices: MediaDeviceInfo[]
– a list of all current video (camera) devicesoutputDevices: MediaDeviceInfo[]
– a list of all current output (speaker) deviceshasMic: boolean
– does at least one audio device existhasCamera: boolean
– does at least one video device existhasMicPermissions: boolean
– flag indicating if microphone permissions have been grantedhasCameraPermissions: boolean
– flag indicating if camera permissions have been grantedhasOutputDeviceSupport: boolean
– flag indicating if the browser supports output devicesmicPermissionsRequested: boolean
– internal flag indicating if microphone permissions have been requested yetcameraPermissionsRequested: boolean
– internal flag indicating if camera permissions have been requested yet
Interface for defining the SDK's contract for requesting media.
Important note: anywhere in the SDK where media can be requested this contract will apply.
string
for a specified deviceIdtrue
for sdk default deviceIdnull
for system default devicefalse|undefined
to not request or update media type
interface IMediaRequestOptions {
audio?: string | boolean | null;
video?: string | boolean | null;
videoResolution?: {
width: ConstrainULong,
height: ConstrainULong
} | false;
videoFrameRate?: ConstrainDouble | false;
monitorMicVolume?: boolean;
session?: IExtendedMediaSession;
retryOnFailure?: boolean;
preserveMediaIfOneTypeFails?: boolean;
}
audio?: string | boolean | null
– Optional: value for how to request audio media (See important note above explaining available options)video?: string | boolean | null
– Optional: value for how to request video media (See important note above explaining available options)videoResolution?: { width: ConstrainULong, height: ConstrainULong } | false
– Optional: Video resolution to request from getUserMedia. Default is SDK configured resolution.false
will explicitly not use any resolution including the sdk default-
type ConstrainULong = number | { exact?: number; ideal?: number; max?: number; min?: number; }
-
videoFrameRate?: ConstrainDouble | false
– Optional: default value is{ ideal: 30 }
– Video frame rate to request from getUserMedia.false
will explicitly not use any frameRate.- Example, if is set
videoFrameRate: { ideal: 45 }
then the translated constraint togetUserMedia
will bevideo: { frameRate: { ideal: 45 } }
- Example, if is set
monitorMicVolume?: boolean
– Optional: default is SDK configured default – Flag to emit volume change events for audio tracks. If this is aboolean
value, it will override the SDK default configuration ofmonitorMicVolume
. If it is not aboolean
(ie. leftundefined
) then the SDK default will be usedsession?: IExtendedMediaSession
– Optional: Session to associate logs to. It will also tieaudioTrackVolume
events to a sessionId if requesting audio withmonitorMicVolume = true
retryOnFailure?: boolean;
– Optional: Flag to retry media (if available) when it fails. Example would be if a specific microphone is requested, but fails. With this flag set, it will try again with the SDK default microphone and/or the system default microphone.preserveMediaIfOneTypeFails?: boolean;
– Optional: Flag to ignore media errors for one type if the other type succeeds. Example: if media is requested for audio & video, but audio fails – with this flag set, a warning will be logged for the failed audio but a valid stream will be returned with the successful video media.- Notes:
- This setting is only taken into consideration if both media types are requested. It has no effect if only one is requested.
- If using this flag, you may need to verify what tracks are received on the returned stream because it is not guaranteed that the stream will contain both types of media.
- If both types of media fail, the error for the audio stream request will be thrown.
- Notes:
Interface for available options when requesting to update outgoing media.
interface IUpdateOutgoingMedia extends ISdkMediaDeviceIds {
sessionId?: string;
session?: IExtendedMediaSession;
stream?: MediaStream;
}
- See ISdkMediaDeviceIds for deviceId options
sessionId?: string
– Optional: session id to update media for (this ORsession
is required)session?: IExtendedMediaSession
– Optional: session to update media for (this ORsessionId
is required)stream?: MediaStream
– Optional:MediaStream
to update the session with
Interface for defining the SDK's contract for how to request media with specific deviceIds.
See note for IMediaRequestOptions requesting media requesting params
interface ISdkMediaDeviceIds {
videoDeviceId?: string | boolean | null;
audioDeviceId?: string | boolean | null;
}
videDeviceId?: string | boolean | null
– Optional: Request video media in the following manner:string
for a specified deviceIdtrue
for sdk default deviceIdnull
for system default devicefalse|undefined
to not request video
audioDeviceId?: string | boolean | null
– Optional: Request audio media in the following manner:string
for a specified deviceIdtrue
for sdk default deviceIdnull
for system default devicefalse|undefined
to not request audio
There are many nuances and differences with how individual browsers and Operating Systems (OS) handle media, errors, devices, and permissions. The SDK attempts to account for as many of these as possible. Here are some limitations and observations when working with media in the browser:
On macOS, the user has to grant the browser permission to use certain media. These options can be found at System Preferences > Security & Privacy > Privacy. Here are the following permissions and their corresponding responses:
- Camera
- If not granted, the browser will throw an error when attempting to request video media
- Microphone
- If not granted, the browser will still return an audio media track that looks normal and not throw an error. However, there will be no actual audio being picked up by the microphone. It will act as if in a hardware mute state. See Microphone OS Permissions and Hardware Mute for more information.
- Screen Recording (which is the permission needed for sharing a screen)
- If not granted, the browser will still prompt the use for a scren selection. However, the user will only be able to share the browser-in-use window or any desktop screen with only the background present (ie. they will not see any other applications).
There is no way for the browser to know if a microphone is in a hardware mute state.
When a microphone is in a hardware mute state, the audio media track will still appear
to be normal (meaning track.enabled === true
& track.muted === false
). However,
there will be no actual audio being picked up by the microphone.
Two reasons a microphone would end up in hardware mute:
- The microphone has a mute button on the physical device, external of the browser
- The OS has not been granted permissions for microphone usage. See Mac OS for more information about OS permissions and behaviors.
To account for hardware mute scenarios, you will need implement logic for checking the volume on an audio track. See the audioTrackVolume event for more details. Also, see requestMediaPermissions() for important notes about requesting audio permissions when the OS has not given permissions.
Chrome handles media and permissions in a very straight forward manner.
If a user grants or denies permissions for a media type then the decision will be remembered for that domain until the user manually resets the permission via chrome settings.
If a user grants or denies permissions for a media type then the decision will only be remembered as long as the chrome instance is alive. Once the window closes, all previous permissions reset. The user also still has the option to reset permissions via chrome settings.
If a user denies screen share permissions in chrome by clicking the Cancel button on the screen selection prompt, you will still be able to request the screen stream to prompt the user to select a screen again.
Firefox has a Remember this decision (Rtd) checkbox when prompting for a media type. This checkbox changes the behavior for both Normal and Private Firefox instances in different ways.
- If Rtd is checked:
- The media type and domain will be added to Firefox's privacy tab with
the selected decision. (You can navigate to
about:preferences#privacy
then scroll down to the Permissions section to view remembered media decisions and domains). - For permissions granted: the media type will not prompt for user approval of any future media requests for the granted media type.
- For permissions denied: the browser will throw an error for denied permissions and never ask the user for approval for any future media requests (until the permission setting is reset)
- The media type and domain will be added to Firefox's privacy tab with
the selected decision. (You can navigate to
- If Rtd is not checked:
- The media type and domain will not be added to Firefox's privacy tab with the selected decision
- For permissions granted: the media type will always prompt for user approval of any future media requests. Meaning the user will have to approve any new media requests even if they have already approved some in the past.
- For permissions denies: the media type will still prompt for user approval of any future media requests. Meaning the user will again have the option to approve or deny any new media requests even if they have denied some in the past.
- The browser
devicechange
event will not fire when the devices change. This can cause issues when enumerating and attempting to spin up media with specific devices in the SDK.
A few caveats about Rtd behavior:
- Rtd is not presented to users if they are using Firefox in Private Mode. This is the main difference between a regular Firefox instance and a Private Mode Firefox instance.
- If Rtd is checked in a non Private Mode browser, the domain & media type
result will still be read by Private Mode browsers.
- Example: if Rtd is checked for approving
audio
onhttps://yourwebsit.com
, the user will not be prompted foraudio
media permissions onhttps://yourwebsit.com
even if they visit that site in a Private Mode Firefox instance.
- Example: if Rtd is checked for approving
- Since permissions are not always guaranteed in Firefox because of the Rtd behavior, even after using requestMediaPermissions() to gain permissions, it is recommended to use the permissions event to watch for changes in the permissions state since permissions could be denied anytime startMedia() is called.
If a user denies screen share permissions in Firefox by clicking the Cancel button on the screen selection prompt, you will still be able to request the screen stream, however, it will throw an error instantly and not prompt the user to select a screen. In order to reset the permission to allow for the prompt again, the user has to do 1 of 2 things:
- Refresh the browser
- Reset the screen share permissions (which will be set to "Share the Screen – Temporarily Blocked". This can be found by clicking the button just to the left of the URL in the navigation bar).
One way to account for this is to time how long it takes for startDisplayMedia() to throw a permissions error. If it is almost instant, then you may be able to assume that the user has temporarily denied screen share permissions.