Skip to content

Медиа-путь 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 в конференции

Подключение к конференции состоит из двух независимых частей:

  1. Control lifecycle: connect_to_conference_request, device_params, device_connect, device_disconnect.
  2. 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 другого клиента:

  1. VideograceClient вычисляет ключ устройства dev_<device_id>_<client_id>_<device_type>.
  2. В режиме auto/webrtc сначала запускается WebRTC subscribe.
  3. WebRTC session создает RTCPeerConnection, формирует offer и отправляет signaling через ControlWS.
  4. После answer и ICE connected session помечается mediaStatus=ok.
  5. Для audio создается/подключается <audio> и выполняется recovery воспроизведения (play(), output device, gesture kick).
  6. Для 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:

  1. UI включает микрофон/камеру/демонстрацию.
  2. VideograceClient сериализует local media operation, чтобы не было пересечения быстрых on/off.
  3. Local session делает capture (mic_session, cam_session, screen_session).
  4. Клиент отправляет device_params.
  5. Сервер отвечает self device_connect с device_id, author_ssrc, port, crypto params.
  6. VideograceClient запускает WebRTC publish для этого device.
  7. Publish session отправляет offer через ControlWS и ждет answer/ICE.
  8. После connected local 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/completed pending 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.