<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: spechshop </title>
    <description>The latest articles on Forem by spechshop  (@spechshop).</description>
    <link>https://forem.com/spechshop</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2142329%2F882bfa61-415c-490c-aa52-5ca4aac10662.png</url>
      <title>Forem: spechshop </title>
      <link>https://forem.com/spechshop</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/spechshop"/>
    <language>en</language>
    <item>
      <title>SpechPhone: Softphone SIP Web com PHP (Swoole) e WebSockets 🚀</title>
      <dc:creator>spechshop </dc:creator>
      <pubDate>Tue, 06 Jan 2026 10:01:01 +0000</pubDate>
      <link>https://forem.com/spechshop/spechphone-softphone-sip-web-com-php-swoole-e-websockets-527o</link>
      <guid>https://forem.com/spechshop/spechphone-softphone-sip-web-com-php-swoole-e-websockets-527o</guid>
      <description>&lt;p&gt;Olá, comunidade! 👋&lt;/p&gt;

&lt;p&gt;Estou animado para compartilhar o status atual do &lt;strong&gt;SpechPhone&lt;/strong&gt;, um softphone SIP web open-source que desafia o convencional ao não utilizar WebRTC. Em vez disso, utilizamos o poder do &lt;strong&gt;PHP com Swoole&lt;/strong&gt; no backend para processamento de mídia em tempo real e &lt;strong&gt;WebSockets&lt;/strong&gt; para o transporte de áudio PCM.&lt;/p&gt;

&lt;p&gt;O projeto está evoluindo rapidamente e gostaria de mostrar como está a interface e as funcionalidades disponíveis hoje.&lt;/p&gt;

&lt;h2&gt;
  
  
  📱 Interface Principal (Dialer)
&lt;/h2&gt;

&lt;p&gt;A interface de chamada foi projetada para ser intuitiva e responsiva. Temos um discador completo com feedback visual, controles de volume independentes para microfone e retorno de áudio, além de indicadores de nível de sinal (VU meters).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqogrpz83qevxyt1t871u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqogrpz83qevxyt1t871u.png" alt="Interface de Chamada" width="800" height="388"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Tela principal: Discador, controles de chamada e monitoramento de áudio.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Bastidores Técnicos:&lt;/strong&gt;&lt;br&gt;
O front-end interage com handlers específicos para gerenciar o estado da chamada:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;startCall.php&lt;/code&gt;&lt;/strong&gt;: Ao discar, este handler cria uma corrotina Swoole dedicada que gerencia toda a sinalização SIP (INVITE) e aloca portas UDP dinâmicas para o RTP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;checkCall.php&lt;/code&gt;&lt;/strong&gt;: Atua como um &lt;em&gt;heartbeat&lt;/em&gt;, sincronizando o status da UI (timer, botões) com a corrotina no backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;dtmf.php&lt;/code&gt;&lt;/strong&gt;: O envio de tons utiliza o padrão RFC 2833, injetando eventos diretamente no stream RTP via PHP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;hangUpCall.php&lt;/code&gt;&lt;/strong&gt;: Garante o encerramento limpo (BYE) e a liberação imediata dos recursos de áudio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📄 plugins/Message/handlers/startCall.php:44&lt;/p&gt;


&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Server&lt;/span&gt; &lt;span class="nv"&gt;$socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$fd&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;?bool&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
   &lt;span class="nv"&gt;$vault&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\spechphoneVault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/data/spechphone/devices.vault'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SPECH_VAULT_KEY_HEX'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
   &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🎧 Suporte Avançado a Codecs
&lt;/h2&gt;

&lt;p&gt;Uma das grandes vantagens da nossa arquitetura é o controle total sobre a transcodificação. O SpechPhone suporta nativamente uma grande variedade de codecs, processados diretamente no backend PHP.&lt;/p&gt;

