Olá, comunidade!
Há um tempo eu publiquei aqui no DEV um artigo apresentando o SpechPhone, um softphone SIP web open-source feito com PHP, Swoole e WebSockets, com uma proposta meio fora da curva:
criar um softphone web sem depender de WebRTC.
Na primeira versão, o foco estava em provar que era possível fazer chamadas SIP pelo navegador usando um backend PHP assíncrono, controlando sinalização SIP, áudio PCM e transporte em tempo real com Swoole.
Agora o projeto deu um salto importante.
O SpechPhone deixou de ser apenas uma demonstração de discagem web e passou a caminhar para uma arquitetura mais completa de softphone SIP, com:
- recebimento de chamadas inbound;
- controle de estado em tempo real;
- bridge de mídia RTP ↔ PCM;
- suporte a SIP MESSAGE;
- interface de chat integrada;
- melhorias no áudio via WebSocket;
- controle de volume dinâmico;
- arquitetura mais limpa para chamadas ativas.
E o mais importante: ainda sem WebRTC no caminho da mídia.
Repositório:
https://github.com/spechshop/spechphone
O que mudou nesta nova fase?
A primeira versão do SpechPhone já conseguia iniciar chamadas, registrar conta SIP, configurar codec e transportar áudio entre o navegador e o backend.
Mas ainda faltava uma parte essencial para um softphone de verdade:
receber chamadas.
Nesta atualização, a branch inbound introduz um fluxo mais completo para chamadas recebidas, incluindo tratamento de INVITE, ACK, CANCEL e BYE.
Isso muda bastante a natureza do projeto.
Antes ele era um discador web experimental.
Agora ele começa a se comportar como um endpoint SIP real.
Recebimento de chamadas inbound
O fluxo inbound funciona mais ou menos assim:
- O usuário configura e registra sua conta SIP no SpechPhone.
- O
server.phpescuta SIP via UDP na porta4000. - Quando chega um
INVITE, o backend identifica o usuário de destino. - O navegador recebe um evento em tempo real informando a chamada recebida.
- Ao aceitar, o servidor responde com
200 OKcontendo SDP local. - A mídia RTP é ligada à ponte interna RTP ↔ PCM.
- O áudio chega ao navegador via WebSocket.
- Ao desligar,
BYE/CANCELlimpam o estado da chamada.
Em termos práticos, o SpechPhone agora consegue tocar no navegador quando uma chamada SIP chega.
Pequeno detalhe técnico: isso parece simples escrito em oito linhas, mas no mundo SIP cada header tem autoestima própria. Se errar To-tag, Call-ID, CSeq ou rota reversa, o softphone vira decoração digital.
CallState: estado compartilhado com Swoole Table
Uma das mudanças importantes foi a introdução de um gerenciador de estado para chamadas.
O SpechPhone agora usa estruturas em memória com Swoole Table para rastrear:
- chamadas recebidas;
- chamadas ativas;
- bindings SIP;
- usuário SIP associado ao navegador;
- IP/porta RTP remoto;
- codec negociado;
- worker dono da chamada;
- status da sessão.
A ideia é simples: em um servidor assíncrono, principalmente com múltiplos workers, não dá para depender só de variável local solta no vento.
O CallState vira uma espécie de “quadro branco compartilhado” do runtime.
Ele mantém informações como:
CallState::$incomingCalls;
CallState::$activeCalls;
CallState::$sipBindings;
Isso permite que handlers diferentes saibam se existe uma chamada tocando, se ela já foi aceita, qual usuário pertence àquela chamada e como encerrar tudo corretamente.
Esse tipo de controle é essencial para eventos como:
- aceitar chamada;
- rejeitar chamada;
- desligar;
- receber
BYE; - lidar com timeout RTP;
- sincronizar múltiplas abas abertas no navegador.
Bridge RTP ↔ PCM: o coração da mídia
A parte mais interessante continua sendo a mídia.
O SpechPhone não envia RTP diretamente para o navegador e não depende do WebRTC para negociar ICE, STUN, TURN ou DTLS-SRTP.
A arquitetura segue outro caminho:
SIP/RTP peer
↓
RTP UDP
↓
PHP + Swoole + libspech
↓
PCM interno
↓
WebSocket de áudio
↓
Browser
E o caminho inverso também existe:
Microfone do navegador
↓
PCM via WebSocket
↓
audio.php
↓
UDP interno
↓
encode para codec SIP
↓
RTP para o peer remoto
A classe CallMediaBridge centraliza boa parte desse fluxo.
Ela cria um socket UDP local para conversar com o audio.php, registra callbacks para receber PCM decodificado e inicia corrotinas para o caminho:
- RTP → decode → PCM → navegador;
- navegador → PCM → encode → RTP.
Essa separação deixa a arquitetura mais clara: o plano SIP não precisa carregar toda a responsabilidade da mídia nas costas.
audio.php como servidor dedicado de áudio
Outra decisão arquitetural importante foi manter o audio.php como servidor dedicado para a ponte de áudio.
Ele atua como ponto intermediário entre:
- WebSocket do navegador;
- UDP interno;
- chamadas ativas;
- buffers PCM;
- frequência de áudio;
- controle de reconexão.
Na prática, isso ajuda a isolar a mídia da sinalização.
O server.php cuida do HTTP/WSS/SIP/control plane.
O audio.php cuida da parte mais ingrata: áudio em tempo real.
É aquela divisão saudável: um cuida do drama SIP, o outro cuida do chiado existencial do PCM.
Codecs suportados
O SpechPhone continua com foco em controle direto dos codecs.
A depender do ambiente e das extensões disponíveis, o projeto trabalha com:
- PCMA;
- PCMU;
- G.729;
- L16;
- Opus;
- telephone-event / DTMF RFC2833.
A negociação usa SDP remoto para escolher o codec compatível e gerar uma resposta local.
A vantagem dessa abordagem é ter controle total do pipeline.
O navegador não precisa suportar o codec SIP do tronco. Ele pode trabalhar com PCM, enquanto o backend faz a conversão necessária para o mundo VoIP.
Isso abre espaço para integrações mais avançadas no futuro, como:
- gravação;
- transcrição;
- VAD;
- agente de voz;
- análise de áudio;
- filtros;
- mixagem;
- monitoramento de volume;
- detecção de fala.
SIP MESSAGE e chat integrado
Outra novidade forte é o suporte a mensagens SIP.
O SpechPhone agora tem um caminho integrado para envio e recebimento de mensagens de texto usando MESSAGE.
No lado do backend, existe um armazenamento simples em:
/data/spechphone/messages.json
Ele mantém:
- conversas;
- histórico;
- mensagens não lidas;
- participantes;
- última mensagem;
- eventos em tempo real.
Quando uma nova mensagem chega, o backend pode empurrar um evento WebSocket para o cliente conectado.
Isso transforma o SpechPhone em algo além de um discador.
Ele começa a virar uma interface SIP web completa: chamada + mensagem + presença futura + controle de conta.
A interface de mensagens ainda é simples, mas já cria uma base interessante para comunicação em tempo real dentro do próprio navegador.
Interface mais modular
A UI também evoluiu.
Agora o projeto organiza melhor as áreas principais:
- discador;
- chamadas;
- mensagens;
- configurações de áudio;
- configuração SIP;
- notificações;
- timers;
- medidores de sinal;
- barra de chamada ativa.
Algumas capturas da nova fase:
Melhorias recentes no áudio
As versões recentes também trouxeram melhorias práticas no áudio e na experiência da chamada:
- controle de volume dinâmico com
GainNode; - sliders visuais para microfone e retorno;
- persistência de configurações em
localStorage; - melhorias no buffer PCM;
- validação de codec/frequência;
- controle de reconexão;
- logs mais ricos no client e no server;
- limpeza de conexões antigas;
- redução de inconsistências em chamadas WebSocket.
Essas mudanças são menos chamativas que “agora recebe chamada”, mas são justamente o tipo de ajuste que separa demo bonita de ferramenta utilizável.
Áudio em tempo real é cruel: um buffer mal-humorado e o usuário já acha que está falando de dentro de uma lata de sardinha.
Por que não WebRTC?
Essa pergunta sempre aparece.
O SpechPhone não é uma tentativa de dizer que WebRTC é ruim.
WebRTC é excelente.
Mas ele também traz uma pilha grande:
- ICE;
- STUN;
- TURN;
- SDP próprio;
- codecs controlados pelo browser;
- políticas de mídia;
- NAT traversal;
- comportamento diferente entre navegadores;
- dependências que nem sempre fazem sentido em uma infraestrutura SIP já existente.
O SpechPhone segue outra filosofia:
se o servidor já fala SIP e RTP, então o navegador pode ser apenas a interface, e o backend controla a mídia.
Isso dá mais trabalho?
Sim.
Dá mais controle?
Muito.
É uma abordagem para quem quer entender e controlar a pilha inteira, do INVITE ao pacote RTP.
Arquitetura resumida
Browser
├── Interface Web
├── WebSocket de controle
└── WebSocket de áudio PCM
│
▼
server.php
├── HTTPS/WSS
├── SIP UDP :4000
├── roteamento de eventos
├── controle de chamadas
├── CallState / Swoole Tables
└── integração com libspech
│
▼
audio.php
├── WebSocket de áudio
├── UDP interno :9600
├── buffers PCM
├── relay de áudio
└── ponte com chamadas ativas
│
▼
SIP/RTP peer
├── Asterisk
├── FreeSWITCH
├── OpenSIPS
├── trunk SIP
└── provedor VoIP
Instalação básica
git clone https://github.com/spechshop/spechphone
cd spechphone
git clone https://github.com/spechshop/libspech
wget https://github.com/spechshop/pcg729/releases/download/PCG729/php
sudo mv php /usr/local/bin/php
sudo chmod +x /usr/local/bin/php
git submodule update --init --recursive
cp .env.example .env
Depois configure sua chave:
SPECH_VAULT_KEY_HEX=sua_chave_aqui
E rode os servidores:
php server.php
Em outro terminal:
php audio.php
Status do projeto
O SpechPhone ainda está em desenvolvimento ativo.
A branch inbound já é altamente funcional, mas ainda deve ser tratada como experimental para produção.
O foco agora é endurecer a base:
- melhorar estabilidade inbound;
- refinar tratamento de NAT;
- lapidar encerramento de chamadas;
- melhorar compatibilidade com PBXs;
- evoluir o chat SIP;
- preparar o terreno para recursos de mídia mais avançados.
Contribuições
O projeto é open-source e qualquer contribuição é bem-vinda.
Você pode ajudar com:
- testes com Asterisk, FreeSWITCH, OpenSIPS e provedores SIP;
- issues com logs reais;
- melhorias na UI;
- ajustes de compatibilidade SIP;
- melhorias no buffer de áudio;
- documentação;
- exemplos de configuração;
- testes de codecs.
Repositório:
https://github.com/spechshop/spechphone
Se o projeto te interessar, uma estrela no GitHub também ajuda bastante.
Open-source vive de código, café e pequenas doses de validação social. Principalmente café.
Conclusão
O SpechPhone começou como uma provocação técnica:
será que dá para fazer um softphone SIP web em PHP, com Swoole, sem WebRTC?
A resposta inicial foi: sim, dá.
Agora a nova pergunta é outra:
dá para transformar isso em um softphone web SIP realmente utilizável?
Com chamadas inbound, bridge RTP ↔ PCM, controle de estado, áudio em tempo real e SIP MESSAGE integrado, o projeto chegou em uma fase muito mais interessante.
Ainda tem chão pela frente, mas a base ficou mais séria.
E, sinceramente, ver PHP segurando SIP, RTP, PCM, WebSocket e chat em tempo real no mesmo ecossistema é aquele tipo de insanidade técnica que dá gosto de construir.
Até a próxima atualização.


