@@ -8,6 +8,7 @@ Please see LICENSE in the repository root for full details.
8
8
import {
9
9
combineLatest ,
10
10
filter ,
11
+ identity ,
11
12
map ,
12
13
merge ,
13
14
of ,
@@ -18,7 +19,7 @@ import {
18
19
type Observable ,
19
20
} from "rxjs" ;
20
21
import { createMediaDeviceObserver } from "@livekit/components-core" ;
21
- import { logger } from "matrix-js-sdk/lib/logger" ;
22
+ import { logger as rootLogger } from "matrix-js-sdk/lib/logger" ;
22
23
23
24
import {
24
25
audioInput as audioInputSetting ,
@@ -36,6 +37,7 @@ import { getUrlParams } from "../UrlParams";
36
37
// This hardcoded id is used in EX ios! It can only be changed in coordination with
37
38
// the ios swift team.
38
39
const EARPIECE_CONFIG_ID = "earpiece-id" ;
40
+ const logger = rootLogger . getChild ( "[MediaDevices]" ) ;
39
41
40
42
export type DeviceLabel =
41
43
| { type : "name" ; name : string }
@@ -93,15 +95,15 @@ export const iosDeviceMenu$ = navigator.userAgent.includes("iPhone")
93
95
94
96
function availableRawDevices$ (
95
97
kind : MediaDeviceKind ,
96
- usingNames $ : Observable < boolean > ,
98
+ recomputeDevicesWithPermissions $ : Observable < boolean > ,
97
99
scope : ObservableScope ,
98
100
) : Observable < MediaDeviceInfo [ ] > {
99
- return usingNames $. pipe (
100
- switchMap ( ( usingNames ) =>
101
+ return recomputeDevicesWithPermissions $. pipe (
102
+ switchMap ( ( recomputeDevicesWithPermissions ) =>
101
103
createMediaDeviceObserver (
102
104
kind ,
103
105
( e ) => logger . error ( "Error creating MediaDeviceObserver" , e ) ,
104
- usingNames ,
106
+ recomputeDevicesWithPermissions ,
105
107
) ,
106
108
) ,
107
109
startWith ( [ ] ) ,
@@ -145,7 +147,11 @@ function selectDevice$<Label>(
145
147
146
148
class AudioInput implements MediaDevice < DeviceLabel , SelectedAudioInputDevice > {
147
149
private readonly availableRaw$ : Observable < MediaDeviceInfo [ ] > =
148
- availableRawDevices$ ( "audioinput" , this . usingNames$ , this . scope ) ;
150
+ availableRawDevices$ (
151
+ "audioinput" ,
152
+ this . recomputeDevicesWithPermissions$ ,
153
+ this . scope ,
154
+ ) ;
149
155
150
156
public readonly available$ = this . availableRaw$ . pipe (
151
157
map ( buildDeviceMap ) ,
@@ -179,17 +185,21 @@ class AudioInput implements MediaDevice<DeviceLabel, SelectedAudioInputDevice> {
179
185
}
180
186
181
187
public constructor (
182
- private readonly usingNames $ : Observable < boolean > ,
188
+ private readonly recomputeDevicesWithPermissions $ : Observable < boolean > ,
183
189
private readonly scope : ObservableScope ,
184
- ) { }
190
+ ) {
191
+ this . available$ . subscribe ( ( available ) => {
192
+ logger . info ( "[audio-input] available devices:" , available ) ;
193
+ } ) ;
194
+ }
185
195
}
186
196
187
197
class AudioOutput
188
198
implements MediaDevice < AudioOutputDeviceLabel , SelectedAudioOutputDevice >
189
199
{
190
200
public readonly available$ = availableRawDevices$ (
191
201
"audiooutput" ,
192
- this . usingNames $,
202
+ this . recomputeDevicesWithPermissions $,
193
203
this . scope ,
194
204
) . pipe (
195
205
map ( ( availableRaw ) => {
@@ -230,9 +240,13 @@ class AudioOutput
230
240
}
231
241
232
242
public constructor (
233
- private readonly usingNames $ : Observable < boolean > ,
243
+ private readonly recomputeDevicesWithPermissions $ : Observable < boolean > ,
234
244
private readonly scope : ObservableScope ,
235
- ) { }
245
+ ) {
246
+ this . available$ . subscribe ( ( available ) => {
247
+ logger . info ( "[audio-output] available devices:" , available ) ;
248
+ } ) ;
249
+ }
236
250
}
237
251
238
252
class ControlledAudioOutput
@@ -295,13 +309,16 @@ class ControlledAudioOutput
295
309
window . controls . onOutputDeviceSelect ?.( device . id ) ;
296
310
}
297
311
} ) ;
312
+ this . available$ . subscribe ( ( available ) => {
313
+ logger . info ( "[controlled-output] available devices:" , available ) ;
314
+ } ) ;
298
315
}
299
316
}
300
317
301
318
class VideoInput implements MediaDevice < DeviceLabel , SelectedDevice > {
302
319
public readonly available$ = availableRawDevices$ (
303
320
"videoinput" ,
304
- this . usingNames $,
321
+ this . recomputeDevicesWithPermissions $,
305
322
this . scope ,
306
323
) . pipe ( map ( buildDeviceMap ) ) ;
307
324
@@ -318,21 +335,28 @@ class VideoInput implements MediaDevice<DeviceLabel, SelectedDevice> {
318
335
}
319
336
320
337
public constructor (
321
- private readonly usingNames $ : Observable < boolean > ,
338
+ private readonly recomputeDevicesWithPermissions $ : Observable < boolean > ,
322
339
private readonly scope : ObservableScope ,
323
- ) { }
340
+ ) {
341
+ // This also has the purpose of subscribing to the available devices
342
+ this . available$ . subscribe ( ( available ) => {
343
+ logger . info ( "[video-input] available devices:" , available ) ;
344
+ } ) ;
345
+ }
324
346
}
325
347
326
348
export class MediaDevices {
327
- private readonly deviceNamesRequest $ = new Subject < void > ( ) ;
349
+ private readonly requests $ = new Subject < boolean > ( ) ;
328
350
/**
329
351
* Requests that the media devices be populated with the names of each
330
352
* available device, rather than numbered identifiers. This may invoke a
331
353
* permissions pop-up, so it should only be called when there is a clear user
332
354
* intent to view the device list.
333
355
*/
334
356
public requestDeviceNames ( ) : void {
335
- this . deviceNamesRequest$ . next ( ) ;
357
+ void navigator . mediaDevices . enumerateDevices ( ) . then ( ( result ) => {
358
+ this . requests$ . next ( ! result . some ( ( device ) => device . label ) ) ;
359
+ } ) ;
336
360
}
337
361
338
362
// Start using device names as soon as requested. This will cause LiveKit to
@@ -341,26 +365,33 @@ export class MediaDevices {
341
365
// you to do to receive device names in lieu of a more explicit permissions
342
366
// API. This flag never resets to false, because once permissions are granted
343
367
// the first time, the user won't be prompted again until reload of the page.
344
- private readonly usingNames$ = this . deviceNamesRequest$ . pipe (
345
- map ( ( ) => true ) ,
368
+ private readonly recomputeDevicesWithPermissions$ = this . requests$ . pipe (
346
369
startWith ( false ) ,
347
- this . scope . state ( ) ,
370
+ identity ,
371
+ this . scope . stateNonDistinct ( ) ,
348
372
) ;
349
373
350
374
public readonly audioInput : MediaDevice <
351
375
DeviceLabel ,
352
376
SelectedAudioInputDevice
353
- > = new AudioInput ( this . usingNames $, this . scope ) ;
377
+ > = new AudioInput ( this . recomputeDevicesWithPermissions $, this . scope ) ;
354
378
355
379
public readonly audioOutput : MediaDevice <
356
380
AudioOutputDeviceLabel ,
357
381
SelectedAudioOutputDevice
358
382
> = getUrlParams ( ) . controlledAudioDevices
359
383
? new ControlledAudioOutput ( this . scope )
360
- : new AudioOutput ( this . usingNames $, this . scope ) ;
384
+ : new AudioOutput ( this . recomputeDevicesWithPermissions $, this . scope ) ;
361
385
362
386
public readonly videoInput : MediaDevice < DeviceLabel , SelectedDevice > =
363
- new VideoInput ( this . usingNames $, this . scope ) ;
387
+ new VideoInput ( this . recomputeDevicesWithPermissions $, this . scope ) ;
364
388
365
- public constructor ( private readonly scope : ObservableScope ) { }
389
+ public constructor ( private readonly scope : ObservableScope ) {
390
+ this . recomputeDevicesWithPermissions$ . subscribe ( ( recompute ) => {
391
+ logger . info (
392
+ "[MediaDevices] recomputeDevicesWithPermissions$ changed:" ,
393
+ recompute ,
394
+ ) ;
395
+ } ) ;
396
+ }
366
397
}
0 commit comments