[SDK] Let developers override the whole transport

The developer setting that overrode the LiveKit service URL becomes a
JSON transport, so that a `url`-only or combined transport can be tested
against a homeserver that does not advertise one yet. The settings modal
shows each LiveKit room's role instead of a never-populated isLocal flag.

On the SDK the participation options take a whole Transport for the override and the fallback instead of two LiveKit service URLs, which also takes the last LiveKit knowledge out of transport discovery.

(cherry picked from commit d3eb40db0bcf7370418c4948049b3eac176bbdc1)
This commit is contained in:
Robin
2026-10-13 13:00:58 +02:00
committed by Timo K.
parent b4bd1f81a0
commit 4aa249daf5
4 changed files with 42 additions and 54 deletions
+10 -7
View File
@@ -110,10 +110,13 @@ export interface RTCParticipationOptions {
timings?: Partial<SessionTimings>; timings?: Partial<SessionTimings>;
/** Limits on what is published; LiveKit's defaults otherwise. */ /** Limits on what is published; LiveKit's defaults otherwise. */
mediaQuality?: MediaQuality; mediaQuality?: MediaQuality;
/** Use this transport instead of asking the homeserver. */ /**
transportUrl?: string; * Use this transport instead of asking the homeserver. Any shape the
/** Use this transport when the homeserver advertises none. */ * backend serves: for LiveKit a `url`, a `livekit_service_url` or both.
fallbackTransportUrl?: string; */
transport?: Transport;
/** Use this transport when the homeserver advertises none of the backend's type. */
fallbackTransport?: Transport;
/** /**
* What carries the media. LiveKit, configured from the options above, when * What carries the media. LiveKit, configured from the options above, when
* left out; see `createLivekitBackend` for building one with other options. * left out; see `createLivekitBackend` for building one with other options.
@@ -265,9 +268,6 @@ export interface RTCMember {
* against `backendIdentities`, not this. * against `backendIdentities`, not this.
*/ */
rtcBackendIdentity: string; rtcBackendIdentity: string;
userId: string;
deviceId: string;
/** The membership's own id; what the Matrix 2.0 identity is derived from. */
/** /**
* Every identity this member may publish under on its transport: the * Every identity this member may publish under on its transport: the
* participant identity the SFU sees, and the sender of its data packets. * participant identity the SFU sees, and the sender of its data packets.
@@ -277,6 +277,9 @@ export interface RTCMember {
* Opaque to a host. * Opaque to a host.
*/ */
backendIdentities: readonly string[]; backendIdentities: readonly string[];
userId: string;
deviceId: string;
/** The membership's own id; what the Matrix 2.0 identity is derived from. */
memberId: string; memberId: string;
/** Which transport this member is on; undefined when the membership has none. */ /** Which transport this member is on; undefined when the membership has none. */
transport$: Behavior<TransportMetadata | undefined>; transport$: Behavior<TransportMetadata | undefined>;
+11 -15
View File
@@ -16,28 +16,24 @@ import { TransportAutoDiscovery } from "./TransportAutoDiscovery";
/** /**
* The transport the local member advertises: the one the host names, else the * The transport the local member advertises: the one the host names, else the
* homeserver's first of the type the backend serves, else the host's fallback. * homeserver's first of the type the backend serves, else the host's fallback.
* The two URL options are LiveKit service URLs, and turning one into a
* membership transport is the one piece of LiveKit knowledge outside the
* backend. It stays here, for hosts that configure a URL rather than a
* transport, and goes once the options take a `Transport`.
*/ */
export async function discoverLocalTransport( export async function discoverLocalTransport(
client: Pick<MatrixClient, "getDomain" | "_unstable_getRTCTransports">, client: Pick<MatrixClient, "getDomain" | "_unstable_getRTCTransports">,
transportType: string, transportType: string,
{ {
transportUrl, transport: override,
fallbackTransportUrl, fallbackTransport,
}: Pick<RTCParticipationOptions, "transportUrl" | "fallbackTransportUrl">, }: Pick<RTCParticipationOptions, "transport" | "fallbackTransport">,
logger: Logger, logger: Logger,
): Promise<Transport> { ): Promise<Transport> {
const transport = transportUrl const transport =
? { type: "livekit", livekit_service_url: transportUrl } override ??
: await new TransportAutoDiscovery({ (await new TransportAutoDiscovery({
client, client,
transportType, transportType,
fallbackTransportUrl, fallbackTransport,
logger, logger,
}).discoverPreferredTransport(); }).discoverPreferredTransport());
if (transport === null) if (transport === null)
throw new MatrixRTCTransportMissingError(client.getDomain() ?? ""); throw new MatrixRTCTransportMissingError(client.getDomain() ?? "");
return transport; return transport;
@@ -15,10 +15,7 @@ import {
} from "vitest"; } from "vitest";
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 Transport } from "matrix-js-sdk/lib/matrixrtc";
type LivekitTransport,
type Transport,
} from "matrix-js-sdk/lib/matrixrtc";
import { import {
TransportAutoDiscovery, TransportAutoDiscovery,
@@ -27,12 +24,12 @@ import {
type DiscoveryClient = TransportAutoDiscoveryProps["client"]; type DiscoveryClient = TransportAutoDiscoveryProps["client"];
const backendTransport: LivekitTransport = { const backendTransport: Transport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://backend.example.org", livekit_service_url: "https://backend.example.org",
}; };
const configTransport: LivekitTransport = { const configTransport: Transport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://config.example.org", livekit_service_url: "https://config.example.org",
}; };
@@ -65,7 +62,7 @@ describe("TransportAutoDiscovery", () => {
const discovery = new TransportAutoDiscovery({ const discovery = new TransportAutoDiscovery({
client, client,
transportType: "livekit", transportType: "livekit",
fallbackTransportUrl: configTransport.livekit_service_url, fallbackTransport: configTransport,
logger: rootLogger, logger: rootLogger,
}); });
@@ -96,7 +93,7 @@ describe("TransportAutoDiscovery", () => {
const discovery = new TransportAutoDiscovery({ const discovery = new TransportAutoDiscovery({
client, client,
transportType: "livekit", transportType: "livekit",
fallbackTransportUrl: "https://config.example.org", fallbackTransport: configTransport,
logger: rootLogger, logger: rootLogger,
}); });
@@ -120,7 +117,7 @@ describe("TransportAutoDiscovery", () => {
const discovery = new TransportAutoDiscovery({ const discovery = new TransportAutoDiscovery({
client, client,
transportType: "livekit", transportType: "livekit",
fallbackTransportUrl: configTransport.livekit_service_url, fallbackTransport: configTransport,
logger: rootLogger, logger: rootLogger,
}); });
@@ -137,7 +134,7 @@ describe("TransportAutoDiscovery", () => {
const discovery = new TransportAutoDiscovery({ const discovery = new TransportAutoDiscovery({
client, client,
transportType: "livekit", transportType: "livekit",
fallbackTransportUrl: undefined, fallbackTransport: undefined,
logger: rootLogger, logger: rootLogger,
}); });
+14 -22
View File
@@ -19,26 +19,26 @@ export interface TransportAutoDiscoveryProps {
client: TransportDiscoveryClient; client: TransportDiscoveryClient;
/** The transport type the media backend serves. */ /** The transport type the media backend serves. */
transportType: string; transportType: string;
/** A LiveKit service URL to use when the homeserver advertises none. */ /** A transport to use when the homeserver advertises none of the type. */
fallbackTransportUrl?: string; fallbackTransport?: Transport;
logger: Logger; logger: Logger;
} }
export class TransportAutoDiscovery { export class TransportAutoDiscovery {
private readonly client: TransportDiscoveryClient; private readonly client: TransportDiscoveryClient;
private readonly transportType: string; private readonly transportType: string;
private readonly fallbackTransportUrl: string | undefined; private readonly fallbackTransport: Transport | undefined;
private readonly logger: Logger; private readonly logger: Logger;
public constructor({ public constructor({
client, client,
transportType, transportType,
fallbackTransportUrl, fallbackTransport,
logger, logger,
}: TransportAutoDiscoveryProps) { }: TransportAutoDiscoveryProps) {
this.client = client; this.client = client;
this.transportType = transportType; this.transportType = transportType;
this.fallbackTransportUrl = fallbackTransportUrl; this.fallbackTransport = fallbackTransport;
this.logger = logger.getChild("[TransportAutoDiscovery]"); this.logger = logger.getChild("[TransportAutoDiscovery]");
} }
@@ -46,15 +46,18 @@ export class TransportAutoDiscovery {
// 1) backend transports // 1) backend transports
const backendTransport = await this.tryBackendTransports(); const backendTransport = await this.tryBackendTransports();
if (backendTransport) { if (backendTransport) {
this.logger.info("Found backend transport:", backendTransport); this.logger.info(
`Found backend transport: ${JSON.stringify(backendTransport)}`,
);
return backendTransport; return backendTransport;
} }
// 2) app config URL // 2) the host's fallback
const configTransport = this.tryConfigTransport(); if (this.fallbackTransport) {
if (configTransport) { this.logger.info(
this.logger.info("Found app config transport:", configTransport); `Found fallback transport: ${JSON.stringify(this.fallbackTransport)}`,
return configTransport; );
return this.fallbackTransport;
} }
return null; return null;
@@ -89,15 +92,4 @@ export class TransportAutoDiscovery {
} }
return null; return null;
} }
private tryConfigTransport(): Transport | null {
const url = this.fallbackTransportUrl;
if (url) {
return {
type: "livekit",
livekit_service_url: url,
};
}
return null;
}
} }