Медиа-путь web-client
В web-client медиа-путь управляется VideograceClient.ts. Основной транспорт для браузерных медиа - WebRTC. MediaMuxSocket/WSM остается резервным путем для сценариев, где WebRTC не поднялся изначально или принудительно выбран режим vg.mediaTransport=wsm.
flowchart TB
VG[VideograceClient.ts]
WRTCPub[webrtc_publish_session.js<br/>локальные audio/video/screen]
WRTCSub[webrtc_subscribe_session.js<br/>удаленный audio]
WRTCVideoSub[webrtc_video_subscribe_session.js<br/>удаленные video/screen]
Mux[media_mux_socket.js]
MC[media_channel.js<br/>удаленные audio/video]
Mic[mic_session.js<br/>локальный audio]
Cam[cam_session.js<br/>локальный video]
Screen[screen_session.js<br/>локальная демонстрация]
RTP[rtp_wsm_utils.js<br/>сборка/разбор WSM frames]
VG --> WRTCPub
VG --> WRTCSub
VG --> WRTCVideoSub
VG --> MC
VG --> Mic
VG --> Cam
VG --> Screen
MC --> Mux
Mic --> Mux
Cam --> Mux
Screen --> Mux
Mux --> RTP
Ответственность модулей
| Модуль | Ответственность |
|---|---|
VideograceClient.ts |
Control lifecycle, состояние конференции, создание/удаление устройств, интеграция с UI. |
webrtc_publish_session.js |
Исходящая WebRTC-публикация: микрофон/камера/демонстрация, offer/answer через ControlWS, состояние ICE, bitrate. |
webrtc_subscribe_session.js |
Входящая WebRTC-подписка на audio, подключение/запуск audio element, проверка health. |
webrtc_video_subscribe_session.js |
Входящая WebRTC-подписка на video/screen, подключение video element, stats, recovery keyframe/decode. |
media_mux_socket.js |
Один WSMedia WebSocket, connect/reconnect, регистрация и снятие handlers по ssrc, demux входящих frames. |
media_channel.js |
Рендеринг удаленного audio/video, lifecycle decoder'а, RTP init, ForceKeyFrame. |
mic_session.js |
Захват локального микрофона, Opus encode, RTP packetization, отправка через mux. |
cam_session.js |
Захват локальной камеры, H.264 encode, разбиение на RTP, обработка RTCP keyframe. |
screen_session.js |
Захват локальной демонстрации, H.264 encode, разбиение на RTP. |
rtp_wsm_utils.js |
Сборка/разбор WSM binary header, RTP helpers, AES-GCM helpers для crypto payload. |
Режим транспорта
Режим во время выполнения задается localStorage.vg.mediaTransport:
| Значение | Поведение |
|---|---|
auto или отсутствует |
Сначала WebRTC. Если WebRTC не поднялся при первичном subscribe/publish, допускается WSM fallback. |
webrtc |
Только WebRTC. Ошибка WebRTC не переключает медиа в WSM. |
wsm |
Принудительно WSM для совместимости/диагностики. |
Важное ограничение: если WebRTC уже был поднят и затем стал stalled после stale/reconnect, recovery не должен деградировать в WSM. В этом случае выполняется WebRTC restart/backoff; WSM допустим только как резервный путь при первичном подключении или как принудительный режим.
Текущий browser-инвариант
flowchart LR
One[Один MediaMuxSocket]
Many[Много SSRC handlers]
Device[Lifecycle устройств через ControlWS]
Device --> Many
Many --> One
Браузерный медиа-путь не должен создавать отдельный media WebSocket на каждое устройство. Если добавляется новый тип медиа, он должен зарегистрировать новый ssrc в MediaMuxSocket.
Для WebRTC действует другой инвариант: одно remote/local устройство соответствует одной RTCPeerConnection, но signaling и lifecycle все равно идут через ControlWS. Создание/удаление устройств выполняется только через control commands, а не через WebRTC callbacks.
Сериализация локальных операций
Быстрые операции выключения/включения микрофона и камеры сериализуются на уровне VideograceClient. Это не Safari-specific workaround; сериализация защищает device lifecycle от пересечения операций startLocalCapture, device_params, attachRemote, disconnect_device.
sequenceDiagram
participant UI
participant VG as VideograceClient
participant Session as Mic/Camera Session
participant Control as ControlWS
participant Mux as MediaMuxSocket
UI->>VG: toggleCam(true)
VG->>Session: startLocalCapture()
VG->>Control: device_params
Control-->>VG: device_connect my=1
VG->>Session: attachRemote(author_ssrc, port)
Session->>Mux: register author_ssrc
Session->>Mux: send RTP frames
Reconnect mux
MediaMuxSocket владеет reconnect'ом общего media transport. При reconnect он вызывает зарегистрированные handlers:
- локальные media sessions выставляют
_canEncode=true; - удаленные media channels повторно отправляют RTP init;
- удаленные video channels повторно запрашивают keyframe.
Создание устройств во время media reconnect не повторяется.
Lifecycle media в конференции
Подключение к конференции состоит из двух независимых частей:
- Control lifecycle:
connect_to_conference_request,device_params,device_connect,device_disconnect. - Lifecycle media transport: WebRTC/WSM publish и subscribe для конкретных device id/SSRC.
sequenceDiagram
participant UI
participant VG as VideograceClient
participant Control as ControlWS
participant RTC as WebRTC session
participant Remote as Удаленный peer/device
UI->>VG: joinConference(has_microphone=true)
VG->>Control: connect_to_conference_request
Control-->>VG: connect_to_conference_response
VG->>Control: device_params(локальные mic/cam)
Control-->>VG: device_connect(self + удаленные devices)
VG->>RTC: publish локальных devices
VG->>RTC: subscribe удаленных devices
RTC-->>VG: media health / состояние connection
device_connect является источником правды для media sessions. Если пришел remote audio/video device, VideograceClient создает subscribe session. Если пришел self device после device_params, клиент привязывает уже захваченную local session к server-side device id и запускает publish.
Входящий RTC: удаленное воспроизведение
Входящий поток (remote audio, remote video, remote screen) создается из device_connect другого клиента:
VideograceClientвычисляет ключ устройстваdev_<device_id>_<client_id>_<device_type>.- В режиме
auto/webrtcсначала запускается WebRTC subscribe. - WebRTC session создает
RTCPeerConnection, формирует offer и отправляет signaling через ControlWS. - После answer и ICE connected session помечается
mediaStatus=ok. - Для audio создается/подключается
<audio>и выполняется recovery воспроизведения (play(), output device, gesture kick). - Для video создается/подключается
<video>в remote slot; при decode stalls запрашивается keyframe.
Состояния health:
| Состояние | Значение |
|---|---|
ok |
RTP/frames/audio progress есть. |
connecting |
PeerConnection создана, идет offer/answer/ICE. |
stalled |
PeerConnection может быть connected, но нет входящего RTP/progress. |
error |
Исчерпан restart budget или WebRTC изначально не поднялся в принудительном режиме webrtc. |
Перезапуск входящего WebRTC:
- health probe при
stalledвызываетscheduleRemoteWebRTCRestart; - если уже есть restart timer, новый не ставится;
- текущая WebRTC session останавливается с reason
webrtc_stalled_restart; - subscribe создается заново для того же
sourceDevice; - если попытка завершилась
answer timeoutили другой ошибкой, session остаетсяstalled, а не сразуerror; - следующий retry ставится с backoff:
700ms,1200ms,2000ms,3200ms,5000ms, затем8000ms; - общий лимит restart-попыток -
6; - после лимита session получает
mediaStatus=error, UI может показать красный RTC.
После restore_stale_session=true клиент дополнительно проходит по активным remote WebRTC sessions. Все sessions в stalled/error планируются на retry примерно через 900ms, чтобы не ждать следующего health tick.
WSM fallback для входящих потоков:
- разрешен при первичном WebRTC failure в режиме
auto; - разрешен при принудительном
vg.mediaTransport=wsm; - не разрешен для уже установленной WebRTC session после stale/stall/restart.
Исходящий RTC: локальный publish
Исходящие потоки (mic, cam, screen) создаются после локального capture и серверного device_params:
- UI включает микрофон/камеру/демонстрацию.
VideograceClientсериализует local media operation, чтобы не было пересечения быстрых on/off.- Local session делает capture (
mic_session,cam_session,screen_session). - Клиент отправляет
device_params. - Сервер отвечает self
device_connectсdevice_id,author_ssrc,port, crypto params. VideograceClientзапускает WebRTC publish для этого device.- Publish session отправляет offer через ControlWS и ждет answer/ICE.
- После
connectedlocal media status становитсяok.
Recovery локального publish:
connection_state/ice_state=failedпланирует recovery через250ms;disconnectedдля audio планирует recovery через1200ms;disconnectedдля video/screen планирует recovery через1800ms;- после
restore_stale_session=trueвсе активные local WebRTC publish sessions пинаются на recovery через700ms; - при успешном
connected/completedpending recovery timer очищается; - recovery не отправляет новый
device_params, а переиспользуетsourceDeviceтекущей publish session; - старый
RTCPeerConnectionостанавливается, после чего создается новая publish session для того же server-side device id.
sequenceDiagram
participant Browser
participant VG as VideograceClient
participant Control as ControlWS
participant Pub as WebRTCPublishSession
participant RTC as RTC node
Browser--xControl: stale / 1006 / потеря в фоне
Control->>Control: reconnect с тем же control_instance_id
Control-->>VG: connect_response(restore_stale_session=true)
VG->>VG: scheduleLocalWebRTCPublishRecoveryForActive(700ms)
VG->>Pub: stop old RTCPeerConnection
VG->>Pub: start publish с тем же device_id/sourceDevice
Pub->>Control: WebRTC publish offer
Control-->>Pub: answer + ICE
Pub-->>VG: connected
Media policy при stale restore
При stale restore нельзя повторять полный conference join. Повторный connect_to_conference_request или повторный device_params для уже активного local device может создать дубликаты устройств. Поэтому политика такая:
- control-состояние восстанавливается через
restore_stale_session; activeConferenceTagиisInConferenceвосстанавливаются изstale_conference_tag;- существующие remote sessions сохраняют
sourceDeviceи перезапускаются только на уровне transport; - существующие local publish sessions сохраняют
sourceDeviceи republish-ятся без новогоdevice_params; - stale/duplicate self
device_connectигнорируется, если он не соответствует pending local create sequence; - stale local created device без активной local session отключается через
disconnectLocalPublishedDevice.
Диагностика
Полезные маркеры логов:
| Маркер | Что означает |
|---|---|
restored stale control session, keeping media sessions alive |
Сервер восстановил command session; полный rejoin не нужен. |
remote WebRTC restart did not connect; scheduling retry |
Restart subscribe не получил answer/ICE, будет повтор. |
remote WebRTC restart limit reached |
Restart budget исчерпан, session переходит в error. |
recovering local WebRTC publish |
Запущен republish исходящего mic/cam/screen. |
remote WSM playback disabled; keeping WebRTC-only media |
Код попытался уйти в WSM после уже установленного RTC, fallback заблокирован. |
remote audio subscribed via WebRTC / remote video subscribed via WebRTC |
Входящий поток успешно поднят через WebRTC. |
mic published via WebRTC / video published via WebRTC |
Исходящий поток успешно опубликован через WebRTC. |
При разборе инцидента важно разделять:
- control reconnect: WebSocket
1006,connect_response,restore_stale_session; - inbound subscribe: remote audio/video restart,
answer timeout,no inbound RTP progress; - outbound publish: local mic/cam
disconnected/failed, publish retry; - playback/autoplay: audio/video element
play(),recoverPlayback, output device.