Přeskočit obsah

Hlasové hovory — specifikace (budoucí fáze)

Přehled

Peer-to-peer hlasová komunikace integrovaná do ChatWindow. Hovor je iniciován tlačítkem 🎙 ve vstupní liště ConversationPanel — prozatím disabled. Backend signalizace přes existující FastAPI WebSocket.


Architektura

Volající                Backend (FastAPI WS)             Přijímající
    │                          │                               │
    │── /ws/voice/call ───────►│                               │
    │   {conv_id, offer_sdp}   │── push: incoming_call ───────►│
    │                          │   {conv_id, caller, sdp}      │
    │◄─ answer_sdp ────────────│◄── answer ────────────────────│
    │                          │   {conv_id, sdp}              │
    │── ICE candidates ────────►│◄── ICE candidates ────────────│
    │◄─────────────────────────│──────────────────────────────►│
    │                          │                               │
    │  ← přímý RTP proud (P2P nebo přes TURN) →               │

Komponenty

Komponenta Tech Umístění
Signalizace FastAPI WebSocket backend/app/routers/voice.py
Audio capture/playback sounddevice launcher voice_call.py
Kódování Opus via opuslib launcher voice_call.py
ICE/STUN veřejný STUN (stun.l.google.com:19302) launcher
TURN fallback vlastní coturn na vm-gateway infrastructure/
SDP exchange přes WS signalizaci

Backend — nové endpointy

WS  /ws/voice/{conv_id}    — signalizace (offer/answer/ICE/hangup)
POST /voice/call            — zahájení hovoru (notifikace ostatním)
POST /voice/hangup          — ukončení
GET  /voice/status/{conv_id} — aktivní hovor v konverzaci?

WebSocket zprávy

// offer (volající → backend → přijímající)
{"type": "offer", "sdp": "...", "caller_id": "uuid"}

// answer (přijímající → backend → volající)
{"type": "answer", "sdp": "..."}

// ICE candidate (oba směry)
{"type": "ice", "candidate": {"candidate": "...", "sdpMid": "0"}}

// hangup
{"type": "hangup"}

// incoming_call (push od backendu)
{"type": "incoming_call", "conv_id": "...", "caller_name": "..."}

Launcher — nové soubory

voice_call.py

class VoiceCall:
    def __init__(self, session, conv_id, ws_url):
        ...

    def start_call(self):       # inicializace sounddevice + WebSocket
    def answer_call(self, sdp): # přijmout příchozí hovor
    def send_audio(self):       # vlákno: capture mic → Opus → send
    def recv_audio(self):       # vlákno: recv → Opus decode → playback
    def hangup(self):           # ukončit hovor, zavřít WS

voice_ui.py

  • IncomingCallDialog — popup "Příchozí hovor od X" s Přijmout/Odmítnout
  • ActiveCallBar — lišta v ConversationPanel (mute, hangup, duration timer)

ChatWindow — integrace

  1. ConversationPanel._voice_btn (🎙) → VoiceCall.start_call()
  2. chat_poll.py nebo samostatný WS listener → IncomingCallDialog
  3. ActiveCallBar se zobrazí nad vstupní lištou po dobu trvání hovoru

Nastavení zařízení

Výběr mikrofonu a reproduktorů je již implementován v Nastavení → Zvuk: - settings.get("audio_input") → název zařízení pro sounddevice.InputStream - settings.get("audio_output") → název zařízení pro sounddevice.OutputStream - settings.get("audio_volume") → hlasitost 0–100


Závislosti (přidat do requirements.txt)

sounddevice>=0.4.6
opuslib>=3.0.1     # Opus codec (vyžaduje libopus.dll na Windows)
websockets>=12.0   # pro WS voice spojení

Windows: libopus.dll

Opus není součástí Windows. Nutný postup: 1. Stáhnout opus.dll z libopus.org (precompiled) nebo vcpkg 2. Přidat do dist/ adresáře vedle .exe 3. PyInstaller spec: přidat ('opus.dll', '.') do datas


Priorita a scope

  • Fáze: V (po F4 Chat a F5 Support)
  • Rozsah MVP: 1:1 hovory v DM konverzacích, skupinové hovory mimo scope MVP
  • Šifrování: SRTP (vestavěno v libopus přes WebRTC) nebo TLS transport
  • Nahrávání: mimo scope (GDPR komplikace)
  • Video: mimo scope

Odhadovaný rozsah implementace

Část Odhad
Backend WS signalizace ~200 řádků
voice_call.py audio engine ~300 řádků
voice_ui.py dialogy ~150 řádků
Integrace do ChatWindow ~80 řádků
TURN server konfigurace ~50 řádků konfigurace
Celkem ~780 řádků