Кнопка «Фон» в тулбаре комнаты и выбор фона в превью на входе: три готовые
сцены и свои картинки из профиля. Фон применяется процессором к самому
публикуемому треку (`LocalVideoTrack.setProcessor`), а НЕ пересозданием
`RoomOptions` — ссылка на них обязана оставаться стабильной, иначе
`LiveKitRoom` переподключается к комнате.
Фича только для ДЕСКТОПА, и «десктоп» определяется по возможностям устройства
(`pointer: fine` + `hover: hover` + `maxTouchPoints`), а НЕ по ширине окна:
узкое окно на десктопе — всё ещё десктоп, а широкий планшет — всё ещё планшет,
который сегментация греет. На мобильном кнопки нет вовсе, а не задизейбленной.
Ассеты сегментации отдаются СО СВОЕГО домена: библиотека по умолчанию тянет
wasm с jsdelivr, а модель с storage.googleapis.com, и в закрытом контуре фича
молча не работала бы. Модель (Apache 2.0, см. NOTICE.txt) лежит в репозитории,
wasm-рантайм (~19 МБ) копируется из node_modules плагином сборки. Сама
библиотека и модель грузятся ЛЕНИВО — только когда фон реально включают, вход
в конференцию не стал медленнее.
Три дефолтные сцены — собственные векторные рисунки (`design/backgrounds/`),
а не фотографии из интернета: у нарисованной сцены нет чужой лицензии, а
продукт расходится по инсталляциям, и проверять права на каждую копию некому.
Свои картинки — в профиле, до 10 штук, с уменьшением до 1280px и переводом в
WebP прямо в браузере перед отправкой. Удаление применённого сейчас фона
сбрасывает выбор на «без фона»: хранится ключ записи, а не URL картинки.
Смена камеры фон не теряет (`restartTrack` перезапускает процессор сам),
выключение и включение камеры — навешивает его на новый трек заново.
⚠️ Прокси dev-сервера для `/media/` — обязательно со слэшем: ключ `/media`
Vite матчит префиксом и перехватывает заодно `/mediapipe/...`, из-за чего
модель получала 404 и фон молча не включался.
47 lines
2.1 KiB
TypeScript
47 lines
2.1 KiB
TypeScript
import { useMyBackgrounds } from '@/hooks/useMyBackgrounds'
|
||
import { useVirtualBackground } from '@/hooks/useVirtualBackground'
|
||
import { BackgroundDialog } from '@/components/room/BackgroundDialog'
|
||
|
||
interface VirtualBackgroundControllerProps {
|
||
/** Открыта ли модалка выбора; сам фон применяется независимо от этого. */
|
||
open: boolean
|
||
onClose: () => void
|
||
/** Аутентифицированный участник — только у него есть свои картинки (гость их иметь не может). */
|
||
canManageOwn: boolean
|
||
}
|
||
|
||
/**
|
||
* Живёт ВНУТРИ `<LiveKitRoom>` и держит замену фона включённой всё время
|
||
* пребывания в комнате.
|
||
*
|
||
* Почему это отдельный компонент, а не хук прямо в `RoomPage`:
|
||
* `useVirtualBackground` читает локального участника через `useLocalParticipant`,
|
||
* а тот берёт комнату из `RoomContext` — контекст создаётся самим
|
||
* `<LiveKitRoom>`, и в теле `RoomPage` (которое этот элемент только
|
||
* возвращает) его ещё нет.
|
||
*
|
||
* Компонент смонтирован ВСЁ время, пока модуль включён, а не только пока
|
||
* открыта модалка: фон должен переживать закрытие окна выбора, выключение и
|
||
* повторное включение камеры и смену устройства (см. докстринг хука).
|
||
*/
|
||
export function VirtualBackgroundController({
|
||
open,
|
||
onClose,
|
||
canManageOwn,
|
||
}: VirtualBackgroundControllerProps) {
|
||
const { items } = useMyBackgrounds(canManageOwn)
|
||
const { backgroundKey, selectBackground, status } = useVirtualBackground(true, items)
|
||
|
||
if (!open) return null
|
||
return (
|
||
<BackgroundDialog
|
||
onClose={onClose}
|
||
value={backgroundKey}
|
||
onChange={selectBackground}
|
||
customBackgrounds={items}
|
||
status={status}
|
||
canManageOwn={canManageOwn}
|
||
/>
|
||
)
|
||
}
|