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

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:

  1. O usuário configura e registra sua conta SIP no SpechPhone.
  2. O server.php escuta SIP via UDP na porta 4000.
  3. Quando chega um INVITE, o backend identifica o usuário de destino.
  4. O navegador recebe um evento em tempo real informando a chamada recebida.
  5. Ao aceitar, o servidor responde com 200 OK contendo SDP local.
  6. A mídia RTP é ligada à ponte interna RTP ↔ PCM.
  7. O áudio chega ao navegador via WebSocket.
  8. 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.

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:

In Call
Keyboard
Messages

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.

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post

Supabase Scheduled Functions Monitoring: How to Catch Missed Runs Before They Break Production

Related Posts