11import browserama from 'browserama' ;
22
33import { GenesysCloudWebrtcSdk } from './client' ;
4- import { IMediaRequestOptions , IEnumeratedDevices } from './types/interfaces' ;
4+ import { IMediaRequestOptions , IEnumeratedDevices , IJingleSession } from './types/interfaces' ;
55import { SdkErrorTypes } from './types/enums' ;
66import { throwSdkError } from './utils' ;
77
@@ -227,28 +227,131 @@ export const stopListeningForDeviceChanges = function () {
227227 navigator . mediaDevices . removeEventListener ( 'devicechange' , handleDeviceChange ) ;
228228} ;
229229
230+ /**
231+ * Look through the cached enumerated devices and match based on
232+ * the passed in track's `kind` and `label`
233+ * @param track media stream track with the label to search for
234+ */
235+ export const findCachedDeviceByTrackLabel = ( track ?: MediaStreamTrack ) : MediaDeviceInfo | undefined => {
236+ if ( ! track ?. kind ) return ;
237+ const availableDevices = getCachedEnumeratedDevices ( ) ;
238+ const devicesToSearch = track . kind === 'video' ? availableDevices . videoDevices : availableDevices . audioDevices ;
239+ return devicesToSearch . find ( d => d . label === track . label ) ;
240+ } ;
241+
242+ /**
243+ * Look through the cached enumerated devices and match based on
244+ * the passed in output deviceId
245+ * @param id output deviceId
246+ */
247+ export const findCachedOutputDeviceById = ( id ?: string ) : MediaDeviceInfo | undefined => {
248+ return getCachedEnumeratedDevices ( ) . outputDevices . find ( d => d . deviceId === id ) ;
249+ } ;
250+
251+ /**
252+ * Utility to log device changes. It will use the passed in `from` track _or_
253+ * look up the currently used devices via the sender (based on labels) to see
254+ * what device is currently in use. Then it will log out the device changing `to`
255+ * as the new device.
256+ *
257+ * NOTE: if the system default is being used and then changes (which will force
258+ * devices to be enumerated) the device will not be able to be looked up in the
259+ * cached devices. So the caller will need to pass in the "old" system default(s)
260+ * as `fromVideoTrack` and/or `fromAudioTrack`.
261+ *
262+ * @param sdk sdk instance
263+ * @param session session devices are changing for
264+ * @param action action taken
265+ * @param devicesChange devices changing to/from
266+ */
267+ export function logDeviceChange (
268+ sdk : GenesysCloudWebrtcSdk ,
269+ session : IJingleSession ,
270+ action : 'sessionStarted' | 'changingDevices' ,
271+ devicesChange : {
272+ toVideoTrack ?: MediaStreamTrack ,
273+ fromVideoTrack ?: MediaStreamTrack ,
274+ toAudioTrack ?: MediaStreamTrack ,
275+ fromAudioTrack ?: MediaStreamTrack ,
276+ toOutputDeviceId ?: string
277+ } = { }
278+ ) : void {
279+ let currentVideoTrack : MediaStreamTrack ;
280+ let currentAudioTrack : MediaStreamTrack ;
281+ const currentOutputDeviceId : string = session . _outputAudioElement ?. sinkId ;
282+ const screenShareTrackId = session . _screenShareStream ?. getVideoTracks ( ) [ 0 ] ?. id ;
283+
284+ /* grab the currect device being used */
285+ session . pc . getSenders ( )
286+ . filter ( s => s . track && s . track . id !== screenShareTrackId )
287+ . forEach ( sender => {
288+ if ( sender . track . kind === 'audio' ) {
289+ currentAudioTrack = sender . track ;
290+ } else /* if (sender.track.kind === 'video') */ {
291+ currentVideoTrack = sender . track ;
292+ }
293+ } ) ;
294+
295+ const availableDevices = getCachedEnumeratedDevices ( ) ;
296+ const details = {
297+ /* meta data */
298+ action,
299+ availableDevices,
300+ sessionId : session . id ,
301+ conversationId : session . conversationId ,
302+ /* the device being switched from and/or currently being used.
303+ if a track was passed in, we will assume the caller knows what it is doing
304+ and we will use that track for logging. Otherwise, we will look up the device */
305+ currentVideoDevice : devicesChange . fromVideoTrack ? { deviceId : undefined , groupId : undefined , label : devicesChange . fromVideoTrack . label } : findCachedDeviceByTrackLabel ( currentVideoTrack ) ,
306+ currentAudioDevice : devicesChange . fromAudioTrack ? { deviceId : undefined , groupId : undefined , label : devicesChange . fromAudioTrack . label } : findCachedDeviceByTrackLabel ( currentAudioTrack ) ,
307+ currentOutputDevice : findCachedOutputDeviceById ( currentOutputDeviceId ) ,
308+ /* the device being switched to */
309+ newVideoDevice : findCachedDeviceByTrackLabel ( devicesChange . toVideoTrack ) ,
310+ newAudioDevice : findCachedDeviceByTrackLabel ( devicesChange . toAudioTrack ) ,
311+ newOutputDevice : findCachedOutputDeviceById ( devicesChange . toOutputDeviceId ) ,
312+ /* other potentially useful information to log */
313+ sessionVideoMute : session . videoMuted ,
314+ sessionAudioMute : session . audioMuted ,
315+ hasDevicePermissions : hasDevicePermissions ( availableDevices )
316+ } ;
317+
318+ sdk . logger . info ( 'media devices changing for session' , details ) ;
319+ }
320+
321+ /**
322+ * Get the currently cached enumerated devices
323+ */
324+ export function getCachedEnumeratedDevices ( ) : IEnumeratedDevices {
325+ /* return a copy of cached devices */
326+ return {
327+ audioDevices : enumeratedDevices . audioDevices . slice ( ) ,
328+ videoDevices : enumeratedDevices . videoDevices . slice ( ) ,
329+ outputDevices : enumeratedDevices . outputDevices . slice ( )
330+ } ;
331+ }
332+
230333export async function getEnumeratedDevices ( sdk : GenesysCloudWebrtcSdk , forceRefresh : boolean = false ) : Promise < IEnumeratedDevices > {
231334 if ( ! window . navigator . mediaDevices || ! window . navigator . mediaDevices . enumerateDevices ) {
232335 sdk . logger . warn ( 'Unable to enumerate devices' ) ;
233336 enumeratedDevices . videoDevices = [ ] ;
234337 enumeratedDevices . audioDevices = [ ] ;
235338 enumeratedDevices . outputDevices = [ ] ;
236- return enumeratedDevices ;
339+ return getCachedEnumeratedDevices ( ) ;
237340 }
238341
239342 if ( ! isListeningForDeviceChanges ) {
240343 navigator . mediaDevices . addEventListener ( 'devicechange' , handleDeviceChange . bind ( sdk ) ) ;
241344 isListeningForDeviceChanges = true ;
242345 }
243346
244- /* if devices haven't changed, no forceRefresh, & we have permission - return the cache devices */
347+ /* if devices haven't changed, no forceRefresh, & we have permissions - return the cache devices */
245348 if ( ! refreshDevices && ! forceRefresh && hasDevicePermissions ( enumeratedDevices ) ) {
246349 sdk . logger . debug ( 'Returning cached enumerated devices' , { devices : enumeratedDevices } ) ;
247- return enumeratedDevices ;
350+ return getCachedEnumeratedDevices ( ) ;
248351 }
249352
250353 try {
251- const oldDevices = { ... enumeratedDevices } ;
354+ const oldDevices = getCachedEnumeratedDevices ( ) ;
252355 enumeratedDevices . videoDevices = [ ] ;
253356 enumeratedDevices . audioDevices = [ ] ;
254357 enumeratedDevices . outputDevices = [ ] ;
@@ -271,7 +374,8 @@ export async function getEnumeratedDevices (sdk: GenesysCloudWebrtcSdk, forceRef
271374 }
272375
273376 sdk . logger . debug ( 'Enumerated devices' , { devices : enumeratedDevices } ) ;
274- return enumeratedDevices ;
377+ /* the "cache" was just updated with the new devices, this will make a copy */
378+ return getCachedEnumeratedDevices ( ) ;
275379}
276380
277381function mapOldToNewDevices ( oldEnumeratedDevices : IEnumeratedDevices | undefined , newDevices : MediaDeviceInfo [ ] ) : MediaDeviceInfo [ ] {
0 commit comments