WebRTC em rede móvel: por que o servidor TURN é obrigatório
Uma chamada de vídeo que funciona no Wi-Fi do escritório falha no 4G do usuário. O que STUN e TURN resolvem, como gerar credenciais temporárias e os outros cuidados que aprendemos construindo um interfone por vídeo.
No Portal dos Ipês, o visitante chama o morador por um interfone na portaria, e o morador atende por vídeo no próprio celular, de onde estiver. Em desenvolvimento, com os dois aparelhos na mesma rede Wi-Fi, qualquer configuração funciona. Em produção, o celular do morador está no 4G, e ali uma parte das chamadas não conecta sem ajuda. O motivo é o caminho da rede entre os dois aparelhos, e a solução é um servidor TURN, que por isso entrou na arquitetura antes do lançamento.
Como uma chamada WebRTC se conecta
WebRTC separa duas coisas:
- Sinalização: os aparelhos trocam a descrição da chamada (SDP) e os possíveis endereços de cada um (candidatos ICE). Isso passa pelo seu servidor, normalmente por WebSocket.
- Mídia: áudio e vídeo vão direto de um aparelho para o outro, sem passar pelo servidor, sempre que possível.
O "sempre que possível" é o problema. Cada aparelho está atrás de um roteador ou da rede da operadora, que traduz endereços (NAT). Para a mídia ir direto, cada lado precisa descobrir um endereço pelo qual o outro consiga alcançá-lo.
STUN resolve o caso fácil
Um servidor STUN responde ao aparelho: "é assim que eu vejo o seu endereço público". Com NATs comuns, isso basta para os dois lados se encontrarem, e a chamada fica totalmente ponto a ponto.
Redes móveis costumam usar CGNAT, em que a operadora compartilha um mesmo IP público entre muitos clientes. Quando esse NAT atribui uma porta de saída diferente para cada destino (o chamado NAT simétrico), o endereço que o STUN enxergou não serve para o outro aparelho, e a conexão direta falha.
TURN resolve o resto
Um servidor TURN retransmite a mídia: os dois aparelhos se conectam a ele, e ele repassa os pacotes. Funciona em praticamente qualquer rede, porque cada lado só precisa sair para a internet. O preço é que o vídeo passa a consumir banda do seu servidor.
O ICE escolhe sozinho: tenta o caminho direto primeiro e só usa o TURN quando nada mais funciona. Basta oferecer os dois:
const conexao = new RTCPeerConnection({
iceServers: [
{ urls: "stun:stun.l.google.com:19302" },
{
urls: [
"turn:turn.exemplo.com.br:3478?transport=udp",
"turn:turn.exemplo.com.br:3478?transport=tcp",
"turns:turn.exemplo.com.br:5349?transport=tcp",
],
username,
credential,
},
],
})Ofereça TCP e TLS na porta 5349 além do UDP: algumas redes corporativas e de hotel bloqueiam UDP e só deixam passar tráfego que parece HTTPS.
Usamos o coturn, de código aberto, num container ao lado da aplicação. No firewall, além das portas 3478 e 5349, é preciso liberar a faixa de portas que o TURN usa para retransmitir a mídia.
Credenciais temporárias, nunca fixas
Uma senha fixa de TURN no código do aplicativo vira, cedo ou tarde, um retransmissor
gratuito para qualquer um. O coturn aceita credenciais temporárias derivadas de um
segredo compartilhado (opção use-auth-secret): a API gera uma credencial por chamada,
válida por pouco tempo, e o segredo nunca sai do servidor.
import { createHmac } from "node:crypto"
export function credenciaisTurn(segredo: string, usuarioId: string, validadeSegundos = 600) {
const expiraEm = Math.floor(Date.now() / 1000) + validadeSegundos
const username = `${expiraEm}:${usuarioId}`
const credential = createHmac("sha1", segredo).update(username).digest("base64")
return { username, credential }
}O próprio TURN confere a assinatura e a validade, sem consultar a sua API.
Outros cuidados que só aparecem em produção
- A rede muda no meio da chamada. O celular troca do Wi-Fi para o 4G e a conexão cai.
RTCPeerConnection.restartIce()renegocia os caminhos sem desligar a chamada. - A qualidade precisa se adaptar. Com
getStats()periódico dá para medir perda e atraso e reduzir resolução e taxa de quadros antes que o vídeo congele, chegando a só áudio quando a rede não aguenta. - O morador não está com o app aberto. A chamada precisa acordar o celular, o que no navegador significa Web Push. No iPhone, isso só funciona com o site instalado na tela de início (iOS 16.4 ou superior), e a interface precisa ensinar esse passo.
- Teste fora do escritório. A página de teste de ICE do projeto WebRTC mostra quais
candidatos o seu TURN devolve: se aparece um do tipo
relay, o retransmissor está funcionando.
Quando dá para ficar sem TURN
Se as chamadas acontecem sempre dentro de uma rede controlada, como entre aparelhos da mesma empresa na mesma LAN, STUN, ou nem isso, resolve. Para qualquer produto em que um dos lados é um celular na rua, conte com o TURN desde o primeiro dia: é a diferença entre um vídeo que funciona no teste e um que funciona para o usuário.
Precisa de vídeo, voz ou tempo real no seu produto? Fale com a gente.
Fontes
- RFC 8445 — Interactive Connectivity Establishment (ICE)
- RFC 8656 — Traversal Using Relays around NAT (TURN)
- A REST API for Access to TURN Services — IETF draft
- coturn — repositório oficial
- RTCPeerConnection: restartIce() — MDN
- Web Push for Web Apps on iOS and iPadOS — WebKit
- Trickle ICE — WebRTC samples