Merge pull request #4307 from element-hq/update-js-sdk

Adapt to matrix-js-sdk removal of livekit_alias and oldest_membership
This commit is contained in:
Robin
2026-09-30 10:41:37 +02:00
committed by GitHub
19 changed files with 101 additions and 156 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ homeserver does not support sticky events. This is the default.
MSC4354 and all clients on v0.17.0 or later. The local membership requests its MSC4354 and all clients on v0.17.0 or later. The local membership requests its
token from the Matrix 2.0 JWT endpoint of the token from the Matrix 2.0 JWT endpoint of the
[MatrixRTC Authorization Service](https://github.com/element-hq/lk-jwt-service) [MatrixRTC Authorization Service](https://github.com/element-hq/lk-jwt-service)
and identifies the room by a hashed identity instead of a `livekit_alias`. and identifies the room by a hashed identity instead of the Matrix `room_id`.
(Remote memberships always try the new endpoint first and fall back to the (Remote memberships always try the new endpoint first and fall back to the
legacy one, so remote participants can be on either.) legacy one, so remote participants can be on either.)
+1 -1
View File
@@ -103,7 +103,7 @@
"livekit-client": "^2.18.1", "livekit-client": "^2.18.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"loglevel": "^1.9.1", "loglevel": "^1.9.1",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#robin/remove-oldest-membership",
"matrix-widget-api": "^1.18.0", "matrix-widget-api": "^1.18.0",
"node-stdlib-browser": "^1.3.1", "node-stdlib-browser": "^1.3.1",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
+18 -18
View File
@@ -191,8 +191,8 @@ importers:
specifier: ^1.9.1 specifier: ^1.9.1
version: 1.9.2 version: 1.9.2
matrix-js-sdk: matrix-js-sdk:
specifier: github:matrix-org/matrix-js-sdk#develop specifier: github:matrix-org/matrix-js-sdk#robin/remove-oldest-membership
version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/24929be0e741be6a5d0a7226f1c682e245263b8a version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6defa47adae28feb7e3906dde25089e0dce42ece
matrix-widget-api: matrix-widget-api:
specifier: ^1.18.0 specifier: ^1.18.0
version: 1.19.0 version: 1.19.0
@@ -1194,8 +1194,8 @@ packages:
'@types/dom-mediacapture-transform': ^0.1.9 '@types/dom-mediacapture-transform': ^0.1.9
livekit-client: ^1.12.0 || ^2.1.0 livekit-client: ^1.12.0 || ^2.1.0
'@matrix-org/matrix-sdk-crypto-wasm@18.5.0': '@matrix-org/matrix-sdk-crypto-wasm@18.9.0':
resolution: {integrity: sha512-E826Hy1rG26LanPjtSsOiVRcVoHfSgPgj2r2Xsb5RPScpaKi9XJADQ0u3dNjRCitZPX4oyLNl1FDd5AfDlmIwQ==} resolution: {integrity: sha512-NtLzNDTEfNASBjNCTGZHuk7N0jrQhS6Zx2CM857rCHZYYh/rpbKGQNrJ7azktm4S1P5Jy3WU3nwJTGohGw26qQ==}
engines: {node: '>= 18'} engines: {node: '>= 18'}
'@mdx-js/react@3.1.1': '@mdx-js/react@3.1.1':
@@ -3659,8 +3659,8 @@ packages:
constants-browserify@1.0.0: constants-browserify@1.0.0:
resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==}
content-type@3.0.0: content-type@3.1.1:
resolution: {integrity: sha512-AIi5H6p0xk5uknXcN3/rmhP8jgp69OfSe/JuKiQAFprJ7UGw7mwj7m4XcmDzlrnJDG+cGpphAINGdU3g3g7kDw==} resolution: {integrity: sha512-GW4qUsfFo59d0HbUibDlWv5wPz+vAAcaTWbKIuKCf0JkC7wWkSyf8f13IpXn5JkeMlB2P8iTSSIjwXljorg2vA==}
engines: {node: '>=22'} engines: {node: '>=22'}
convert-source-map@2.0.0: convert-source-map@2.0.0:
@@ -4680,9 +4680,9 @@ packages:
matrix-events-sdk@0.0.1: matrix-events-sdk@0.0.1:
resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==} resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==}
matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/24929be0e741be6a5d0a7226f1c682e245263b8a: matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6defa47adae28feb7e3906dde25089e0dce42ece:
resolution: {gitHosted: true, integrity: sha512-0EGwbXzvG88sBOxq4ZlCRwT4VpQj2xkizfyy0A3bTSK0ysb6/gXJ/o1WPFoqCjRGMDgPq4gAsk92xGtHA7EhPg==, tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/24929be0e741be6a5d0a7226f1c682e245263b8a} resolution: {gitHosted: true, integrity: sha512-jHFmI2C29HJnzs/FIrqdmQD6ytyunyy627onhrxI+xxTGSfYrWhcTUFstNiCAaMt3fc0nNi9aVvLPD3IIglF2A==, tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6defa47adae28feb7e3906dde25089e0dce42ece}
version: 42.2.0 version: 42.3.0
engines: {node: '>=22.0.0'} engines: {node: '>=22.0.0'}
matrix-widget-api@1.19.0: matrix-widget-api@1.19.0:
@@ -4895,8 +4895,8 @@ packages:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'} engines: {node: '>=10'}
p-retry@8.0.0: p-retry@8.0.1:
resolution: {integrity: sha512-kFVqH1HxOHp8LupNsOys7bSV09VYTRLxarH/mokO4Rqhk6wGi70E0jh4VzvVGXfEVNggHoHLAMWsQqHyU1Ey9A==} resolution: {integrity: sha512-NAigyu8hfvJe7MsS18mnc4is0MZtau3QMdBklaJKK23oBQb6occO3UPM3vHmTckW8KhfyjMZaUOqdTFEYliHgg==}
engines: {node: '>=22'} engines: {node: '>=22'}
p-try@2.2.0: p-try@2.2.0:
@@ -7101,7 +7101,7 @@ snapshots:
'@types/dom-mediacapture-transform': 0.1.11 '@types/dom-mediacapture-transform': 0.1.11
livekit-client: 2.22.3(@types/dom-mediacapture-record@1.0.22) livekit-client: 2.22.3(@types/dom-mediacapture-record@1.0.22)
'@matrix-org/matrix-sdk-crypto-wasm@18.5.0': {} '@matrix-org/matrix-sdk-crypto-wasm@18.9.0': {}
'@mdx-js/react@3.1.1(@types/react@19.2.18)(react@19.2.8)': '@mdx-js/react@3.1.1(@types/react@19.2.18)(react@19.2.8)':
dependencies: dependencies:
@@ -9163,7 +9163,7 @@ snapshots:
constants-browserify@1.0.0: {} constants-browserify@1.0.0: {}
content-type@3.0.0: {} content-type@3.1.1: {}
convert-source-map@2.0.0: {} convert-source-map@2.0.0: {}
@@ -10249,17 +10249,17 @@ snapshots:
matrix-events-sdk@0.0.1: {} matrix-events-sdk@0.0.1: {}
matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/24929be0e741be6a5d0a7226f1c682e245263b8a: matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6defa47adae28feb7e3906dde25089e0dce42ece:
dependencies: dependencies:
'@babel/runtime': 8.0.0 '@babel/runtime': 8.0.0
'@matrix-org/matrix-sdk-crypto-wasm': 18.5.0 '@matrix-org/matrix-sdk-crypto-wasm': 18.9.0
another-json: 0.2.0 another-json: 0.2.0
bs58: 6.0.0 bs58: 6.0.0
content-type: 3.0.0 content-type: 3.1.1
loglevel: 1.9.2 loglevel: 1.9.2
matrix-events-sdk: 0.0.1 matrix-events-sdk: 0.0.1
matrix-widget-api: 1.19.0 matrix-widget-api: 1.19.0
p-retry: 8.0.0 p-retry: 8.0.1
sdp-transform: 3.0.0 sdp-transform: 3.0.0
unhomoglyph: 1.0.6 unhomoglyph: 1.0.6
@@ -10584,7 +10584,7 @@ snapshots:
dependencies: dependencies:
p-limit: 3.1.0 p-limit: 3.1.0
p-retry@8.0.0: p-retry@8.0.1:
dependencies: dependencies:
is-network-error: 1.3.2 is-network-error: 1.3.2
+2 -2
View File
@@ -43,7 +43,7 @@ import {
import { type Logger, logger as rootLogger } from "matrix-js-sdk/lib/logger"; import { type Logger, logger as rootLogger } from "matrix-js-sdk/lib/logger";
import { import {
MembershipManagerEvent, MembershipManagerEvent,
type LivekitTransportConfig, type LivekitTransport,
type MatrixRTCSession, type MatrixRTCSession,
type RTCCallIntent, type RTCCallIntent,
type RTCNotificationType, type RTCNotificationType,
@@ -624,7 +624,7 @@ export function createCallViewModel$(
), ),
muteStates, muteStates,
joinMatrixRTC: ( joinMatrixRTC: (
transport: LivekitTransportConfig, transport: LivekitTransport,
delayedLeaveTimings: ResolvedDelayedLeaveTimings, delayedLeaveTimings: ResolvedDelayedLeaveTimings,
) => { ) => {
return enterRTCSession( return enterRTCSession(
@@ -8,7 +8,7 @@ Please see LICENSE in the repository root for full details.
import { import {
Status as RTCMemberStatus, Status as RTCMemberStatus,
type LivekitTransportConfig, type LivekitTransport,
type MatrixRTCSession, type MatrixRTCSession,
} from "matrix-js-sdk/lib/matrixrtc"; } from "matrix-js-sdk/lib/matrixrtc";
import { import {
@@ -121,8 +121,7 @@ const mockedClient = {
}; };
describe("enterRTCSession", () => { describe("enterRTCSession", () => {
const transport: LivekitTransportConfig = { const transport: LivekitTransport = {
livekit_alias: "roomId",
livekit_service_url: "http://my-livekit-service-url.com", livekit_service_url: "http://my-livekit-service-url.com",
type: "livekit", type: "livekit",
}; };
@@ -157,8 +156,7 @@ describe("enterRTCSession", () => {
memberId: "@alice:example.org:DEVICE", memberId: "@alice:example.org:DEVICE",
userId: "@alice:example.org", userId: "@alice:example.org",
}, },
[], [transport],
transport,
expect.objectContaining({ manageMediaKeys: true }), expect.objectContaining({ manageMediaKeys: true }),
); );
}); });
@@ -178,7 +176,6 @@ describe("enterRTCSession", () => {
expect(mockedSession.joinRTCSession).toHaveBeenLastCalledWith( expect(mockedSession.joinRTCSession).toHaveBeenLastCalledWith(
expect.any(Object), expect.any(Object),
[],
expect.any(Object), expect.any(Object),
expect.objectContaining({ expect.objectContaining({
keyRotationParticipantLimit: 50, keyRotationParticipantLimit: 50,
@@ -190,7 +187,6 @@ describe("enterRTCSession", () => {
enterRTCSession(mockedSession, ownMemberMock, transport, options); enterRTCSession(mockedSession, ownMemberMock, transport, options);
expect(mockedSession.joinRTCSession).toHaveBeenLastCalledWith( expect(mockedSession.joinRTCSession).toHaveBeenLastCalledWith(
expect.anything(),
expect.anything(), expect.anything(),
expect.anything(), expect.anything(),
expect.objectContaining({ expect.objectContaining({
@@ -256,7 +252,7 @@ describe("LocalMembership", () => {
it("throws error on missing RTC config error", () => { it("throws error on missing RTC config error", () => {
withTestScheduler(({ scope, hot, expectObservable }) => { withTestScheduler(({ scope, hot, expectObservable }) => {
const localTransport$ = scope.behavior<null | LivekitTransportConfig>( const localTransport$ = scope.behavior<null | LivekitTransport>(
hot("1ms #", {}, new MatrixRTCTransportMissingError("domain.com")), hot("1ms #", {}, new MatrixRTCTransportMissingError("domain.com")),
null, null,
); );
@@ -321,7 +317,7 @@ describe("LocalMembership", () => {
const mockTransportConfig = { const mockTransportConfig = {
livekit_service_url: "a", livekit_service_url: "a",
} as LivekitTransportConfig; } as LivekitTransport;
const mockTransport = { const mockTransport = {
transport: mockTransportConfig, transport: mockTransportConfig,
@@ -19,7 +19,6 @@ import { type MatrixClient } from "matrix-js-sdk";
import { import {
Status as RTCSessionStatus, Status as RTCSessionStatus,
type LivekitTransport, type LivekitTransport,
type LivekitTransportConfig,
type MatrixRTCSession, type MatrixRTCSession,
type RTCCallIntent, type RTCCallIntent,
type RTCNotificationType, type RTCNotificationType,
@@ -144,7 +143,7 @@ interface Props {
connectionManager: IConnectionManager; connectionManager: IConnectionManager;
createPublisherFactory: (connection: Connection) => Publisher; createPublisherFactory: (connection: Connection) => Publisher;
joinMatrixRTC: ( joinMatrixRTC: (
transport: LivekitTransportConfig, transport: LivekitTransport,
delayedLeaveTimings: ResolvedDelayedLeaveTimings, delayedLeaveTimings: ResolvedDelayedLeaveTimings,
) => void; ) => void;
homeserverConnected: HomeserverConnected; homeserverConnected: HomeserverConnected;
@@ -966,7 +965,7 @@ interface EnterRTCSessionOptions {
export function enterRTCSession( export function enterRTCSession(
rtcSession: MatrixRTCSession, rtcSession: MatrixRTCSession,
ownMembershipIdentity: CallMembershipIdentityParts, ownMembershipIdentity: CallMembershipIdentityParts,
transport: LivekitTransportConfig, transport: LivekitTransport,
{ {
encryptMedia, encryptMedia,
matrixRTCMode, matrixRTCMode,
@@ -988,18 +987,6 @@ export function enterRTCSession(
} = Config.get(); } = Config.get();
const retryInterval = sessionConfig.network_error_retry_ms; const retryInterval = sessionConfig.network_error_retry_ms;
// For backwards compatibility with Element Call versions that do not do Matrix 2.0,
// we add the livekit alias to the transport.
let backwardCompatibleTransport: LivekitTransport | LivekitTransportConfig;
if (matrixRTCMode === MatrixRTCMode.Matrix_2_0) {
backwardCompatibleTransport = transport;
} else {
backwardCompatibleTransport = {
livekit_alias: rtcSession.room.roomId,
...transport,
};
}
// Set maximumNetworkErrorRetryCount such that we will consider the client // Set maximumNetworkErrorRetryCount such that we will consider the client
// disconnected as soon as either it fails to sync for longer than the grace // disconnected as soon as either it fails to sync for longer than the grace
// period, or it is likely that a delayed leave event has been sent. // period, or it is likely that a delayed leave event has been sent.
@@ -1011,11 +998,7 @@ export function enterRTCSession(
// Multi-sfu does not need a preferred foci list. just the focus that is actually used. // Multi-sfu does not need a preferred foci list. just the focus that is actually used.
// TODO where/how do we track errors originating from the ongoing rtcSession? // TODO where/how do we track errors originating from the ongoing rtcSession?
rtcSession.joinRTCSession( rtcSession.joinRTCSession(ownMembershipIdentity, [transport], {
ownMembershipIdentity,
[],
backwardCompatibleTransport,
{
notificationType, notificationType,
callIntent, callIntent,
manageMediaKeys: encryptMedia, manageMediaKeys: encryptMedia,
@@ -1029,6 +1012,5 @@ export function enterRTCSession(
keyRotationParticipantLimit: sessionConfig.key_rotation_participant_limit, keyRotationParticipantLimit: sessionConfig.key_rotation_participant_limit,
unstableSendStickyEvents: matrixRTCMode === MatrixRTCMode.Matrix_2_0, unstableSendStickyEvents: matrixRTCMode === MatrixRTCMode.Matrix_2_0,
maximumNetworkErrorRetryCount: maximumNetworkErrorRetryCount, maximumNetworkErrorRetryCount: maximumNetworkErrorRetryCount,
}, });
);
} }
@@ -5,7 +5,7 @@ SPDX-License-IdFentifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details. Please see LICENSE in the repository root for full details.
*/ */
import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
import { type MatrixClient } from "matrix-js-sdk"; import { type MatrixClient } from "matrix-js-sdk";
import { logger as rootLogger, type Logger } from "matrix-js-sdk/lib/logger"; import { logger as rootLogger, type Logger } from "matrix-js-sdk/lib/logger";
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager"; import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
@@ -34,34 +34,13 @@ interface Props {
matrixRTCMode: MatrixRTCMode; matrixRTCMode: MatrixRTCMode;
} }
// TODO livekit_alias-cleanup
// 1. We need to move away from transports map to connections!!!
//
// 2. We need to stop sending livekit_alias all together
//
//
// 1.
// Transports are just the jwt service adress but do not contain the information which room on this transport to use.
// That requires slot and roomId.
//
// We need one connection per room on the transport.
//
// We need an object that contains:
// transport
// roomId
// slotId
//
// To map to the connections. Prosposal: `ConnectionIdentifier`
//
// 2.
// We need to make sure we do not sent livekit_alias in sticky events and that we drop all code for sending state events!
export interface LocalTransport { export interface LocalTransport {
transport: LivekitTransportConfig; transport: LivekitTransport;
sfuConfig: SFUConfig; sfuConfig: SFUConfig;
} }
export function isLocalTransport( export function isLocalTransport(
obj: LivekitTransportConfig | LocalTransport, obj: LivekitTransport | LocalTransport,
): obj is LocalTransport { ): obj is LocalTransport {
return "transport" in obj && "sfuConfig" in obj; return "transport" in obj && "sfuConfig" in obj;
} }
@@ -90,7 +69,7 @@ export async function getLocalTransport({
const customUrl = customLivekitUrl.value$.value; const customUrl = customLivekitUrl.value$.value;
// Respect the user's custom URL, if set // Respect the user's custom URL, if set
const transport: LivekitTransportConfig | null = customUrl const transport: LivekitTransport | null = customUrl
? { type: "livekit", livekit_service_url: customUrl } ? { type: "livekit", livekit_service_url: customUrl }
: await discovery.discoverPreferredTransport(); : await discovery.discoverPreferredTransport();
@@ -131,7 +110,7 @@ export async function getLocalTransport({
* @throws FailToGetOpenIdToken, NoMatrix2AuthorizationService * @throws FailToGetOpenIdToken, NoMatrix2AuthorizationService
*/ */
async function doOpenIdAndJWTFromUrl( async function doOpenIdAndJWTFromUrl(
transport: LivekitTransportConfig, transport: LivekitTransport,
matrixRTCMode: MatrixRTCMode, matrixRTCMode: MatrixRTCMode,
membership: CallMembershipIdentityParts, membership: CallMembershipIdentityParts,
roomId: string, roomId: string,
@@ -16,7 +16,7 @@ import {
import { MatrixError } from "matrix-js-sdk"; import { MatrixError } from "matrix-js-sdk";
import { logger as rootLogger } from "matrix-js-sdk/lib/logger"; import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
import { import {
type LivekitTransportConfig, type LivekitTransport,
type Transport, type Transport,
} from "matrix-js-sdk/lib/matrixrtc"; } from "matrix-js-sdk/lib/matrixrtc";
@@ -28,12 +28,12 @@ import {
type DiscoveryClient = RtcTransportAutoDiscoveryProps["client"]; type DiscoveryClient = RtcTransportAutoDiscoveryProps["client"];
const backendTransport: LivekitTransportConfig = { const backendTransport: LivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://backend.example.org", livekit_service_url: "https://backend.example.org",
}; };
const configTransport: LivekitTransportConfig = { const configTransport: LivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://config.example.org", livekit_service_url: "https://config.example.org",
}; };
@@ -5,8 +5,8 @@ SPDX-License-IdFentifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details. Please see LICENSE in the repository root for full details.
*/ */
import { import {
isLivekitTransportConfig, isLivekitTransport,
type LivekitTransportConfig, type LivekitTransport,
} from "matrix-js-sdk/lib/matrixrtc"; } from "matrix-js-sdk/lib/matrixrtc";
import { type MatrixClient } from "matrix-js-sdk"; import { type MatrixClient } from "matrix-js-sdk";
import { type Logger } from "matrix-js-sdk/lib/logger"; import { type Logger } from "matrix-js-sdk/lib/logger";
@@ -40,7 +40,7 @@ export class RtcTransportAutoDiscovery {
this.logger = logger.getChild("[RtcTransportAutoDiscovery]"); this.logger = logger.getChild("[RtcTransportAutoDiscovery]");
} }
public async discoverPreferredTransport(): Promise<LivekitTransportConfig | null> { public async discoverPreferredTransport(): Promise<LivekitTransport | null> {
// 1) backend transports // 1) backend transports
const backendTransport = await this.tryBackendTransports(); const backendTransport = await this.tryBackendTransports();
if (backendTransport) { if (backendTransport) {
@@ -67,7 +67,7 @@ export class RtcTransportAutoDiscovery {
* This will not throw errors, but instead just log them and return null if the expected config is not found or malformed. * This will not throw errors, but instead just log them and return null if the expected config is not found or malformed.
* @private * @private
*/ */
private async tryBackendTransports(): Promise<LivekitTransportConfig | null> { private async tryBackendTransports(): Promise<LivekitTransport | null> {
const client = this.client; const client = this.client;
// MSC4143: Attempt to fetch transports from backend. // MSC4143: Attempt to fetch transports from backend.
this.logger.info("First try to use getRTCTransports end point ..."); this.logger.info("First try to use getRTCTransports end point ...");
@@ -75,7 +75,7 @@ export class RtcTransportAutoDiscovery {
const transportList = await doNetworkOperationWithRetry(async () => const transportList = await doNetworkOperationWithRetry(async () =>
client._unstable_getRTCTransports(), client._unstable_getRTCTransports(),
); );
const first = transportList.find(isLivekitTransportConfig); const first = transportList.find(isLivekitTransport);
if (first) { if (first) {
return first; return first;
} else { } else {
@@ -90,7 +90,7 @@ export class RtcTransportAutoDiscovery {
return null; return null;
} }
private tryConfigTransport(): LivekitTransportConfig | null { private tryConfigTransport(): LivekitTransport | null {
const url = this.resolvedConfig.livekit?.livekit_service_url; const url = this.resolvedConfig.livekit?.livekit_service_url;
if (url) { if (url) {
return { return {
@@ -30,7 +30,7 @@ import fetchMock from "fetch-mock";
import EventEmitter from "events"; import EventEmitter from "events";
import { type IOpenIDToken } from "matrix-js-sdk"; import { type IOpenIDToken } from "matrix-js-sdk";
import { logger, type Logger } from "matrix-js-sdk/lib/logger"; import { logger, type Logger } from "matrix-js-sdk/lib/logger";
import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
import { import {
Connection, Connection,
@@ -57,7 +57,7 @@ let fakeLocalParticipant: MockedObject<LocalParticipant>;
const ROOM_ID = "!roomID:example.org"; const ROOM_ID = "!roomID:example.org";
const livekitFocus: LivekitTransportConfig = { const livekitFocus: LivekitTransport = {
livekit_service_url: "https://matrix-rtc.example.org/livekit/jwt", livekit_service_url: "https://matrix-rtc.example.org/livekit/jwt",
type: "livekit", type: "livekit",
}; };
@@ -228,9 +228,7 @@ describe("Start connection states", () => {
capturedState = capturedStates.pop(); capturedState = capturedStates.pop();
if (capturedState instanceof Error) { if (capturedState instanceof Error) {
expect(capturedState.message).toEqual("Something went wrong"); expect(capturedState.message).toEqual("Something went wrong");
expect(connection.transport.livekit_alias).toEqual( expect(connection.transport).toEqual(livekitFocus);
livekitFocus.livekit_alias,
);
} else { } else {
expect.fail("Expected FailedToStart state but got " + capturedState); expect.fail("Expected FailedToStart state but got " + capturedState);
} }
@@ -290,9 +288,7 @@ describe("Start connection states", () => {
expect(capturedState.cause.message).toContain( expect(capturedState.cause.message).toContain(
"Failed to look up user info from homeserver", "Failed to look up user info from homeserver",
); );
expect(connection.transport.livekit_alias).toEqual( expect(connection.transport).toEqual(livekitFocus);
livekitFocus.livekit_alias,
);
} else { } else {
expect.fail("Expected FailedToStart state but got " + capturedState); expect.fail("Expected FailedToStart state but got " + capturedState);
} }
@@ -357,9 +353,7 @@ describe("Start connection states", () => {
expect(capturedState.cause.message).toContain( expect(capturedState.cause.message).toContain(
"Failed to connect to livekit", "Failed to connect to livekit",
); );
expect(connection.transport.livekit_alias).toEqual( expect(connection.transport).toEqual(livekitFocus);
livekitFocus.livekit_alias,
);
} else { } else {
expect.fail( expect.fail(
"Expected FailedToStart state but got " + JSON.stringify(capturedState), "Expected FailedToStart state but got " + JSON.stringify(capturedState),
@@ -22,7 +22,7 @@ import {
type Track, type Track,
type TrackPublication, type TrackPublication,
} from "livekit-client"; } from "livekit-client";
import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
import { BehaviorSubject, map } from "rxjs"; import { BehaviorSubject, map } from "rxjs";
import { type Logger } from "matrix-js-sdk/lib/logger"; import { type Logger } from "matrix-js-sdk/lib/logger";
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager"; import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
@@ -52,7 +52,7 @@ export interface ConnectionOpts {
/** The identity parts to use on this connection */ /** The identity parts to use on this connection */
ownMembershipIdentity: CallMembershipIdentityParts; ownMembershipIdentity: CallMembershipIdentityParts;
/** The media transport to connect to. */ /** The media transport to connect to. */
transport: LivekitTransportConfig; transport: LivekitTransport;
/** The Matrix client to use for OpenID and SFU config requests. */ /** The Matrix client to use for OpenID and SFU config requests. */
client: OpenIDClientParts; client: OpenIDClientParts;
/** The room ID this connection is associated with. */ /** The room ID this connection is associated with. */
@@ -107,7 +107,7 @@ export class Connection {
/** /**
* The media transport to connect to. * The media transport to connect to.
*/ */
public readonly transport: LivekitTransportConfig; public readonly transport: LivekitTransport;
public readonly livekitRoom: LivekitRoom; public readonly livekitRoom: LivekitRoom;
@@ -16,7 +16,7 @@ import { logger, type Logger } from "matrix-js-sdk/lib/logger";
// imported as inline to support worker when loaded from a cdn (cross domain) // imported as inline to support worker when loaded from a cdn (cross domain)
import E2EEWorker from "livekit-client/e2ee-worker?worker&inline"; import E2EEWorker from "livekit-client/e2ee-worker?worker&inline";
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager"; import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
import { type ObservableScope } from "../../ObservableScope.ts"; import { type ObservableScope } from "../../ObservableScope.ts";
import { Connection } from "./Connection.ts"; import { Connection } from "./Connection.ts";
@@ -44,7 +44,7 @@ import {
export interface ConnectionFactory { export interface ConnectionFactory {
createConnection( createConnection(
scope: ObservableScope, scope: ObservableScope,
transport: LivekitTransportConfig, transport: LivekitTransport,
ownMembershipIdentity: CallMembershipIdentityParts, ownMembershipIdentity: CallMembershipIdentityParts,
logger: Logger, logger: Logger,
sfuConfig?: SFUConfig, sfuConfig?: SFUConfig,
@@ -103,7 +103,7 @@ export class ECConnectionFactory implements ConnectionFactory {
*/ */
public createConnection( public createConnection(
scope: ObservableScope, scope: ObservableScope,
transport: LivekitTransportConfig, transport: LivekitTransport,
ownMembershipIdentity: CallMembershipIdentityParts, ownMembershipIdentity: CallMembershipIdentityParts,
logger: Logger, logger: Logger,
sfuConfig?: SFUConfig, sfuConfig?: SFUConfig,
@@ -7,7 +7,7 @@ Please see LICENSE in the repository root for full details.
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { BehaviorSubject, NEVER } from "rxjs"; import { BehaviorSubject, NEVER } from "rxjs";
import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
import { type RemoteParticipant } from "livekit-client"; import { type RemoteParticipant } from "livekit-client";
import { logger } from "matrix-js-sdk/lib/logger"; import { logger } from "matrix-js-sdk/lib/logger";
@@ -24,12 +24,12 @@ import { type Behavior } from "../../Behavior.ts";
// Some test constants // Some test constants
const TRANSPORT_1: LivekitTransportConfig = { const TRANSPORT_1: LivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://lk.example.org", livekit_service_url: "https://lk.example.org",
}; };
const TRANSPORT_2: LivekitTransportConfig = { const TRANSPORT_2: LivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://lk.sample.com", livekit_service_url: "https://lk.sample.com",
}; };
@@ -47,7 +47,7 @@ beforeEach(() => {
vi.mocked(fakeConnectionFactory).createConnection = vi vi.mocked(fakeConnectionFactory).createConnection = vi
.fn() .fn()
.mockImplementation( .mockImplementation(
(scope: ObservableScope, transport: LivekitTransportConfig) => { (scope: ObservableScope, transport: LivekitTransport) => {
const mockConnection = { const mockConnection = {
transport, transport,
remoteParticipants$: new BehaviorSubject([]), remoteParticipants$: new BehaviorSubject([]),
@@ -225,7 +225,7 @@ describe("connectionManagerData$ stream", () => {
// Used in test to control fake connections' remoteParticipants$ streams // Used in test to control fake connections' remoteParticipants$ streams
let fakeRemoteParticipantsStreams: Map<string, Behavior<RemoteParticipant[]>>; let fakeRemoteParticipantsStreams: Map<string, Behavior<RemoteParticipant[]>>;
function keyForTransport(transport: LivekitTransportConfig): string { function keyForTransport(transport: LivekitTransport): string {
return `${transport.livekit_service_url}`; return `${transport.livekit_service_url}`;
} }
@@ -233,7 +233,7 @@ describe("connectionManagerData$ stream", () => {
fakeRemoteParticipantsStreams = new Map(); fakeRemoteParticipantsStreams = new Map();
function getRemoteParticipantsFor( function getRemoteParticipantsFor(
transport: LivekitTransportConfig, transport: LivekitTransport,
): Behavior<RemoteParticipant[]> { ): Behavior<RemoteParticipant[]> {
return ( return (
fakeRemoteParticipantsStreams.get(keyForTransport(transport)) ?? fakeRemoteParticipantsStreams.get(keyForTransport(transport)) ??
@@ -245,7 +245,7 @@ describe("connectionManagerData$ stream", () => {
vi.mocked(fakeConnectionFactory).createConnection = vi vi.mocked(fakeConnectionFactory).createConnection = vi
.fn() .fn()
.mockImplementation( .mockImplementation(
(scope: ObservableScope, transport: LivekitTransportConfig) => { (scope: ObservableScope, transport: LivekitTransport) => {
const fakeRemoteParticipants$ = new BehaviorSubject< const fakeRemoteParticipants$ = new BehaviorSubject<
RemoteParticipant[] RemoteParticipant[]
>([]); >([]);
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details. Please see LICENSE in the repository root for full details.
*/ */
import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
import { import {
combineLatest, combineLatest,
map, map,
@@ -56,7 +56,7 @@ export class ConnectionManagerData {
} }
} }
private getKey(transport: LivekitTransportConfig): string { private getKey(transport: LivekitTransport): string {
// This is enough as a key because the ConnectionManager is already scoped by room. // This is enough as a key because the ConnectionManager is already scoped by room.
// We also do not need to consider the slotId at this point since each `MatrixRTCSession` is already scoped by `slotDescription: {id, application}`. // We also do not need to consider the slotId at this point since each `MatrixRTCSession` is already scoped by `slotDescription: {id, application}`.
return transport.livekit_service_url; return transport.livekit_service_url;
@@ -67,13 +67,13 @@ export class ConnectionManagerData {
} }
public getConnectionForTransport( public getConnectionForTransport(
transport: LivekitTransportConfig, transport: LivekitTransport,
): Connection | null { ): Connection | null {
return this.store.get(this.getKey(transport))?.connection ?? null; return this.store.get(this.getKey(transport))?.connection ?? null;
} }
public getParticipantsForTransport( public getParticipantsForTransport(
transport: LivekitTransportConfig, transport: LivekitTransport,
): RemoteParticipant[] { ): RemoteParticipant[] {
const key = this.getKey(transport); const key = this.getKey(transport);
const existing = this.store.get(key); const existing = this.store.get(key);
@@ -88,7 +88,7 @@ interface Props {
scope: ObservableScope; scope: ObservableScope;
connectionFactory: ConnectionFactory; connectionFactory: ConnectionFactory;
localTransport$: Observable<LocalTransport>; localTransport$: Observable<LocalTransport>;
remoteTransports$: Behavior<Epoch<LivekitTransportConfig[]>>; remoteTransports$: Behavior<Epoch<LivekitTransport[]>>;
logger: Logger; logger: Logger;
ownMembershipIdentity: CallMembershipIdentityParts; ownMembershipIdentity: CallMembershipIdentityParts;
@@ -147,7 +147,7 @@ export function createConnectionManager$({
* externally this is modified via `registerTransports()`. * externally this is modified via `registerTransports()`.
*/ */
const localAndRemoteTransports$: Behavior< const localAndRemoteTransports$: Behavior<
Epoch<(LivekitTransportConfig | LocalTransport)[]> Epoch<(LivekitTransport | LocalTransport)[]>
> = scope.behavior( > = scope.behavior(
combineLatest([localTransportAsArray$, remoteTransports$]).pipe( combineLatest([localTransportAsArray$, remoteTransports$]).pipe(
// Combine local and remote transports into one transport array // Combine local and remote transports into one transport array
@@ -271,7 +271,7 @@ export function createConnectionManager$({
return { connectionManagerData$ }; return { connectionManagerData$ };
} }
function removeDuplicateTransports<T extends LivekitTransportConfig>( function removeDuplicateTransports<T extends LivekitTransport>(
transports: T[], transports: T[],
): T[] { ): T[] {
return transports.reduce((acc, transport) => { return transports.reduce((acc, transport) => {
@@ -42,13 +42,11 @@ const fallbackMemberId = (userId: string, deviceId: string): string =>
const transportA: LivekitTransport = { const transportA: LivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://lk.example.org", livekit_service_url: "https://lk.example.org",
livekit_alias: "!alias:example.org",
}; };
const transportB: LivekitTransport = { const transportB: LivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://lk.sample.com", livekit_service_url: "https://lk.sample.com",
livekit_alias: "!alias:sample.com",
}; };
const bobMembership = mockRtcMembership("@bob:example.org", "DEV000", { const bobMembership = mockRtcMembership("@bob:example.org", "DEV000", {
@@ -126,7 +124,7 @@ function createEpochedMemberships$(m$: Observable<CallMembership[]>): {
const membershipsWithTransport$ = memberships$.pipe( const membershipsWithTransport$ = memberships$.pipe(
mapEpoch((members) => { mapEpoch((members) => {
return members.map((m) => { return members.map((m) => {
const tr = m.getTransport(m); const tr = m.getTransport();
return { return {
membership: m, membership: m,
transport: transport:
@@ -152,7 +150,7 @@ test("should signal participant on a connection that is publishing", async () =>
); );
const connection = { const connection = {
transport: bobMembership.getTransport(bobMembership), transport: bobMembership.getTransport(),
} as unknown as Connection; } as unknown as Connection;
const dataWithPublisher = new ConnectionManagerData(); const dataWithPublisher = new ConnectionManagerData();
dataWithPublisher.add(connection, [ dataWithPublisher.add(connection, [
@@ -195,7 +193,7 @@ test("should signal participant on a connection that is not publishing", async (
); );
const connection = { const connection = {
transport: bobMembership.getTransport(bobMembership), transport: bobMembership.getTransport(),
} as unknown as Connection; } as unknown as Connection;
const dataWithPublisher = new ConnectionManagerData(); const dataWithPublisher = new ConnectionManagerData();
dataWithPublisher.add(connection, []); dataWithPublisher.add(connection, []);
@@ -8,7 +8,7 @@ Please see LICENSE in the repository root for full details.
import { type LocalParticipant, type RemoteParticipant } from "livekit-client"; import { type LocalParticipant, type RemoteParticipant } from "livekit-client";
import { import {
type CallMembership, type CallMembership,
type LivekitTransportConfig, type LivekitTransport,
} from "matrix-js-sdk/lib/matrixrtc"; } from "matrix-js-sdk/lib/matrixrtc";
import { combineLatest, filter, map } from "rxjs"; import { combineLatest, filter, map } from "rxjs";
import { logger } from "matrix-js-sdk/lib/logger"; import { logger } from "matrix-js-sdk/lib/logger";
@@ -60,7 +60,7 @@ export interface RemoteMatrixLivekitMember extends MatrixLivekitMember {
interface Props { interface Props {
scope: ObservableScope; scope: ObservableScope;
membershipsWithTransport$: Behavior< membershipsWithTransport$: Behavior<
Epoch<{ membership: CallMembership; transport?: LivekitTransportConfig }[]> Epoch<{ membership: CallMembership; transport?: LivekitTransport }[]>
>; >;
connectionManager: IConnectionManager; connectionManager: IConnectionManager;
localUser: { deviceId: string; userId: string }; localUser: { deviceId: string; userId: string };
@@ -165,7 +165,7 @@ export function createRemoteMatrixLivekitMembers$({
// TODO add back in the callviewmodel pauseWhen(this.pretendToBeDisconnected$) // TODO add back in the callviewmodel pauseWhen(this.pretendToBeDisconnected$)
// TODO add this to the JS-SDK // TODO add this to the JS-SDK
export function areLivekitTransportsEqual<T extends LivekitTransportConfig>( export function areLivekitTransportsEqual<T extends LivekitTransport>(
t1: T | null, t1: T | null,
t2: T | null, t2: T | null,
): boolean { ): boolean {
@@ -10,7 +10,7 @@ import { BehaviorSubject, NEVER } from "rxjs";
import { type Room as LivekitRoom } from "livekit-client"; import { type Room as LivekitRoom } from "livekit-client";
import EventEmitter from "events"; import EventEmitter from "events";
import fetchMock from "fetch-mock"; import fetchMock from "fetch-mock";
import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
import { logger } from "matrix-js-sdk/lib/logger"; import { logger } from "matrix-js-sdk/lib/logger";
import { import {
@@ -149,7 +149,7 @@ test("bob, carl, then bob joining no tracks yet", () => {
a: expect.toSatisfy((co) => a: expect.toSatisfy((co) =>
areLivekitTransportsEqual( areLivekitTransportsEqual(
co.transport, co.transport,
bobMembership.transports[0]! as LivekitTransportConfig, bobMembership.transports[0]! as LivekitTransport,
), ),
), ),
}); });
@@ -186,7 +186,7 @@ test("bob, carl, then bob joining no tracks yet", () => {
expect( expect(
areLivekitTransportsEqual( areLivekitTransportsEqual(
connection.transport, connection.transport,
carlMembership.transports[0]! as LivekitTransportConfig, carlMembership.transports[0]! as LivekitTransport,
), ),
).toBe(true); ).toBe(true);
return true; return true;
@@ -216,7 +216,7 @@ test("bob, carl, then bob joining no tracks yet", () => {
expect( expect(
areLivekitTransportsEqual( areLivekitTransportsEqual(
connection.transport, connection.transport,
daveMembership.transports[0]! as LivekitTransportConfig, daveMembership.transports[0]! as LivekitTransport,
), ),
).toBe(true); ).toBe(true);
return true; return true;
+8 -11
View File
@@ -7,10 +7,10 @@ Please see LICENSE in the repository root for full details.
import { import {
type CallMembership, type CallMembership,
type LivekitTransportConfig, type LivekitTransport,
type MatrixRTCSession, type MatrixRTCSession,
MatrixRTCSessionEvent, MatrixRTCSessionEvent,
isLivekitTransportConfig, isLivekitTransport,
} from "matrix-js-sdk/lib/matrixrtc"; } from "matrix-js-sdk/lib/matrixrtc";
import { fromEvent } from "rxjs"; import { fromEvent } from "rxjs";
@@ -31,35 +31,32 @@ export const membershipsAndTransports$ = (
memberships$: Behavior<Epoch<CallMembership[]>>, memberships$: Behavior<Epoch<CallMembership[]>>,
): { ): {
membershipsWithTransport$: Behavior< membershipsWithTransport$: Behavior<
Epoch<{ membership: CallMembership; transport?: LivekitTransportConfig }[]> Epoch<{ membership: CallMembership; transport?: LivekitTransport }[]>
>; >;
transports$: Behavior<Epoch<LivekitTransportConfig[]>>; transports$: Behavior<Epoch<LivekitTransport[]>>;
} => { } => {
const membershipsWithTransport$: Behavior< const membershipsWithTransport$: Behavior<
Epoch< Epoch<
{ {
membership: CallMembership; membership: CallMembership;
transport: LivekitTransportConfig | undefined; transport: LivekitTransport | undefined;
}[] }[]
> >
> = scope.behavior( > = scope.behavior(
memberships$.pipe( memberships$.pipe(
mapEpoch((memberships) => { mapEpoch((memberships) => {
return memberships.map((membership) => { return memberships.map((membership) => {
const oldestMembership = memberships[0] ?? membership; const transport = membership.getTransport();
const transport = membership.getTransport(oldestMembership);
return { return {
membership, membership,
transport: isLivekitTransportConfig(transport) transport: isLivekitTransport(transport) ? transport : undefined,
? transport
: undefined,
}; };
}); });
}), }),
), ),
); );
const transports$: Behavior<Epoch<LivekitTransportConfig[]>> = scope.behavior( const transports$: Behavior<Epoch<LivekitTransport[]>> = scope.behavior(
membershipsWithTransport$.pipe( membershipsWithTransport$.pipe(
mapEpoch((mts) => mts.flatMap(({ transport: t }) => (t ? [t] : []))), mapEpoch((mts) => mts.flatMap(({ transport: t }) => (t ? [t] : []))),
), ),
-1
View File
@@ -212,7 +212,6 @@ export function mockEmitter<T>(): EmitterMock<T> {
export const exampleTransport: LivekitTransport = { export const exampleTransport: LivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://lk.example.org", livekit_service_url: "https://lk.example.org",
livekit_alias: "!alias:example.org",
}; };
export const exampleSfuConfig: SFUConfig = { export const exampleSfuConfig: SFUConfig = {