Files
Projeto-Portaria/prompts/FASE-4-chamada.md
2026-07-22 15:55:55 -03:00

4.2 KiB
Raw Blame History

FASE 4 — Videochamada

Objetivo

LiveKit integrado ponta a ponta: emissão de tokens, sala por visita, vídeo no navegador do visitante, e degradação graciosa quando o SFU falha.

Pré-requisitos

FASES 03 concluídas. Ler docs/05-INFRA-DOCKER.md §3 e §4, e docs/01-ARQUITETURA.md §5.5.

Faça a POC antes de escrever código de produção. Suba livekit-server + coturn e feche uma chamada entre um navegador móvel em 4G real e um Android, medindo o tempo até o primeiro frame. É o maior risco técnico do projeto e o mais barato de derrubar cedo. Se a POC falhar, o problema quase sempre é a faixa UDP fechada no firewall.

Tarefas

1. Porta de vídeo

interface VideoCallProvider {
    suspend fun criarSala(visitId: UUID): RoomInfo
    suspend fun emitirToken(visitId: UUID, participante: Participant): String
    suspend fun encerrarSala(visitId: UUID)
    suspend fun saudavel(): Boolean
}

Adaptador LiveKitProvider. A porta existe para permitir trocar por serviço gerenciado sem tocar nos apps — decisão registrada em 01-ARQUITETURA.md ADR-003.

2. Emissão de tokens

Token JWT do LiveKit gerado pelo backend, nunca pelo cliente. Escopo mínimo:

  • roomJoin apenas na sala daquela visita
  • canPublish para vídeo e áudio
  • canPublishData: false
  • TTL de 10 minutos, alinhado à expiração da visita

room_name derivado determinísticamente de visitId, gravado em visits.room_name.

Um token nunca dá acesso a outra sala. Verifique isso com teste explícito.

3. Ciclo de vida da sala

Sala criada na transição para EM_CHAMADA, não antes — criar em PENDENTE desperdiça recurso do SFU em visitas que nunca são atendidas.

Encerrada ao chegar em estado final. empty_timeout: 120 no LiveKit é a rede de segurança para sala órfã.

4. Vídeo na web do visitante

SDK livekit-client carregado por import() dinâmico, apenas quando a visita entra em EM_CHAMADA. Ele sozinho pesa mais que todo o resto do app — carregá-lo no bundle inicial destruiria o orçamento de 200KB da FASE 3.

Vídeo remoto em tela cheia, auto-preview pequeno no canto, botão de mudo e de encerrar. Indicador de qualidade de conexão.

5. Degradação graciosa

Circuit breaker (Resilience4j) sobre o VideoCallProvider. Ao abrir:

LiveKit fora ─► AUDIO         (mesma sala, sem vídeo, muito menos banda)
                 └─► FOTO_TEXTO  (visitante manda foto, morador aprova sem chamada)
                       └─► FILA_OPERADOR (resolução por telefone)

O nível vigente vai em visits.degraded_mode, é emitido como DEGRADED_MODE no WebSocket, aparece em /actuator/health e no painel admin.

Comunique a degradação, nunca a esconda. Esconder faz o usuário achar que o produto quebrou; nomear faz o produto parecer resiliente — que é o que ele é.

6. Configuração de mídia

Limite de resolução imposto no servidor, não no cliente: 480p a ~600kbps. 480p basta para reconhecer alguém na portaria; 1080p multiplicaria o custo de banda por quatro sem melhorar nenhuma decisão. Ver o dimensionamento em 05-INFRA-DOCKER.md §4.

Simulcast desligado (só dois participantes). adaptiveStream e dynacast ligados. TURN configurado com o segredo do coturn.

7. Métricas

portaria.call.time_to_first_frame · portaria.call.duration · portaria.call.failed_ratio · portaria.call.turn_usage_ratio (uso alto de TURN indica problema de rede na portaria) · portaria.call.degraded_ratio.

Critérios de aceite

  • POC validada em rede real antes do código de produção
  • Chamada completa entre navegador móvel em 4G e Android
  • Tempo até o primeiro frame < 3s em rede boa
  • Teste: token de uma sala é rejeitado em outra sala
  • Teste: com LiveKit derrubado, o sistema degrada para áudio e depois para foto+texto
  • Teste: sala encerrada em todos os estados finais
  • SDK do LiveKit não aparece no bundle inicial do visitante
  • Bundle inicial continua ≤ 200KB gzip
  • Chamada funciona atrás de NAT simétrico (valida o coturn)

Não faça nesta fase

  • Gravação (módulo video_recording, FASE 8)
  • App do morador (FASE 5)
  • Mais de 2 participantes por sala