simple subject instead of derived observable

This commit is contained in:
Timo
2025-06-23 13:57:52 +02:00
committed by Robin
parent e47a743f1e
commit c03e2c4bde
2 changed files with 20 additions and 42 deletions

View File

@@ -8,7 +8,6 @@ Please see LICENSE in the repository root for full details.
import {
combineLatest,
filter,
identity,
map,
merge,
of,
@@ -95,15 +94,16 @@ export const iosDeviceMenu$ =
function availableRawDevices$(
kind: MediaDeviceKind,
recomputeDevicesWithPermissions$: Observable<boolean>,
updateAvailableDeviceRequests$: Observable<boolean>,
scope: ObservableScope,
): Observable<MediaDeviceInfo[]> {
return recomputeDevicesWithPermissions$.pipe(
switchMap((recomputeDevicesWithPermissions) =>
return updateAvailableDeviceRequests$.pipe(
startWith(false),
switchMap((withPermissions) =>
createMediaDeviceObserver(
kind,
(e) => logger.error("Error creating MediaDeviceObserver", e),
recomputeDevicesWithPermissions,
withPermissions,
),
),
startWith([]),
@@ -149,7 +149,7 @@ class AudioInput implements MediaDevice<DeviceLabel, SelectedAudioInputDevice> {
private readonly availableRaw$: Observable<MediaDeviceInfo[]> =
availableRawDevices$(
"audioinput",
this.recomputeDevicesWithPermissions$,
this.updateAvailableDeviceRequests$,
this.scope,
);
@@ -185,7 +185,7 @@ class AudioInput implements MediaDevice<DeviceLabel, SelectedAudioInputDevice> {
}
public constructor(
private readonly recomputeDevicesWithPermissions$: Observable<boolean>,
private readonly updateAvailableDeviceRequests$: Observable<boolean>,
private readonly scope: ObservableScope,
) {
this.available$.subscribe((available) => {
@@ -199,7 +199,7 @@ class AudioOutput
{
public readonly available$ = availableRawDevices$(
"audiooutput",
this.recomputeDevicesWithPermissions$,
this.updateAvailableDeviceRequests$,
this.scope,
).pipe(
map((availableRaw) => {
@@ -240,7 +240,7 @@ class AudioOutput
}
public constructor(
private readonly recomputeDevicesWithPermissions$: Observable<boolean>,
private readonly updateAvailableDeviceRequests$: Observable<boolean>,
private readonly scope: ObservableScope,
) {
this.available$.subscribe((available) => {
@@ -321,7 +321,7 @@ class ControlledAudioOutput
class VideoInput implements MediaDevice<DeviceLabel, SelectedDevice> {
public readonly available$ = availableRawDevices$(
"videoinput",
this.recomputeDevicesWithPermissions$,
this.updateAvailableDeviceRequests$,
this.scope,
).pipe(map(buildDeviceMap));
@@ -338,7 +338,7 @@ class VideoInput implements MediaDevice<DeviceLabel, SelectedDevice> {
}
public constructor(
private readonly recomputeDevicesWithPermissions$: Observable<boolean>,
private readonly updateAvailableDeviceRequests$: Observable<boolean>,
private readonly scope: ObservableScope,
) {
// This also has the purpose of subscribing to the available devices
@@ -349,7 +349,7 @@ class VideoInput implements MediaDevice<DeviceLabel, SelectedDevice> {
}
export class MediaDevices {
private readonly requests$ = new Subject<boolean>();
private readonly updateAvailableDeviceRequests$ = new Subject<boolean>();
/**
* Requests that the media devices be populated with the names of each
* available device, rather than numbered identifiers. This may invoke a
@@ -364,41 +364,31 @@ export class MediaDevices {
// we only actually update the requests$ subject if there are no
// devices with a label, because otherwise we already have the permission
// to access the devices.
this.requests$.next(!result.some((device) => device.label));
this.updateAvailableDeviceRequests$.next(
!result.some((device) => device.label),
);
});
}
// Start using device names as soon as requested. This will cause LiveKit to
// briefly request device permissions and acquire media streams for each
// device type while calling `enumerateDevices`, which is what browsers want
// you to do to receive device names in lieu of a more explicit permissions
// API. This flag never resets to false, because once permissions are granted
// the first time, the user won't be prompted again until reload of the page.
private readonly recomputeDevicesWithPermissions$ = this.requests$.pipe(
startWith(false),
identity,
this.scope.stateNonDistinct(),
);
public readonly audioInput: MediaDevice<
DeviceLabel,
SelectedAudioInputDevice
> = new AudioInput(this.recomputeDevicesWithPermissions$, this.scope);
> = new AudioInput(this.updateAvailableDeviceRequests$, this.scope);
public readonly audioOutput: MediaDevice<
AudioOutputDeviceLabel,
SelectedAudioOutputDevice
> = getUrlParams().controlledAudioDevices
? new ControlledAudioOutput(this.scope)
: new AudioOutput(this.recomputeDevicesWithPermissions$, this.scope);
: new AudioOutput(this.updateAvailableDeviceRequests$, this.scope);
public readonly videoInput: MediaDevice<DeviceLabel, SelectedDevice> =
new VideoInput(this.recomputeDevicesWithPermissions$, this.scope);
new VideoInput(this.updateAvailableDeviceRequests$, this.scope);
public constructor(private readonly scope: ObservableScope) {
this.recomputeDevicesWithPermissions$.subscribe((recompute) => {
this.updateAvailableDeviceRequests$.subscribe((recompute) => {
logger.info(
"[MediaDevices] recomputeDevicesWithPermissions$ changed:",
"[MediaDevices] updateAvailableDeviceRequests$ changed:",
recompute,
);
});

View File

@@ -38,9 +38,6 @@ export class ObservableScope {
shareReplay({ bufferSize: 1, refCount: false }),
);
private readonly stateNonDistinctImpl: MonoTypeOperator = (o$) =>
o$.pipe(this.bind(), shareReplay({ bufferSize: 1, refCount: false }));
/**
* Transforms an Observable into a hot state Observable which replays its
* latest value upon subscription, skips updates with identical values, and
@@ -50,15 +47,6 @@ export class ObservableScope {
return this.stateImpl;
}
/**
* Transforms an Observable into a hot state Observable which replays its
* latest value upon subscription, skips updates with identical values, and
* is bound to this scope.
*/
public stateNonDistinct(): MonoTypeOperator {
return this.stateNonDistinctImpl;
}
/**
* Ends the scope, causing any bound Observables to complete.
*/