&lt;p&gt;Na aba de Áudio, é possível visualizar e selecionar os codecs disponíveis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;G.729 &amp;amp; Opus:&lt;/strong&gt; Para alta compressão e qualidade.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;PCMA/PCMU:&lt;/strong&gt; Padrões clássicos G.711.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Wideband:&lt;/strong&gt; G.722, Speex, L16 (até 48kHz).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;GSM &amp;amp; iLBC:&lt;/strong&gt; Para cenários de banda estreita.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0jzgd7n9c7gaqhekaxz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0jzgd7n9c7gaqhekaxz.png" alt="Página de seleção de codecs" width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Painel de Codecs: No final todos se tornarão PCM devolvido para o browser.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Bastidores Técnicos:&lt;/strong&gt;&lt;br&gt;
A mágica acontece na integração entre o &lt;code&gt;startCall.php&lt;/code&gt; e o servidor de áudio &lt;code&gt;audio.php&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline de Áudio (Sem WebRTC)&lt;/strong&gt;: O navegador envia/recebe PCM raw via WebSocket (porta 8888) para o &lt;code&gt;audio.php&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bridge UDP&lt;/strong&gt;: O &lt;code&gt;audio.php&lt;/code&gt; encaminha esse áudio via UDP local (porta 9600) para a corrotina da chamada.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transcoding Nativo&lt;/strong&gt;: O PHP recebe o PCM, aplica a codificação selecionada (ex: comprime para G.729 ou Opus) usando a biblioteca &lt;code&gt;libspech&lt;/code&gt; e envia para o tronco SIP. O processo inverso (decodificação) ocorre na chegada dos pacotes RTP, garantindo compatibilidade universal sem depender de codecs do browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📄 audio.php:54&lt;/p&gt;


&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$udp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Swoole\Coroutine\Socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;AF_INET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SOCK_DGRAM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$udp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🎧 Servidor UDP aguardando pacotes em 9600...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ⚙️ Configuração SIP Simplificada
&lt;/h2&gt;

&lt;p&gt;Conectar-se ao seu servidor VoIP (Asterisk, FreeSWITCH, Opensips) é simples. A tela de configurações permite definir o servidor, credenciais de autenticação e o codec preferencial para o tronco SIP.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F69dyr3zcq6q3dwir7esj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F69dyr3zcq6q3dwir7esj.png" alt="Configurações SIP" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Configuração da conta SIP e preferências de conexão.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Bastidores Técnicos:&lt;/strong&gt;&lt;br&gt;
A segurança e a validação são prioridades no handler &lt;strong&gt;&lt;code&gt;saveConfig.php&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validação Ativa&lt;/strong&gt;: Antes de salvar, o sistema instancia um &lt;code&gt;trunkController&lt;/code&gt; e tenta realizar um registro real (SIP REGISTER) no servidor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feedback Imediato&lt;/strong&gt;: Se as credenciais estiverem erradas, o usuário é notificado na hora, evitando configurações "quebradas".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistência Segura&lt;/strong&gt;: Apenas após o sucesso (200 OK), os dados são criptografados e armazenados no &lt;code&gt;devices.vault&lt;/code&gt;. O handler &lt;strong&gt;&lt;code&gt;register.php&lt;/code&gt;&lt;/strong&gt; pode então ser usado para manter a sessão ativa (keep-alive).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📄 plugins/Message/handlers/saveConfig.php:103&lt;/p&gt;


&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$trunkController&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$socket&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
       &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'notify'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="s1"&gt;'data'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
           &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'bg-danger text-white'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Registro falhou, verifique as credenciais fornecidas"&lt;/span&gt;
       &lt;span class="p"&gt;]&lt;/span&gt;
   &lt;span class="p"&gt;]));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;




&lt;p&gt;O projeto está em fase &lt;strong&gt;Beta&lt;/strong&gt; e o desenvolvimento continua ativo, focado agora em aprimorar a estabilidade e implementar o recebimento de chamadas.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Confira o código e contribua no GitHub:&lt;/strong&gt; &lt;a href="https://github.com/spechshop/spechphone" rel="noopener noreferrer"&gt;https://github.com/spechshop/spechphone&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fiquem ligados para mais novidades! 🚀&lt;/p&gt;

