Audio flow
Audio flow в VideoGrace состоит из двух независимых направлений:
- publish - локальный микрофон захватывается, кодируется в Opus и отправляется в media transport;
- receive - удаленный audio device подключается по событию
device_connect, RTP frames принимаются, декодируются и воспроизводятся.
Control channel управляет жизненным циклом устройств. Media transport переносит только media payload и не должен создавать или удалять server-side devices сам по себе.
Для web-client основной audio transport - WebRTC:
- local microphone publish идет через
WebRTCPublishSession; - remote microphone receive идет через
WebRTCSubscribeSession; - WSM/
MediaChannelостается fallback для initial WebRTC failure или forcedvg.mediaTransport=wsm.
Local microphone publish через WebRTC
sequenceDiagram
participant UI
participant Client as VideograceClient
participant Control as Control channel
participant Mic as MicSession
participant Pub as WebRTCPublishSession
participant RTC as RTC node / WebRTC Gateway
participant Translator
UI->>Client: toggleMic(true)
Client->>Mic: start capture
Mic-->>Client: capture ready(sample_rate, channels)
Client->>Control: device_params(device_type=mic, codec=opus)
Control-->>Client: device_connect(my=1, device_id, author_ssrc, port)
Control-->>Client: local audio device created
Client->>Pub: create RTCPeerConnection + add microphone track
Pub->>Control: webrtc_offer(scope=audio, binding)
Control-->>Pub: webrtc_answer + ICE
Pub->>RTC: ICE/DTLS/SRTP Opus
RTC->>Translator: plain RTP(author_ssrc, port)
Binding для publish берется из self device_connect: device_id, author_ssrc, port, secure_key. Повторный publish после stale использует тот же sourceDevice и не отправляет новый device_params.
Remote audio receive через WebRTC
sequenceDiagram
participant Control as Control channel
participant Client as VideograceClient
participant Sub as WebRTCSubscribeSession
participant RTC as RTC node / WebRTC Gateway
participant Translator
participant Player as Audio playback
Control-->>Client: device_connect(my=0, device_type=mic, receiver_ssrc, port)
Client->>Sub: create RTCPeerConnection(recvonly)
Sub->>Control: webrtc_offer(scope=audio-subscribe, binding)
Control-->>Sub: webrtc_answer + ICE
RTC->>Translator: register receiver_ssrc / RTP init
Translator-->>RTC: plain RTP(author_ssrc)
RTC-->>Sub: WebRTC audio track
Sub->>Player: attach audio element + play()
Remote WebRTC audio использует нативный browser playback path. JS Opus decoder и AudioContext являются частью WSM fallback, а не основного WebRTC path.
Pipeline
flowchart LR
subgraph Publish[Local WebRTC publish]
Capture[Microphone capture]
Track[MediaStreamTrack audio]
PCOut[RTCPeerConnection sendonly/sendrecv]
GatewayIn[RTC node]
RTPOut[Translator RTP author_ssrc]
end
subgraph Receive[Remote WebRTC receive]
RTPIn[Translator RTP author_ssrc]
GatewayOut[RTC node]
PCIn[RTCPeerConnection recvonly]
TrackIn[Remote audio track]
Play[HTMLAudioElement playback]
end
Capture --> Track --> PCOut --> GatewayIn --> RTPOut
RTPIn --> GatewayOut --> PCIn --> TrackIn --> Play
WSM fallback audio path
WSM fallback сохраняет старую схему:
- local
MicSessionкодирует Opus в JS/WASM и отправляет RTP черезMediaMuxSocket; - remote
MediaChannelпринимает RTP поreceiver_ssrc, декодирует Opus и воспроизводит PCM; - один
MediaMuxSocketобслуживает много SSRC.
Fallback разрешен только если WebRTC не поднялся изначально в режиме auto или если выбран forced vg.mediaTransport=wsm. После уже успешного WebRTC publish/subscribe stale recovery не должен переключать audio в WSM.
Reconnect и stale behavior
stateDiagram-v2
[*] --> Capturing
Capturing --> PublishingRTC: device_connect my=1 + WebRTC publish connected
PublishingRTC --> PublishStalled: ICE/connection disconnected
PublishStalled --> RepublishRTC: local publish recovery timer
RepublishRTC --> PublishingRTC: new RTCPeerConnection, same device_id
PublishingRTC --> Stopping: toggleMic(false) / leave
Stopping --> [*]
Reconnect media transport не должен пересоздавать microphone device. После stale restore локальная WebRTC publish-сессия перезапускается с тем же device_id/author_ssrc/port.
Текущие тайминги web-client:
restore_stale_session=trueпинает active local publish recovery через700ms;ice_state/connection_state=failed- recovery через250ms;disconnectedдля audio - recovery через1200ms;- successful
connected/completedочищает pending recovery timer.
Remote audio receive recovery:
- health
stalledзапускает remote WebRTC restart; answer timeoutна restart не считается финальным error, а ставит retry с backoff;- лимит restart-попыток -
6; - после
restore_stale_session=trueremote sessions вstalled/errorполучают retry через900ms.
Invariants
device_paramsсоздает server-side microphone device; media transport его не создает.device_idживет в control lifecycle.author_ssrcиспользуется локальнойMicSessionдля отправки.receiver_ssrcиспользуется remote audio session для приема.- Повторный
device_connectдля того же remote audio device не должен создавать вторую playback session. toggleMic(false)должен остановить capture, encoder, media attach и отправитьdisconnect_device.- Reconnect media transport не должен менять
device_id. - WebRTC recovery после stale должен переиспользовать
sourceDevice, а не делать новыйdevice_params. - WSM fallback не должен включаться после уже успешного WebRTC audio endpoint.
Diagnostics
Для audio-инцидента в логах нужны:
device_id,client_id,device_type;author_ssrcилиreceiver_ssrc;- capture format: sample rate, channels;
- codec mode: Opus channels, bitrate;
- WebRTC endpoint id,
rtc_node_id, offer/answer timing; - ICE/connection state;
- first RTP packet sent/received на gateway/translator bridge;
- decoder init/reinit для WSM fallback;
- playback underrun/overrun;
- media transport close/reconnect reason.
Typical failures
- Локальный индикатор микрофона активен, но удаленный участник не слышит звук: проверить
device_params, localdevice_connect my=1, WebRTC publish offer/answer, ICE connected, first RTP на RTC node/Translator. - Remote WebRTC connected, но звука нет: проверить inbound RTP progress, audio element
play(), output device, mute state. - После reconnect звук не возвращается: проверить
restore_stale_session, local publish recovery, remote subscribe restart retry/backoff, без новогоdevice_params. - WSM неожиданно поднялся после stale: это нарушение политики; WSM допустим только initial fallback/forced mode.
- После выключения/включения микрофона появляется несколько audio sessions: проверить идемпотентность
device_connectи очистку старойMicSession.