mirror of
https://github.com/vector-im/element-call.git
synced 2026-01-18 02:32:27 +00:00
Merge pull request #3353 from element-hq/toger5/device-permissions-request-possible-fix
Skip unnecassary media devices permissions requests (video feed flicker when opening settings)
This commit is contained in:
@@ -184,6 +184,16 @@ export const LobbyView: FC<Props> = ({
|
||||
null) as LocalVideoTrack | null,
|
||||
[tracks],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (videoTrack && videoInputId === undefined) {
|
||||
// If we have a video track but no videoInputId,
|
||||
// we have to update the available devices. So that we select the first
|
||||
// available video input device as the default instead of the `""` id.
|
||||
devices.requestDeviceNames();
|
||||
}
|
||||
}, [devices, videoInputId, videoTrack]);
|
||||
|
||||
useTrackProcessorSync(videoTrack);
|
||||
const showSwitchCamera = useShowSwitchCamera(
|
||||
useObservable(
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
type Observable,
|
||||
} from "rxjs";
|
||||
import { createMediaDeviceObserver } from "@livekit/components-core";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
|
||||
|
||||
import {
|
||||
audioInput as audioInputSetting,
|
||||
@@ -33,10 +33,12 @@ import {
|
||||
} from "../controls";
|
||||
import { getUrlParams } from "../UrlParams";
|
||||
import { platform } from "../Platform";
|
||||
import { switchWhen } from "../utils/observable";
|
||||
|
||||
// This hardcoded id is used in EX ios! It can only be changed in coordination with
|
||||
// the ios swift team.
|
||||
const EARPIECE_CONFIG_ID = "earpiece-id";
|
||||
const logger = rootLogger.getChild("[MediaDevices]");
|
||||
|
||||
export type DeviceLabel =
|
||||
| { type: "name"; name: string }
|
||||
@@ -96,13 +98,25 @@ function availableRawDevices$(
|
||||
usingNames$: Observable<boolean>,
|
||||
scope: ObservableScope,
|
||||
): Observable<MediaDeviceInfo[]> {
|
||||
const logError = (e: Error): void =>
|
||||
logger.error("Error creating MediaDeviceObserver", e);
|
||||
const devices$ = createMediaDeviceObserver(kind, logError, false);
|
||||
const devicesWithNames$ = createMediaDeviceObserver(kind, logError, true);
|
||||
|
||||
return usingNames$.pipe(
|
||||
switchMap((usingNames) =>
|
||||
createMediaDeviceObserver(
|
||||
kind,
|
||||
(e) => logger.error("Error creating MediaDeviceObserver", e),
|
||||
usingNames,
|
||||
),
|
||||
switchMap((withNames) =>
|
||||
withNames
|
||||
? // It might be that there is already a media stream running somewhere,
|
||||
// and so we can do without requesting a second one. Only switch to the
|
||||
// device observer that explicitly requests the names if we see that
|
||||
// names are in fact missing from the initial device enumeration.
|
||||
devices$.pipe(
|
||||
switchWhen(
|
||||
(devices, i) => i === 0 && devices.every((d) => !d.label),
|
||||
devicesWithNames$,
|
||||
),
|
||||
)
|
||||
: devices$,
|
||||
),
|
||||
startWith([]),
|
||||
scope.state(),
|
||||
@@ -181,7 +195,11 @@ class AudioInput implements MediaDevice<DeviceLabel, SelectedAudioInputDevice> {
|
||||
public constructor(
|
||||
private readonly usingNames$: Observable<boolean>,
|
||||
private readonly scope: ObservableScope,
|
||||
) {}
|
||||
) {
|
||||
this.available$.subscribe((available) => {
|
||||
logger.info("[audio-input] available devices:", available);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class AudioOutput
|
||||
@@ -232,7 +250,11 @@ class AudioOutput
|
||||
public constructor(
|
||||
private readonly usingNames$: Observable<boolean>,
|
||||
private readonly scope: ObservableScope,
|
||||
) {}
|
||||
) {
|
||||
this.available$.subscribe((available) => {
|
||||
logger.info("[audio-output] available devices:", available);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ControlledAudioOutput
|
||||
@@ -298,6 +320,9 @@ class ControlledAudioOutput
|
||||
window.controls.onOutputDeviceSelect?.(device.id);
|
||||
}
|
||||
});
|
||||
this.available$.subscribe((available) => {
|
||||
logger.info("[controlled-output] available devices:", available);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +348,12 @@ class VideoInput implements MediaDevice<DeviceLabel, SelectedDevice> {
|
||||
public constructor(
|
||||
private readonly usingNames$: Observable<boolean>,
|
||||
private readonly scope: ObservableScope,
|
||||
) {}
|
||||
) {
|
||||
// This also has the purpose of subscribing to the available devices
|
||||
this.available$.subscribe((available) => {
|
||||
logger.info("[video-input] available devices:", available);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class MediaDevices {
|
||||
|
||||
@@ -5,7 +5,17 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type Observable, defer, finalize, scan, startWith, tap } from "rxjs";
|
||||
import {
|
||||
type Observable,
|
||||
concat,
|
||||
defer,
|
||||
finalize,
|
||||
map,
|
||||
scan,
|
||||
startWith,
|
||||
takeWhile,
|
||||
tap,
|
||||
} from "rxjs";
|
||||
|
||||
const nothing = Symbol("nothing");
|
||||
|
||||
@@ -39,6 +49,29 @@ export function accumulate<State, Event>(
|
||||
events$.pipe(scan(update, initial), startWith(initial));
|
||||
}
|
||||
|
||||
const switchSymbol = Symbol("switch");
|
||||
|
||||
/**
|
||||
* RxJS operator which behaves like the input Observable (A) until it emits a
|
||||
* value satisfying the given predicate, then behaves like Observable B.
|
||||
*
|
||||
* The switch is immediate; the value that triggers the switch will not be
|
||||
* present in the output.
|
||||
*/
|
||||
export function switchWhen<A, B>(
|
||||
predicate: (a: A, index: number) => boolean,
|
||||
b$: Observable<B>,
|
||||
) {
|
||||
return (a$: Observable<A>): Observable<A | B> =>
|
||||
concat(
|
||||
a$.pipe(
|
||||
map((a, index) => (predicate(a, index) ? switchSymbol : a)),
|
||||
takeWhile((a) => a !== switchSymbol),
|
||||
) as Observable<A>,
|
||||
b$,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the current value of a state Observable without reacting to future
|
||||
* changes.
|
||||
|
||||
Reference in New Issue
Block a user