Use the unstable LiveKit transport type

UnstableLivekitTransport covers every shape a homeserver may advertise
while MSC4195 evolves: a LiveKit SFU `url`, a `livekit_service_url`, or
both. Switching to it means a service URL is no longer guaranteed, so the
remaining code that relies on one checks for it explicitly: delegation
probing skips transports without one, the connection map keys transports
by whichever URL they carry, and the OpenID flow rejects transports
without a service URL until the client-server API flow lands.
This commit is contained in:
Robin
2026-10-12 10:30:52 +02:00
committed by Johannes Marbach
parent d5bbb43e3a
commit 83e1396bdd
25 changed files with 157 additions and 127 deletions
+2 -2
View File
@@ -16,12 +16,12 @@ import classNames from "classnames";
import { Modal } from "./Modal"; import { Modal } from "./Modal";
import styles from "./RTCConnectionStats.module.css"; import styles from "./RTCConnectionStats.module.css";
import mediaViewStyles from "../src/tile/MediaView.module.css"; import mediaViewStyles from "../src/tile/MediaView.module.css";
import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
interface Props { interface Props {
audio?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats; audio?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats;
video?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats; video?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats;
transport?: LivekitTransport; transport?: UnstableLivekitTransport;
rtcBackendIdentity?: string; rtcBackendIdentity?: string;
} }
+2 -2
View File
@@ -19,10 +19,10 @@ import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
import { useEarpieceAudioConfig } from "../MediaDevicesContext"; import { useEarpieceAudioConfig } from "../MediaDevicesContext";
import { useReactiveState } from "../useReactiveState"; import { useReactiveState } from "../useReactiveState";
import * as controls from "../controls"; import * as controls from "../controls";
import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
export interface MatrixAudioRendererProps { export interface MatrixAudioRendererProps {
transport: LivekitTransport; transport: UnstableLivekitTransport;
livekitRoom: LivekitRoom; livekitRoom: LivekitRoom;
/** /**
* The list of participant identities to render audio for. * The list of participant identities to render audio for.
+2 -2
View File
@@ -17,7 +17,7 @@ import {
import { act } from "react"; import { act } from "react";
import { type RoomMember } from "matrix-js-sdk"; import { type RoomMember } from "matrix-js-sdk";
import { import {
type LivekitTransport, type UnstableLivekitTransport,
type CallMembership, type CallMembership,
} from "matrix-js-sdk/lib/matrixrtc"; } from "matrix-js-sdk/lib/matrixrtc";
@@ -46,7 +46,7 @@ vitest.mock("../useAudioContext");
vitest.mock("../soundUtils"); vitest.mock("../soundUtils");
vitest.mock("../rtcSessionHelpers", async (importOriginal) => ({ vitest.mock("../rtcSessionHelpers", async (importOriginal) => ({
...(await importOriginal()), ...(await importOriginal()),
makeTransport: (): [LivekitTransport] => [exampleTransport], makeTransport: (): [UnstableLivekitTransport] => [exampleTransport],
})); }));
afterEach(() => { afterEach(() => {
+6 -1
View File
@@ -699,7 +699,12 @@ export const InCallView: FC<InCallViewProps> = ({
livekitAlias: connectionItem.livekitAlias, livekitAlias: connectionItem.livekitAlias,
// TODO compute is local or tag it in the livekit room items already // TODO compute is local or tag it in the livekit room items already
isLocal: undefined, isLocal: undefined,
url: connectionItem.transport.livekit_service_url, url:
// It should be relatively clear to the user which kind of URL
// this is, based on the scheme (ws:// vs. http://)
"url" in connectionItem.transport
? connectionItem.transport.url
: connectionItem.transport.livekit_service_url,
}))} }))}
/> />
</> </>
@@ -31,7 +31,7 @@ import {
type CallMembership, type CallMembership,
type IRTCNotificationContent, type IRTCNotificationContent,
MatrixRTCSessionEvent, MatrixRTCSessionEvent,
type LivekitTransport, type UnstableLivekitTransport,
} from "matrix-js-sdk/lib/matrixrtc"; } from "matrix-js-sdk/lib/matrixrtc";
import { deepCompare } from "matrix-js-sdk/lib/utils"; import { deepCompare } from "matrix-js-sdk/lib/utils";
@@ -97,7 +97,7 @@ vi.mock(
"../state/CallViewModel/localMember/localTransport", "../state/CallViewModel/localMember/localTransport",
async (importOriginal) => ({ async (importOriginal) => ({
...(await importOriginal()), ...(await importOriginal()),
makeTransport: async (): Promise<LivekitTransport> => makeTransport: async (): Promise<UnstableLivekitTransport> =>
Promise.resolve(exampleTransport), Promise.resolve(exampleTransport),
}), }),
); );
+4 -4
View File
@@ -46,7 +46,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 LivekitTransport, type UnstableLivekitTransport,
type MatrixRTCSession, type MatrixRTCSession,
type RTCCallIntent, type RTCCallIntent,
type RTCNotificationType, type RTCNotificationType,
@@ -217,7 +217,7 @@ export interface CallViewModelOptions {
*/ */
windowSize$: Behavior<{ width: number; height: number }>; windowSize$: Behavior<{ width: number; height: number }>;
/** Optional value overriding the local transport, for testing purposes. */ /** Optional value overriding the local transport, for testing purposes. */
localTransport?: LivekitTransport; localTransport?: UnstableLivekitTransport;
/** Optional value overriding the connection factory, for testing purposes. */ /** Optional value overriding the connection factory, for testing purposes. */
connectionFactory?: ConnectionFactory; connectionFactory?: ConnectionFactory;
/** The version & compatibility mode of MatrixRTC that we should use. */ /** The version & compatibility mode of MatrixRTC that we should use. */
@@ -278,7 +278,7 @@ interface LayoutScanState {
export type LivekitRoomItem = { export type LivekitRoomItem = {
livekitRoom: LivekitRoom; livekitRoom: LivekitRoom;
participants: string[]; participants: string[];
transport: LivekitTransport; transport: UnstableLivekitTransport;
}; };
/** /**
@@ -619,7 +619,7 @@ export function createCallViewModel$(
), ),
muteStates, muteStates,
joinMatrixRTC: ( joinMatrixRTC: (
transport: LivekitTransport, transport: UnstableLivekitTransport,
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 LivekitTransport, type UnstableLivekitTransport,
type MatrixRTCSession, type MatrixRTCSession,
} from "matrix-js-sdk/lib/matrixrtc"; } from "matrix-js-sdk/lib/matrixrtc";
import { import {
@@ -127,7 +127,7 @@ const mockedClient = {
}; };
describe("enterRTCSession", () => { describe("enterRTCSession", () => {
const transport: LivekitTransport = { const transport: UnstableLivekitTransport = {
livekit_service_url: "http://my-livekit-service-url.com", livekit_service_url: "http://my-livekit-service-url.com",
type: "livekit", type: "livekit",
}; };
@@ -306,7 +306,7 @@ describe("LocalMembership", () => {
const mockTransport = { const mockTransport = {
livekit_service_url: "a", livekit_service_url: "a",
} as LivekitTransport; } as UnstableLivekitTransport;
const connectionTransportAConnected = { const connectionTransportAConnected = {
livekitRoom: mockLivekitRoom({ livekitRoom: mockLivekitRoom({
@@ -459,7 +459,7 @@ describe("LocalMembership", () => {
it("tracks livekit state correctly", async () => { it("tracks livekit state correctly", async () => {
const scope = new ObservableScope(); const scope = new ObservableScope();
const connectionMap = new ConnectionMap(); const connectionMap = new ConnectionMap();
const localTransport$ = new Subject<LivekitTransport>(); const localTransport$ = new Subject<UnstableLivekitTransport>();
const connectionMap$ = new BehaviorSubject(new Epoch(connectionMap)); const connectionMap$ = new BehaviorSubject(new Epoch(connectionMap));
const publishers: Publisher[] = []; const publishers: Publisher[] = [];
@@ -18,7 +18,7 @@ import {
import { observeParticipantEvents } from "@livekit/components-core"; import { observeParticipantEvents } from "@livekit/components-core";
import { import {
Status as RTCSessionStatus, Status as RTCSessionStatus,
type LivekitTransport, type UnstableLivekitTransport,
type MatrixRTCSession, type MatrixRTCSession,
type RTCCallIntent, type RTCCallIntent,
type RTCNotificationType, type RTCNotificationType,
@@ -141,13 +141,13 @@ interface Props {
connectionMap$: Behavior<Epoch<ConnectionMap>>; connectionMap$: Behavior<Epoch<ConnectionMap>>;
createPublisherFactory: (connection: Connection) => Publisher; createPublisherFactory: (connection: Connection) => Publisher;
joinMatrixRTC: ( joinMatrixRTC: (
transport: LivekitTransport, transport: UnstableLivekitTransport,
delayedLeaveTimings: ResolvedDelayedLeaveTimings, delayedLeaveTimings: ResolvedDelayedLeaveTimings,
) => void; ) => void;
homeserverConnected: HomeserverConnected; homeserverConnected: HomeserverConnected;
roomId: string; roomId: string;
ownMembershipIdentity: CallMembershipIdentityParts; ownMembershipIdentity: CallMembershipIdentityParts;
localTransport$: Observable<LivekitTransport>; localTransport$: Observable<UnstableLivekitTransport>;
client: ClientOpenIDParts; client: ClientOpenIDParts;
matrixRTCSession: Pick< matrixRTCSession: Pick<
MatrixRTCSession, MatrixRTCSession,
@@ -307,15 +307,16 @@ export const createLocalMembership$ = ({
const joinParams$ = scope.behavior( const joinParams$ = scope.behavior(
localTransport$.pipe( localTransport$.pipe(
switchMap(async (transport) => { switchMap(async (transport) => {
const transportSupportsDelegation = checkDelegationSupport( const transportSupportsDelegation =
"livekit_service_url" in transport &&
(await checkDelegationSupport(
transport.livekit_service_url + "/delegate_delayed_leave", transport.livekit_service_url + "/delegate_delayed_leave",
`transport ${transport.livekit_service_url}`, `transport ${transport.livekit_service_url}`,
); ));
return { return {
transport, transport,
delegationSupported: delegationSupported:
(await homeserverSupportsDelegation) || transportSupportsDelegation || (await homeserverSupportsDelegation),
(await transportSupportsDelegation),
}; };
}), }),
), ),
@@ -715,6 +716,8 @@ export const createLocalMembership$ = ({
async ([joinParams, delayId]) => { async ([joinParams, delayId]) => {
if (joinParams?.delegationSupported && delayId !== null) { if (joinParams?.delegationSupported && delayId !== null) {
try { try {
if (!("livekit_service_url" in joinParams.transport))
throw new Error("Transport has no livekit_service_url");
// This will technically cause the service to issue a new JWT token, // This will technically cause the service to issue a new JWT token,
// but it's safe to discard. We're only interested in triggering // but it's safe to discard. We're only interested in triggering
// delegation. // delegation.
@@ -731,10 +734,7 @@ export const createLocalMembership$ = ({
}); });
} catch (e) { } catch (e) {
// TODO: Surface this to the user as a service interruption? // TODO: Surface this to the user as a service interruption?
logger.error( logger.error(`Failed to delegate leave`, e);
`Failed to delegate leave to ${joinParams.transport.livekit_service_url}`,
e,
);
} }
} }
}, },
@@ -967,7 +967,7 @@ interface EnterRTCSessionOptions {
* *
* @param rtcSession - The MatrixRTCSession to join. * @param rtcSession - The MatrixRTCSession to join.
* @param ownMembershipIdentity - Options for entering the RTC session. * @param ownMembershipIdentity - Options for entering the RTC session.
* @param transport - The LivekitTransport to use for this session. * @param transport - The UnstableLivekitTransport to use for this session.
* @param options - `encryptMedia`: Whether to encrypt media. `matrixRTCMode`: The * @param options - `encryptMedia`: Whether to encrypt media. `matrixRTCMode`: The
* Matrix RTC mode to use. `delayedLeaveTimings`: The preferred timings for * Matrix RTC mode to use. `delayedLeaveTimings`: The preferred timings for
* delayed leave events. `sendNotificationType`: Whether and what kind of * delayed leave events. `sendNotificationType`: Whether and what kind of
@@ -978,7 +978,7 @@ interface EnterRTCSessionOptions {
export function enterRTCSession( export function enterRTCSession(
rtcSession: MatrixRTCSession, rtcSession: MatrixRTCSession,
ownMembershipIdentity: CallMembershipIdentityParts, ownMembershipIdentity: CallMembershipIdentityParts,
transport: LivekitTransport, transport: UnstableLivekitTransport,
{ {
encryptMedia, encryptMedia,
matrixRTCMode, matrixRTCMode,
@@ -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 LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
import { type MatrixClient } from "matrix-js-sdk"; import { type MatrixClient } from "matrix-js-sdk";
import { logger as rootLogger } from "matrix-js-sdk/lib/logger"; import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
@@ -20,7 +20,7 @@ import { RtcTransportAutoDiscovery } from "./RtcTransportAutoDiscovery.ts";
*/ */
export async function getLocalTransport( export async function getLocalTransport(
client: Pick<MatrixClient, "getDomain" | "_unstable_getRTCTransports">, client: Pick<MatrixClient, "getDomain" | "_unstable_getRTCTransports">,
): Promise<LivekitTransport> { ): Promise<UnstableLivekitTransport> {
const discovery = new RtcTransportAutoDiscovery({ const discovery = new RtcTransportAutoDiscovery({
client, client,
resolvedConfig: Config.get(), resolvedConfig: Config.get(),
@@ -29,7 +29,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: LivekitTransport | null = customUrl const transport: UnstableLivekitTransport | null = customUrl
? { type: "livekit", livekit_service_url: customUrl } ? { type: "livekit", livekit_service_url: customUrl }
: await discovery.discoverPreferredTransport(); : await discovery.discoverPreferredTransport();
@@ -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 LivekitTransport, type UnstableLivekitTransport,
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: LivekitTransport = { const backendTransport: UnstableLivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://backend.example.org", livekit_service_url: "https://backend.example.org",
}; };
const configTransport: LivekitTransport = { const configTransport: UnstableLivekitTransport = {
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 {
isLivekitTransport, isUnstableLivekitTransport,
type LivekitTransport, type UnstableLivekitTransport,
} 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<LivekitTransport | null> { public async discoverPreferredTransport(): Promise<UnstableLivekitTransport | 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<LivekitTransport | null> { private async tryBackendTransports(): Promise<UnstableLivekitTransport | 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(isLivekitTransport); const first = transportList.find(isUnstableLivekitTransport);
if (first) { if (first) {
return first; return first;
} else { } else {
@@ -90,7 +90,7 @@ export class RtcTransportAutoDiscovery {
return null; return null;
} }
private tryConfigTransport(): LivekitTransport | null { private tryConfigTransport(): UnstableLivekitTransport | null {
const url = this.resolvedConfig.livekit?.livekit_service_url; const url = this.resolvedConfig.livekit?.livekit_service_url;
if (url) { if (url) {
return { return {
@@ -29,7 +29,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 LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
import { import {
Connection, Connection,
@@ -54,8 +54,9 @@ let fakeLocalParticipant: MockedObject<LocalParticipant>;
const ROOM_ID = "!roomID:example.org"; const ROOM_ID = "!roomID:example.org";
const SLOT_ID = "m.call#ROOM"; const SLOT_ID = "m.call#ROOM";
const livekitFocus: LivekitTransport = { const livekitServiceUrl = "https://matrix-rtc.example.org/livekit/jwt";
livekit_service_url: "https://matrix-rtc.example.org/livekit/jwt", const livekitFocus: UnstableLivekitTransport = {
livekit_service_url: livekitServiceUrl,
type: "livekit", type: "livekit",
}; };
@@ -122,7 +123,7 @@ function setupRemoteConnection(): Connection {
livekitRoomFactory: () => fakeLivekitRoom, livekitRoomFactory: () => fakeLivekitRoom,
}; };
fetchMock.post(`${livekitFocus.livekit_service_url}/get_token`, () => { fetchMock.post(`${livekitServiceUrl}/get_token`, () => {
return { return {
status: 200, status: 200,
body: { body: {
@@ -250,9 +251,7 @@ describe("Start connection states", () => {
const deferredSFU = Promise.withResolvers<void>(); const deferredSFU = Promise.withResolvers<void>();
// mock the /get_token call // mock the /get_token call
fetchMock.post( fetchMock.post(`${livekitServiceUrl}/get_token`, async () => {
`${livekitFocus.livekit_service_url}/get_token`,
async () => {
await deferredSFU.promise; await deferredSFU.promise;
return { return {
status: 500, status: 500,
@@ -261,8 +260,7 @@ describe("Start connection states", () => {
error: "Failed to look up user info from homeserver", error: "Failed to look up user info from homeserver",
}, },
}; };
}, });
);
connection.start().catch(() => { connection.start().catch(() => {
// expected to throw // expected to throw
@@ -317,7 +315,7 @@ describe("Start connection states", () => {
const deferredSFU = Promise.withResolvers<void>(); const deferredSFU = Promise.withResolvers<void>();
// mock the /get_token call // mock the /get_token call
fetchMock.post(`${livekitFocus.livekit_service_url}/get_token`, () => { fetchMock.post(`${livekitServiceUrl}/get_token`, () => {
return { return {
status: 200, status: 200,
body: { body: {
@@ -23,7 +23,7 @@ import {
type Track, type Track,
type TrackPublication, type TrackPublication,
} from "livekit-client"; } from "livekit-client";
import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
import { BehaviorSubject } from "rxjs"; import { BehaviorSubject } 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";
@@ -50,7 +50,7 @@ export interface ConnectionOpts {
/** Whether we want to publish or only subscribe on this connection. */ /** Whether we want to publish or only subscribe on this connection. */
role: "publisher" | "subscriber"; role: "publisher" | "subscriber";
/** The media transport to connect to. */ /** The media transport to connect to. */
transport: LivekitTransport; transport: UnstableLivekitTransport;
/** The Matrix client to use for OpenID and SFU config requests. */ /** The Matrix client to use for OpenID and SFU config requests. */
client: ClientOpenIDParts; client: ClientOpenIDParts;
/** The ID of the Matrix room in which the session takes place. */ /** The ID of the Matrix room in which the session takes place. */
@@ -97,7 +97,7 @@ export class Connection {
/** /**
* The media transport to connect to. * The media transport to connect to.
*/ */
public readonly transport: LivekitTransport; public readonly transport: UnstableLivekitTransport;
public readonly livekitRoom: LivekitRoom; public readonly livekitRoom: LivekitRoom;
@@ -377,6 +377,8 @@ export class Connection {
} }
protected async getSFUConfig(): Promise<SFUConfig> { protected async getSFUConfig(): Promise<SFUConfig> {
if (!("livekit_service_url" in this.transport))
throw new Error("Transport has no livekit_service_url");
return await getSFUConfig({ return await getSFUConfig({
client: this.client, client: this.client,
membership: this.ownMembershipIdentity, membership: this.ownMembershipIdentity,
@@ -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 LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } 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";
@@ -42,7 +42,7 @@ export interface ConnectionFactory {
createConnection( createConnection(
scope: ObservableScope, scope: ObservableScope,
role: "publisher" | "subscriber", role: "publisher" | "subscriber",
transport: LivekitTransport, transport: UnstableLivekitTransport,
ownMembershipIdentity: CallMembershipIdentityParts, ownMembershipIdentity: CallMembershipIdentityParts,
logger: Logger, logger: Logger,
): Connection; ): Connection;
@@ -103,7 +103,7 @@ export class ECConnectionFactory implements ConnectionFactory {
public createConnection( public createConnection(
scope: ObservableScope, scope: ObservableScope,
role: "publisher" | "subscriber", role: "publisher" | "subscriber",
transport: LivekitTransport, transport: UnstableLivekitTransport,
ownMembershipIdentity: CallMembershipIdentityParts, ownMembershipIdentity: CallMembershipIdentityParts,
logger: Logger, logger: Logger,
): Connection { ): Connection {
@@ -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 } from "rxjs"; import { BehaviorSubject } from "rxjs";
import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } 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";
@@ -21,18 +21,18 @@ import { type Behavior } from "../../Behavior.ts";
// Some test constants // Some test constants
const TRANSPORT_1: LivekitTransport = { const TRANSPORT_1: UnstableLivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://lk.example.org", livekit_service_url: "https://lk.example.org",
}; };
const TRANSPORT_2: LivekitTransport = { const TRANSPORT_2: UnstableLivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://lk.sample.com", livekit_service_url: "https://lk.sample.com",
}; };
function findConnection( function findConnection(
transport: LivekitTransport, transport: UnstableLivekitTransport,
connections: Connection[], connections: Connection[],
): Connection | undefined { ): Connection | undefined {
return connections.find((c) => return connections.find((c) =>
@@ -53,7 +53,11 @@ beforeEach(() => {
vi.mocked(fakeConnectionFactory).createConnection = vi vi.mocked(fakeConnectionFactory).createConnection = vi
.fn() .fn()
.mockImplementation( .mockImplementation(
(scope: ObservableScope, _role: unknown, transport: LivekitTransport) => { (
scope: ObservableScope,
_role: unknown,
transport: UnstableLivekitTransport,
) => {
const mockConnection = { const mockConnection = {
transport, transport,
remoteParticipants$: new BehaviorSubject([]), remoteParticipants$: new BehaviorSubject([]),
@@ -224,15 +228,17 @@ describe("connectionMap$", () => {
// 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: LivekitTransport): string { function keyForTransport(transport: UnstableLivekitTransport): string {
return `${transport.livekit_service_url}`; return "livekit_service_url" in transport
? transport.livekit_service_url
: transport.url;
} }
beforeEach(() => { beforeEach(() => {
fakeRemoteParticipantsStreams = new Map(); fakeRemoteParticipantsStreams = new Map();
function getRemoteParticipantsFor( function getRemoteParticipantsFor(
transport: LivekitTransport, transport: UnstableLivekitTransport,
): Behavior<RemoteParticipant[]> { ): Behavior<RemoteParticipant[]> {
return ( return (
fakeRemoteParticipantsStreams.get(keyForTransport(transport)) ?? fakeRemoteParticipantsStreams.get(keyForTransport(transport)) ??
@@ -247,7 +253,7 @@ describe("connectionMap$", () => {
( (
scope: ObservableScope, scope: ObservableScope,
_role: unknown, _role: unknown,
transport: LivekitTransport, transport: UnstableLivekitTransport,
) => { ) => {
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 LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
import { combineLatest, map, of, switchMap, tap } from "rxjs"; import { combineLatest, map, of, switchMap, tap } from "rxjs";
import { type Logger } from "matrix-js-sdk/lib/logger"; import { type Logger } from "matrix-js-sdk/lib/logger";
import { type RemoteParticipant } from "livekit-client"; import { type RemoteParticipant } from "livekit-client";
@@ -23,7 +23,17 @@ import { generateItemsWithEpoch } from "../../../utils/observable.ts";
import { areLivekitTransportsEqual } from "./MatrixLivekitMembers.ts"; import { areLivekitTransportsEqual } from "./MatrixLivekitMembers.ts";
import { type ConnectionFactory } from "./ConnectionFactory.ts"; import { type ConnectionFactory } from "./ConnectionFactory.ts";
function removeDuplicateTransports<T extends LivekitTransport>( /**
* The URL by which a transport is identified: its JWT service URL, or failing
* that, its SFU URL.
*/
function urlOf(transport: UnstableLivekitTransport): string {
return "livekit_service_url" in transport
? transport.livekit_service_url
: transport.url;
}
function removeDuplicateTransports<T extends UnstableLivekitTransport>(
transports: T[], transports: T[],
): T[] { ): T[] {
return transports.reduce((acc, transport) => { return transports.reduce((acc, transport) => {
@@ -56,10 +66,10 @@ export class ConnectionMap {
} }
} }
private getKey(transport: LivekitTransport): string { private getKey(transport: UnstableLivekitTransport): string {
// This is enough as a key because the ConnectionMap is already scoped by room. // This is enough as a key because the ConnectionMap 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 urlOf(transport);
} }
public getConnections(): Connection[] { public getConnections(): Connection[] {
@@ -67,13 +77,13 @@ export class ConnectionMap {
} }
public getConnectionForTransport( public getConnectionForTransport(
transport: LivekitTransport, transport: UnstableLivekitTransport,
): Connection | null { ): Connection | null {
return this.map.get(this.getKey(transport))?.connection ?? null; return this.map.get(this.getKey(transport))?.connection ?? null;
} }
public getParticipantsForTransport( public getParticipantsForTransport(
transport: LivekitTransport, transport: UnstableLivekitTransport,
): RemoteParticipant[] { ): RemoteParticipant[] {
const existing = this.map.get(this.getKey(transport)); const existing = this.map.get(this.getKey(transport));
if (existing) { if (existing) {
@@ -86,8 +96,8 @@ export class ConnectionMap {
interface Props { interface Props {
scope: ObservableScope; scope: ObservableScope;
connectionFactory: ConnectionFactory; connectionFactory: ConnectionFactory;
localTransport: LivekitTransport; localTransport: UnstableLivekitTransport;
remoteTransports$: Behavior<Epoch<LivekitTransport[]>>; remoteTransports$: Behavior<Epoch<UnstableLivekitTransport[]>>;
logger: Logger; logger: Logger;
ownMembershipIdentity: CallMembershipIdentityParts; ownMembershipIdentity: CallMembershipIdentityParts;
@@ -118,7 +128,7 @@ export function createConnectionMap$({
* All transports currently represented in the connection map. This list does * All transports currently represented in the connection map. This list does
* not include duplicate transports. * not include duplicate transports.
*/ */
const localAndRemoteTransports$: Behavior<Epoch<LivekitTransport[]>> = const localAndRemoteTransports$: Behavior<Epoch<UnstableLivekitTransport[]>> =
scope.behavior( scope.behavior(
remoteTransports$.pipe( remoteTransports$.pipe(
mapEpoch((remoteTransports) => mapEpoch((remoteTransports) =>
@@ -142,17 +152,14 @@ export function createConnectionMap$({
const role = areLivekitTransportsEqual(transport, localTransport) const role = areLivekitTransportsEqual(transport, localTransport)
? ("publisher" as const) ? ("publisher" as const)
: ("subscriber" as const); : ("subscriber" as const);
yield { yield { keys: [role, urlOf(transport)], data: transport };
keys: [role, transport.livekit_service_url],
data: undefined,
};
} }
}, },
(scope, _data$, role, serviceUrl) => { (scope, transport$, role, _url) => {
const connection = connectionFactory.createConnection( const connection = connectionFactory.createConnection(
scope, scope,
role, role,
{ type: "livekit", livekit_service_url: serviceUrl }, transport$.value,
ownMembershipIdentity, ownMembershipIdentity,
logger, logger,
); );
@@ -9,7 +9,7 @@ import { describe, test, expect, beforeEach, afterEach, vi } from "vitest";
import { logger } from "matrix-js-sdk/lib/logger"; import { logger } from "matrix-js-sdk/lib/logger";
import { import {
type CallMembership, type CallMembership,
type LivekitTransport, type UnstableLivekitTransport,
} from "matrix-js-sdk/lib/matrixrtc"; } from "matrix-js-sdk/lib/matrixrtc";
import { BehaviorSubject, combineLatest, map, type Observable } from "rxjs"; import { BehaviorSubject, combineLatest, map, type Observable } from "rxjs";
@@ -38,12 +38,12 @@ let testScope: ObservableScope;
const fallbackMemberId = (userId: string, deviceId: string): string => const fallbackMemberId = (userId: string, deviceId: string): string =>
`${userId}:${deviceId}`; `${userId}:${deviceId}`;
const transportA: LivekitTransport = { const transportA: UnstableLivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://lk.example.org", livekit_service_url: "https://lk.example.org",
}; };
const transportB: LivekitTransport = { const transportB: UnstableLivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://lk.sample.com", livekit_service_url: "https://lk.sample.com",
}; };
@@ -111,7 +111,9 @@ test("should signal participant not yet connected to livekit", async () => {
function createEpochedMemberships$(m$: Observable<CallMembership[]>): { function createEpochedMemberships$(m$: Observable<CallMembership[]>): {
memberships$: Observable<Epoch<CallMembership[]>>; memberships$: Observable<Epoch<CallMembership[]>>;
membershipsWithTransport$: Observable< membershipsWithTransport$: Observable<
Epoch<{ membership: CallMembership; transport?: LivekitTransport }[]> Epoch<
{ membership: CallMembership; transport?: UnstableLivekitTransport }[]
>
>; >;
} { } {
const memberships$ = m$.pipe(trackEpoch()); const memberships$ = m$.pipe(trackEpoch());
@@ -122,7 +124,9 @@ function createEpochedMemberships$(m$: Observable<CallMembership[]>): {
return { return {
membership: m, membership: m,
transport: transport:
tr?.type === "livekit" ? (tr as LivekitTransport) : undefined, tr?.type === "livekit"
? (tr as UnstableLivekitTransport)
: undefined,
}; };
}); });
}), }),
@@ -8,10 +8,11 @@ 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 LivekitTransport, type UnstableLivekitTransport,
} 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";
import { deepCompare } from "matrix-js-sdk/lib/utils";
import { type Behavior } from "../../Behavior"; import { type Behavior } from "../../Behavior";
import { type ConnectionMap } from "./ConnectionMap"; import { type ConnectionMap } from "./ConnectionMap";
@@ -60,7 +61,9 @@ export interface RemoteMatrixLivekitMember extends MatrixLivekitMember {
interface Props { interface Props {
scope: ObservableScope; scope: ObservableScope;
membershipsWithTransport$: Behavior< membershipsWithTransport$: Behavior<
Epoch<{ membership: CallMembership; transport?: LivekitTransport }[]> Epoch<
{ membership: CallMembership; transport?: UnstableLivekitTransport }[]
>
>; >;
connectionMap$: Behavior<Epoch<ConnectionMap>>; connectionMap$: Behavior<Epoch<ConnectionMap>>;
localUser: { deviceId: string; userId: string }; localUser: { deviceId: string; userId: string };
@@ -162,12 +165,12 @@ 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 LivekitTransport>( export function areLivekitTransportsEqual<T extends UnstableLivekitTransport>(
t1: T | null, t1: T | null,
t2: T | null, t2: T | null,
): boolean { ): boolean {
if (t1 && t2) { if (t1 && t2) {
return t1.livekit_service_url === t2.livekit_service_url; return deepCompare(t1, t2);
} }
return !t1 && !t2; return !t1 && !t2;
} }
@@ -10,7 +10,7 @@ import { BehaviorSubject } 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 LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
import { logger } from "matrix-js-sdk/lib/logger"; import { logger } from "matrix-js-sdk/lib/logger";
import { import {
@@ -152,7 +152,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 LivekitTransport, bobMembership.transports[0]! as UnstableLivekitTransport,
), ),
), ),
}); });
@@ -189,7 +189,7 @@ test("bob, carl, then bob joining no tracks yet", () => {
expect( expect(
areLivekitTransportsEqual( areLivekitTransportsEqual(
connection.transport, connection.transport,
carlMembership.transports[0]! as LivekitTransport, carlMembership.transports[0]! as UnstableLivekitTransport,
), ),
).toBe(true); ).toBe(true);
return true; return true;
@@ -219,7 +219,7 @@ test("bob, carl, then bob joining no tracks yet", () => {
expect( expect(
areLivekitTransportsEqual( areLivekitTransportsEqual(
connection.transport, connection.transport,
daveMembership.transports[0]! as LivekitTransport, daveMembership.transports[0]! as UnstableLivekitTransport,
), ),
).toBe(true); ).toBe(true);
return true; return true;
+12 -7
View File
@@ -7,10 +7,10 @@ Please see LICENSE in the repository root for full details.
import { import {
type CallMembership, type CallMembership,
type LivekitTransport, type UnstableLivekitTransport,
type MatrixRTCSession, type MatrixRTCSession,
MatrixRTCSessionEvent, MatrixRTCSessionEvent,
isLivekitTransport, isUnstableLivekitTransport,
} from "matrix-js-sdk/lib/matrixrtc"; } from "matrix-js-sdk/lib/matrixrtc";
import { fromEvent } from "rxjs"; import { fromEvent } from "rxjs";
@@ -31,15 +31,17 @@ export const membershipsAndTransports$ = (
memberships$: Behavior<Epoch<CallMembership[]>>, memberships$: Behavior<Epoch<CallMembership[]>>,
): { ): {
membershipsWithTransport$: Behavior< membershipsWithTransport$: Behavior<
Epoch<{ membership: CallMembership; transport?: LivekitTransport }[]> Epoch<
{ membership: CallMembership; transport?: UnstableLivekitTransport }[]
>
>; >;
transports$: Behavior<Epoch<LivekitTransport[]>>; transports$: Behavior<Epoch<UnstableLivekitTransport[]>>;
} => { } => {
const membershipsWithTransport$: Behavior< const membershipsWithTransport$: Behavior<
Epoch< Epoch<
{ {
membership: CallMembership; membership: CallMembership;
transport: LivekitTransport | undefined; transport: UnstableLivekitTransport | undefined;
}[] }[]
> >
> = scope.behavior( > = scope.behavior(
@@ -49,14 +51,17 @@ export const membershipsAndTransports$ = (
const transport = membership.getTransport(); const transport = membership.getTransport();
return { return {
membership, membership,
transport: isLivekitTransport(transport) ? transport : undefined, transport: isUnstableLivekitTransport(transport)
? transport
: undefined,
}; };
}); });
}), }),
), ),
); );
const transports$: Behavior<Epoch<LivekitTransport[]>> = scope.behavior( const transports$: Behavior<Epoch<UnstableLivekitTransport[]>> =
scope.behavior(
membershipsWithTransport$.pipe( membershipsWithTransport$.pipe(
mapEpoch((mts) => mts.flatMap(({ transport: t }) => (t ? [t] : []))), mapEpoch((mts) => mts.flatMap(({ transport: t }) => (t ? [t] : []))),
), ),
+3 -3
View File
@@ -40,7 +40,7 @@ import { E2eeType } from "../../e2ee/e2eeType";
import { observeInboundRtpStreamStats$ } from "./observeRtpStreamStats"; import { observeInboundRtpStreamStats$ } from "./observeRtpStreamStats";
import { type UserMediaViewModel } from "./UserMediaViewModel"; import { type UserMediaViewModel } from "./UserMediaViewModel";
import { type ScreenShareViewModel } from "./ScreenShareViewModel"; import { type ScreenShareViewModel } from "./ScreenShareViewModel";
import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
// TODO: Encryption status is kinda broken and thus unused right now. Remove? // TODO: Encryption status is kinda broken and thus unused right now. Remove?
export enum EncryptionStatus { export enum EncryptionStatus {
@@ -63,7 +63,7 @@ export interface BaseMemberMediaViewModel extends BaseMediaViewModel {
* The LiveKit transport on which this member should be publishing. Exposed * The LiveKit transport on which this member should be publishing. Exposed
* for debugging. * for debugging.
*/ */
transport$: Behavior<LivekitTransport | undefined>; transport$: Behavior<UnstableLivekitTransport | undefined>;
/** /**
* Whether there should be a warning that this media is unencrypted. * Whether there should be a warning that this media is unencrypted.
*/ */
@@ -76,7 +76,7 @@ export interface MemberMediaInputs extends BaseMediaViewModel {
livekitRoom$: Behavior<LivekitRoom | undefined>; livekitRoom$: Behavior<LivekitRoom | undefined>;
audioSource: AudioSource; audioSource: AudioSource;
videoSource: VideoSource; videoSource: VideoSource;
transport$: Behavior<LivekitTransport | undefined>; transport$: Behavior<UnstableLivekitTransport | undefined>;
encryptionSystem: EncryptionSystem; encryptionSystem: EncryptionSystem;
} }
+2 -2
View File
@@ -51,7 +51,7 @@ import { type RemoteUserMediaViewModel } from "../state/media/RemoteUserMediaVie
import { type UserMediaViewModel } from "../state/media/UserMediaViewModel"; import { type UserMediaViewModel } from "../state/media/UserMediaViewModel";
import { type RingingMediaViewModel } from "../state/media/RingingMediaViewModel"; import { type RingingMediaViewModel } from "../state/media/RingingMediaViewModel";
import { RingingStatus } from "./RingingStatus"; import { RingingStatus } from "./RingingStatus";
import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
interface TileProps { interface TileProps {
ref?: Ref<HTMLDivElement>; ref?: Ref<HTMLDivElement>;
@@ -104,7 +104,7 @@ interface UserMediaTileProps extends TileProps {
playbackMuted: boolean; playbackMuted: boolean;
waitingForMedia?: boolean; waitingForMedia?: boolean;
primaryButton?: ReactNode; primaryButton?: ReactNode;
transport: LivekitTransport | undefined; transport: UnstableLivekitTransport | undefined;
} }
/** /**
+2 -2
View File
@@ -33,7 +33,7 @@ import { ReactionIndicator } from "../reactions/ReactionIndicator";
import { RTCConnectionStats } from "../RTCConnectionStats"; import { RTCConnectionStats } from "../RTCConnectionStats";
import videoPlaceholder from "../graphics/video-placeholder.gif"; import videoPlaceholder from "../graphics/video-placeholder.gif";
import { autoVideoFit } from "../utils/videoFit"; import { autoVideoFit } from "../utils/videoFit";
import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
interface Props extends ComponentProps<typeof animated.div> { interface Props extends ComponentProps<typeof animated.div> {
className?: string; className?: string;
@@ -70,7 +70,7 @@ interface Props extends ComponentProps<typeof animated.div> {
/** /**
* The LiveKit transport, mainly for debugging purposes. * The LiveKit transport, mainly for debugging purposes.
*/ */
transport?: LivekitTransport; transport?: UnstableLivekitTransport;
/** /**
* Called whenever the aspect ratio of the video content becomes known or * Called whenever the aspect ratio of the video content becomes known or
* otherwise changes. * otherwise changes.
+2 -2
View File
@@ -54,7 +54,7 @@ import { platform } from "../Platform";
import { type RingingMediaViewModel } from "../state/media/RingingMediaViewModel"; import { type RingingMediaViewModel } from "../state/media/RingingMediaViewModel";
import { RingingStatus } from "./RingingStatus"; import { RingingStatus } from "./RingingStatus";
import { useRootElement } from "../RootElementContext"; import { useRootElement } from "../RootElementContext";
import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
interface SpotlightItemBaseProps { interface SpotlightItemBaseProps {
ref?: Ref<HTMLDivElement>; ref?: Ref<HTMLDivElement>;
@@ -75,7 +75,7 @@ interface SpotlightItemBaseProps {
interface SpotlightMemberMediaItemBaseProps extends SpotlightItemBaseProps { interface SpotlightMemberMediaItemBaseProps extends SpotlightItemBaseProps {
video: TrackReferenceOrPlaceholder | undefined; video: TrackReferenceOrPlaceholder | undefined;
unencryptedWarning: boolean; unencryptedWarning: boolean;
transport: LivekitTransport | undefined; transport: UnstableLivekitTransport | undefined;
} }
interface SpotlightUserMediaItemBaseProps extends SpotlightMemberMediaItemBaseProps { interface SpotlightUserMediaItemBaseProps extends SpotlightMemberMediaItemBaseProps {
+2 -2
View File
@@ -26,7 +26,7 @@ import {
import { import {
CallMembership, CallMembership,
type LivekitFocusSelection, type LivekitFocusSelection,
type LivekitTransport, type UnstableLivekitTransport,
type MatrixRTCSession, type MatrixRTCSession,
MatrixRTCSessionEvent, MatrixRTCSessionEvent,
type MatrixRTCSessionEventHandlerMap, type MatrixRTCSessionEventHandlerMap,
@@ -208,7 +208,7 @@ export function mockEmitter<T>(): EmitterMock<T> {
}; };
} }
export const exampleTransport: LivekitTransport = { export const exampleTransport: UnstableLivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: "https://lk.example.org", livekit_service_url: "https://lk.example.org",
}; };