42 lines
1.9 KiB
TypeScript
42 lines
1.9 KiB
TypeScript
|
|
import { useEffect } from 'react';
|
||
|
|
import { useAtomValue } from 'jotai';
|
||
|
|
import { CallEmbed } from '../plugins/call';
|
||
|
|
import { settingsAtom } from '../state/settings';
|
||
|
|
import { useStateEvent } from './useStateEvent';
|
||
|
|
import { StateEvent } from '../../types/matrix/room';
|
||
|
|
import { buildQualityPayload, RoomQualityContent } from '../utils/callQuality';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* [P5-31] Apply the user's call quality settings (clamped by any room-level
|
||
|
|
* cap) to the Element Call fork via the `io.lotus.set_quality` widget action.
|
||
|
|
*
|
||
|
|
* The fork stores the settings and re-applies them on every (re)publish and
|
||
|
|
* reconnect, so we only need to (re)send when the payload changes or the widget
|
||
|
|
* becomes ready — no need to poll the track lifecycle here.
|
||
|
|
*/
|
||
|
|
export function useCallQuality(embed: CallEmbed): void {
|
||
|
|
const { callAudioBitrate, screenshareBitrate, screenshareFramerate } = useAtomValue(settingsAtom);
|
||
|
|
|
||
|
|
const roomQualityEvent = useStateEvent(embed.room, StateEvent.LotusRoomQuality);
|
||
|
|
const roomCaps = roomQualityEvent?.getContent<RoomQualityContent>();
|
||
|
|
|
||
|
|
// Depend on the primitive cap values (not the event object) so re-renders
|
||
|
|
// don't resend needlessly.
|
||
|
|
const audioCap = roomCaps?.audio_max_kbps;
|
||
|
|
const ssCap = roomCaps?.screenshare_max_kbps;
|
||
|
|
const fpsCap = roomCaps?.screenshare_max_fps;
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const payload = buildQualityPayload(
|
||
|
|
{ callAudioBitrate, screenshareBitrate, screenshareFramerate },
|
||
|
|
{ audio_max_kbps: audioCap, screenshare_max_kbps: ssCap, screenshare_max_fps: fpsCap },
|
||
|
|
);
|
||
|
|
const send = (): void => embed.control.setQuality(payload);
|
||
|
|
// Send now (settings are sticky fork-side even if tracks aren't up yet) and
|
||
|
|
// again once the widget signals ready, in case the transport wasn't up.
|
||
|
|
send();
|
||
|
|
const off = embed.onReady(send);
|
||
|
|
return off;
|
||
|
|
}, [embed, callAudioBitrate, screenshareBitrate, screenshareFramerate, audioCap, ssCap, fpsCap]);
|
||
|
|
}
|