Biblioteca VoIP SIP/RTP em tempo real para PHP, construída com corrotinas Swoole
libspech fornece:
📘 Nova Documentação: Veja SIGNALING_ARRAYS.md para entender em profundidade como os arrays de sinalização SIP são construídos e processados.
Baixe a última release do berzersks/pcg729. Esta release inclui todas as extensões necessárias (Swoole, bcg729 baseado no Belladone BCG729, Opus, psampler) pré-compiladas e prontas para uso.
Siga as instruções de instalação fornecidas na release para configurar o ambiente.
O repositório inclui um exemplo executável em example.php.
.env:
cp .env.example .env
# Edite .env com suas credenciais
php example.php
<?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
$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');
});
Veja o arquivo example.php para um exemplo completo. Recursos principais demonstrados:
.env)A biblioteca utiliza variáveis de ambiente para gerenciar credenciais SIP de forma segura:
SIP_USERNAME=seu_username
SIP_PASSWORD=sua_password
SIP_DOMAIN=sip.example.com
SIP_PORT=5060
SIP_TIMEOUT=120
A classe trunkController oferece os seguintes callbacks para gerenciar eventos de chamada:
// Chamada recebida (ringing)
$phone->onRinging(function ($phone) {
echo "Tocando...\n";
});
// Chamada atendida
$phone->onAnswer(function (trunkController $phone) {
$phone->receiveMedia();
// ... processar áudio ...
});
// Chamada finalizada (hangup)
$phone->onHangup(function (trunkController $phone) {
$phone->close();
});
// Pressionamento de tecla (DTMF)
$phone->onKeyPress(function ($event, $peer) {
echo "Dígito recebido: " . $event . "\n";
});
// Receber áudio PCM
$phone->onReceivePcm(function ($pcmData, $peer, trunkController $phone) {
// $pcmData: dados de áudio em formato PCM
// $peer: endereço IP e porta do servidor RTP remoto
// $phone: instância do controlador
});
// Registro no servidor SIP
$phone->register($retries);
// Fazer chamada de saída
$phone->call($number);
// Configurar codec SDP
$phone->mountLineCodecSDP('opus/48000/2'); // Opus, 48kHz, estéreo
$phone->mountLineCodecSDP('L16/8000'); // Linear PCM, 8kHz
// Gerenciamento de áudio
$phone->receiveMedia(); // Iniciar recepção
$phone->defineAudioFile('file.wav'); // Definir arquivo para envio
$phone->saveBufferToWavFile('out.wav', $data);
// Enviar DTMF (RFC 2833)
$phone->send2833($number, $duration); // $duration em ms
// Configuração
$phone->defineTimeout($seconds); // Timeout em segundos
$phone->close(); // Finalizar conexão
Payloads suportados/disponíveis no codebase:
| Codec | Tipo de Payload | Taxa de Amostragem | Status | Notas/Extensão |
|---|---|---|---|---|
| PCMU (G.711 µ-law) | 0 | 8 kHz | Integrado | Nenhuma extensão extra necessária |
| PCMA (G.711 A-law) | 8 | 8 kHz | Integrado | Nenhuma extensão extra necessária |
| G.729 | 18 | 8 kHz | Integrado | Incluído na release pcg729 (baseado no Belladone BCG729) |
| Opus | 111 | 48 kHz | Integrado | Incluído na release pcg729 |
| L16 (Linear PCM) | 96 | 8 kHz | Integrado | psampler incluído para reamostragem |
| telephone-event (DTMF) | 101 | 8 kHz | Integrado | RFC 2833 para sinalização DTMF |
Toda a I/O é não-bloqueante usando corrotinas Swoole:
\Swoole\Coroutine\run(function () {
\Swoole\Coroutine::create(function () {
// Cada coroutine = contexto de chamada isolado
// I/O não-bloqueante
$phone->register(2); // Yield até resposta
$phone->call($number); // Yield até resposta
$phone->receiveMedia(); // Yield em loop RTP
// Nenhum thread, nenhum callback hell - código sequencial
});
});
Benefícios:
<?php
use libspech\Sip\trunkController;
\Swoole\Coroutine\run(function () {
$calls = [
'5511999999999',
'5511888888888',
];
foreach ($calls as $number) {
\Swoole\Coroutine::create(function () use ($number) {
$phone = new trunkController($username, $password, $host, 5060);
$phone->register(2);
$audioBuffer = '';
$phone->onReceivePcm(function ($pcmData, $peer, $p) use (&$audioBuffer) {
$audioBuffer .= $pcmData;
});
$phone->onHangup(function ($p) use (&$audioBuffer, $number) {
$filename = "call_{$number}_" . date('YmdHis') . ".wav";
$p->saveBufferToWavFile($filename, $audioBuffer);
echo "Gravado: $filename\n";
});
$phone->call($number);
});
}
});
<?php
$phone->onKeyPress(function ($digit, $peer) use ($phone) {
switch ($digit) {
case '1':
echo "Opção 1 pressionada\n";
$phone->defineAudioFile('menu_option1.wav');
break;
case '2':
echo "Opção 2 pressionada\n";
$phone->defineAudioFile('menu_option2.wav');
break;
case '*':
echo "Retornar ao menu\n";
$phone->defineAudioFile('main_menu.wav');
break;
case '#':
echo "Finalizar chamada\n";
$phone->close();
break;
}
});
O SpechPhone utiliza WebSocket para comunicação em tempo real entre o navegador e o servidor. Todas as mensagens seguem o formato JSON.
{
"action": "nome_da_acao",
"data": {
"parametro1": "valor1",
"parametro2": "valor2"
}
}
{
"action": "call",
"data": {
"number": "551140040104",
"codec": "opus"
}
}
{
"action": "hangup",
"data": {}
}
{
"action": "dtmf",
"data": {
"digit": "1"
}
}
{
"action": "audio_control",
"data": {
"mute": true,
"volume": 0.8
}
}
{
"action": "send_audio",
"data": {
"pcm": "base64_encoded_audio_data",
"sampleRate": 48000
}
}
{
"event": "call_status",
"data": {
"status": "ringing|answered|hangup",
"callId": "unique_call_id"
}
}
{
"event": "audio_data",
"data": {
"pcm": "base64_encoded_audio",
"sampleRate": 48000,
"channels": 1
}
}
{
"event": "dtmf_received",
"data": {
"digit": "5"
}
}
{
"event": "error",
"data": {
"code": "ERROR_CODE",
"message": "Descrição do erro"
}
}
Causa: A porta 443 requer privilégios de root.
Solução:
# Opção 1: Execute com sudo
sudo php middleware.php
# Opção 2: Use uma porta alternativa (edite configInterface.json)
{
"port": 8443
}
# Opção 3: Use setcap para permitir bind em portas baixas
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/php
Causa: Certificados SSL não foram gerados.
Solução:
# O sistema gera automaticamente, mas você pode forçar:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
Causas possíveis:
Soluções:
// Ajuste o buffer adaptativo na libspech
$phone->enableAdaptiveBuffer(true);
$phone->setBufferSize(200); // ms
// Aumente o número de workers Swoole
"serverSettings": {
"worker_num": 4,
"task_worker_num": 2
}
Causa: Credenciais SIP inválidas ou servidor inacessível.
Solução:
# Verifique conectividade
ping sip.example.com
# Teste porta SIP
nc -vz sip.example.com 5060
# Verifique logs
tail -f /var/log/spechphone.log
Causa: HTTPS/WSS não está configurado ou navegador não tem permissão.
Solução:
Causa: Codec não está disponível no runtime pcg729.
Solução:
# Verifique codecs disponíveis
php -m | grep -E "bcg729|opus|psampler"
# Se ausentes, baixe o runtime correto
curl -L https://github.com/spechshop/pcg729/releases/download/current/php -o php
chmod +x php
sudo cp php /usr/local/bin/php
Causas possíveis:
Soluções:
// Ajuste configInterface.json
{
"serverSettings": {
"worker_num": 8,
"max_coroutine": 3000,
"enable_coroutine": true
}
}
Causa: Timeout ou proxy/load balancer intermediário.
Solução:
// Ajuste keepalive em configInterface.json
{
"serverSettings": {
"heartbeat_check_interval": 30,
"heartbeat_idle_time": 60
}
}
# Defina nível de log
export SPECH_LOG_LEVEL=debug
# Execute com output verbose
php middleware.php 2>&1 | tee server.log
# Capture pacotes RTP
sudo tcpdump -i any -n 'udp port 10000-62000' -w rtp.pcap
# Analise com Wireshark
wireshark rtp.pcap
// Adicione ao seu código
\Swoole\Coroutine::stats(); // Retorna estatísticas de corrotinas
upstream spechphone {
server 127.0.0.1:8443;
}
server {
listen 443 ssl http2;
server_name phone.example.com;
ssl_certificate /etc/letsencrypt/live/phone.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/phone.example.com/privkey.pem;
location / {
proxy_pass https://spechphone;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 86400;
}
}
Crie /etc/systemd/system/spechphone-middleware.service:
[Unit]
Description=SpechPhone Middleware Server
After=network.target
[Service]
Type=simple
User=spechphone
WorkingDirectory=/opt/spechphone
ExecStart=/usr/local/bin/php middleware.php
Restart=always
RestartSec=5
Environment="SPECH_VAULT_KEY_HEX=your_key_here"
[Install]
WantedBy=multi-user.target
Crie /etc/systemd/system/spechphone-audio.service:
[Unit]
Description=SpechPhone Audio Server
After=network.target
[Service]
Type=simple
User=spechphone
WorkingDirectory=/opt/spechphone
ExecStart=/usr/local/bin/php audio.php
Restart=always
RestartSec=5
Environment="SPECH_VAULT_KEY_HEX=your_key_here"
[Install]
WantedBy=multi-user.target
Ative os serviços:
sudo systemctl daemon-reload
sudo systemctl enable spechphone-middleware spechphone-audio
sudo systemctl start spechphone-middleware spechphone-audio
sudo systemctl status spechphone-middleware spechphone-audio
# Permitir HTTPS
sudo ufw allow 443/tcp
# Permitir SIP
sudo ufw allow 5060/udp
# Permitir RTP (ajuste conforme necessário)
sudo ufw allow 10000:62000/udp
# Ative o firewall
sudo ufw enable
Crie Dockerfile:
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
openssl \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY . .
RUN curl -L https://github.com/spechshop/pcg729/releases/download/current/php -o /usr/local/bin/php \
&& chmod +x /usr/local/bin/php
EXPOSE 443 5060/udp 10000-62000/udp
CMD ["php", "middleware.php"]
Crie docker-compose.yml:
version: '3.8'
services:
middleware:
build: .
ports:
- "443:443"
- "5060:5060/udp"
- "10000-62000:10000-62000/udp"
environment:
- SPECH_VAULT_KEY_HEX=${SPECH_VAULT_KEY_HEX}
volumes:
- ./plugins:/app/plugins
- ./libspech:/app/libspech
restart: unless-stopped
audio:
build: .
command: php audio.php
environment:
- SPECH_VAULT_KEY_HEX=${SPECH_VAULT_KEY_HEX}
volumes:
- ./plugins:/app/plugins
- ./libspech:/app/libspech
restart: unless-stopped
Execute:
docker-compose up -d
Adicione endpoint de métricas no seu código:
// plugins/Request/metrics.php
$server->on('request', function ($request, $response) {
if ($request->server['request_uri'] === '/metrics') {
$metrics = [
'active_calls' => getActiveCalls(),
'total_calls' => getTotalCalls(),
'cpu_usage' => sys_getloadavg()[0],
'memory_usage' => memory_get_usage(true),
];
$output = '';
foreach ($metrics as $key => $value) {
$output .= "spechphone_{$key} {$value}\n";
}
$response->header('Content-Type', 'text/plain');
$response->end($output);
}
});
#!/bin/bash
# backup.sh
BACKUP_DIR="/backup/spechphone"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Backup de configurações
tar -czf $BACKUP_DIR/config_$DATE.tar.gz plugins/configInterface.json .env
# Backup de logs
tar -czf $BACKUP_DIR/logs_$DATE.tar.gz /var/log/spechphone.log
# Manter apenas últimos 7 dias
find $BACKUP_DIR -mtime +7 -delete
Adicione ao crontab:
0 2 * * * /opt/spechphone/backup.sh
Q: O SpechPhone é gratuito?
A: Sim, é open source sob licença Apache 2.0.
Q: Posso usar em produção?
A: O projeto está em beta. Use com cautela em produção e teste extensivamente.
Q: Suporta recebimento de chamadas?
A: Ainda não. Esta funcionalidade está em desenvolvimento.
Q: Funciona no Windows?
A: Recomendamos Linux/macOS. Windows pode funcionar com WSL2.
Q: Qual a latência média?
A: Entre 50-150ms dependendo da rede e codec utilizado.
Q: Quantas chamadas simultâneas suporta?
A: Centenas, dependendo dos recursos do servidor (CPU, RAM, rede).
Q: Posso usar com Asterisk/FreeSWITCH?
A: Sim, o SpechPhone é compatível com qualquer servidor SIP padrão.
Q: Suporta IPv6?
A: Sim, desde que o runtime e rede estejam configurados.
Q: Preciso de WebRTC?
A: Não. O SpechPhone usa WebSocket + PCM direto, sem WebRTC.
Q: Qual codec oferece melhor qualidade?
A: Opus (48kHz) oferece a melhor qualidade. G.729 oferece melhor compressão.
Q: Posso adicionar outros codecs?
A: Sim, através da extensão do runtime pcg729 ou via FFI.
Q: Qual codec usar para economizar banda?
A: G.729 (8kbps) ou PCMU/PCMA (64kbps).
Q: É seguro usar em produção?
A: Use HTTPS/WSS, firewall adequado e atualizações regulares.
Q: Suporta SRTP?
A: Não. O RTP é transportado cru (UDP) no backend.
Q: Como proteger credenciais SIP?
A: Use SPECH_VAULT_KEY_HEX para criptografar configurações sensíveis.
Q: Preciso de VPN?
A: Não é obrigatório, mas recomendado para segurança adicional.
Ambiente de teste:
| Métrica | Valor |
|---|---|
| Chamadas simultâneas | 500+ |
| Latência média (Opus) | 80ms |
| Latência média (G.729) | 65ms |
| Uso de CPU (100 chamadas) | 45% |
| Uso de RAM (100 chamadas) | 2.8GB |
| Throughput RTP | 150 Mbps |
# Aumente limites de arquivos abertos
echo "* soft nofile 65536" >> /etc/security/limits.conf
echo "* hard nofile 65536" >> /etc/security/limits.conf
# Otimize rede
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216
sysctl -w net.ipv4.udp_mem="8192 16384 32768"
{
"serverSettings": {
"worker_num": 8,
"task_worker_num": 4,
"max_coroutine": 5000,
"socket_buffer_size": 2097152,
"buffer_output_size": 2097152,
"enable_coroutine": true,
"max_connection": 10000
}
}
# Use Xdebug para profiling
php -d xdebug.mode=profile middleware.php
# Ou valgrind
valgrind --tool=callgrind php middleware.php
Legenda:
Spechshop é uma stack VoIP em PHP + Swoole focada em sinalização SIP-like via UDP, coordenação de workers RTP por CPU e gerenciamento via WebSocket/HTTP(S). Todo o estado é mantido em Swoole\Table, com snapshots JSON e SQLite para dados administrativos.
Swoole.Table em memóriataskset; I/O assíncrono por corrotinasrtpc_snapshot.jsonmanage/plugins/configInterface.json; inicia audio.phptaskset, screen, sockets UDP)swoole (5.x recomendado), sqlite3, json, mbstring, pcntl, posixbcg729 (G.729), opus, resampler (ex: libsamplerate)build-essential/gcc, make, php-dev, libopus-dev, libsamplerate-dev, taskset, screenfullchain.pem e privkey.pem na raiz ou configurar em manage/plugins/configInterface.jsongit clone <url_do_repositorio> spechshop
cd spechshop
pecl install swoole
apt install php-sqlite3 php-mbstring
# TODO: compilar/instalar bcg729, opus e Resampler
fullchain.pem e privkey.pem na raiz ou ajustar o JSON de config.plugins/configInterface.json (principal) e manage/plugins/configInterface.json (gerenciamento).logs/, recordings/ e arquivos JSON de snapshot.O script backup controla serviços, backups e diagnósticos:
# list / save / restore backups
./backup list
./backup save my_2025_11_16
./backup restore my_2025_11_16
# delete a version
./backup delete my_2025_11_16
# status e configuração
./backup status
./backup config
# diagnósticos
./backup services
./backup diagnose
./backup diagnose server
./backup killport 9503
# controlar serviços
./backup start server
./backup stop server
./backup restart server
./backup start-all
./backup stop-all
Serviços gerenciados (nomes podem variar): server (UDP 5060), manage (WebSocket/HTTP(S)), media (mediaServer + workers). Há também cron-manager.php para instalar/gerenciar jobs cron.
plugins/configInterface.json — porta 5060, worker_num, enable_coroutine, listas de autoload/hot-reload.manage/plugins/configInterface.json — host, port, ssl, certificados, serverSettings, MIME whitelist.O painel é servido por manage/middleware.php com rotas HTTP/WS. Ele expõe dashboards de chamadas, configuração de troncos, estatísticas de workers RTP e controle de serviços. A UI usa os assets em manage/css, manage/js e baseia-se em templates de manage/plugins/Request. A partir dele derivaram-se os projetos open-source atuais (libspech, spechphone) que compartilham a base de sinalização/mídia.
server.php — servidor UDP de sinalização (5060)mediaServer.php, rtpServer.php, rtpw3.php — dispatcher de mídia, RPC UDP, workers por coremanage/middleware.php — gestão WebSocket/HTTP(S); inicia audio.phprunner.php, runnerServer.php, runner-worker.php — pipeline runner TCP 9504plugins/ — módulos core (Utils, Packet, Request, Start, Extension, Message)manage/ — UI/API, assets e SQLite; autoload/config próprioslogs/, recordings/ — logs e gravações opcionaisdatabase.json, calls.json, trunks.json, rtpc_snapshot.json, etc.Não há testes automatizados no repositório no momento. Recomenda-se adicionar smoke tests (subir plano de mídia e enviar pacotes UDP), unit tests para parsers e utilitários e fluxos ponta-a-ponta.
⚠️ IMPORTANTE: Projeto em fase beta e em desenvolvimento. O recebimento de chamadas ainda será implementado.
Processamento de áudio e corrotinas: cada trunkController roda em uma corrotina via Swoole, permitindo centenas de chamadas simultâneas. A libspech identifica codecs (G.729, Opus, PCMA/U, L16) e decodifica em tempo real via funções nativas C do runtime pcg729. O áudio decodificado é entregue ao controlador WebSocket em chunks PCM para o navegador consumir diretamente com webkitAudioContext, evitando a complexidade do WebRTC.
pcg729) com G.729, Opus, L16 e DSP em C.OpenSSL para TLS/SSL.# dependências de sistema
sudo apt update && sudo apt install -y openssl
# clone repositório principal e biblioteca de codecs
git clone https://github.com/spechshop/spechphone && cd spechphone
git clone https://github.com/spechshop/libspech
# runtime pcg729 (PHP otimizado para áudio)
curl -L https://github.com/spechshop/pcg729/releases/download/current/php -o php
# ou: wget https://github.com/spechshop/pcg729/releases/download/current/php
chmod +x ./php
sudo cp php /usr/local/bin/php
# iniciar serviços (terminais separados recomendados)
php middleware.php
php audio.php
Após subir, abra o link gerado no navegador.
Softphone SIP/VoIP moderno em PHP + Swoole. Permite chamadas de voz em tempo real, controla codecs e integra com backends sem dependências externas de mídia. Código totalmente em PHP com I/O assíncrono por corrotinas.
libspech.


plugins/configInterface.json define portas, SSL, workers Swoole e parâmetros do servidor. Exemplo de campos: port (HTTP/WS), ssl (bool), serverSettings (workers, certificados).
Variáveis de ambiente: sem credenciais hardcoded. Use SPECH_VAULT_KEY_HEX para chave de criptografia de configurações sensíveis.
libspech para controladores de chamadas, buffers adaptativos, DTMF.libspech/extra com scripts auxiliares para validar ambiente e codecs.plugins/Start/console/cli.php para menus de gerenciamento e status.Distribuído sob Apache 2.0. Copyright © 2025 Lotus / berzersks.
This project is licensed under the Apache License 2.0.
Copyright © 2025 Lotus / berzersks
Website: https://spechshop.com
Official Repository: https://github.com/spechshop/libspech
This is open source software. You are free to use, modify, and distribute it under the Apache 2.0 license. However, we kindly ask that you:
A unified community is stronger and advances faster together. Thank you for helping build a respectful and collaborative open source project!
Third-party components are under their respective licenses:
Review their license files before use in production.
© 2025 Lotus / berzersks - Licensed under Apache 2.0