</description>
      <category>voip</category>
      <category>sip</category>
      <category>softphone</category>
      <category>php</category>
    </item>
    <item>
      <title>VOIP Calls + Resample + PHP</title>
      <dc:creator>spechshop </dc:creator>
      <pubDate>Sun, 04 Jan 2026 04:17:39 +0000</pubDate>
      <link>https://forem.com/spechshop/voip-calls-resample-php-ifg</link>
      <guid>https://forem.com/spechshop/voip-calls-resample-php-ifg</guid>
      <description>&lt;h2&gt;
  
  
  VoIP Real-Time e Áudio 48 kHz no PHP: Guia Hands-On com Swoole
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota importante sobre arquitetura:&lt;/strong&gt; O SpechPhone que vamos explorar funciona de um jeito diferente do que você talvez esteja acostumado. Aqui, subimos &lt;strong&gt;dois servidores PHP&lt;/strong&gt; (&lt;code&gt;middleware.php&lt;/code&gt; e &lt;code&gt;audio.php&lt;/code&gt;) que entregam áudio via &lt;strong&gt;RTP no backend + WebSocket (PCM) pro browser&lt;/strong&gt; — sem passar pelo caminho tradicional do &lt;strong&gt;Asterisk/AGI&lt;/strong&gt;. É uma abordagem mais direta que facilita a compreensão do fluxo completo.&lt;br&gt;
Referência: &lt;a href="https://github.com/spechshop/spechphone/blob/volume-dev/README.md" rel="noopener noreferrer"&gt;https://github.com/spechshop/spechphone/blob/volume-dev/README.md&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  O que você vai construir (de verdade)
&lt;/h2&gt;

&lt;p&gt;Ao final deste guia, você terá nas mãos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Um softphone web funcional&lt;/strong&gt; que faz SIP/RTP em tempo real, com interface no browser via WebSocket
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Um pipeline de mídia completo&lt;/strong&gt; onde o backend recebe RTP, decodifica e envia PCM em chunks pro cliente
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uma implementação prática&lt;/strong&gt; de I/O assíncrono com corrotinas por sessão&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E o melhor: você vai entender cada peça desse quebra-cabeça.&lt;/p&gt;

&lt;h2&gt;
  
  
  1) Entendendo a arquitetura
&lt;/h2&gt;

&lt;p&gt;Vamos começar pelo básico: como tudo isso se encaixa? O SpechPhone é construído sobre &lt;strong&gt;PHP + Swoole&lt;/strong&gt;, trabalhando com &lt;strong&gt;mídia em RTP/UDP&lt;/strong&gt; no backend e &lt;strong&gt;PCM via WebSocket&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;Referência: &lt;a href="https://github.com/spechshop/spechphone/blob/volume-dev/README.md" rel="noopener noreferrer"&gt;https://github.com/spechshop/spechphone/blob/volume-dev/README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Os dois pilares da aplicação:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;middleware.php&lt;/code&gt;&lt;/strong&gt;: servidor HTTP/WebSocket para interface web e controle de chamadas + servidor UDP para sinalização SIP
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;audio.php&lt;/code&gt;&lt;/strong&gt;: servidor HTTP/WebSocket + UDP que recebe streams RTP decodificados, mixa múltiplos canais de áudio e distribui via WebSocket para os clientes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cada um tem seu papel bem definido, e você vai ver como eles conversam entre si.&lt;/p&gt;

&lt;p&gt;Referência: &lt;a href="https://github.com/spechshop/spechphone" rel="noopener noreferrer"&gt;https://github.com/spechshop/spechphone&lt;/a&gt; (seção "Directory Structure" do README)&lt;/p&gt;

&lt;h2&gt;
  
  
  2) O segredo do Swoole: corrotinas e I/O assíncrono
&lt;/h2&gt;

&lt;p&gt;O conceito fundamental aqui é &lt;strong&gt;I/O assíncrono com corrotinas&lt;/strong&gt;. Independente da linguagem, quando você domina esse padrão, consegue lidar com operações em tempo real de forma eficiente.&lt;/p&gt;

