instance_settings.media_limits (publish_quality_cap: off/720p/360p/180p, stage_max_tiles: 4/9/16/25) — новая вкладка «Нагрузка» в админке, дефолты (off/25) сохраняют текущее поведение существующих инсталляций. Настройка отдаётся не только GET /admin/settings, но и в join-ответе (JoinOut) — участнику нужно иметь её на руках ДО публикации трека, а /admin/settings доступен только администратору. Потолок качества применяется через RoomOptions.publishDefaults (videoEncoding + videoSimulcastLayers на пресетах VideoPresets LiveKit) — режет битрейт верхнего слоя симулкаста, реальное разрешение WebRTC подстраивает сам. Лимит плиток — фильтрация STAGE_GRID_LAYOUTS по columns*rows в StageGrid, лишние участники уходят на страницу пагинации вместо подписки. Значение приезжает в joinState вместе с токеном ДО первого рендера LiveKitRoom (RoomPage не рендерит его, пока joinState не заполнен целиком), поэтому смена настройки не переподключает уже вошедшего участника — roomOptions пересчитывается по стабильной ссылке на joinState, которая после подключения не меняется. Проверено вживую на локальном стенде (docker compose --profile media): сохранение/персист настроек, join отдаёт актуальные значения, уже подключённый участник не разрывается при смене настройки в другом окне.
37 lines
2.3 KiB
TypeScript
37 lines
2.3 KiB
TypeScript
import { VideoPresets, type TrackPublishDefaults } from 'livekit-client'
|
||
import type { PublishQualityCap } from '@/api/conferences'
|
||
|
||
/**
|
||
* Потолок качества публикации → `TrackPublishDefaults` для `RoomOptions.publishDefaults`
|
||
* (см. `RoomPage.tsx`, `roomOptions`).
|
||
*
|
||
* Ограничивается только `videoEncoding` (битрейт/framerate верхнего слоя
|
||
* симулкаста) и набор дополнительных слоёв `videoSimulcastLayers` — НЕ
|
||
* фактическое разрешение захвата камеры (`videoCaptureDefaults`, трогать
|
||
* его не входит в задачу). WebRTC сам подстраивает реальное разрешение
|
||
* кодирования под урезанный битрейт (`degradationPreference`), поэтому
|
||
* проверять эффект нужно по фактическому битрейту исходящего видео, а не по
|
||
* заявленному разрешению потока.
|
||
*
|
||
* Слои каждого потолка — все пресеты LiveKit НИЖЕ и РАВНО потолку (без
|
||
* дефолтного «h180, h360», который иначе подставился бы сам при пустом
|
||
* `videoSimulcastLayers` и мог бы превысить потолок 180p).
|
||
*/
|
||
const PUBLISH_DEFAULTS_BY_CAP: Record<Exclude<PublishQualityCap, 'off'>, TrackPublishDefaults> = {
|
||
'180p': { videoEncoding: VideoPresets.h180.encoding, videoSimulcastLayers: [] },
|
||
'360p': { videoEncoding: VideoPresets.h360.encoding, videoSimulcastLayers: [VideoPresets.h180] },
|
||
'720p': {
|
||
videoEncoding: VideoPresets.h720.encoding,
|
||
videoSimulcastLayers: [VideoPresets.h180, VideoPresets.h360],
|
||
},
|
||
}
|
||
|
||
/**
|
||
* `off` — `undefined`: `publishDefaults` не задаётся вовсе, поведение
|
||
* библиотеки не отличается от состояния до появления настройки (см.
|
||
* критерий готовности «дефолты сохраняют текущее поведение»).
|
||
*/
|
||
export function buildPublishDefaults(cap: PublishQualityCap): TrackPublishDefaults | undefined {
|
||
return cap === 'off' ? undefined : PUBLISH_DEFAULTS_BY_CAP[cap]
|
||
}
|