Перейти к основному содержимому

Jitsi: нативный клиент lib-jitsi-meet на Next.js 16

Как фронт PIN входит в медиа-комнату нативной библиотекой lib-jitsi-meet (без веб-SPA jitsi-meet, без iframe) и мостит медиа-события в собственный SignalR-мессенджер. Медиа идёт клиент ↔ SFU; SignalR — только UI-нотификации и приглашения (авторитет ростера — presence-вебхук prosody, jitsi-rooms.md).

Поток входа

  1. Получить дескриптор входа. Из REST (start/answer/join/media-token) или из hub-инвайта (CallInvite/ConferenceInvite) приходит MediaJoinModel: { RoomName, AccessToken (JWT), SignalingUrl, MediaDomain, IceServers, ExpiresAt }.
  2. JitsiConnection. Собрать options из дескриптора (serviceUrl = SignalingUrl, hosts.domain = MediaDomain, JWT = AccessToken) → new JitsiMeetJS.JitsiConnection(...)connect().
  3. initJitsiConference. По событию CONNECTION_ESTABLISHEDconnection.initJitsiConference(RoomName, conferenceOptions).
  4. Локальные треки. JitsiMeetJS.createLocalTracks({ devices: ['audio','video'] }); демонстрация экрана — createLocalTracks({ devices: ['desktop'] }) только при context.features['screen-sharing'] (jitsi-jwt.md).
  5. conference.join() → добавить локальные треки.
  6. Подписка на события (см. ниже) и мост в SignalR/Zustand.
  7. Обновление токена. До ExpiresAt — запросить новый MediaJoinModel (media-token), пересоздать JitsiConnection при необходимости (в Jitsi нет refresh-flow, jitsi-jwt.md).

События lib-jitsi-meet → мост в SignalR/стор

Клиентские события — источник живого UI; авторитетный состав приходит отдельно через hub (MediaParticipant, из presence-вебхука). Мост односторонний: события Jitsi обновляют локальный UI-стор, но ростер сверяется с hub-проекцией.

Событие JitsiConferenceEventsЗначение на проводеUI-действие
CONFERENCE_JOINEDconference.joinedвошли; если features.recording && isModerator — стартовать запись (ниже)
CONFERENCE_LEFT / CONFERENCE_FAILEDconference.left / conference.failedвыход/ошибка → закрыть комнату в UI
USER_JOINEDconference.userJoinedдобавить тайл (сверить с hub-ростером)
USER_LEFTconference.userLeftубрать тайл
TRACK_ADDEDconference.trackAddedприклеить медиа-трек к тайлу
RECORDER_STATE_CHANGEDconference.recorderStateChangedиндикатор записи (on/off/pending)
DOMINANT_SPEAKER_CHANGEDconference.dominantSpeakerподсветить активного спикера

Авто-запись (модератор)

При CONFERENCE_JOINED, если context.features.recording === true и occupant — модератор (jitsi-jwt.md), клиент стартует запись через RecordingManager:

conference.startRecording({
mode: 'file',
appData: JSON.stringify({
file_recording_metadata: { pin: { conferenceId, tenantId } }
})
})

mode: 'file' (из recordingConstants.mode.FILE) — запись в файл (не стрим). appData.file_recording_metadata пробрасывается до jibri: mod_jibri_session.lua обогащает его initiator и conference_details.session_id, а pin.* сохраняется до finalize-скрипта (recording-pipeline.md). Живой статус записи — RECORDER_STATE_CHANGED (значения on/off/pending); авторитетная готовность файла — отдельно через hub RecordingState(Available) и REST-скачивание.

FSD-раскладка (talent.frontends.pin / front-end)

СлойСодержимое
shared/lib/jitsiобёртка над lib-jitsi-meet: init connection/conference, createLocalTracks, типы событий
entities/callмодели CallSession/Conference/Participant (Zod-схемы ответов), селекторы
features/call-start, features/call-answer, features/call-screen-share, features/call-recordпользовательские действия (features не импортируют features)
widgets/call-roomUI комнаты: сетка тайлов, контролы, индикатор записи
shared/store (Zustand call-store)живое UI-состояние комнаты (участники, треки, статус записи)
  • Компоненты комнаты — 'use client'; загрузка библиотеки — next/dynamic с ssr: false (WebRTC/window недоступны в RSC/SSR).
  • lib-jitsi-meet не публикуется в npm — вендорится из thirdparty/jitsi/lib-jitsi-meet (сборка npm run build → UMD/ESM, ../README.md); версия фиксируется в монорепо.
  • Внешний ввод (ответы REST/hub) валидируется Zod в entities/call перед попаданием в стор.

Конфиг-кнобы клиента

КнобЗначениеЗачем
disableThirdPartyRequeststrueникаких внешних вызовов (аналитика/gravatar) — приватность + self-host
p2p.enabledtrue для 1:1медиа напрямую между двумя (JVB не нужен); при 3+ — escalation на SFU
serviceUrl / hosts.domainиз SignalingUrl/MediaDomainendpoint prosody

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы