Skip to main content

Voice & Video Calls (VoIP)

Baileys (UDMODZ Edition) provides first-class support for WhatsApp VoIP voice and video calls powered by the native WhatsApp WebAssembly engine (whatsapp.wasm), WebRTC relay transport, and ffmpeg audio/video feeders.

Prerequisites​

  1. FFmpeg: Required for decoding/encoding audio and video streams. Ensure ffmpeg is installed and available in your system PATH, or specify its custom binary path via ffmpegPath.
  2. WebRTC: For WebRTC peer relay transport, install @roamhq/wrtc:
npm install @roamhq/wrtc

Quick Start​

You can initiate, answer, and manage calls directly on your existing socket via conn.call:

import makeWASocket from 'amiudmodz'

const conn = makeWASocket({ /* ...your auth & config */ })

conn.ev.on('connection.update', async ({ connection }) => {
if (connection === 'open') {
// 1. Audio Call
const voiceCall = await conn.call.voice('1234567890', {
audioSource: './media/music.mp3',
durationMs: 60000 // auto-hangup after 60s
})

// 2. Video Call
const videoCall = await conn.call.video('1234567890', {
videoSource: './media/sample.mp4',
audioSource: './media/music.mp3',
durationMs: 120000
})
}
})

Making Calls​

1-on-1 Voice Call​

const call = await conn.call.voice('1234567890', {
audioSource: 'podcast.mp3',
durationMs: 180000 // 3 minutes
})

1-on-1 Video Call​

const call = await conn.call.video('1234567890', {
videoSource: 'video.mp4',
audioSource: 'audio.mp3',
durationMs: 180000
})

Unified conn.call API​

const call = await conn.call('1234567890', {
video: true, // or video: false for voice call
videoSource: 'clip.mp4',
audioSource: 'audio.mp3',
durationMs: 60000
})

Group Calls​

Initiate group calls to all members or selected participants in a group:

const groupCall = await conn.call.group('[email protected]', {
video: true,
videoSource: 'announcement.mp4',
audioSource: 'announcement.mp3',
participants: ['[email protected]', '[email protected]'] // optional: filter participants
})

Joining Ongoing Group Calls​

When your bot receives a group call invite/offer, you can join the call directly:

conn.ev.on('call', async (callEvents) => {
for (const callEvent of callEvents) {
if (callEvent.status === 'offer' && callEvent.isGroup) {
const call = await conn.call.join({
callId: callEvent.id,
callCreatorJid: callEvent.from,
groupJid: callEvent.chatId
}, {
video: true,
videoSource: 'video.mp4',
audioSource: 'audio.mp3'
})
}
}
})

Answering Incoming Calls​

Listen for incoming call offers using the call event, then answer using conn.call.accept():

conn.ev.on('call', async (callEvents) => {
for (const call of callEvents) {
if (call.status === 'offer') {
console.log(`Incoming call from: ${call.from}`)

// Accept call with mic enabled
await conn.call.accept(true)
}
}
})

Media Feeders & Playlists​

Baileys features built-in AudioFeeder and VideoFeeder modules that transcode local files, remote streams, or synthetic sources in real time using ffmpeg and pace them to the VoIP engine.

Audio Playlist & Queue Management​

const call = await conn.call.voice('1234567890', {
playlist: [
'intro.mp3',
'track1.mp3',
'track2.mp3'
],
endWhenQueueEmpty: true, // Hang up automatically when playlist finishes
idleGraceMs: 3000, // Wait 3s before hanging up on queue finish
audioStartDelayMs: 1800 // Pacing delay after connection before starting audio
})

// Dynamic runtime queue manipulation:
call.enqueue('bonus_track.mp3') // Add track to queue
call.enqueue(['track4.mp3', 'track5.mp3'])
call.skip() // Skip current track
call.play('urgent_message.mp3') // Clear queue and play immediately

console.log('Now playing:', call.nowPlaying())
console.log('Tracks in queue:', call.queued())

Video Playlist & Queue Management​

const call = await conn.call.video('1234567890', {
videoPlaylist: [
'clip1.mp4',
'clip2.mp4'
],
audioSource: 'soundtrack.mp3'
})

// Dynamic runtime video queue manipulation:
call.enqueueVideo('clip3.mp4')
call.skipVideo()
call.playVideo('live_stream.mp4')

console.log('Now playing video:', call.nowPlayingVideo())
console.log('Videos in queue:', call.queuedVideo())

Screen Sharing​

You can start calls with screen sharing enabled, or toggle it dynamically during an active video call:

const call = await conn.call.video('1234567890', {
screenShare: true,
videoSource: 'presentation.mp4',
audioSource: 'narration.mp3'
})

// Dynamic toggle:
call.startScreenShare()
console.log('Is screen sharing:', call.isScreenShare())
call.stopScreenShare()

ActiveCall Events & Lifecycle​

Every call returns an ActiveCall instance (an EventEmitter) providing real-time event updates:

const call = await conn.call.video('1234567890', {
videoSource: 'video.mp4',
audioSource: 'audio.mp3'
})

// Ringing state
call.on('ringing', () => {
console.log('Call is ringing on recipient phone...')
})

// Call accepted and media stream established
call.on('connected', () => {
console.log('Call connected!')
})

// Audio track lifecycle
call.on('track', (trackPath) => {
console.log('Now playing audio track:', trackPath)
})

call.on('trackend', (trackPath) => {
console.log('Finished audio track:', trackPath)
})

// Video track lifecycle
call.on('videotrack', (videoPath) => {
console.log('Now playing video track:', videoPath)
})

call.on('videotrackend', (videoPath) => {
console.log('Finished video track:', videoPath)
})

// Playlist idle (all queued tracks played)
call.on('idle', () => {
console.log('Queue completed!')
})

// Incoming audio from the peer (Float32Array PCM chunks)
call.on('audio', (pcmBuffer) => {
// Can be piped to a recorder or speech-to-text engine
})

// Call termination
call.on('ended', (reason) => {
console.log(`Call terminated. Reason: ${reason}`)
// Possible reasons: 'local_end', 'remote_end', 'ended', 'disconnect', etc.
})

// Controls:
call.mute(true) // Mute microphone
call.mute(false) // Unmute microphone
call.end() // Hang up call
await call.waitForEnd() // Wait until the call completes

Standalone VoipClient Usage​

If you prefer to manage the VoIP client independently of the socket wrapper:

import { makeVoipClient, VoipClient } from 'amiudmodz'

// Create & connect client with existing logged-in socket:
const voip = await makeVoipClient(sock, {
debug: true,
ffmpegPath: '/usr/bin/ffmpeg' // custom ffmpeg path if needed
})

const call = await voip.call('1234567890', {
video: true,
videoSource: 'test.mp4'
})

// Clean up when done:
voip.disconnect()

Call Options Reference​

OptionTypeDefaultDescription
durationMsnumber120000Total call duration in ms before auto-hangup (0 for unlimited).
audioSourcestring'silence'Path to audio or video file to stream.
playliststring[][]Array of audio files to queue and play sequentially.
endWhenQueueEmptybooleantrueAutomatically end the call when audio playlist finishes.
idleGraceMsnumber0Delay before hanging up after queue becomes empty.
audioStartDelayMsnumber1800Delay in ms after call connects before feeding audio.
video / isVideobooleanfalseEnable video calling mode.
videoSourcestring'black'Path to MP4/MKV video file to stream.
videoPlayliststring[][]Array of video files to queue and play sequentially.
screenSharebooleanfalseEnable screen sharing video mode.