2023-08-18 09:03:21 +01:00
|
|
|
/*
|
2024-09-06 10:22:13 +02:00
|
|
|
Copyright 2023, 2024 New Vector Ltd.
|
2023-08-18 09:03:21 +01:00
|
|
|
|
2025-02-18 17:59:58 +00:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
2024-09-06 10:22:13 +02:00
|
|
|
Please see LICENSE in the repository root for full details.
|
2023-08-18 09:03:21 +01:00
|
|
|
*/
|
|
|
|
|
|
2024-06-19 16:41:52 +02:00
|
|
|
import {
|
2025-08-05 15:13:04 +02:00
|
|
|
type MatrixRTCSession,
|
2025-09-30 16:47:45 +02:00
|
|
|
isLivekitTransportConfig,
|
|
|
|
|
type LivekitTransportConfig,
|
|
|
|
|
type LivekitTransport,
|
2025-03-13 13:58:43 +01:00
|
|
|
} from "matrix-js-sdk/lib/matrixrtc";
|
2025-08-05 15:13:04 +02:00
|
|
|
import { logger } from "matrix-js-sdk/lib/logger";
|
2025-03-13 13:58:43 +01:00
|
|
|
import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery";
|
2023-08-18 09:03:21 +01:00
|
|
|
|
|
|
|
|
import { PosthogAnalytics } from "./analytics/PosthogAnalytics";
|
|
|
|
|
import { Config } from "./config/Config";
|
2025-10-22 23:27:38 -04:00
|
|
|
import { ElementWidgetActions, widget } from "./widget";
|
2025-10-03 14:43:22 -04:00
|
|
|
import { MatrixRTCTransportMissingError } from "./utils/errors";
|
2025-07-18 10:58:50 -04:00
|
|
|
import { getUrlParams } from "./UrlParams";
|
2025-08-05 15:13:04 +02:00
|
|
|
import { getSFUConfigWithOpenID } from "./livekit/openIDSFU.ts";
|
2023-08-18 09:03:21 +01:00
|
|
|
|
2024-06-19 16:41:52 +02:00
|
|
|
const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
|
2023-08-18 09:03:21 +01:00
|
|
|
|
2025-08-27 14:01:01 +02:00
|
|
|
export function getLivekitAlias(rtcSession: MatrixRTCSession): string {
|
|
|
|
|
// For now we assume everything is a room-scoped call
|
|
|
|
|
return rtcSession.room.roomId;
|
|
|
|
|
}
|
2024-06-19 16:41:52 +02:00
|
|
|
|
2025-10-03 14:43:22 -04:00
|
|
|
async function makeTransportInternal(
|
2025-08-27 14:01:01 +02:00
|
|
|
rtcSession: MatrixRTCSession,
|
2025-09-30 16:47:45 +02:00
|
|
|
): Promise<LivekitTransport> {
|
2025-10-03 14:43:22 -04:00
|
|
|
logger.log("Searching for a preferred transport");
|
2025-08-27 14:01:01 +02:00
|
|
|
const livekitAlias = getLivekitAlias(rtcSession);
|
2025-08-05 17:44:22 +02:00
|
|
|
|
2025-10-03 14:43:22 -04:00
|
|
|
// TODO-MULTI-SFU: Either remove this dev tool or make it more official
|
|
|
|
|
const urlFromStorage =
|
|
|
|
|
localStorage.getItem("robin-matrixrtc-auth") ??
|
|
|
|
|
localStorage.getItem("timo-focus-url");
|
2025-08-28 11:02:41 +02:00
|
|
|
if (urlFromStorage !== null) {
|
2025-10-03 14:43:22 -04:00
|
|
|
const transportFromStorage: LivekitTransport = {
|
2025-08-28 11:02:41 +02:00
|
|
|
type: "livekit",
|
|
|
|
|
livekit_service_url: urlFromStorage,
|
|
|
|
|
livekit_alias: livekitAlias,
|
|
|
|
|
};
|
2025-10-03 14:43:22 -04:00
|
|
|
logger.log(
|
|
|
|
|
"Using LiveKit transport from local storage: ",
|
|
|
|
|
transportFromStorage,
|
|
|
|
|
);
|
|
|
|
|
return transportFromStorage;
|
2025-08-28 11:02:41 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-17 16:44:50 +00:00
|
|
|
// Prioritize the .well-known/matrix/client, if available, over the configured SFU
|
|
|
|
|
const domain = rtcSession.room.client.getDomain();
|
2025-08-05 15:13:04 +02:00
|
|
|
if (domain) {
|
|
|
|
|
// we use AutoDiscovery instead of relying on the MatrixClient having already
|
|
|
|
|
// been fully configured and started
|
|
|
|
|
const wellKnownFoci = (await AutoDiscovery.getRawClientConfig(domain))?.[
|
|
|
|
|
FOCI_WK_KEY
|
|
|
|
|
];
|
|
|
|
|
if (Array.isArray(wellKnownFoci)) {
|
2025-10-03 14:43:22 -04:00
|
|
|
const transport: LivekitTransportConfig | undefined = wellKnownFoci.find(
|
2025-09-30 16:47:45 +02:00
|
|
|
(f) => f && isLivekitTransportConfig(f),
|
2025-08-27 14:01:01 +02:00
|
|
|
);
|
2025-10-03 14:43:22 -04:00
|
|
|
if (transport !== undefined) {
|
|
|
|
|
logger.log("Using LiveKit transport from .well-known: ", transport);
|
|
|
|
|
return { ...transport, livekit_alias: livekitAlias };
|
2025-08-05 17:44:22 +02:00
|
|
|
}
|
2025-08-05 15:13:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const urlFromConf = Config.get().livekit?.livekit_service_url;
|
|
|
|
|
if (urlFromConf) {
|
2025-10-03 14:43:22 -04:00
|
|
|
const transportFromConf: LivekitTransport = {
|
2025-08-05 15:13:04 +02:00
|
|
|
type: "livekit",
|
|
|
|
|
livekit_service_url: urlFromConf,
|
|
|
|
|
livekit_alias: livekitAlias,
|
|
|
|
|
};
|
2025-10-03 14:43:22 -04:00
|
|
|
logger.log("Using LiveKit transport from config: ", transportFromConf);
|
|
|
|
|
return transportFromConf;
|
2025-08-05 15:13:04 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-03 14:43:22 -04:00
|
|
|
throw new MatrixRTCTransportMissingError(domain ?? "");
|
2025-08-27 14:01:01 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-03 14:43:22 -04:00
|
|
|
export async function makeTransport(
|
2025-08-27 14:01:01 +02:00
|
|
|
rtcSession: MatrixRTCSession,
|
2025-09-30 16:47:45 +02:00
|
|
|
): Promise<LivekitTransport> {
|
2025-10-03 14:43:22 -04:00
|
|
|
const transport = await makeTransportInternal(rtcSession);
|
2025-08-27 14:01:01 +02:00
|
|
|
// this will call the jwt/sfu/get endpoint to pre create the livekit room.
|
2025-08-27 15:07:55 +02:00
|
|
|
await getSFUConfigWithOpenID(
|
|
|
|
|
rtcSession.room.client,
|
2025-10-03 14:43:22 -04:00
|
|
|
transport.livekit_service_url,
|
|
|
|
|
transport.livekit_alias,
|
2025-08-27 15:07:55 +02:00
|
|
|
);
|
2025-10-03 14:43:22 -04:00
|
|
|
return transport;
|
2025-08-05 15:13:04 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-13 13:52:01 +02:00
|
|
|
export interface EnterRTCSessionOptions {
|
|
|
|
|
encryptMedia: boolean;
|
|
|
|
|
/** EXPERIMENTAL: If true, will use the multi-sfu codepath where each member connects to its SFU instead of everyone connecting to an elected on. */
|
2025-10-22 12:53:22 +02:00
|
|
|
useMultiSfu: boolean;
|
|
|
|
|
preferStickyEvents: boolean;
|
2025-10-13 13:52:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* TODO! document this function properly
|
|
|
|
|
* @param rtcSession
|
|
|
|
|
* @param transport
|
|
|
|
|
* @param options
|
|
|
|
|
*/
|
2024-06-19 16:41:52 +02:00
|
|
|
export async function enterRTCSession(
|
2023-10-16 17:45:06 +01:00
|
|
|
rtcSession: MatrixRTCSession,
|
2025-10-03 14:43:22 -04:00
|
|
|
transport: LivekitTransport,
|
2025-10-22 14:13:31 +02:00
|
|
|
{ encryptMedia, useMultiSfu, preferStickyEvents }: EnterRTCSessionOptions,
|
2025-10-22 12:53:22 +02:00
|
|
|
): Promise<void> {
|
2023-08-18 09:03:21 +01:00
|
|
|
PosthogAnalytics.instance.eventCallEnded.cacheStartCall(new Date());
|
|
|
|
|
PosthogAnalytics.instance.eventCallStarted.track(rtcSession.room.roomId);
|
|
|
|
|
|
|
|
|
|
// This must be called before we start trying to join the call, as we need to
|
|
|
|
|
// have started tracking by the time calls start getting created.
|
2023-10-25 13:44:33 +02:00
|
|
|
// groupCallOTelMembership?.onJoinCall();
|
2023-08-18 09:03:21 +01:00
|
|
|
|
2024-11-11 16:53:37 +00:00
|
|
|
const { features, matrix_rtc_session: matrixRtcSessionConfig } = Config.get();
|
2024-07-05 21:10:16 +09:00
|
|
|
const useDeviceSessionMemberEvents =
|
2024-11-11 16:53:37 +00:00
|
|
|
features?.feature_use_device_session_member_events;
|
2025-09-25 13:02:43 +01:00
|
|
|
const { sendNotificationType: notificationType, callIntent } = getUrlParams();
|
2025-10-03 14:43:22 -04:00
|
|
|
// Multi-sfu does not need a preferred foci list. just the focus that is actually used.
|
2025-09-30 16:47:45 +02:00
|
|
|
rtcSession.joinRoomSession(
|
2025-10-03 14:43:22 -04:00
|
|
|
useMultiSfu ? [] : [transport],
|
|
|
|
|
useMultiSfu ? transport : undefined,
|
2025-09-30 16:47:45 +02:00
|
|
|
{
|
|
|
|
|
notificationType,
|
|
|
|
|
callIntent,
|
|
|
|
|
manageMediaKeys: encryptMedia,
|
|
|
|
|
...(useDeviceSessionMemberEvents !== undefined && {
|
|
|
|
|
useLegacyMemberEvents: !useDeviceSessionMemberEvents,
|
|
|
|
|
}),
|
|
|
|
|
delayedLeaveEventRestartMs:
|
|
|
|
|
matrixRtcSessionConfig?.delayed_leave_event_restart_ms,
|
|
|
|
|
delayedLeaveEventDelayMs:
|
|
|
|
|
matrixRtcSessionConfig?.delayed_leave_event_delay_ms,
|
|
|
|
|
delayedLeaveEventRestartLocalTimeoutMs:
|
|
|
|
|
matrixRtcSessionConfig?.delayed_leave_event_restart_local_timeout_ms,
|
|
|
|
|
networkErrorRetryMs: matrixRtcSessionConfig?.network_error_retry_ms,
|
|
|
|
|
makeKeyDelay: matrixRtcSessionConfig?.wait_for_key_rotation_ms,
|
|
|
|
|
membershipEventExpiryMs:
|
|
|
|
|
matrixRtcSessionConfig?.membership_event_expiry_ms,
|
2025-10-22 14:13:31 +02:00
|
|
|
useExperimentalToDeviceTransport: true,
|
2025-10-22 12:53:22 +02:00
|
|
|
unstableSendStickyEvents: preferStickyEvents,
|
2025-09-30 16:47:45 +02:00
|
|
|
},
|
|
|
|
|
);
|
2025-03-05 09:25:52 -05:00
|
|
|
if (widget) {
|
|
|
|
|
try {
|
|
|
|
|
await widget.api.transport.send(ElementWidgetActions.JoinCall, {});
|
|
|
|
|
} catch (e) {
|
|
|
|
|
logger.error("Failed to send join action", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-18 09:03:21 +01:00
|
|
|
}
|