mirror of
https://github.com/vector-im/element-call.git
synced 2026-10-16 23:45:50 +00:00
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:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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(() => {
|
||||||
|
|||||||
@@ -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),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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 =
|
||||||
transport.livekit_service_url + "/delegate_delayed_leave",
|
"livekit_service_url" in transport &&
|
||||||
`transport ${transport.livekit_service_url}`,
|
(await checkDelegationSupport(
|
||||||
);
|
transport.livekit_service_url + "/delegate_delayed_leave",
|
||||||
|
`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,19 +251,16 @@ 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`,
|
await deferredSFU.promise;
|
||||||
async () => {
|
return {
|
||||||
await deferredSFU.promise;
|
status: 500,
|
||||||
return {
|
body: {
|
||||||
status: 500,
|
errcode: "M_LOOKUP_FAILED",
|
||||||
body: {
|
error: "Failed to look up user info from homeserver",
|
||||||
errcode: "M_LOOKUP_FAILED",
|
},
|
||||||
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;
|
||||||
|
|||||||
@@ -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,18 +51,21 @@ 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[]>> =
|
||||||
membershipsWithTransport$.pipe(
|
scope.behavior(
|
||||||
mapEpoch((mts) => mts.flatMap(({ transport: t }) => (t ? [t] : []))),
|
membershipsWithTransport$.pipe(
|
||||||
),
|
mapEpoch((mts) => mts.flatMap(({ transport: t }) => (t ? [t] : []))),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
membershipsWithTransport$,
|
membershipsWithTransport$,
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
@@ -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",
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user