diff --git a/CHANGELOG.md b/CHANGELOG.md index 37f5c6e2..0967aebe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to - 🐛(helm) render periodSeconds and failureThreshold on probes - 🐛(backend) report the app release to Sentry instead of "NA" - 🐛(frontend) play the waiting room notification sound on every arrival +- 🐛(frontend) apply the saved reception resolution when joining a meeting #1714 - 🐛(backend) acknowledge unknown LiveKit webhook events instead of 422 - 🔒️(backend) enforce display name setting on rename API - 🔒️(backend) reject inactive users in resource server backend diff --git a/src/frontend/src/features/rooms/livekit/components/VideoResolutionSubscription.tsx b/src/frontend/src/features/rooms/livekit/components/VideoResolutionSubscription.tsx index b045140a..d1d907a0 100644 --- a/src/frontend/src/features/rooms/livekit/components/VideoResolutionSubscription.tsx +++ b/src/frontend/src/features/rooms/livekit/components/VideoResolutionSubscription.tsx @@ -1,17 +1,16 @@ import { useEffect } from 'react' import { useRoomContext } from '@livekit/components-react' import { - type RemoteParticipant, + type RemoteTrack, type RemoteTrackPublication, RoomEvent, Track, - VideoQuality, } from 'livekit-client' import { useSnapshot } from 'valtio' import { userChoicesStore } from '@/stores/userChoices' /** - * Sets initial video quality for new participants as they join. + * Applies the saved reception quality to every remote camera. * LiveKit doesn't allow handling video quality preferences at the room level. */ export const VideoResolutionSubscription = () => { @@ -19,30 +18,40 @@ export const VideoResolutionSubscription = () => { const room = useRoomContext() useEffect(() => { - if (!room) return + if (!room || videoSubscribeQuality === undefined) return - const handleTrackPublished = ( - publication: RemoteTrackPublication, - _participant: RemoteParticipant - ) => { - // By default, the maximum quality is set to high + const applyQuality = (publication: RemoteTrackPublication) => { if ( - videoSubscribeQuality === undefined || - videoSubscribeQuality === VideoQuality.HIGH - ) - return - - if ( - publication.kind === Track.Kind.Video && - publication.source !== Track.Source.ScreenShare + publication.kind !== Track.Kind.Video || + publication.source === Track.Source.ScreenShare || + publication.videoQuality === videoSubscribeQuality ) { - publication.setVideoQuality(videoSubscribeQuality) + return } + publication.setVideoQuality(videoSubscribeQuality) } + // Cameras we are already receiving: those published before this effect ran, + // and all of them again whenever the preference changes mid-call. + room.remoteParticipants.forEach((participant) => + participant.videoTrackPublications.forEach(applyQuality) + ) + + const handleTrackPublished = (publication: RemoteTrackPublication) => + applyQuality(publication) + + // TrackPublished is not raised for cameras that were already sending when we + // joined, but it is the earliest point for the ones that start after us. + const handleTrackSubscribed = ( + _track: RemoteTrack, + publication: RemoteTrackPublication + ) => applyQuality(publication) + room.on(RoomEvent.TrackPublished, handleTrackPublished) + room.on(RoomEvent.TrackSubscribed, handleTrackSubscribed) return () => { room.off(RoomEvent.TrackPublished, handleTrackPublished) + room.off(RoomEvent.TrackSubscribed, handleTrackSubscribed) } }, [room, videoSubscribeQuality]) diff --git a/src/frontend/src/features/settings/components/tabs/VideoTab.tsx b/src/frontend/src/features/settings/components/tabs/VideoTab.tsx index 7b307031..39f24411 100644 --- a/src/frontend/src/features/settings/components/tabs/VideoTab.tsx +++ b/src/frontend/src/features/settings/components/tabs/VideoTab.tsx @@ -34,7 +34,7 @@ const EMPTY_PROPS = {} export const VideoTab = ({ id }: VideoTabProps) => { const { t } = useTranslation('settings', { keyPrefix: 'video' }) const room = useRoomContext() - const { localParticipant, remoteParticipants } = room + const { localParticipant } = room const { videoDeviceId, @@ -88,22 +88,6 @@ export const VideoTab = ({ id }: VideoTabProps) => { }) } - /** - * Updates video quality for all existing remote video tracks when user preference changes. - * LiveKit doesn't support setting video quality preferences at the room level for remote participants, - * so this function applies the selected quality to all existing remote video tracks. - * Hook useVideoResolutionSubscription updates quality preferences of new participants joining. - */ - const updateExistingRemoteVideoQuality = (selectedQuality: VideoQuality) => { - remoteParticipants.forEach((participant) => { - participant.videoTrackPublications.forEach((publication) => { - if (publication.videoQuality !== selectedQuality) { - publication.setVideoQuality(selectedQuality) - } - }) - }) - } - useEffect(() => { let videoTrack: LocalVideoTrack | null = null @@ -231,9 +215,7 @@ export const VideoTab = ({ id }: VideoTabProps) => { selectedKey={videoSubscribeQuality?.toString()} onSelectionChange={(key) => { if (key == undefined) return - const selectedQuality = Number(String(key)) - saveVideoSubscribeQuality(selectedQuality) - updateExistingRemoteVideoQuality(selectedQuality) + saveVideoSubscribeQuality(Number(String(key))) }} style={{ width: '100%',