mirror of
https://github.com/vector-im/element-call.git
synced 2026-11-07 00:52:28 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9432d3b600 | ||
|
|
be4388c8de | ||
|
|
37a2e49be1 | ||
|
|
334fcd5c1f | ||
|
|
5d34f57567 |
@@ -2,6 +2,7 @@ node_modules
|
||||
.DS_Store
|
||||
.env
|
||||
dist
|
||||
dist-embedded
|
||||
dist-ssr
|
||||
*.local
|
||||
*.bkp
|
||||
|
||||
@@ -361,6 +361,33 @@ pnpm backend
|
||||
# podman-compose -f docker-compose-dev.yml up
|
||||
```
|
||||
|
||||
The two Element Web instances load the Element Call widget from
|
||||
`./dist-embedded`, which is mounted over the copy bundled in the Element Web
|
||||
image. Build it from the current sources before starting the backend, and keep
|
||||
it rebuilding on change:
|
||||
|
||||
```sh
|
||||
VITE_MATRIX_RTC_MODE=matrix_2_0 pnpm build:embedded:development --outDir dist-embedded --watch
|
||||
```
|
||||
|
||||
Build it at least once before the first `pnpm backend`, since Docker otherwise
|
||||
creates an empty `dist-embedded` directory that the build cannot write to.
|
||||
Reload the page after a rebuild to pick up the new widget. The widget has no
|
||||
`config.json` in this setup, so `VITE_MATRIX_RTC_MODE` sets the default
|
||||
MatrixRTC mode of the build instead (see
|
||||
[docs/matrix_rtc_modes.md](docs/matrix_rtc_modes.md)); it can still be changed
|
||||
under Settings → Developer. Do not delete `dist-embedded` while the backend is
|
||||
running: the containers hold a mount of the directory, and a new directory of
|
||||
the same name is not picked up until they are recreated.
|
||||
|
||||
> [!NOTE]
|
||||
> When Docker runs in a VM (colima, Docker Desktop), the SFUs' container IPs
|
||||
> are not routable from the host, so they advertise `127.0.0.1` as their ICE
|
||||
> candidate instead and the browser reaches them through the published ports.
|
||||
> This only works in Chromium-based browsers: Firefox ignores loopback
|
||||
> candidates, and calls drop about fifteen seconds after joining. Media runs
|
||||
> over ICE-TCP, since the UDP port ranges are not forwarded out of the VM.
|
||||
|
||||
> [!NOTE]
|
||||
> To ensure your local development frontend functions properly, you’ll need to
|
||||
> add certificate exceptions in your browser for `https://localhost:3000` and
|
||||
|
||||
@@ -79,4 +79,6 @@ rc_message:
|
||||
matrix_rtc:
|
||||
transports:
|
||||
- type: livekit
|
||||
livekit_service_url: https://matrix-rtc.othersite.m.localhost/livekit/jwt
|
||||
# Omitting livekit_service_url forces usage of the C-S endpoints.
|
||||
# livekit_service_url: https://matrix-rtc.othersite.m.localhost/livekit/jwt
|
||||
url: wss://matrix-rtc.othersite.m.localhost/livekit/sfu
|
||||
|
||||
@@ -79,4 +79,6 @@ rc_message:
|
||||
matrix_rtc:
|
||||
transports:
|
||||
- type: livekit
|
||||
livekit_service_url: https://matrix-rtc.m.localhost/livekit/jwt
|
||||
# Omitting livekit_service_url forces usage of the C-S endpoints.
|
||||
# livekit_service_url: https://matrix-rtc.m.localhost/livekit/jwt
|
||||
url: wss://matrix-rtc.m.localhost/livekit/sfu
|
||||
|
||||
@@ -157,6 +157,9 @@ services:
|
||||
pull_policy: always
|
||||
volumes:
|
||||
- ./backend/ew.test.config.json:/app/config.json:Z
|
||||
# Replaces the Element Call widget bundled in the image with the embedded
|
||||
# build of the current sources. See "Backend" in README.md.
|
||||
- ./dist-embedded:/app/widgets/element-call:ro,Z
|
||||
environment:
|
||||
ELEMENT_WEB_PORT: 8081
|
||||
ports:
|
||||
@@ -169,6 +172,9 @@ services:
|
||||
pull_policy: always
|
||||
volumes:
|
||||
- ./backend/ew.test.othersite.config.json:/app/config.json:Z
|
||||
# Replaces the Element Call widget bundled in the image with the embedded
|
||||
# build of the current sources. See "Backend" in README.md.
|
||||
- ./dist-embedded:/app/widgets/element-call:ro,Z
|
||||
environment:
|
||||
ELEMENT_WEB_PORT: 18081
|
||||
ports:
|
||||
|
||||
@@ -46,3 +46,11 @@ Valid values are `compatibility` and `matrix_2_0`; an invalid value is
|
||||
ignored (with a warning) and the user's choice applies. Pinning `matrix_2_0` on a
|
||||
homeserver without sticky event support makes joining fail with a "sticky events
|
||||
required" error.
|
||||
|
||||
A build can change the default mode, for setups without a `config.json` by setting
|
||||
`VITE_MATRIX_RTC_MODE` at build time:
|
||||
|
||||
```sh
|
||||
VITE_MATRIX_RTC_MODE=matrix_2_0 pnpm build:embedded
|
||||
```
|
||||
|
||||
|
||||
Generated
+264
-535
File diff suppressed because it is too large
Load Diff
Generated
-9
@@ -1,9 +0,0 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: false
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.: {}
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
MatrixRTCSessionEvent,
|
||||
MembershipManagerEvent,
|
||||
type Transport,
|
||||
type UnstableLivekitTransport,
|
||||
} from "matrix-js-sdk/lib/matrixrtc";
|
||||
import { BehaviorSubject, EMPTY, Subject } from "rxjs";
|
||||
import { describe, expect, it, type Mock, vi } from "vitest";
|
||||
@@ -47,16 +46,10 @@ import {
|
||||
MockRTCSession,
|
||||
} from "./utils/test";
|
||||
|
||||
const transport: UnstableLivekitTransport = {
|
||||
const transport: Transport = {
|
||||
type: "livekit",
|
||||
livekit_service_url: "https://lk.example.org",
|
||||
};
|
||||
|
||||
const transportWithAlias: Transport = {
|
||||
livekit_alias: "!room:example.org",
|
||||
...transport,
|
||||
};
|
||||
|
||||
const userId = "@alice:example.org";
|
||||
/** A token with the publish grant, as a homeserver issues it for its own SFU. */
|
||||
const publisherJWT = [
|
||||
@@ -230,7 +223,7 @@ describe("createRTCParticipation", () => {
|
||||
await flushPromises();
|
||||
expect(join).toHaveBeenCalledWith(
|
||||
{ userId, deviceId, memberId: legacyMemberId },
|
||||
[transportWithAlias],
|
||||
[transport],
|
||||
expect.objectContaining({
|
||||
manageMediaKeys: true,
|
||||
unstableSendStickyEvents: false,
|
||||
@@ -326,7 +319,7 @@ describe("createRTCParticipation", () => {
|
||||
await flushPromises();
|
||||
expect(join).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
[transportWithAlias],
|
||||
[transport],
|
||||
expect.objectContaining({ delayedLeaveEventDelayMs: 7_200_000 }),
|
||||
);
|
||||
expect(fake.delegateDelayedLeave).not.toHaveBeenCalled();
|
||||
|
||||
@@ -174,20 +174,14 @@ export function createRTCParticipation(
|
||||
timings.syncDisconnectGracePeriodMs,
|
||||
),
|
||||
joinMatrixRTC: (transport, delayedLeave) =>
|
||||
joinJsSdkSession(
|
||||
room.roomId,
|
||||
jsSdkSession,
|
||||
ownMembershipIdentity,
|
||||
transport,
|
||||
{
|
||||
encryptMedia: encryptionSystem.kind !== E2eeType.NONE,
|
||||
matrixRTCMode,
|
||||
sendNotificationType: options.sendNotificationType,
|
||||
applicationData: options.applicationData,
|
||||
timings,
|
||||
delayedLeave,
|
||||
},
|
||||
),
|
||||
joinJsSdkSession(jsSdkSession, ownMembershipIdentity, transport, {
|
||||
encryptMedia: encryptionSystem.kind !== E2eeType.NONE,
|
||||
matrixRTCMode,
|
||||
sendNotificationType: options.sendNotificationType,
|
||||
applicationData: options.applicationData,
|
||||
timings,
|
||||
delayedLeave,
|
||||
}),
|
||||
membershipManagerError$: fromEvent(
|
||||
jsSdkSession,
|
||||
MatrixRTCSessionEvent.MembershipManagerError,
|
||||
|
||||
@@ -6,7 +6,6 @@ Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import {
|
||||
type UnstableLivekitTransport,
|
||||
type MatrixRTCSession,
|
||||
type Transport,
|
||||
} from "matrix-js-sdk/lib/matrixrtc";
|
||||
@@ -16,16 +15,11 @@ import { defaultSessionTimings, MatrixRTCMode } from "../config";
|
||||
import { ownMemberMock } from "../utils/test";
|
||||
import { joinJsSdkSession } from "./joinJsSdkSession";
|
||||
|
||||
const transport: UnstableLivekitTransport = {
|
||||
const transport: Transport = {
|
||||
type: "livekit",
|
||||
livekit_service_url: "https://lk.example.org",
|
||||
};
|
||||
|
||||
const transportWithAlias: Transport = {
|
||||
livekit_alias: "!room:example.org",
|
||||
...transport,
|
||||
};
|
||||
|
||||
/** A session that only records the join; nothing else of it is touched. */
|
||||
function session(): MatrixRTCSession & {
|
||||
join: Mock<MatrixRTCSession["join"]>;
|
||||
@@ -45,7 +39,7 @@ describe("joinJsSdkSession", () => {
|
||||
keyRotationParticipantLimit: 20,
|
||||
};
|
||||
|
||||
joinJsSdkSession("!room:example.org", s, ownMemberMock, transport, {
|
||||
joinJsSdkSession(s, ownMemberMock, transport, {
|
||||
encryptMedia: true,
|
||||
matrixRTCMode: MatrixRTCMode.Compatibility,
|
||||
sendNotificationType: "ring",
|
||||
@@ -57,7 +51,7 @@ describe("joinJsSdkSession", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(s.join).toHaveBeenCalledWith(ownMemberMock, [transportWithAlias], {
|
||||
expect(s.join).toHaveBeenCalledWith(ownMemberMock, [transport], {
|
||||
notificationType: "ring",
|
||||
callIntent: undefined,
|
||||
applicationData: {},
|
||||
@@ -78,7 +72,7 @@ describe("joinJsSdkSession", () => {
|
||||
it("gives up on the network once the delayed leave has probably been sent", () => {
|
||||
const s = session();
|
||||
|
||||
joinJsSdkSession("!room:example.org", s, ownMemberMock, transport, {
|
||||
joinJsSdkSession(s, ownMemberMock, transport, {
|
||||
encryptMedia: false,
|
||||
matrixRTCMode: MatrixRTCMode.Compatibility,
|
||||
timings: { ...defaultSessionTimings, networkErrorRetryMs: 4_000 },
|
||||
@@ -87,7 +81,7 @@ describe("joinJsSdkSession", () => {
|
||||
|
||||
expect(s.join).toHaveBeenCalledWith(
|
||||
ownMemberMock,
|
||||
[transportWithAlias],
|
||||
[transport],
|
||||
expect.objectContaining({
|
||||
manageMediaKeys: false,
|
||||
maximumNetworkErrorRetryCount: 4,
|
||||
@@ -98,30 +92,7 @@ describe("joinJsSdkSession", () => {
|
||||
it("lifts the call intent out of the application data and sends sticky events in Matrix 2.0 mode", () => {
|
||||
const s = session();
|
||||
|
||||
joinJsSdkSession("!room:example.org", s, ownMemberMock, transport, {
|
||||
encryptMedia: true,
|
||||
matrixRTCMode: MatrixRTCMode.Matrix_2_0,
|
||||
applicationData: { "m.call.intent": "audio", custom: 1 },
|
||||
timings: defaultSessionTimings,
|
||||
delayedLeave: defaultSessionTimings.delayedLeave,
|
||||
});
|
||||
|
||||
expect(s.join).toHaveBeenCalledWith(
|
||||
ownMemberMock,
|
||||
[transportWithAlias],
|
||||
expect.objectContaining({
|
||||
callIntent: "audio",
|
||||
applicationData: { custom: 1 },
|
||||
unstableSendStickyEvents: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not add livekit_alias to newer transport format", () => {
|
||||
const s = session();
|
||||
const transport = { type: "livekit", url: "wss://lk.example.org" };
|
||||
|
||||
joinJsSdkSession("!room:example.org", s, ownMemberMock, transport, {
|
||||
joinJsSdkSession(s, ownMemberMock, transport, {
|
||||
encryptMedia: true,
|
||||
matrixRTCMode: MatrixRTCMode.Matrix_2_0,
|
||||
applicationData: { "m.call.intent": "audio", custom: 1 },
|
||||
|
||||
@@ -35,7 +35,6 @@ interface Options {
|
||||
* @throws If the join could not be started.
|
||||
*/
|
||||
export function joinJsSdkSession(
|
||||
roomId: string,
|
||||
session: JsSdkRTCSession,
|
||||
ownMembershipIdentity: CallMembershipIdentityParts,
|
||||
transport: Transport,
|
||||
@@ -59,37 +58,20 @@ export function joinJsSdkSession(
|
||||
timings.syncDisconnectGracePeriodMs,
|
||||
delayedLeave.delay_ms,
|
||||
);
|
||||
|
||||
session.join(
|
||||
ownMembershipIdentity,
|
||||
// TODO: `livekit_alias` should be unused these days, but some Ruma-based
|
||||
// clients still expect it to be present. Remove from `publishedTransports`
|
||||
// as soon as https://github.com/ruma/ruma/pull/2642 and
|
||||
// https://github.com/matrix-org/matrix-rust-sdk/pull/7335 are widely
|
||||
// deployed.
|
||||
[
|
||||
{
|
||||
...("livekit_service_url" in transport
|
||||
? { livekit_alias: roomId }
|
||||
: {}),
|
||||
...transport,
|
||||
},
|
||||
],
|
||||
{
|
||||
notificationType: sendNotificationType,
|
||||
callIntent,
|
||||
applicationData: rest,
|
||||
manageMediaKeys: encryptMedia,
|
||||
delayedLeaveEventRestartMs: delayedLeave.restart_ms,
|
||||
delayedLeaveEventDelayMs: delayedLeave.delay_ms,
|
||||
delayedLeaveEventRestartLocalTimeoutMs: delayedLeave.restart_timeout_ms,
|
||||
networkErrorRetryMs: timings.networkErrorRetryMs,
|
||||
makeKeyDelay: timings.waitForKeyRotationMs,
|
||||
membershipEventExpiryMs: timings.membershipEventExpiryMs,
|
||||
keyRotationParticipantLimit: timings.keyRotationParticipantLimit,
|
||||
unstableSendStickyEvents: matrixRTCMode === MatrixRTCMode.Matrix_2_0,
|
||||
maximumNetworkErrorRetryCount:
|
||||
Math.ceil(maxWaitMs / timings.networkErrorRetryMs) + 1,
|
||||
},
|
||||
);
|
||||
session.join(ownMembershipIdentity, [transport], {
|
||||
notificationType: sendNotificationType,
|
||||
callIntent,
|
||||
applicationData: rest,
|
||||
manageMediaKeys: encryptMedia,
|
||||
delayedLeaveEventRestartMs: delayedLeave.restart_ms,
|
||||
delayedLeaveEventDelayMs: delayedLeave.delay_ms,
|
||||
delayedLeaveEventRestartLocalTimeoutMs: delayedLeave.restart_timeout_ms,
|
||||
networkErrorRetryMs: timings.networkErrorRetryMs,
|
||||
makeKeyDelay: timings.waitForKeyRotationMs,
|
||||
membershipEventExpiryMs: timings.membershipEventExpiryMs,
|
||||
keyRotationParticipantLimit: timings.keyRotationParticipantLimit,
|
||||
unstableSendStickyEvents: matrixRTCMode === MatrixRTCMode.Matrix_2_0,
|
||||
maximumNetworkErrorRetryCount:
|
||||
Math.ceil(maxWaitMs / timings.networkErrorRetryMs) + 1,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,12 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { describe, expect, it, beforeEach } from "vitest";
|
||||
import { describe, expect, it, beforeEach, vi } from "vitest";
|
||||
import { logger } from "matrix-js-sdk/lib/logger";
|
||||
|
||||
import { MatrixRTCMode } from "../config/ConfigOptions";
|
||||
import {
|
||||
Setting,
|
||||
parseResolution,
|
||||
seedSettingsFromConfig,
|
||||
defaultMatrixRTCMode,
|
||||
screenShareCodec,
|
||||
screenShareResolution,
|
||||
screenShareFramerate,
|
||||
@@ -38,6 +41,28 @@ describe("parseResolution", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("defaultMatrixRTCMode", () => {
|
||||
it("is Compatibility when the build sets nothing", () => {
|
||||
expect(defaultMatrixRTCMode(undefined)).toBe(MatrixRTCMode.Compatibility);
|
||||
});
|
||||
|
||||
it.each([MatrixRTCMode.Compatibility, MatrixRTCMode.Matrix_2_0])(
|
||||
"takes a valid build value (%s)",
|
||||
(mode) => {
|
||||
expect(defaultMatrixRTCMode(mode)).toBe(mode);
|
||||
},
|
||||
);
|
||||
|
||||
it("ignores an invalid build value and warns", () => {
|
||||
const warn = vi.spyOn(logger, "warn").mockImplementation(() => {});
|
||||
expect(defaultMatrixRTCMode("nonsense")).toBe(MatrixRTCMode.Compatibility);
|
||||
expect(warn).toHaveBeenCalledWith(
|
||||
expect.stringContaining("VITE_MATRIX_RTC_MODE"),
|
||||
);
|
||||
warn.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Setting", () => {
|
||||
describe("seedFromConfig", () => {
|
||||
it("updates value when no localStorage value exists", () => {
|
||||
|
||||
@@ -149,9 +149,20 @@ export const enableExtendedLivekitLogs = new Setting<boolean>(
|
||||
false,
|
||||
);
|
||||
|
||||
export function defaultMatrixRTCMode(
|
||||
buildValue: string | undefined = import.meta.env.VITE_MATRIX_RTC_MODE,
|
||||
): MatrixRTCMode {
|
||||
if (buildValue === undefined) return MatrixRTCMode.Compatibility;
|
||||
if (Object.values<string>(MatrixRTCMode).includes(buildValue)) {
|
||||
return buildValue as MatrixRTCMode;
|
||||
}
|
||||
logger.warn(`Ignoring invalid VITE_MATRIX_RTC_MODE: ${buildValue}`);
|
||||
return MatrixRTCMode.Compatibility;
|
||||
}
|
||||
|
||||
export const matrixRTCMode = new Setting<MatrixRTCMode>(
|
||||
"matrix-rtc-mode",
|
||||
MatrixRTCMode.Compatibility,
|
||||
defaultMatrixRTCMode(),
|
||||
);
|
||||
|
||||
export const customTransport = new Setting<UnstableLivekitTransport | null>(
|
||||
|
||||
@@ -181,6 +181,8 @@ export const initializeWidget = (
|
||||
updateDelayedEvents: true,
|
||||
sendSticky: true,
|
||||
receiveSticky: true,
|
||||
rtcLivekitGetToken: true,
|
||||
rtcLivekitDelegateDelayedLeave: true,
|
||||
},
|
||||
roomId,
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user