AI News Hub Logo

AI News Hub

SpechPhone: agora com chamadas inbound, chat SIP e bridge RTP ↔ PCM em PHP + Swoole

DEV Community
spechshop

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 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. O fluxo inbound funciona mais ou menos assim: O usuário configura e registra sua conta SIP no SpechPhone. O server.php escuta SIP via UDP na porta 4000. 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 OK contendo SDP local. A mídia RTP é ligada à ponte interna RTP ↔ PCM. O áudio chega ao navegador via WebSocket. Ao desligar, BYE/CANCEL limpam 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. 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. 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. 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. 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. 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. 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: 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. 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. 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 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 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. 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é. 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.