VoIP Real-Time e Áudio 48 kHz no PHP: Guia Hands-On com Swoole
Este guia demonstra como construir um softphone completo com áudio de alta qualidade (48 kHz) usando PHP moderno com Swoole. Vamos explorar VoIP e processamento de áudio em tempo real através de uma implementação prática.
Nota importante sobre arquitetura: O SpechPhone que vamos explorar funciona de um jeito diferente do que você talvez esteja acostumado. Aqui, subimos dois servidores PHP (
middleware.phpeaudio.php) que entregam áudio via RTP no backend + WebSocket (PCM) pro browser — sem passar pelo caminho tradicional do Asterisk/AGI. É uma abordagem mais direta que facilita a compreensão do fluxo completo.
Referência: https://github.com/spechshop/spechphone/blob/volume-dev/README.md
O que você vai construir (de verdade)
Ao final deste guia, você terá nas mãos:
- Um softphone web funcional que faz SIP/RTP em tempo real, com interface no browser via WebSocket
- Um pipeline de mídia completo onde o backend recebe RTP, decodifica e envia PCM em chunks pro cliente
- Uma implementação prática de I/O assíncrono com corrotinas por sessão
E o melhor: você vai entender cada peça desse quebra-cabeça.
1) Entendendo a arquitetura
Vamos começar pelo básico: como tudo isso se encaixa? O SpechPhone é construído sobre PHP + Swoole, trabalhando com mídia em RTP/UDP no backend e PCM via WebSocket direto pro browser. Nada de WebRTC/SRTP/ICE/DTLS aqui — é uma abordagem mais crua e direta, o que facilita muito pra entender o que está acontecendo em cada camada.
Referência: https://github.com/spechshop/spechphone/blob/volume-dev/README.md
Os dois pilares da aplicação:
-
middleware.php: servidor HTTP/WebSocket para interface web e controle de chamadas + servidor UDP para sinalização SIP -
audio.php: servidor HTTP/WebSocket + UDP que recebe streams RTP decodificados, mixa múltiplos canais de áudio e distribui via WebSocket para os clientes
Cada um tem seu papel bem definido, e você vai ver como eles conversam entre si.
Referência: https://github.com/spechshop/spechphone (seção "Directory Structure" do README)
2) O segredo do Swoole: corrotinas e I/O assíncrono
O conceito fundamental aqui é I/O assíncrono com corrotinas. Independente da linguagem, quando você domina esse padrão, consegue lidar com operações em tempo real de forma eficiente.
O SpechPhone abre uma coroutine dedicada pra cada trunkController, o que significa que você pode ter várias chamadas simultâneas rodando sem bloquear o processo principal. É a aplicação prática de concorrência cooperativa — um padrão que funciona em qualquer ecossistema que o implemente.
Referência: https://github.com/spechshop/spechphone/blob/volume-dev/README.md
Detalhe importante: Estamos falando de Swoole (a extensão/runtime oficial), não OpenSwoole. São projetos diferentes, então fique atento na hora de instalar.
3) Mão na massa: subir o SpechPhone localmente
Hora de colocar a mão no teclado. O README do SpechPhone já entrega tudo mastigado: clone, instale o runtime e suba os servidores. Simples assim.
# deps
sudo apt update && sudo apt install -y openssl
# repo + lib
git clone https://github.com/spechshop/spechphone && cd spechphone
git clone https://github.com/spechshop/libspech
# runtime php otimizado (pcg729)
curl -L https://github.com/spechshop/pcg729/releases/download/current/php -o php
chmod +x ./php
sudo cp php /usr/local/bin/php
# start (em terminais separados)
php middleware.php
php audio.php
Referência do trecho acima: https://github.com/spechshop/spechphone/blob/volume-dev/README.md
Dica prática: Abra dois terminais lado a lado. Deixe os logs rolando e observe como os servidores se comunicam. É bem instrutivo ver o fluxo acontecendo em tempo real.
4) Áudio 48 kHz: onde entra (de verdade) e por que isso importa
Aqui é onde a coisa fica interessante. Quando falamos de telefonia tradicional, você está preso a 8 kHz (aquele som meio "de telefone", sabe?). Mas com Opus a 48 kHz, você tem qualidade de áudio próxima do que ouve em streaming de música. É uma diferença que você sente na primeira chamada.
O SpechPhone trabalha com duas peças que se complementam:
1) Oferta de Opus/48kHz via SDP no trunkController (libspech). É aqui que você diz pro outro lado: "ei, eu falo Opus em alta qualidade":
// Oferecer codec Opus em SDP
$phone->mountLineCodecSDP('opus/48000/2');
Referência do snippet: https://github.com/spechshop/libspech/blob/spech/README.md
2) Decodificação dinâmica + PCM em chunks pro browser. Os pacotes RTP chegam, são processados pela libspech (usando funções nativas via runtime pcg729), e o áudio decodificado é entregue ao controlador WebSocket em PCM puro, pronto pra ser consumido no browser via webkitAudioContext.
Referência: https://github.com/spechshop/spechphone/blob/volume-dev/README.md
Em outras palavras: você negocia Opus, recebe RTP, decodifica e entrega PCM. Simples, direto e poderoso.
5) Um exemplo mínimo de chamada — vendo corrotinas e eventos na prática
Esse é o "hello world" da stack. Aqui você registra no servidor SIP, oferece o codec, reage a eventos (tocando, atendido, desligado) e recebe áudio. Tudo isso em menos de 50 linhas:
<?php
use libspech\Sip\trunkController;
include 'plugins/autoloader.php';
\Swoole\Coroutine\run(function () {
$username = getenv('SIP_USERNAME');
$password = getenv('SIP_PASSWORD');
$domain = getenv('SIP_DOMAIN');
$host = gethostbyname($domain);
$phone = new trunkController($username, $password, $host, 5060);
if (!$phone->register(2)) {
throw new \Exception('Falha no registro');
}
// Oferecer codec Opus em SDP (48 kHz)
$phone->mountLineCodecSDP('opus/48000/2');
$phone->onRinging(function ($phone) {
echo "Tocando...\n";
});
$phone->onAnswer(function (trunkController $phone) {
echo "Atendido. Recebendo mídia...\n";
$phone->receiveMedia();
\Swoole\Coroutine::sleep(10);
});
$phone->onReceiveAudio(function ($pcmData, $peer, trunkController $phone) {
echo "Recebido: " . strlen($pcmData) . " bytes\n";
});
$phone->onHangup(function (trunkController $phone) {
echo "Chamada finalizada\n";
$phone->close();
});
$phone->call('5511999999999');
});
Referência (onde esse exemplo está documentado): https://github.com/spechshop/libspech/blob/spech/README.md
Referência do exemplo completo: https://github.com/spechshop/libspech/blob/spech/example.php
O que está acontecendo aqui? Você registra no servidor SIP, configura callbacks pra cada evento da chamada (ringing, answer, hangup) e, quando atendido, começa a receber chunks de áudio PCM. Cada callback roda na sua própria coroutine, sem travar nada.
6) Três exercícios práticos (pra você realmente aprender fazendo)
Ler código é legal, mas mexer nele é melhor. Aqui vão três experimentos rápidos pra você sentir como tudo funciona:
1) Troque o codec e compare
- Mude
opus/48000/2paraL16/8000(o README do libspech também menciona L16/8000) e compare o que você recebe emonReceiveAudio. Você vai notar diferença no tamanho dos chunks e na qualidade do áudio. - Referência: https://github.com/spechshop/libspech/blob/spech/README.md
2) Instrumente o fluxo de PCM
- Deixe o log de
strlen($pcmData)rodando e observe os padrões: tamanho dos chunks, frequência de chegada, variações (jitter aparente). É fascinante ver como o áudio flui em tempo real. - Referência: https://github.com/spechshop/libspech/blob/spech/README.md
3) Faça a UI reagir aos eventos
- O SpechPhone usa um cliente "thin" via WebSocket. Faça o frontend reagir aos eventos "ringing/answered/hangup" que vêm do backend. Comece simples: apenas logando na tela. Depois você pode adicionar animações, indicadores visuais, etc.
- Referência: https://github.com/spechshop/spechphone/blob/volume-dev/README.md
Links principais (pra você não se perder)
Aqui estão todos os recursos que você vai precisar:
- SpechPhone (repo): https://github.com/spechshop/spechphone
- SpechPhone (README, branch volume-dev): https://github.com/spechshop/spechphone/blob/volume-dev/README.md
- libspech (repo): https://github.com/spechshop/libspech
- libspech (README, branch spech): https://github.com/spechshop/libspech/blob/spech/README.md
- libspech (example.php): https://github.com/spechshop/libspech/blob/spech/example.php
- pcg729 (runtime release "current/php"): https://github.com/spechshop/pcg729/releases/tag/current
E agora? Clone o repo, suba os servidores e faça sua primeira chamada. Os conceitos apresentados aqui são aplicáveis a qualquer stack que implemente I/O assíncrono com corrotinas. Boa sorte! 🚀
Top comments (0)