&lt;p&gt;O SpechPhone abre uma coroutine dedicada pra cada &lt;code&gt;trunkController&lt;/code&gt;, 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.&lt;/p&gt;

&lt;p&gt;Referência: &lt;a href="https://github.com/spechshop/spechphone/blob/volume-dev/README.md" rel="noopener noreferrer"&gt;https://github.com/spechshop/spechphone/blob/volume-dev/README.md&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Detalhe importante:&lt;/strong&gt; Estamos falando de &lt;strong&gt;Swoole&lt;/strong&gt; (a extensão/runtime oficial), não OpenSwoole. São projetos diferentes, então fique atento na hora de instalar.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3) Mão na massa: subir o SpechPhone localmente
&lt;/h2&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# deps&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; openssl

&lt;span class="c"&gt;# repo + lib&lt;/span&gt;
git clone https://github.com/spechshop/spechphone &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;spechphone
git clone https://github.com/spechshop/libspech

&lt;span class="c"&gt;# runtime php otimizado (pcg729)&lt;/span&gt;
curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/spechshop/pcg729/releases/download/current/php &lt;span class="nt"&gt;-o&lt;/span&gt; php
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ./php
&lt;span class="nb"&gt;sudo cp &lt;/span&gt;php /usr/local/bin/php

&lt;span class="c"&gt;# start (em terminais separados)&lt;/span&gt;
php middleware.php
php audio.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Referência do trecho acima: &lt;a href="https://github.com/spechshop/spechphone/blob/volume-dev/README.md" rel="noopener noreferrer"&gt;https://github.com/spechshop/spechphone/blob/volume-dev/README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dica prática:&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  4) Áudio 48 kHz: onde entra (de verdade) e por que isso importa
&lt;/h2&gt;

&lt;p&gt;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ê &lt;strong&gt;sente&lt;/strong&gt; na primeira chamada.&lt;/p&gt;

&lt;p&gt;O SpechPhone trabalha com duas peças que se complementam:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Oferta de Opus/48kHz via SDP&lt;/strong&gt; no &lt;code&gt;trunkController&lt;/code&gt; (libspech). É aqui que você diz pro outro lado: "ei, eu falo Opus em alta qualidade":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Oferecer codec Opus em SDP&lt;/span&gt;
&lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;mountLineCodecSDP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'opus/48000/2'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Referência do snippet: &lt;a href="https://github.com/spechshop/libspech/blob/spech/README.md" rel="noopener noreferrer"&gt;https://github.com/spechshop/libspech/blob/spech/README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Decodificação dinâmica + PCM em chunks pro browser&lt;/strong&gt;. Os pacotes RTP chegam, são processados pela &lt;code&gt;libspech&lt;/code&gt; (usando funções nativas via runtime &lt;code&gt;pcg729&lt;/code&gt;), e o áudio decodificado é entregue ao controlador WebSocket em PCM puro, pronto pra ser consumido no browser via &lt;code&gt;webkitAudioContext&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Referência: &lt;a href="https://github.com/spechshop/spechphone/blob/volume-dev/README.md" rel="noopener noreferrer"&gt;https://github.com/spechshop/spechphone/blob/volume-dev/README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Em outras palavras: você negocia Opus, recebe RTP, decodifica e entrega PCM. Simples, direto e poderoso.&lt;/p&gt;

&lt;h2&gt;
  
  
  5) Um exemplo mínimo de chamada — vendo corrotinas e eventos na prática
