The advertised livekit_alias in membership is deprecated
This commit is contained in:
@@ -78,8 +78,8 @@ export type OpenIDClientParts = Pick<
|
|||||||
* @param membership Our own membership identity parts used to send to jwt service.
|
* @param membership Our own membership identity parts used to send to jwt service.
|
||||||
* @param serviceUrl The URL of the livekit SFU service
|
* @param serviceUrl The URL of the livekit SFU service
|
||||||
* @param roomId The room id used in the jwt request. This is NOT the livekit_alias. The jwt service will provide the alias. It maps matrix room ids <-> Livekit aliases.
|
* @param roomId The room id used in the jwt request. This is NOT the livekit_alias. The jwt service will provide the alias. It maps matrix room ids <-> Livekit aliases.
|
||||||
* @param opts Additional options to modify which endpoint with which data will be used to aquire the jwt token.
|
* @param opts Additional options to modify which endpoint with which data will be used to acquire the jwt token.
|
||||||
* @param opts.forceJwtEndpoint This will use the old jwt endpoint which will create the rtc backend identity based on string concatination
|
* @param opts.forceJwtEndpoint This will use the old jwt endpoint which will create the rtc backend identity based on string concatenation
|
||||||
* instead of a hash.
|
* instead of a hash.
|
||||||
* This function by default uses whatever is possible with the current jwt service installed next to the SFU.
|
* This function by default uses whatever is possible with the current jwt service installed next to the SFU.
|
||||||
* For remote connections this does not matter, since we will not publish there we can rely on the newest option.
|
* For remote connections this does not matter, since we will not publish there we can rely on the newest option.
|
||||||
|
|||||||
@@ -468,6 +468,7 @@ export function createCallViewModel$(
|
|||||||
|
|
||||||
const connectionFactory = new ECConnectionFactory(
|
const connectionFactory = new ECConnectionFactory(
|
||||||
client,
|
client,
|
||||||
|
matrixRoom.roomId,
|
||||||
mediaDevices,
|
mediaDevices,
|
||||||
trackProcessorState$,
|
trackProcessorState$,
|
||||||
livekitKeyProvider,
|
livekitKeyProvider,
|
||||||
@@ -496,12 +497,13 @@ export function createCallViewModel$(
|
|||||||
ownMembershipIdentity,
|
ownMembershipIdentity,
|
||||||
});
|
});
|
||||||
|
|
||||||
const matrixLivekitMembers$ = createMatrixLivekitMembers$({
|
const matrixLivekitMembers$: Behavior<Epoch<RemoteMatrixLivekitMember[]>> =
|
||||||
scope: scope,
|
createMatrixLivekitMembers$({
|
||||||
membershipsWithTransport$:
|
scope: scope,
|
||||||
membershipsAndTransports.membershipsWithTransport$,
|
membershipsWithTransport$:
|
||||||
connectionManager: connectionManager,
|
membershipsAndTransports.membershipsWithTransport$,
|
||||||
});
|
connectionManager: connectionManager,
|
||||||
|
});
|
||||||
|
|
||||||
const connectOptions$ = scope.behavior(
|
const connectOptions$ = scope.behavior(
|
||||||
matrixRTCMode$.pipe(
|
matrixRTCMode$.pipe(
|
||||||
|
|||||||
@@ -7,10 +7,9 @@ Please see LICENSE in the repository root for full details.
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
type CallMembership,
|
type CallMembership,
|
||||||
isLivekitTransport,
|
|
||||||
type LivekitTransport,
|
|
||||||
isLivekitTransportConfig,
|
isLivekitTransportConfig,
|
||||||
type Transport,
|
type Transport,
|
||||||
|
type LivekitTransportConfig,
|
||||||
} from "matrix-js-sdk/lib/matrixrtc";
|
} from "matrix-js-sdk/lib/matrixrtc";
|
||||||
import { MatrixError, type MatrixClient } from "matrix-js-sdk";
|
import { MatrixError, type MatrixClient } from "matrix-js-sdk";
|
||||||
import {
|
import {
|
||||||
@@ -57,6 +56,7 @@ interface Props {
|
|||||||
"getDomain" | "baseUrl" | "_unstable_getRTCTransports"
|
"getDomain" | "baseUrl" | "_unstable_getRTCTransports"
|
||||||
> &
|
> &
|
||||||
OpenIDClientParts;
|
OpenIDClientParts;
|
||||||
|
// Used by the jwt service to create the livekit room and compute the livekit alias.
|
||||||
roomId: string;
|
roomId: string;
|
||||||
useOldestMember$: Behavior<boolean>;
|
useOldestMember$: Behavior<boolean>;
|
||||||
forceJwtEndpoint$: Behavior<JwtEndpointVersion>;
|
forceJwtEndpoint$: Behavior<JwtEndpointVersion>;
|
||||||
@@ -90,11 +90,11 @@ export enum JwtEndpointVersion {
|
|||||||
// 2.
|
// 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!
|
// 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 LocalTransportWithSFUConfig {
|
export interface LocalTransportWithSFUConfig {
|
||||||
transport: LivekitTransport;
|
transport: LivekitTransportConfig;
|
||||||
sfuConfig: SFUConfig;
|
sfuConfig: SFUConfig;
|
||||||
}
|
}
|
||||||
export function isLocalTransportWithSFUConfig(
|
export function isLocalTransportWithSFUConfig(
|
||||||
obj: LivekitTransport | LocalTransportWithSFUConfig,
|
obj: LivekitTransportConfig | LocalTransportWithSFUConfig,
|
||||||
): obj is LocalTransportWithSFUConfig {
|
): obj is LocalTransportWithSFUConfig {
|
||||||
return "transport" in obj && "sfuConfig" in obj;
|
return "transport" in obj && "sfuConfig" in obj;
|
||||||
}
|
}
|
||||||
@@ -137,11 +137,10 @@ export const createLocalTransport$ = ({
|
|||||||
return transport;
|
return transport;
|
||||||
}),
|
}),
|
||||||
switchMap((transport) => {
|
switchMap((transport) => {
|
||||||
if (transport !== null && isLivekitTransport(transport)) {
|
if (transport !== null && isLivekitTransportConfig(transport)) {
|
||||||
// Get the open jwt token to connect to the sfu
|
// Get the open jwt token to connect to the sfu
|
||||||
const computeLocalTransportWithSFUConfig =
|
const computeLocalTransportWithSFUConfig =
|
||||||
async (): Promise<LocalTransportWithSFUConfig> => {
|
async (): Promise<LocalTransportWithSFUConfig> => {
|
||||||
// await sleep(1000);
|
|
||||||
return {
|
return {
|
||||||
transport,
|
transport,
|
||||||
sfuConfig: await getSFUConfigWithOpenID(
|
sfuConfig: await getSFUConfigWithOpenID(
|
||||||
@@ -288,18 +287,6 @@ async function makeTransport(
|
|||||||
transport: {
|
transport: {
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
livekit_service_url: url,
|
livekit_service_url: url,
|
||||||
// WARNING PLS READ ME!!!
|
|
||||||
// This looks unintuitive especially considering that `sfuConfig.livekitAlias` exists.
|
|
||||||
// Why do we not use: `livekit_alias: sfuConfig.livekitAlias`
|
|
||||||
//
|
|
||||||
// - This is going to be used for sending our state event transport (focus_preferred)
|
|
||||||
// - In sticky events it is expected to NOT send this field at all. The transport is only the `type`, `livekit_service_url`
|
|
||||||
// - If we set it to the hased alias we get from the jwt, we will end up using the hashed alias as the body.roomId field
|
|
||||||
// in v0.16.0. (It will use oldest member transport. It is using the transport.livekit_alias as the body.roomId)
|
|
||||||
//
|
|
||||||
// TLDR this is a temporal field that allow for comaptibilty but the spec expects it to not exists. (but its existance also does not break anything)
|
|
||||||
// It is just named poorly: It was intetended to be the actual alias. But now we do pseudonymys ids so we use a hashed alias.
|
|
||||||
livekit_alias: roomId,
|
|
||||||
},
|
},
|
||||||
sfuConfig,
|
sfuConfig,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,7 +26,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 } from "matrix-js-sdk/lib/logger";
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc/LivekitTransport";
|
import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Connection,
|
Connection,
|
||||||
@@ -51,8 +51,9 @@ let fakeLivekitRoom: MockedObject<LivekitRoom>;
|
|||||||
let localParticipantEventEmiter: EventEmitter;
|
let localParticipantEventEmiter: EventEmitter;
|
||||||
let fakeLocalParticipant: MockedObject<LocalParticipant>;
|
let fakeLocalParticipant: MockedObject<LocalParticipant>;
|
||||||
|
|
||||||
const livekitFocus: LivekitTransport = {
|
const ROOM_ID = "!roomID:example.org";
|
||||||
livekit_alias: "!roomID:example.org",
|
|
||||||
|
const livekitFocus: LivekitTransportConfig = {
|
||||||
livekit_service_url: "https://matrix-rtc.example.org/livekit/jwt",
|
livekit_service_url: "https://matrix-rtc.example.org/livekit/jwt",
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
};
|
};
|
||||||
@@ -112,6 +113,7 @@ function setupTest(): void {
|
|||||||
function setupRemoteConnection(): Connection {
|
function setupRemoteConnection(): Connection {
|
||||||
const opts: ConnectionOpts = {
|
const opts: ConnectionOpts = {
|
||||||
client: client,
|
client: client,
|
||||||
|
roomId: ROOM_ID,
|
||||||
transport: livekitFocus,
|
transport: livekitFocus,
|
||||||
scope: testScope,
|
scope: testScope,
|
||||||
ownMembershipIdentity: ownMemberMock,
|
ownMembershipIdentity: ownMemberMock,
|
||||||
@@ -154,6 +156,7 @@ describe("Start connection states", () => {
|
|||||||
|
|
||||||
const opts: ConnectionOpts = {
|
const opts: ConnectionOpts = {
|
||||||
client: client,
|
client: client,
|
||||||
|
roomId: ROOM_ID,
|
||||||
transport: livekitFocus,
|
transport: livekitFocus,
|
||||||
scope: testScope,
|
scope: testScope,
|
||||||
ownMembershipIdentity: ownMemberMock,
|
ownMembershipIdentity: ownMemberMock,
|
||||||
@@ -170,6 +173,7 @@ describe("Start connection states", () => {
|
|||||||
|
|
||||||
const opts: ConnectionOpts = {
|
const opts: ConnectionOpts = {
|
||||||
client: client,
|
client: client,
|
||||||
|
roomId: ROOM_ID,
|
||||||
transport: livekitFocus,
|
transport: livekitFocus,
|
||||||
scope: testScope,
|
scope: testScope,
|
||||||
ownMembershipIdentity: ownMemberMock,
|
ownMembershipIdentity: ownMemberMock,
|
||||||
@@ -221,6 +225,7 @@ describe("Start connection states", () => {
|
|||||||
|
|
||||||
const opts: ConnectionOpts = {
|
const opts: ConnectionOpts = {
|
||||||
client: client,
|
client: client,
|
||||||
|
roomId: ROOM_ID,
|
||||||
transport: livekitFocus,
|
transport: livekitFocus,
|
||||||
scope: testScope,
|
scope: testScope,
|
||||||
ownMembershipIdentity: ownMemberMock,
|
ownMembershipIdentity: ownMemberMock,
|
||||||
@@ -279,6 +284,7 @@ describe("Start connection states", () => {
|
|||||||
|
|
||||||
const opts: ConnectionOpts = {
|
const opts: ConnectionOpts = {
|
||||||
client: client,
|
client: client,
|
||||||
|
roomId: ROOM_ID,
|
||||||
transport: livekitFocus,
|
transport: livekitFocus,
|
||||||
scope: testScope,
|
scope: testScope,
|
||||||
ownMembershipIdentity: ownMemberMock,
|
ownMembershipIdentity: ownMemberMock,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
type Room as LivekitRoom,
|
type Room as LivekitRoom,
|
||||||
type RemoteParticipant,
|
type RemoteParticipant,
|
||||||
} from "livekit-client";
|
} from "livekit-client";
|
||||||
import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
|
import { type LivekitTransportConfig } 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";
|
||||||
@@ -49,9 +49,11 @@ 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: LivekitTransport;
|
transport: LivekitTransportConfig;
|
||||||
/** 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. */
|
||||||
|
roomId: string;
|
||||||
/** The observable scope to use for this connection. */
|
/** The observable scope to use for this connection. */
|
||||||
scope: ObservableScope;
|
scope: ObservableScope;
|
||||||
|
|
||||||
@@ -102,7 +104,7 @@ export class Connection {
|
|||||||
/**
|
/**
|
||||||
* The media transport to connect to.
|
* The media transport to connect to.
|
||||||
*/
|
*/
|
||||||
public readonly transport: LivekitTransport;
|
public readonly transport: LivekitTransportConfig;
|
||||||
|
|
||||||
public readonly livekitRoom: LivekitRoom;
|
public readonly livekitRoom: LivekitRoom;
|
||||||
|
|
||||||
@@ -131,6 +133,47 @@ export class Connection {
|
|||||||
* */
|
* */
|
||||||
protected stopped = false;
|
protected stopped = false;
|
||||||
|
|
||||||
|
// TODO: can we just keep the ConnectionOpts object instead of spreading?
|
||||||
|
private readonly client: OpenIDClientParts;
|
||||||
|
private readonly roomId: string;
|
||||||
|
private readonly logger: Logger;
|
||||||
|
private readonly ownMembershipIdentity: CallMembershipIdentityParts;
|
||||||
|
private readonly existingSFUConfig?: SFUConfig;
|
||||||
|
/**
|
||||||
|
* Creates a new connection to a matrix RTC LiveKit backend.
|
||||||
|
*
|
||||||
|
* @param opts - Connection options {@link ConnectionOpts}.
|
||||||
|
*
|
||||||
|
* @param logger - The logger to use.
|
||||||
|
*/
|
||||||
|
public constructor(opts: ConnectionOpts, logger: Logger) {
|
||||||
|
this.ownMembershipIdentity = opts.ownMembershipIdentity;
|
||||||
|
this.existingSFUConfig = opts.existingSFUConfig;
|
||||||
|
this.roomId = opts.roomId;
|
||||||
|
this.logger = logger.getChild(
|
||||||
|
"[Connection " + opts.transport.livekit_service_url + "]",
|
||||||
|
);
|
||||||
|
this.logger.info(
|
||||||
|
`constructor: ${opts.transport.livekit_service_url} roomId: ${this.roomId} withSfuConfig?: ${opts.existingSFUConfig ? JSON.stringify(opts.existingSFUConfig) : "undefined"}`,
|
||||||
|
);
|
||||||
|
const { transport, client, scope } = opts;
|
||||||
|
|
||||||
|
this.scope = scope;
|
||||||
|
this.livekitRoom = opts.livekitRoomFactory();
|
||||||
|
this.transport = transport;
|
||||||
|
this.client = client;
|
||||||
|
|
||||||
|
this.remoteParticipants$ = scope.behavior(
|
||||||
|
// Only tracks remote participants
|
||||||
|
connectedParticipantsObserver(this.livekitRoom),
|
||||||
|
);
|
||||||
|
|
||||||
|
scope.onEnd(() => {
|
||||||
|
this.logger.info(`Connection scope ended, stopping connection`);
|
||||||
|
void this.stop();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the connection.
|
* Starts the connection.
|
||||||
*
|
*
|
||||||
@@ -231,7 +274,7 @@ export class Connection {
|
|||||||
this.client,
|
this.client,
|
||||||
this.ownMembershipIdentity,
|
this.ownMembershipIdentity,
|
||||||
this.transport.livekit_service_url,
|
this.transport.livekit_service_url,
|
||||||
this.transport.livekit_alias,
|
this.roomId,
|
||||||
// dont pass any custom opts for the subscribe only connections
|
// dont pass any custom opts for the subscribe only connections
|
||||||
{},
|
{},
|
||||||
this.logger,
|
this.logger,
|
||||||
@@ -256,42 +299,4 @@ export class Connection {
|
|||||||
`stop: DONE disconnecing from lk room ${this.transport.livekit_service_url}`,
|
`stop: DONE disconnecing from lk room ${this.transport.livekit_service_url}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly client: OpenIDClientParts;
|
|
||||||
private readonly logger: Logger;
|
|
||||||
private readonly ownMembershipIdentity: CallMembershipIdentityParts;
|
|
||||||
private readonly existingSFUConfig?: SFUConfig;
|
|
||||||
/**
|
|
||||||
* Creates a new connection to a matrix RTC LiveKit backend.
|
|
||||||
*
|
|
||||||
* @param opts - Connection options {@link ConnectionOpts}.
|
|
||||||
*
|
|
||||||
* @param logger - The logger to use.
|
|
||||||
*/
|
|
||||||
public constructor(opts: ConnectionOpts, logger: Logger) {
|
|
||||||
this.ownMembershipIdentity = opts.ownMembershipIdentity;
|
|
||||||
this.existingSFUConfig = opts.existingSFUConfig;
|
|
||||||
this.logger = logger.getChild(
|
|
||||||
"[Connection " + opts.transport.livekit_service_url + "]",
|
|
||||||
);
|
|
||||||
this.logger.info(
|
|
||||||
`constructor: ${opts.transport.livekit_service_url} alias: ${opts.transport.livekit_alias} withSfuConfig?: ${opts.existingSFUConfig ? JSON.stringify(opts.existingSFUConfig) : "undefined"}`,
|
|
||||||
);
|
|
||||||
const { transport, client, scope } = opts;
|
|
||||||
|
|
||||||
this.scope = scope;
|
|
||||||
this.livekitRoom = opts.livekitRoomFactory();
|
|
||||||
this.transport = transport;
|
|
||||||
this.client = client;
|
|
||||||
|
|
||||||
this.remoteParticipants$ = scope.behavior(
|
|
||||||
// Only tracks remote participants
|
|
||||||
connectedParticipantsObserver(this.livekitRoom),
|
|
||||||
);
|
|
||||||
|
|
||||||
scope.onEnd(() => {
|
|
||||||
this.logger.info(`Connection scope ended, stopping connection`);
|
|
||||||
void this.stop();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { 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/LivekitTransport";
|
import { type LivekitTransportConfig } 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";
|
||||||
@@ -33,7 +33,7 @@ import { defaultLiveKitOptions } from "../../../livekit/options.ts";
|
|||||||
export interface ConnectionFactory {
|
export interface ConnectionFactory {
|
||||||
createConnection(
|
createConnection(
|
||||||
scope: ObservableScope,
|
scope: ObservableScope,
|
||||||
transport: LivekitTransport,
|
transport: LivekitTransportConfig,
|
||||||
ownMembershipIdentity: CallMembershipIdentityParts,
|
ownMembershipIdentity: CallMembershipIdentityParts,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
sfuConfig?: SFUConfig,
|
sfuConfig?: SFUConfig,
|
||||||
@@ -47,6 +47,7 @@ export class ECConnectionFactory implements ConnectionFactory {
|
|||||||
* Creates a ConnectionFactory for LiveKit connections.
|
* Creates a ConnectionFactory for LiveKit connections.
|
||||||
*
|
*
|
||||||
* @param client - The OpenID client parts for authentication, needed to get openID and JWT tokens.
|
* @param client - The OpenID client parts for authentication, needed to get openID and JWT tokens.
|
||||||
|
* @param roomId - The current room ID.
|
||||||
* @param devices - Used for video/audio out/in capture options.
|
* @param devices - Used for video/audio out/in capture options.
|
||||||
* @param processorState$ - Effects like background blur (only for publishing connection?)
|
* @param processorState$ - Effects like background blur (only for publishing connection?)
|
||||||
* @param livekitKeyProvider - Optional key provider for end-to-end encryption.
|
* @param livekitKeyProvider - Optional key provider for end-to-end encryption.
|
||||||
@@ -57,6 +58,7 @@ export class ECConnectionFactory implements ConnectionFactory {
|
|||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
private client: OpenIDClientParts,
|
private client: OpenIDClientParts,
|
||||||
|
private readonly roomId: string,
|
||||||
private devices: MediaDevices,
|
private devices: MediaDevices,
|
||||||
private processorState$: Behavior<ProcessorState>,
|
private processorState$: Behavior<ProcessorState>,
|
||||||
livekitKeyProvider: BaseKeyProvider | undefined,
|
livekitKeyProvider: BaseKeyProvider | undefined,
|
||||||
@@ -95,7 +97,7 @@ export class ECConnectionFactory implements ConnectionFactory {
|
|||||||
*/
|
*/
|
||||||
public createConnection(
|
public createConnection(
|
||||||
scope: ObservableScope,
|
scope: ObservableScope,
|
||||||
transport: LivekitTransport,
|
transport: LivekitTransportConfig,
|
||||||
ownMembershipIdentity: CallMembershipIdentityParts,
|
ownMembershipIdentity: CallMembershipIdentityParts,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
sfuConfig?: SFUConfig,
|
sfuConfig?: SFUConfig,
|
||||||
@@ -103,6 +105,7 @@ export class ECConnectionFactory implements ConnectionFactory {
|
|||||||
return new Connection(
|
return new Connection(
|
||||||
{
|
{
|
||||||
existingSFUConfig: sfuConfig,
|
existingSFUConfig: sfuConfig,
|
||||||
|
roomId: this.roomId,
|
||||||
transport,
|
transport,
|
||||||
client: this.client,
|
client: this.client,
|
||||||
scope: scope,
|
scope: scope,
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ 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 LivekitTransport,
|
||||||
|
type LivekitTransportConfig,
|
||||||
|
} 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,16 +27,14 @@ import { constant, type Behavior } from "../../Behavior.ts";
|
|||||||
|
|
||||||
// Some test constants
|
// Some test constants
|
||||||
|
|
||||||
const TRANSPORT_1: LivekitTransport = {
|
const TRANSPORT_1: LivekitTransportConfig = {
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
livekit_service_url: "https://lk.example.org",
|
livekit_service_url: "https://lk.example.org",
|
||||||
livekit_alias: "!alias:example.org",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const TRANSPORT_2: LivekitTransport = {
|
const TRANSPORT_2: LivekitTransportConfig = {
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
livekit_service_url: "https://lk.sample.com",
|
livekit_service_url: "https://lk.sample.com",
|
||||||
livekit_alias: "!alias:sample.com",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let fakeConnectionFactory: ConnectionFactory;
|
let fakeConnectionFactory: ConnectionFactory;
|
||||||
|
|||||||
@@ -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 LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc";
|
||||||
import { combineLatest, map, of, switchMap } from "rxjs";
|
import { combineLatest, map, of, switchMap } 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";
|
||||||
@@ -42,8 +42,8 @@ export class ConnectionManagerData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getKey(transport: LivekitTransport): string {
|
private getKey(transport: LivekitTransportConfig): string {
|
||||||
return transport.livekit_service_url + "|" + transport.livekit_alias;
|
return transport.livekit_service_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getConnections(): Connection[] {
|
public getConnections(): Connection[] {
|
||||||
@@ -51,15 +51,15 @@ export class ConnectionManagerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getConnectionForTransport(
|
public getConnectionForTransport(
|
||||||
transport: LivekitTransport,
|
transport: LivekitTransportConfig,
|
||||||
): 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: LivekitTransport,
|
transport: LivekitTransportConfig,
|
||||||
): RemoteParticipant[] {
|
): RemoteParticipant[] {
|
||||||
const key = transport.livekit_service_url + "|" + transport.livekit_alias;
|
const key = this.getKey(transport);
|
||||||
const existing = this.store.get(key);
|
const existing = this.store.get(key);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
return existing.participants;
|
return existing.participants;
|
||||||
@@ -72,7 +72,7 @@ interface Props {
|
|||||||
scope: ObservableScope;
|
scope: ObservableScope;
|
||||||
connectionFactory: ConnectionFactory;
|
connectionFactory: ConnectionFactory;
|
||||||
localTransport$: Behavior<LocalTransportWithSFUConfig | null>;
|
localTransport$: Behavior<LocalTransportWithSFUConfig | null>;
|
||||||
remoteTransports$: Behavior<Epoch<LivekitTransport[]>>;
|
remoteTransports$: Behavior<Epoch<LivekitTransportConfig[]>>;
|
||||||
|
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
ownMembershipIdentity: CallMembershipIdentityParts;
|
ownMembershipIdentity: CallMembershipIdentityParts;
|
||||||
@@ -123,7 +123,7 @@ export function createConnectionManager$({
|
|||||||
* externally this is modified via `registerTransports()`.
|
* externally this is modified via `registerTransports()`.
|
||||||
*/
|
*/
|
||||||
const localAndRemoteTransports$: Behavior<
|
const localAndRemoteTransports$: Behavior<
|
||||||
Epoch<(LivekitTransport | LocalTransportWithSFUConfig)[]>
|
Epoch<(LivekitTransportConfig | LocalTransportWithSFUConfig)[]>
|
||||||
> = scope.behavior(
|
> = scope.behavior(
|
||||||
combineLatest([remoteTransports$, localTransport$]).pipe(
|
combineLatest([remoteTransports$, localTransport$]).pipe(
|
||||||
// Combine local and remote transports into one transport array
|
// Combine local and remote transports into one transport array
|
||||||
@@ -168,19 +168,13 @@ export function createConnectionManager$({
|
|||||||
// This is the local transport only the `LocalTransportWithSFUConfig` has a `sfuConfig` field
|
// This is the local transport only the `LocalTransportWithSFUConfig` has a `sfuConfig` field
|
||||||
const { transport, sfuConfig } = transportWithOrWithoutSfuConfig;
|
const { transport, sfuConfig } = transportWithOrWithoutSfuConfig;
|
||||||
yield {
|
yield {
|
||||||
keys: [
|
keys: [transport.livekit_service_url, sfuConfig],
|
||||||
transport.livekit_service_url,
|
|
||||||
transport.livekit_alias,
|
|
||||||
sfuConfig,
|
|
||||||
],
|
|
||||||
data: undefined,
|
data: undefined,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const transport = transportWithOrWithoutSfuConfig;
|
|
||||||
yield {
|
yield {
|
||||||
keys: [
|
keys: [
|
||||||
transport.livekit_service_url,
|
transportWithOrWithoutSfuConfig.livekit_service_url,
|
||||||
transport.livekit_alias,
|
|
||||||
undefined as undefined | SFUConfig,
|
undefined as undefined | SFUConfig,
|
||||||
],
|
],
|
||||||
data: undefined,
|
data: undefined,
|
||||||
@@ -188,13 +182,12 @@ export function createConnectionManager$({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(scope, _data$, serviceUrl, alias, sfuConfig) => {
|
(scope, _data$, serviceUrl, sfuConfig) => {
|
||||||
const connection = connectionFactory.createConnection(
|
const connection = connectionFactory.createConnection(
|
||||||
scope,
|
scope,
|
||||||
{
|
{
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
livekit_service_url: serviceUrl,
|
livekit_service_url: serviceUrl,
|
||||||
livekit_alias: alias,
|
|
||||||
},
|
},
|
||||||
ownMembershipIdentity,
|
ownMembershipIdentity,
|
||||||
logger,
|
logger,
|
||||||
@@ -254,7 +247,7 @@ export function createConnectionManager$({
|
|||||||
return { connectionManagerData$ };
|
return { connectionManagerData$ };
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeDuplicateTransports<T extends LivekitTransport>(
|
function removeDuplicateTransports<T extends LivekitTransportConfig>(
|
||||||
transports: T[],
|
transports: T[],
|
||||||
): T[] {
|
): T[] {
|
||||||
return transports.reduce((acc, transport) => {
|
return transports.reduce((acc, transport) => {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ 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 LivekitTransport,
|
|
||||||
type CallMembership,
|
type CallMembership,
|
||||||
|
type LivekitTransportConfig,
|
||||||
} 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 as rootLogger } from "matrix-js-sdk/lib/logger";
|
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
|
||||||
@@ -62,7 +62,7 @@ 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?: LivekitTransportConfig }[]>
|
||||||
>;
|
>;
|
||||||
connectionManager: IConnectionManager;
|
connectionManager: IConnectionManager;
|
||||||
}
|
}
|
||||||
@@ -147,18 +147,12 @@ export function createMatrixLivekitMembers$({
|
|||||||
// 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 LivekitTransportConfig>(
|
||||||
t1: T | null,
|
t1: T | null,
|
||||||
t2: T | null,
|
t2: T | null,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (t1 && t2)
|
if (t1 && t2) {
|
||||||
return (
|
return t1.livekit_service_url === t2.livekit_service_url;
|
||||||
t1.livekit_service_url === t2.livekit_service_url &&
|
}
|
||||||
// In case we have different lk rooms in the same SFU (depends on the livekit authorization service)
|
return !t1 && !t2;
|
||||||
// It is only needed in case the livekit authorization service is not behaving as expected (or custom implementation)
|
|
||||||
// Also LivekitTransport is planned to become a `ConnectionIdentifier` which moves this equal somewhere else.
|
|
||||||
t1.livekit_alias === t2.livekit_alias
|
|
||||||
);
|
|
||||||
if (!t1 && !t2) return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ Please see LICENSE in the repository root for full details.
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
type CallMembership,
|
type CallMembership,
|
||||||
isLivekitTransport,
|
type LivekitTransportConfig,
|
||||||
type LivekitTransport,
|
|
||||||
type MatrixRTCSession,
|
type MatrixRTCSession,
|
||||||
MatrixRTCSessionEvent,
|
MatrixRTCSessionEvent,
|
||||||
} from "matrix-js-sdk/lib/matrixrtc";
|
} from "matrix-js-sdk/lib/matrixrtc";
|
||||||
@@ -21,25 +20,33 @@ import {
|
|||||||
type ObservableScope,
|
type ObservableScope,
|
||||||
} from "./ObservableScope";
|
} from "./ObservableScope";
|
||||||
import { type Behavior } from "./Behavior";
|
import { type Behavior } from "./Behavior";
|
||||||
|
import { isLivekitTransportConfig } from "../../../matrix-js-sdk/src/matrixrtc";
|
||||||
|
|
||||||
export const membershipsAndTransports$ = (
|
export const membershipsAndTransports$ = (
|
||||||
scope: ObservableScope,
|
scope: ObservableScope,
|
||||||
memberships$: Behavior<Epoch<CallMembership[]>>,
|
memberships$: Behavior<Epoch<CallMembership[]>>,
|
||||||
): {
|
): {
|
||||||
membershipsWithTransport$: Behavior<
|
membershipsWithTransport$: Behavior<
|
||||||
Epoch<{ membership: CallMembership; transport?: LivekitTransport }[]>
|
Epoch<{ membership: CallMembership; transport?: LivekitTransportConfig }[]>
|
||||||
>;
|
>;
|
||||||
transports$: Behavior<Epoch<LivekitTransport[]>>;
|
transports$: Behavior<Epoch<LivekitTransportConfig[]>>;
|
||||||
} => {
|
} => {
|
||||||
/**
|
/**
|
||||||
* Lists the transports used by ourselves, plus all other MatrixRTC session
|
* Lists the transports used by ourselves, plus all other MatrixRTC session
|
||||||
* members. For completeness this also lists the preferred transport and
|
* members.
|
||||||
* whether we are in multi-SFU mode or sticky events mode (because
|
* For completeness this also lists the preferred transport and
|
||||||
* advertisedTransport$ wants to read them at the same time, and bundling data
|
* whether we are in multi-SFU mode or sticky events mode.
|
||||||
* together when it might change together is what you have to do in RxJS to
|
* `advertisedTransport$` reads these values together, so bundling them avoids inconsistent state or
|
||||||
* avoid reading inconsistent state or observing too many changes.)
|
* excessive updates when using RxJS.
|
||||||
*/
|
*/
|
||||||
const membershipsWithTransport$ = scope.behavior(
|
const membershipsWithTransport$: Behavior<
|
||||||
|
Epoch<
|
||||||
|
{
|
||||||
|
membership: CallMembership;
|
||||||
|
transport: LivekitTransportConfig | undefined;
|
||||||
|
}[]
|
||||||
|
>
|
||||||
|
> = scope.behavior(
|
||||||
memberships$.pipe(
|
memberships$.pipe(
|
||||||
mapEpoch((memberships) => {
|
mapEpoch((memberships) => {
|
||||||
return memberships.map((membership) => {
|
return memberships.map((membership) => {
|
||||||
@@ -47,14 +54,16 @@ export const membershipsAndTransports$ = (
|
|||||||
const transport = membership.getTransport(oldestMembership);
|
const transport = membership.getTransport(oldestMembership);
|
||||||
return {
|
return {
|
||||||
membership,
|
membership,
|
||||||
transport: isLivekitTransport(transport) ? transport : undefined,
|
transport: isLivekitTransportConfig(transport)
|
||||||
|
? transport
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const transports$ = scope.behavior(
|
const transports$: Behavior<Epoch<LivekitTransportConfig[]>> = scope.behavior(
|
||||||
membershipsWithTransport$.pipe(
|
membershipsWithTransport$.pipe(
|
||||||
mapEpoch((mts) => mts.flatMap(({ transport: t }) => (t ? [t] : []))),
|
mapEpoch((mts) => mts.flatMap(({ transport: t }) => (t ? [t] : []))),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user