From 3927c2b7dc5c90ab4ea1b1b6f812e9515efbb752 Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 12 Oct 2026 10:09:50 +0200 Subject: [PATCH] Show the whole transport in debug UI and logs A transport may soon carry a LiveKit SFU `url` instead of, or as well as, a `livekit_service_url`, so a service URL alone no longer identifies it. Pass the whole transport to the tiles' debug stats and print it as JSON in log lines instead of picking out the service URL. --- src/RTCConnectionStats.tsx | 23 ++++++------------- src/state/CallViewModel/CallViewModel.ts | 8 +++---- .../CallViewModel/localMember/LocalMember.ts | 4 ++-- .../localMember/RtcTransportAutoDiscovery.ts | 4 ++-- .../CallViewModel/remoteMembers/Connection.ts | 16 ++++--------- .../remoteMembers/MatrixLivekitMembers.ts | 4 ++-- src/state/media/MemberMediaViewModel.ts | 13 ++++++----- src/tile/GridTile.tsx | 15 ++++++------ src/tile/MediaView.tsx | 9 ++++---- src/tile/SpotlightTile.tsx | 7 +++--- src/utils/test.ts | 6 ++--- 11 files changed, 48 insertions(+), 61 deletions(-) diff --git a/src/RTCConnectionStats.tsx b/src/RTCConnectionStats.tsx index ea9df3f5e..90fefb656 100644 --- a/src/RTCConnectionStats.tsx +++ b/src/RTCConnectionStats.tsx @@ -16,30 +16,21 @@ import classNames from "classnames"; import { Modal } from "./Modal"; import styles from "./RTCConnectionStats.module.css"; import mediaViewStyles from "../src/tile/MediaView.module.css"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; + interface Props { audio?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats; video?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats; - focusUrl?: string; + transport?: LivekitTransport; rtcBackendIdentity?: string; } -const extractDomain = (url: string): string => { - try { - const parsedUrl = new URL(url); - return parsedUrl.hostname; // Returns "kdk.cpm" - } catch (error) { - console.error("Invalid URL:", error); - return url; - } -}; - // This is only used in developer mode for debugging purposes, so we don't need full localization export const RTCConnectionStats: FC = ({ audio, video, - focusUrl, + transport, rtcBackendIdentity, - ...rest }) => { const [showModal, setShowModal] = useState(false); const [modalContents, setModalContents] = useState< @@ -76,10 +67,10 @@ export const RTCConnectionStats: FC = ({ rtcBackendIdentity:{rtcBackendIdentity} - {focusUrl && ( + {transport && (
- -  {extractDomain(focusUrl)} + +  {JSON.stringify(transport)}
)} diff --git a/src/state/CallViewModel/CallViewModel.ts b/src/state/CallViewModel/CallViewModel.ts index 3b457f719..d84953206 100644 --- a/src/state/CallViewModel/CallViewModel.ts +++ b/src/state/CallViewModel/CallViewModel.ts @@ -635,9 +635,7 @@ export function createCallViewModel$( mediaDevices, muteStates, trackProcessorState$, - logger.getChild( - "[Publisher " + connection.transport.livekit_service_url + "]", - ), + logger.getChild(`[Publisher ${JSON.stringify(connection.transport)}]`), controlledAudioDevices, ); }, @@ -829,8 +827,8 @@ export function createCallViewModel$( livekitRoom$: scope.behavior( connection$.pipe(map((c) => c?.livekitRoom)), ), - focusUrl$: scope.behavior( - connection$.pipe(map((c) => c?.transport.livekit_service_url)), + transport$: scope.behavior( + connection$.pipe(map((c) => c?.transport)), ), mediaDevices, pretendToBeDisconnected$: localMembership.reconnecting$, diff --git a/src/state/CallViewModel/localMember/LocalMember.ts b/src/state/CallViewModel/localMember/LocalMember.ts index c06190d3f..29c0da6eb 100644 --- a/src/state/CallViewModel/localMember/LocalMember.ts +++ b/src/state/CallViewModel/localMember/LocalMember.ts @@ -334,7 +334,7 @@ export const createLocalMembership$ = ({ ), tap((connection) => { logger.info( - `Local connection updated: ${connection?.transport?.livekit_service_url}`, + `Local connection updated: ${JSON.stringify(connection?.transport)}`, ); }), ), @@ -406,7 +406,7 @@ export const createLocalMembership$ = ({ scope.reconcile(localConnection$, async (connection) => { logger.info( "reconcile based on new localConnection:", - connection?.transport.livekit_service_url, + JSON.stringify(connection?.transport), ); if (connection !== null) { const publisher = createPublisherFactory(connection); diff --git a/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.ts b/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.ts index aeb6b051a..58a5f1b19 100644 --- a/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.ts +++ b/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.ts @@ -45,7 +45,7 @@ export class RtcTransportAutoDiscovery { const backendTransport = await this.tryBackendTransports(); if (backendTransport) { this.logger.info( - `Found backend transport: ${backendTransport.livekit_service_url}`, + `Found backend transport: ${JSON.stringify(backendTransport)}`, ); return backendTransport; } @@ -54,7 +54,7 @@ export class RtcTransportAutoDiscovery { const configTransport = this.tryConfigTransport(); if (configTransport) { this.logger.info( - `Found app config transport: ${configTransport.livekit_service_url}`, + `Found app config transport: ${JSON.stringify(configTransport)}`, ); return configTransport; } diff --git a/src/state/CallViewModel/remoteMembers/Connection.ts b/src/state/CallViewModel/remoteMembers/Connection.ts index 7face646a..ea9447e99 100644 --- a/src/state/CallViewModel/remoteMembers/Connection.ts +++ b/src/state/CallViewModel/remoteMembers/Connection.ts @@ -140,10 +140,10 @@ export class Connection { this.existingSFUConfig = opts.existingSFUConfig; this.roomId = opts.roomId; this.logger = logger.getChild( - "[Connection " + opts.transport.livekit_service_url + "]", + `[Connection ${JSON.stringify(opts.transport)}]`, ); this.logger.info( - `constructor: ${opts.transport.livekit_service_url} roomId: ${this.roomId} withSfuConfig?: ${opts.existingSFUConfig ? JSON.stringify(opts.existingSFUConfig) : "undefined"}`, + `constructor: roomId: ${this.roomId} withSfuConfig?: ${opts.existingSFUConfig ? JSON.stringify(opts.existingSFUConfig) : "undefined"}`, ); const { transport, client, scope } = opts; @@ -297,9 +297,7 @@ export class Connection { this.existingSFUConfig ?? (await this.getSFUConfigForRemoteConnection()); this.logger.debug( - "Starting Connection to: ", - this.transport.livekit_service_url, - "jwt: ", + "Starting Connection - jwt: ", jwt, "wss: ", url, @@ -400,17 +398,13 @@ export class Connection { * If the connection is already stopped, this is a no-op. */ public async stop(): Promise { - this.logger.debug( - `stop: disconnecing from lk room ${this.transport.livekit_service_url}`, - ); + this.logger.debug("stop: disconnecing from lk room"); if (this.stopped) return; // Mark as stopped before disconnecting so that a connect() aborted by the // disconnect sees the flag and does not report the abort as an error. this.stopped = true; await this.livekitRoom.disconnect(); this._state$.next({ state: "stopped" }); - this.logger.debug( - `stop: DONE disconnecing from lk room ${this.transport.livekit_service_url}`, - ); + this.logger.debug("stop: DONE disconnecing from lk room"); } } diff --git a/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.ts b/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.ts index e7bd9e7ae..d899fd9e2 100644 --- a/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.ts +++ b/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.ts @@ -143,9 +143,9 @@ export function createRemoteMatrixLivekitMembers$({ // Log whether the member could be matched to a LiveKit participant, // since a tile shows "waiting for media" for as long as it cannot. participant$.pipe(scope.bind()).subscribe((p) => { - const url = data$.value.connection?.transport.livekit_service_url; + const transport = data$.value.connection?.transport; logger.info( - `[RemoteMatrixLivekitMembers] ${rtcBackendIdentity}: LiveKit participant ${p ? `matched (${p.sid})` : "missing"} on ${url ?? "no connection"}`, + `[RemoteMatrixLivekitMembers] ${rtcBackendIdentity}: LiveKit participant ${p ? `matched (${p.sid})` : "missing"} on ${transport ? JSON.stringify(transport) : "no connection"}`, ); }); // will only get called once per backend identity. diff --git a/src/state/media/MemberMediaViewModel.ts b/src/state/media/MemberMediaViewModel.ts index a7f4612db..4891809e2 100644 --- a/src/state/media/MemberMediaViewModel.ts +++ b/src/state/media/MemberMediaViewModel.ts @@ -40,6 +40,7 @@ import { E2eeType } from "../../e2ee/e2eeType"; import { observeInboundRtpStreamStats$ } from "./observeRtpStreamStats"; import { type UserMediaViewModel } from "./UserMediaViewModel"; import { type ScreenShareViewModel } from "./ScreenShareViewModel"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; // TODO: Encryption status is kinda broken and thus unused right now. Remove? export enum EncryptionStatus { @@ -59,10 +60,10 @@ export interface BaseMemberMediaViewModel extends BaseMediaViewModel { */ video$: Behavior; /** - * The URL of the LiveKit focus on which this member should be publishing. - * Exposed for debugging. + * The LiveKit transport on which this member should be publishing. Exposed + * for debugging. */ - focusUrl$: Behavior; + transport$: Behavior; /** * Whether there should be a warning that this media is unencrypted. */ @@ -75,7 +76,7 @@ export interface MemberMediaInputs extends BaseMediaViewModel { livekitRoom$: Behavior; audioSource: AudioSource; videoSource: VideoSource; - focusUrl$: Behavior; + transport$: Behavior; encryptionSystem: EncryptionSystem; } @@ -86,7 +87,7 @@ export function createMemberMedia( livekitRoom$, audioSource, videoSource, - focusUrl$, + transport$, encryptionSystem, ...inputs }: MemberMediaInputs, @@ -109,7 +110,7 @@ export function createMemberMedia( return { ...createBaseMedia(inputs), video$, - focusUrl$, + transport$, unencryptedWarning$: scope.behavior( combineLatest( [audio$, video$], diff --git a/src/tile/GridTile.tsx b/src/tile/GridTile.tsx index e00186320..b83d5f5ea 100644 --- a/src/tile/GridTile.tsx +++ b/src/tile/GridTile.tsx @@ -51,6 +51,7 @@ import { type RemoteUserMediaViewModel } from "../state/media/RemoteUserMediaVie import { type UserMediaViewModel } from "../state/media/UserMediaViewModel"; import { type RingingMediaViewModel } from "../state/media/RingingMediaViewModel"; import { RingingStatus } from "./RingingStatus"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; interface TileProps { ref?: Ref; @@ -103,7 +104,7 @@ interface UserMediaTileProps extends TileProps { playbackMuted: boolean; waitingForMedia?: boolean; primaryButton?: ReactNode; - focusUrl: string | undefined; + transport: LivekitTransport | undefined; } /** @@ -120,7 +121,7 @@ const UserMediaTileInner: FC = ({ primaryButton, menu, className, - focusUrl, + transport, displayName, mxcAvatarUrl, focusable, @@ -214,7 +215,7 @@ const UserMediaTileInner: FC = ({ currentReaction={reaction ?? undefined} raisedHandOnClick={raisedHandOnClick} waitingForMedia={waitingForMedia} - focusUrl={focusUrl} + transport={transport} setVideoAspectRatio={vm.setVideoAspectRatio} audioStreamStats={audioStreamStats} videoStreamStats={videoStreamStats} @@ -279,7 +280,7 @@ const LocalUserMediaTile: FC = ({ const mirror = useBehavior(vm.mirror$); const alwaysShow = useBehavior(vm.alwaysShow$); const switchCamera = useBehavior(vm.switchCamera$); - const focusUrl = useBehavior(vm.focusUrl$); + const transport = useBehavior(vm.transport$); const latestAlwaysShow = useLatest(alwaysShow); const onSelectAlwaysShow = useCallback( @@ -334,7 +335,7 @@ const LocalUserMediaTile: FC = ({ menuStart={menuStart} menuEnd={menuEnd} focusable={focusable} - focusUrl={focusUrl} + transport={transport} {...props} /> ); @@ -356,7 +357,7 @@ const RemoteUserMediaTile: FC = ({ const waitingForMedia = useBehavior(vm.waitingForMedia$); const playbackMuted = useBehavior(vm.playbackMuted$); const playbackVolume = useBehavior(vm.playbackVolume$); - const focusUrl = useBehavior(vm.focusUrl$); + const transport = useBehavior(vm.transport$); const onSelectMute = useCallback( (e: Event) => { @@ -398,7 +399,7 @@ const RemoteUserMediaTile: FC = ({ } - focusUrl={focusUrl} + transport={transport} {...props} /> ); diff --git a/src/tile/MediaView.tsx b/src/tile/MediaView.tsx index d57441687..7b22764d5 100644 --- a/src/tile/MediaView.tsx +++ b/src/tile/MediaView.tsx @@ -33,6 +33,7 @@ import { ReactionIndicator } from "../reactions/ReactionIndicator"; import { RTCConnectionStats } from "../RTCConnectionStats"; import videoPlaceholder from "../graphics/video-placeholder.gif"; import { autoVideoFit } from "../utils/videoFit"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; interface Props extends ComponentProps { className?: string; @@ -67,9 +68,9 @@ interface Props extends ComponentProps { videoStreamStats?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats; rtcBackendIdentity?: string; /** - * The focus url, mainly for debugging purposes. + * The LiveKit transport, mainly for debugging purposes. */ - focusUrl?: string; + transport?: LivekitTransport; /** * Called whenever the aspect ratio of the video content becomes known or * otherwise changes. @@ -106,7 +107,7 @@ export const MediaView: FC = ({ audioStreamStats, videoStreamStats, rtcBackendIdentity, - focusUrl, + transport, setVideoAspectRatio: setTheirVideoAspectRatio, ...props }) => { @@ -227,7 +228,7 @@ export const MediaView: FC = ({ diff --git a/src/tile/SpotlightTile.tsx b/src/tile/SpotlightTile.tsx index 71b98e8d8..46cab2566 100644 --- a/src/tile/SpotlightTile.tsx +++ b/src/tile/SpotlightTile.tsx @@ -54,6 +54,7 @@ import { platform } from "../Platform"; import { type RingingMediaViewModel } from "../state/media/RingingMediaViewModel"; import { RingingStatus } from "./RingingStatus"; import { useRootElement } from "../RootElementContext"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; interface SpotlightItemBaseProps { ref?: Ref; @@ -74,7 +75,7 @@ interface SpotlightItemBaseProps { interface SpotlightMemberMediaItemBaseProps extends SpotlightItemBaseProps { video: TrackReferenceOrPlaceholder | undefined; unencryptedWarning: boolean; - focusUrl: string | undefined; + transport: LivekitTransport | undefined; } interface SpotlightUserMediaItemBaseProps extends SpotlightMemberMediaItemBaseProps { @@ -177,13 +178,13 @@ const SpotlightMemberMediaItem: FC = ({ }) => { const video = useBehavior(vm.video$); const unencryptedWarning = useBehavior(vm.unencryptedWarning$); - const focusUrl = useBehavior(vm.focusUrl$); + const transport = useBehavior(vm.transport$); const baseProps: SpotlightMemberMediaItemBaseProps & RefAttributes = { video: video ?? undefined, unencryptedWarning, - focusUrl, + transport, ...props, }; diff --git a/src/utils/test.ts b/src/utils/test.ts index 32e5cce17..48527184e 100644 --- a/src/utils/test.ts +++ b/src/utils/test.ts @@ -358,7 +358,7 @@ export function mockLocalMedia( participant$: constant(localParticipant), encryptionSystem: { kind: E2eeType.PER_PARTICIPANT }, livekitRoom$: constant(mockLivekitRoom({ localParticipant })), - focusUrl$: constant("https://rtc-example.org"), + transport$: constant(exampleTransport), mediaDevices, displayName$: constant(member.rawDisplayName ?? "nodisplayname"), mxcAvatarUrl$: constant(member.getMxcAvatarUrl()), @@ -401,7 +401,7 @@ export function mockRemoteMedia( participant$: constant(participant), encryptionSystem: { kind: E2eeType.PER_PARTICIPANT }, livekitRoom$: constant(livekitRoom), - focusUrl$: constant("https://rtc-example.org"), + transport$: constant(exampleTransport), pretendToBeDisconnected$: constant(false), displayName$: constant(member.rawDisplayName ?? "nodisplayname"), mxcAvatarUrl$: constant(member.getMxcAvatarUrl()), @@ -428,7 +428,7 @@ export function mockRemoteScreenShare( participant$: constant(participant), encryptionSystem: { kind: E2eeType.PER_PARTICIPANT }, livekitRoom$: constant(livekitRoom), - focusUrl$: constant("https://rtc-example.org"), + transport$: constant(exampleTransport), pretendToBeDisconnected$: constant(false), displayName$: constant(member.rawDisplayName ?? "nodisplayname"), mxcAvatarUrl$: constant(member.getMxcAvatarUrl()),