diff --git a/app/components/Participant.tsx b/app/components/Participant.tsx index 3a1db3e8..5a18793e 100644 --- a/app/components/Participant.tsx +++ b/app/components/Participant.tsx @@ -1,7 +1,7 @@ import { VisuallyHidden } from '@radix-ui/react-visually-hidden' import { forwardRef, useEffect, useMemo } from 'react' import { Flipped } from 'react-flip-toolkit' -import { of } from 'rxjs' +import { combineLatest, fromEvent, map, of, switchMap } from 'rxjs' import { useSubscribedState } from '~/hooks/rxjsHooks' import { useDeadPulledTrackMonitor } from '~/hooks/useDeadPulledTrackMonitor' import { useRoomContext } from '~/hooks/useRoomContext' @@ -26,18 +26,40 @@ import { OptionalLink } from './OptionalLink' import { Tooltip } from './Tooltip' import { VideoSrcObject } from './VideoSrcObject' +function useMid(track?: MediaStreamTrack) { + const { peer } = useRoomContext() + const transceivers$ = useMemo( + () => + combineLatest([ + peer.peerConnection$, + peer.peerConnection$.pipe( + switchMap((peerConnection) => fromEvent(peerConnection, 'track')) + ), + ]).pipe(map(([pc]) => pc.getTransceivers())), + [peer.peerConnection$] + ) + const transceivers = useSubscribedState(transceivers$, []) + if (!track) return null + return transceivers.find( + (t) => t.sender.track === track || t.receiver.track === track + )?.mid +} + +interface Props { + flipId: string + isScreenShare?: boolean + showDebugInfo?: boolean + user: User + audioTrack?: MediaStreamTrack + videoTrack?: MediaStreamTrack + isSelf?: boolean + pinnedId?: string + setPinnedId: (id?: string) => void +} + export const Participant = forwardRef< HTMLDivElement, - JSX.IntrinsicElements['div'] & { - flipId: string - isScreenShare?: boolean - user: User - audioTrack?: MediaStreamTrack - videoTrack?: MediaStreamTrack - isSelf?: boolean - pinnedId?: string - setPinnedId: (id?: string) => void - } + JSX.IntrinsicElements['div'] & Props >( ( { @@ -49,11 +71,13 @@ export const Participant = forwardRef< audioTrack, pinnedId, setPinnedId, + showDebugInfo, }, ref ) => { const { data } = useUserMetadata(user.name) const { traceLink, peer, dataSaverMode } = useRoomContext() + const peerConnection = useSubscribedState(peer.peerConnection$) useDeadPulledTrackMonitor( user.tracks.video, @@ -90,6 +114,9 @@ export const Participant = forwardRef< const packetLoss = useSubscribedState(packetLoss$, 0) + const audioMid = useMid(audioTrack) + const videoMid = useMid(videoTrack) + return (
{data.displayName} + {showDebugInfo && peerConnection && ( + + {' '} + {[ + audioMid && `audio mid: ${audioMid}`, + videoMid && `video mid: ${videoMid}`, + ] + .filter(Boolean) + .join(' ')} + + )}
)} diff --git a/app/components/PullAudioTracks.tsx b/app/components/PullAudioTracks.tsx index 9db4fbc5..fe75ec0a 100644 --- a/app/components/PullAudioTracks.tsx +++ b/app/components/PullAudioTracks.tsx @@ -22,12 +22,17 @@ export const PullAudioTracks: FC = ({ - setAudioTrackMap({ ...audioTrackMap, [id]: track }) + setAudioTrackMap((previous) => ({ + ...previous, + [id]: track, + })) } onTrackRemoved={(id) => { - const update = { ...audioTrackMap } - delete update[id] - setAudioTrackMap(update) + setAudioTrackMap((previous) => { + const update = { ...previous } + delete update[id] + return update + }) }} /> {children} diff --git a/app/routes/_room.$roomName.room.tsx b/app/routes/_room.$roomName.room.tsx index 651c958d..4af2ae77 100644 --- a/app/routes/_room.$roomName.room.tsx +++ b/app/routes/_room.$roomName.room.tsx @@ -44,19 +44,8 @@ export const loader = async ({ request, context }: LoaderFunctionArgs) => { }) } -function useGridDebugControls( - { - initialCount, - defaultEnabled, - }: { - initialCount: number - defaultEnabled: boolean - } = { initialCount: 0, defaultEnabled: false } -) { - const [enabled, setEnabled] = useState(defaultEnabled) - const [fakeUsers, setFakeUsers] = useState( - Array.from({ length: initialCount }).map(() => nanoid()) - ) +function useDebugEnabled() { + const [enabled, setEnabled] = useState(false) useEffect(() => { const handler = (e: KeyboardEvent) => { @@ -72,6 +61,20 @@ function useGridDebugControls( } }, [enabled]) + return enabled +} + +function useGridDebugControls({ + initialCount, + enabled, +}: { + initialCount: number + enabled: boolean +}) { + const [fakeUsers, setFakeUsers] = useState( + Array.from({ length: initialCount }).map(() => nanoid()) + ) + const GridDebugControls = useCallback( () => enabled ? ( @@ -128,8 +131,9 @@ function JoinedRoom({ bugReportsEnabled }: { bugReportsEnabled: boolean }) { room: { otherUsers, websocket, identity }, } = useRoomContext() + const debugEnabled = useDebugEnabled() const { GridDebugControls, fakeUsers } = useGridDebugControls({ - defaultEnabled: false, + enabled: debugEnabled, initialCount: 0, }) @@ -224,6 +228,7 @@ function JoinedRoom({ bugReportsEnabled }: { bugReportsEnabled: boolean }) { audioTrack={userMedia.audioStreamTrack} pinnedId={pinnedId} setPinnedId={setPinnedId} + showDebugInfo={debugEnabled} /> )} @@ -238,6 +243,7 @@ function JoinedRoom({ bugReportsEnabled }: { bugReportsEnabled: boolean }) { videoTrack={userMedia.screenShareVideoTrack} pinnedId={pinnedId} setPinnedId={setPinnedId} + showDebugInfo={debugEnabled} /> )} {actorsOnStage.map((user) => ( @@ -254,6 +260,7 @@ function JoinedRoom({ bugReportsEnabled }: { bugReportsEnabled: boolean }) { audioTrack={audioTrack} pinnedId={pinnedId} setPinnedId={setPinnedId} + showDebugInfo={debugEnabled} > )} @@ -267,6 +274,7 @@ function JoinedRoom({ bugReportsEnabled }: { bugReportsEnabled: boolean }) { isScreenShare pinnedId={pinnedId} setPinnedId={setPinnedId} + showDebugInfo={debugEnabled} /> )} @@ -293,6 +301,7 @@ function JoinedRoom({ bugReportsEnabled }: { bugReportsEnabled: boolean }) { flipId={uid.toString()} pinnedId={pinnedId} setPinnedId={setPinnedId} + showDebugInfo={debugEnabled} > )}