VOIP 通话 + 重采样 + PHP
Source: Dev.to
概览
本指南演示如何使用 Swoole 在 PHP 中实现实时 VoIP 通话,音频采样率为 48 kHz。SpechPhone 项目提供了一个在浏览器中运行的软电话,它在后端完成 SIP/RTP 处理,并通过 WebSocket 将 PCM 音频传递给浏览器,无需依赖 Asterisk/AGI。
架构
- middleware.php – 提供 UI 并控制通话的 HTTP/WebSocket 服务器。
- audio.php – 负责混音并流式发送音频包的音频服务器。
媒体流的工作流程如下:
- 后端接收 RTP 包。
- libspech 库使用
pcg729运行时解码这些包。 - 解码后的音频以 PCM 块的形式发送到 WebSocket 客户端,浏览器使用
AudioContext播放。
实现实时性能的关键是使用 Swoole 协程,它们允许在不阻塞主进程的情况下同时处理多个通话。
前置条件与安装
# 系统依赖
sudo apt update && sudo apt install -y openssl
# 克隆仓库
git clone https://github.com/spechshop/spechphone && cd spechphone
git clone https://github.com/spechshop/libspech
# 优化的 PHP 运行时 (pcg729)
curl -L https://github.com/spechshop/pcg729/releases/download/current/php -o php
chmod +x ./php
sudo cp php /usr/local/bin/php
在不同的终端中启动两个服务:
php middleware.php
php audio.php
重要: 项目使用 Swoole 扩展(而非 OpenSwoole)。
Opus / 48 kHz 编解码器提供
trunkController 可以通过 SDP 提供不同的编解码器。要使用 48 kHz 的 Opus:
$phone->mountLineCodecSDP('opus/48000/2');
这会使 RTP 流协商为 48 kHz,从而实现高质量音频。
完整的 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');
});
示例已记录于:
建议的实验
1. 更换提供的编解码器
将编解码器提供行改为 L16/8000,并比较 onReceiveAudio 中收到的字节量。
$phone->mountLineCodecSDP('L16/8000');
2. 对 PCM 流进行仪表化
记录每个收到的 PCM 块的大小(strlen),观察大小、频率和抖动的模式。
3. 更新 UI
实现一个通过 WebSocket 的 “thin” 客户端,响应 ringing、answered 和 hangup 事件,并在页面上显示简短的提示信息。
主要链接
- SpechPhone(仓库) –
- SpechPhone README(volume-dev 分支) –
- libspech(仓库) –
- libspech README(spech 分支) –
- libspech example.php –
- pcg729 运行时发布 –