mirror of
https://github.com/vector-im/element-call.git
synced 2026-04-03 07:10:26 +00:00
Merge branch 'livekit' into toger5/hotfix_mute_loop2
Signed-off-by: Timo K <toger5@hotmail.de>
This commit is contained in:
@@ -51,8 +51,8 @@ export interface MediaDevices {
|
||||
// Cargo-culted from @livekit/components-react
|
||||
function useObservableState<T>(
|
||||
observable: Observable<T> | undefined,
|
||||
startWith: T
|
||||
) {
|
||||
startWith: T,
|
||||
): T {
|
||||
const [state, setState] = useState<T>(startWith);
|
||||
useEffect(() => {
|
||||
// observable state doesn't run in SSR
|
||||
@@ -67,7 +67,7 @@ function useMediaDevice(
|
||||
kind: MediaDeviceKind,
|
||||
fallbackDevice: string | undefined,
|
||||
usingNames: boolean,
|
||||
alwaysDefault: boolean = false
|
||||
alwaysDefault: boolean = false,
|
||||
): MediaDevice {
|
||||
// Make sure we don't needlessly reset to a device observer without names,
|
||||
// once permissions are already given
|
||||
@@ -83,7 +83,7 @@ function useMediaDevice(
|
||||
// kind, which then results in multiple permissions requests.
|
||||
const deviceObserver = useMemo(
|
||||
() => createMediaDeviceObserver(kind, requestPermissions),
|
||||
[kind, requestPermissions]
|
||||
[kind, requestPermissions],
|
||||
);
|
||||
const available = useObservableState(deviceObserver, []);
|
||||
const [selectedId, select] = useState(fallbackDevice);
|
||||
@@ -143,18 +143,18 @@ export const MediaDevicesProvider: FC<Props> = ({ children }) => {
|
||||
const audioInput = useMediaDevice(
|
||||
"audioinput",
|
||||
audioInputSetting,
|
||||
usingNames
|
||||
usingNames,
|
||||
);
|
||||
const audioOutput = useMediaDevice(
|
||||
"audiooutput",
|
||||
audioOutputSetting,
|
||||
useOutputNames,
|
||||
alwaysUseDefaultAudio
|
||||
alwaysUseDefaultAudio,
|
||||
);
|
||||
const videoInput = useMediaDevice(
|
||||
"videoinput",
|
||||
videoInputSetting,
|
||||
usingNames
|
||||
usingNames,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -176,11 +176,11 @@ export const MediaDevicesProvider: FC<Props> = ({ children }) => {
|
||||
|
||||
const startUsingDeviceNames = useCallback(
|
||||
() => setNumCallersUsingNames((n) => n + 1),
|
||||
[setNumCallersUsingNames]
|
||||
[setNumCallersUsingNames],
|
||||
);
|
||||
const stopUsingDeviceNames = useCallback(
|
||||
() => setNumCallersUsingNames((n) => n - 1),
|
||||
[setNumCallersUsingNames]
|
||||
[setNumCallersUsingNames],
|
||||
);
|
||||
|
||||
const context: MediaDevices = useMemo(
|
||||
@@ -197,7 +197,7 @@ export const MediaDevicesProvider: FC<Props> = ({ children }) => {
|
||||
videoInput,
|
||||
startUsingDeviceNames,
|
||||
stopUsingDeviceNames,
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -207,7 +207,8 @@ export const MediaDevicesProvider: FC<Props> = ({ children }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const useMediaDevices = () => useContext(MediaDevicesContext);
|
||||
export const useMediaDevices = (): MediaDevices =>
|
||||
useContext(MediaDevicesContext);
|
||||
|
||||
/**
|
||||
* React hook that requests for the media devices context to be populated with
|
||||
@@ -215,7 +216,10 @@ export const useMediaDevices = () => useContext(MediaDevicesContext);
|
||||
* default because it may involve requesting additional permissions from the
|
||||
* user.
|
||||
*/
|
||||
export const useMediaDeviceNames = (context: MediaDevices, enabled = true) =>
|
||||
export const useMediaDeviceNames = (
|
||||
context: MediaDevices,
|
||||
enabled = true,
|
||||
): void =>
|
||||
useEffect(() => {
|
||||
if (enabled) {
|
||||
context.startUsingDeviceNames();
|
||||
|
||||
@@ -42,14 +42,14 @@ export type OpenIDClientParts = Pick<
|
||||
|
||||
export function useOpenIDSFU(
|
||||
client: OpenIDClientParts,
|
||||
rtcSession: MatrixRTCSession
|
||||
) {
|
||||
rtcSession: MatrixRTCSession,
|
||||
): SFUConfig | undefined {
|
||||
const [sfuConfig, setSFUConfig] = useState<SFUConfig | undefined>(undefined);
|
||||
|
||||
const activeFocus = useActiveFocus(rtcSession);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
(async (): Promise<void> => {
|
||||
const sfuConfig = activeFocus
|
||||
? await getSFUConfigWithOpenID(client, activeFocus)
|
||||
: undefined;
|
||||
@@ -62,20 +62,20 @@ export function useOpenIDSFU(
|
||||
|
||||
export async function getSFUConfigWithOpenID(
|
||||
client: OpenIDClientParts,
|
||||
activeFocus: LivekitFocus
|
||||
activeFocus: LivekitFocus,
|
||||
): Promise<SFUConfig | undefined> {
|
||||
const openIdToken = await client.getOpenIdToken();
|
||||
logger.debug("Got openID token", openIdToken);
|
||||
|
||||
try {
|
||||
logger.info(
|
||||
`Trying to get JWT from call's active focus URL of ${activeFocus.livekit_service_url}...`
|
||||
`Trying to get JWT from call's active focus URL of ${activeFocus.livekit_service_url}...`,
|
||||
);
|
||||
const sfuConfig = await getLiveKitJWT(
|
||||
client,
|
||||
activeFocus.livekit_service_url,
|
||||
activeFocus.livekit_alias,
|
||||
openIdToken
|
||||
openIdToken,
|
||||
);
|
||||
logger.info(`Got JWT from call's active focus URL.`);
|
||||
|
||||
@@ -83,7 +83,7 @@ export async function getSFUConfigWithOpenID(
|
||||
} catch (e) {
|
||||
logger.warn(
|
||||
`Failed to get JWT from RTC session's active focus URL of ${activeFocus.livekit_service_url}.`,
|
||||
e
|
||||
e,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
@@ -93,7 +93,7 @@ async function getLiveKitJWT(
|
||||
client: OpenIDClientParts,
|
||||
livekitServiceURL: string,
|
||||
roomName: string,
|
||||
openIDToken: IOpenIDToken
|
||||
openIDToken: IOpenIDToken,
|
||||
): Promise<SFUConfig> {
|
||||
try {
|
||||
const res = await fetch(livekitServiceURL + "/sfu/get", {
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2023 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
AudioPresets,
|
||||
DefaultReconnectPolicy,
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
ConnectionState,
|
||||
Room,
|
||||
RoomEvent,
|
||||
Track,
|
||||
} from "livekit-client";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
@@ -51,7 +52,7 @@ async function doConnect(
|
||||
livekitRoom: Room,
|
||||
sfuConfig: SFUConfig,
|
||||
audioEnabled: boolean,
|
||||
audioOptions: AudioCaptureOptions
|
||||
audioOptions: AudioCaptureOptions,
|
||||
): Promise<void> {
|
||||
await livekitRoom!.connect(sfuConfig!.url, sfuConfig!.jwt);
|
||||
|
||||
@@ -60,6 +61,14 @@ async function doConnect(
|
||||
// doesn't publish it until you unmute. We want to publish it from the start so we're
|
||||
// always capturing audio: it helps keep bluetooth headsets in the right mode and
|
||||
// mobile browsers to know we're doing a call.
|
||||
if (livekitRoom!.localParticipant.getTrack(Track.Source.Microphone)) {
|
||||
logger.warn(
|
||||
"Pre-creating audio track but participant already appears to have an microphone track: this shouldn't happen!",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Pre-creating microphone track");
|
||||
const audioTracks = await livekitRoom!.localParticipant.createTracks({
|
||||
audio: audioOptions,
|
||||
});
|
||||
@@ -69,6 +78,14 @@ async function doConnect(
|
||||
}
|
||||
if (!audioEnabled) await audioTracks[0].mute();
|
||||
|
||||
// check again having awaited for the track to create
|
||||
if (livekitRoom!.localParticipant.getTrack(Track.Source.Microphone)) {
|
||||
logger.warn(
|
||||
"Publishing pre-created audio track but participant already appears to have an microphone track: this shouldn't happen!",
|
||||
);
|
||||
return;
|
||||
}
|
||||
logger.info("Publishing pre-created mic track");
|
||||
await livekitRoom?.localParticipant.publishTrack(audioTracks[0]);
|
||||
}
|
||||
|
||||
@@ -76,12 +93,12 @@ export function useECConnectionState(
|
||||
initialAudioOptions: AudioCaptureOptions,
|
||||
initialAudioEnabled: boolean,
|
||||
livekitRoom?: Room,
|
||||
sfuConfig?: SFUConfig
|
||||
sfuConfig?: SFUConfig,
|
||||
): ECConnectionState {
|
||||
const [connState, setConnState] = useState(
|
||||
sfuConfig && livekitRoom
|
||||
? livekitRoom.state
|
||||
: ECAddonConnectionState.ECWaiting
|
||||
: ECAddonConnectionState.ECWaiting,
|
||||
);
|
||||
|
||||
const [isSwitchingFocus, setSwitchingFocus] = useState(false);
|
||||
@@ -116,10 +133,10 @@ export function useECConnectionState(
|
||||
!sfuConfigEquals(currentSFUConfig.current, sfuConfig)
|
||||
) {
|
||||
logger.info(
|
||||
`SFU config changed! URL was ${currentSFUConfig.current?.url} now ${sfuConfig?.url}`
|
||||
`SFU config changed! URL was ${currentSFUConfig.current?.url} now ${sfuConfig?.url}`,
|
||||
);
|
||||
|
||||
(async () => {
|
||||
(async (): Promise<void> => {
|
||||
setSwitchingFocus(true);
|
||||
await livekitRoom?.disconnect();
|
||||
setIsInDoConnect(true);
|
||||
@@ -128,7 +145,7 @@ export function useECConnectionState(
|
||||
livekitRoom!,
|
||||
sfuConfig!,
|
||||
initialAudioEnabled,
|
||||
initialAudioOptions
|
||||
initialAudioOptions,
|
||||
);
|
||||
} finally {
|
||||
setIsInDoConnect(false);
|
||||
@@ -149,7 +166,7 @@ export function useECConnectionState(
|
||||
livekitRoom!,
|
||||
sfuConfig!,
|
||||
initialAudioEnabled,
|
||||
initialAudioOptions
|
||||
initialAudioOptions,
|
||||
).finally(() => setIsInDoConnect(false));
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ export function useLiveKit(
|
||||
|
||||
const connectionState = useECConnectionState(
|
||||
{
|
||||
deviceId: initialDevices.current.audioOutput.selectedId,
|
||||
deviceId: initialDevices.current.audioInput.selectedId,
|
||||
},
|
||||
initialMuteStates.current.audio.enabled,
|
||||
room,
|
||||
@@ -252,7 +252,7 @@ export function useLiveKit(
|
||||
useEffect(() => {
|
||||
// Sync the requested devices with LiveKit's devices
|
||||
if (room !== undefined && connectionState === ConnectionState.Connected) {
|
||||
const syncDevice = (kind: MediaDeviceKind, device: MediaDevice) => {
|
||||
const syncDevice = (kind: MediaDeviceKind, device: MediaDevice): void => {
|
||||
const id = device.selectedId;
|
||||
|
||||
// Detect if we're trying to use chrome's default device, in which case
|
||||
|
||||
Reference in New Issue
Block a user