Pular para o conteúdo principal
@zapo-js/voip é o plugin oficial de chamadas de voz para zapo-js. Ele se apoia no sistema de plugins, expõe um WaVoipCoordinator em client.voip e emite eventos voip_* no host client.
Apenas mídia de áudio está implementada. Você pode marcar uma chamada como vídeo no signaling (veja CallOfferOptions.isVideo), mas nenhuma mídia de vídeo é codificada ou transportada.

Instalação

O package tem três peer dependencies, todas obrigatórias em runtime:
  • @roamhq/wrtc (>= 0.10.0) — fornece SCTP para o transporte de relay.
  • libmlow-wasm (^0.1.1) — perfil Opus do WhatsApp, compilado para WebAssembly. Sem etapa de build nativo.
  • zapo-js (^1.0.0) — o host client.
Alguns métodos também precisam de um binário ffmpeg no PATH:
  • loadAudio decodifica o arquivo de áudio via ffmpeg.
  • Streamar áudio com feedLiveAudio a partir de um arquivo igualmente precisa de ffmpeg para produzir chunks Float32Array 16 kHz mono.

Conecte o plugin

Adicione voipPlugin() em WaClientOptions.plugins. O coordinator aparece em client.voip e os eventos voip_* ficam disponíveis em client.on.

Opções do plugin

maxConcurrentCalls
number
padrão:"1"
Máximo de chamadas simultâneas não-ended (ringing, connecting ou active). Aumente para habilitar multi-call em paralelo. startCall de saída rejeita, e chamadas de entrada chegam com acceptBlocked: true, quando o limite é atingido.
logLevel
LogLevel
Log level mínimo para o (verboso) plugin de VoIP. Por padrão usa o nível do host client; defina-o para limitar diagnósticos de forma independente — por exemplo 'warn' para manter o ruído do VoIP fora de um host logger em trace.

A superfície do coordinator

Iniciando uma chamada

peerJid
string
obrigatório
JID bare ou de dispositivo a chamar.
isVideo
boolean
padrão:"false"
Marca a chamada como vídeo nas stanzas de signaling. O encode/transport de mídia de vídeo não está implementado; apenas mídia de áudio trafega.
audioFile
string
Arquivo de áudio a pré-carregar e tocar assim que a chamada conectar (precisa de ffmpeg). Equivalente a chamar loadAudio depois que a chamada ficar ativa.
peerDevices
string[]
JIDs explícitos de dispositivo do peer para tocar; omita para resolvê-los automaticamente.
startCall resolve com o id da nova chamada uma vez que a oferta é enviada. O progresso então chega via voip_call_state. Rejeita quando você está no limite de maxConcurrentCalls ou se a oferta falha em enviar.

Atendendo e encerrando

  • acceptCall(callId) — aceita uma chamada de entrada tocando. Lança se callId for desconhecido ou não estiver em um estado aceitável.
  • rejectCall(callId, reason?) — rejeita uma chamada de entrada tocando (reason padrão Declined). Envia a stanza de reject e em seguida desmonta a chamada.
  • endCall(callId, reason?) — encerra uma chamada ativa ou conectando (reason padrão UserEnded). No-op se a chamada for desconhecida ou já estiver encerrada.

Áudio de saída pré-carregado

Decodifica audioPath via ffmpeg e o enfileira como o áudio de saída, tocado assim que a chamada ficar ativa. Lança se o arquivo estiver ausente ou se ffmpeg não estiver disponível. Para uma fonte ilimitada ou ao vivo (stream de TTS, captura contínua) use modo de áudio externo — veja abaixo.

Mute

Áudio ao vivo de saída (modo externo)

Para fontes que você não consegue pré-carregar — TTS streaming, captura em tempo real — coloque a chamada em modo de áudio externo e bombeie samples conforme chegam. O plugin os bufferiza por meio de um jitter buffer limitado e cria watermarks no buffer de modo que um produtor respeitoso nunca perde áudio.
O contrato dos watermarks: pause o feed quando getLiveBufferMs(callId) >= pauseMs, retome quando ele drenar de volta para resumeMs. pauseMs fica abaixo do threshold de descarte interno do engine, então um produtor que o respeita nunca perde áudio. Se você ignorá-lo, os samples mais antigos são descartados no overflow.

Snapshots

CallInfo captura tudo que você precisa para renderizar UI: callId, peerJid, direction, mediaType, createdAt, callerPn, mais um objeto stateData (state, audioMuted, videoOff, connectedAt, endReason, durationSecs, acceptBlocked, …) e getters derivados isInitiator, isActive, isRinging, isEnded, canAccept, canReject, isAcceptBlocked.

Eventos

Adicione voipPlugin() a plugins e estes eventos ficam disponíveis em client.on. Sem o plugin eles não existem no tipo — veja Plugins.

Enums

Todos os valores do enum são strings minúsculas — use as constantes do enum para evitar erros de digitação.

CallState

CallDirection

CallMediaType

EndCallReason

Formato do áudio

  • Sample rate: 16 kHz.
  • Canais: mono.
  • Formato de sample: Float32Array em [-1.0, 1.0].
  • Codec no fio: perfil Opus do WhatsApp, codificado via libmlow-wasm. O payload type RTP é 120 (PayloadType.WhatsAppOpus).
Tanto a entrada (voip_call_inbound_audio) quanto a saída (feedLiveAudio) usam o mesmo formato Float32Array — mantenha seus buffers neste formato e você consegue roteá-los direto.

Exemplo prático

Um auto-accept mínimo de entrada que grava o PCM de entrada em um buffer em memória:
Para um exemplo mais completo — colocando chamadas de saída, playback pré-carregado vs streamado, hangup-after-audio, gravação WAV — veja examples/voip-example.ts na árvore de source do zapo.

Veja também

Sistema de plugins

Como voipPlugin() se encaixa no WaClient e como autorar o seu próprio.

Eventos

A superfície de eventos do host client; eventos voip_* ficam ao lado dos built-ins.

Créditos

O plugin de VoIP foi construído por:
Última modificação em 30 de junho de 2026