Skip to content

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 или forced vg.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=true remote 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, local device_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.