VOIP Calls + Resample + PHP
Source: Dev.to
Visão geral
Este guia demonstra como usar Swoole para implementar chamadas VoIP em tempo real com áudio de 48 kHz em PHP. O projeto SpechPhone fornece um softphone web que realiza SIP/RTP no backend e entrega áudio PCM ao navegador via WebSocket, sem depender de Asterisk/AGI.
Arquitetura
- middleware.php – servidor HTTP/WebSocket que fornece a UI e controla a chamada.
- audio.php – servidor de áudio responsável por mixar e fazer o streaming dos pacotes de áudio.
O fluxo de mídia funciona da seguinte forma:
- O backend recebe pacotes RTP.
- A biblioteca libspech decodifica os pacotes (usando o runtime
pcg729). - O áudio decodificado é enviado em chunks PCM ao cliente WebSocket, onde é reproduzido com
AudioContextdo navegador.
A chave para o desempenho em tempo real é o uso de corrotinas do Swoole, que permitem múltiplas chamadas simultâneas sem bloquear o processo principal.
Pré‑requisitos e instalação
# dependências do sistema
sudo apt update && sudo apt install -y openssl
# clonar repositórios
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
Em terminais separados, inicie os dois serviços:
php middleware.php
php audio.php
Importante: o projeto utiliza a extensão Swoole (não OpenSwoole).
Oferta de codec Opus / 48 kHz
O trunkController pode oferecer diferentes codecs via SDP. Para usar Opus a 48 kHz:
$phone->mountLineCodecSDP('opus/48000/2');
Isso faz com que o fluxo RTP seja negociado com 48 kHz, permitindo áudio de alta qualidade.
Exemplo completo em PHP
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');
});
O exemplo está documentado em:
Experimentos sugeridos
1. Trocar o codec ofertado
Altere a linha de oferta de codec para L16/8000 e compare a quantidade de bytes recebidos em onReceiveAudio.
$phone->mountLineCodecSDP('L16/8000');
2. Instrumentar o fluxo PCM
Registre o tamanho (strlen) de cada chunk de PCM recebido e observe padrões de tamanho, frequência e jitter.
3. Atualizar a UI
Implemente um cliente “thin” via WebSocket que reage aos eventos ringing, answered e hangup, exibindo mensagens simples na tela.
Links principais
- SpechPhone (repo) –
- SpechPhone README (branch volume-dev) –
- libspech (repo) –
- libspech README (branch spech) –
- libspech example.php –
- pcg729 runtime release –