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
- FFmpeg: Required for decoding/encoding audio and video streams. Ensure
ffmpegis installed and available in your systemPATH, or specify its custom binary path viaffmpegPath. - WebRTC: For WebRTC peer relay transport, install
@roamhq/wrtc:
- npm
- Yarn
- pnpm
npm install @roamhq/wrtc
yarn add @roamhq/wrtc
pnpm add @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
| Option | Type | Default | Description |
|---|---|---|---|
durationMs | number | 120000 | Total call duration in ms before auto-hangup (0 for unlimited). |
audioSource | string | 'silence' | Path to audio or video file to stream. |
playlist | string[] | [] | Array of audio files to queue and play sequentially. |
endWhenQueueEmpty | boolean | true | Automatically end the call when audio playlist finishes. |
idleGraceMs | number | 0 | Delay before hanging up after queue becomes empty. |
audioStartDelayMs | number | 1800 | Delay in ms after call connects before feeding audio. |
video / isVideo | boolean | false | Enable video calling mode. |
videoSource | string | 'black' | Path to MP4/MKV video file to stream. |
videoPlaylist | string[] | [] | Array of video files to queue and play sequentially. |
screenShare | boolean | false | Enable screen sharing video mode. |