콘텐츠로 이동

미디어 파이프라인

Gateway의 미디어 경계는 고정되어 있습니다. WebRTC 쪽은 Opus 48 kHz, /sohri 인바운드 쪽은 PCM 16 kHz mono signed 16-bit little-endian입니다. 아웃바운드 PCM은 16·24·48 kHz를 수용하지만 WebRTC 전송 전에는 항상 48 kHz Opus로 정규화합니다.

flowchart LR
    subgraph IN["마이크 → 백엔드"]
        MIC["WebRTC Opus<br/>48 kHz"]
        DEC["Opus decode<br/>PCM 48 kHz"]
        DOWN["FIR decimator<br/>48 → 16 kHz"]
        CHUNK["PCM s16le mono<br/>3,200 B / 100 ms"]
        WSI["세션 전용 WSS<br/>binary frame"]
        MIC --> DEC --> DOWN --> CHUNK --> WSI
    end

    subgraph OUT["백엔드 → 스피커"]
        WSO["WSS binary<br/>WAV 또는 raw PCM"]
        RATE["format 해석<br/>16 / 24 / 48 kHz"]
        UP["필요 시 FIR<br/>16→48 또는 24→48"]
        FRAME["PCM 48 kHz<br/>960 samples / 20 ms"]
        ENC["Opus encode<br/>20 ms"]
        RTP["paced RTP<br/>WebRTC outbound track"]
        WSO --> RATE --> UP --> FRAME --> ENC --> RTP
    end

인바운드: Opus 48 kHz → PCM 16 kHz

클라이언트의 remote audio track을 받으면 RTP payload를 Opus로 디코딩합니다. 디코딩한 48 kHz mono PCM은 상태형 polyphase FIR decimator로 16 kHz로 낮추고, 다음 고정 크기로 누적합니다.

16,000 samples/s × 0.1 s × 2 bytes = 3,200 bytes

완성된 3,200 B 청크마다 세션 전용 /sohri WSS의 binary frame 하나로 보냅니다. 마지막에 남은 100 ms 미만 조각은 전송하지 않습니다. 이 framing은 백엔드 EPD가 기대하는 100 ms cadence를 유지합니다.

readInboundAudio 경로는 RTP read, Opus decode, sample count와 PCM 송신 실패를 각각 관측합니다. Opus decoder·decimator는 세션별 인스턴스이므로 필터 상태가 섞이지 않습니다.

아웃바운드: PCM 16/24/48 kHz → Opus 48 kHz

Gateway는 두 TTS wire를 지원합니다.

레거시 WAV

tts_audio_metadata 뒤의 binary frame 하나에서 RIFF/WAV header를 파싱합니다. mono PCM s16le와 16·24·48 kHz만 허용합니다. header에 기록된 실제 sample rate를 사용하며 설정값만 믿지 않습니다.

ttsStream=v1

백엔드는 tts_stream_start JSON으로 format을 먼저 알리고 header 없는 raw PCM binary 청크를 연속 전송한 뒤 tts_stream_end로 닫습니다. Gateway는 start의 sample rate를 기억해 이후 청크를 해석합니다. start 없는 청크나 잘못된 format은 재생하지 않습니다.

두 경로 모두 16 kHz는 ×3, 24 kHz는 ×2 FIR interpolator로 48 kHz가 되고, 48 kHz는 그대로 통과합니다. 변환된 PCM은 960 sample = 20 ms 단위로 Opus 인코딩합니다. 끝의 부족한 frame은 0으로 padding해 마지막 음성을 보존합니다.

페이싱과 버퍼

Opus frame 생성 속도와 실제 재생 시각은 분리되어 있습니다. pumpTTS가 frame을 만들고, pumpOutbound가 20 ms ticker에 맞춰 RTP track에 기록합니다. 제한된 채널은 무제한 메모리 증가를 막습니다.

  • enqueue 시점에 frame의 playout generation을 기록합니다.
  • 취소 후 이전 generation frame은 queue에 남아 있어도 기록 직전에 폐기합니다.
  • 세션 종료나 cancel 신호가 오면 대기와 write를 중단합니다.
  • RTCP receiver report의 손실률을 읽어 Opus encoder의 packet-loss percentage를 조정합니다.

혼잡·generation 변경·half-duplex에 따른 폐기는 오류가 아니라 의도된 제어 동작일 수 있습니다. 세부 규칙은 바지인·재생을 참고하세요.

코덱 설정

Opus 출력은 mono, 48 kHz, 20 ms이며 bitrate·complexity·in-band FEC를 설정할 수 있습니다. 유효 범위 밖 값은 기동 시 거부됩니다. DTX는 현재 사용하지 않습니다. Opus 인코더가 포함된 빌드는 opusenc build tag와 libopus/cgo 환경이 필요하므로 테스트 시 테스트·문제 해결의 빌드 항목을 확인하세요.