&lt;/h2&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="n"&gt;libspech\Sip\trunkController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="s1"&gt;'plugins/autoloader.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;\Swoole\Coroutine\run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SIP_USERNAME'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SIP_PASSWORD'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$domain&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SIP_DOMAIN'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$host&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;gethostbyname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$phone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;trunkController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5060&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Falha no registro'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Oferecer codec Opus em SDP (48 kHz)&lt;/span&gt;
    &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;mountLineCodecSDP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'opus/48000/2'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;onRinging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Tocando...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;onAnswer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;trunkController&lt;/span&gt; &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Atendido. Recebendo mídia...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;receiveMedia&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;\Swoole\Coroutine&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;onReceiveAudio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pcmData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;trunkController&lt;/span&gt; &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Recebido: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pcmData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;" bytes&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;onHangup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;trunkController&lt;/span&gt; &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Chamada finalizada&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nv"&gt;$phone&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'5511999999999'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Referência (onde esse exemplo está documentado): &lt;a href="https://github.com/spechshop/libspech/blob/spech/README.md" rel="noopener noreferrer"&gt;https://github.com/spechshop/libspech/blob/spech/README.md&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Referência do exemplo completo: &lt;a href="https://github.com/spechshop/libspech/blob/spech/example.php" rel="noopener noreferrer"&gt;https://github.com/spechshop/libspech/blob/spech/example.php&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O que está acontecendo aqui?&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  6) Três exercícios práticos (pra você realmente aprender fazendo)
&lt;/h2&gt;

&lt;p&gt;Ler código é legal, mas mexer nele é melhor. Aqui vão três experimentos rápidos pra você sentir como tudo funciona:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Troque o codec e compare&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mude &lt;code&gt;opus/48000/2&lt;/code&gt; para &lt;code&gt;L16/8000&lt;/code&gt; (o README do libspech também menciona L16/8000) e compare o que você recebe em &lt;code&gt;onReceiveAudio&lt;/code&gt;. Você vai notar diferença no tamanho dos chunks e na qualidade do áudio.&lt;/li&gt;
&lt;li&gt;Referência: &lt;a href="https://github.com/spechshop/libspech/blob/spech/README.md" rel="noopener noreferrer"&gt;https://github.com/spechshop/libspech/blob/spech/README.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2) Instrumente o fluxo de PCM&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deixe o log de &lt;code&gt;strlen($pcmData)&lt;/code&gt; 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.&lt;/li&gt;
&lt;li&gt;Referência: &lt;a href="https://github.com/spechshop/libspech/blob/spech/README.md" rel="noopener noreferrer"&gt;https://github.com/spechshop/libspech/blob/spech/README.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3) Faça a UI reagir aos eventos&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;li&gt;Referência: &lt;a href="https://github.com/spechshop/spechphone/blob/volume-dev/README.md" rel="noopener noreferrer"&gt;https://github.com/spechshop/spechphone/blob/volume-dev/README.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links principais (pra você não se perder)
&lt;/h2&gt;

&lt;p&gt;Aqui estão todos os recursos que você vai precisar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SpechPhone (repo): &lt;a href="https://github.com/spechshop/spechphone" rel="noopener noreferrer"&gt;https://github.com/spechshop/spechphone&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SpechPhone (README, branch volume-dev): &lt;a href="https://github.com/spechshop/spechphone/blob/volume-dev/README.md" rel="noopener noreferrer"&gt;https://github.com/spechshop/spechphone/blob/volume-dev/README.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;libspech (repo): &lt;a href="https://github.com/spechshop/libspech" rel="noopener noreferrer"&gt;https://github.com/spechshop/libspech&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;libspech (README, branch spech): &lt;a href="https://github.com/spechshop/libspech/blob/spech/README.md" rel="noopener noreferrer"&gt;https://github.com/spechshop/libspech/blob/spech/README.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;libspech (example.php): &lt;a href="https://github.com/spechshop/libspech/blob/spech/example.php" rel="noopener noreferrer"&gt;https://github.com/spechshop/libspech/blob/spech/example.php&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;pcg729 (runtime release "current/php"): &lt;a href="https://github.com/spechshop/pcg729/releases/tag/current" rel="noopener noreferrer"&gt;https://github.com/spechshop/pcg729/releases/tag/current&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;E agora?&lt;/strong&gt; 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! 🚀&lt;br&gt;
&lt;a href="https://dev.tourl"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>voip</category>
      <category>opensource</category>
      <category>rtp</category>
    </item>
  </channel>
</rss>
